2019年8月15日Coding1253 字约 8 分钟


Laravel 虚拟钱包扩展包

项目地址

https://github.com/MoeCasts/laravel-wallet

自动化测试

Build Status
Scrutinizer Code Quality
Code Coverage
Build Status
Code Intelligence Status

功能

  • HasWallet
  • 充值
  • 取款
  • 兑换
  • 转账
  • 支付
  • 退款

安装

需求

  • PHP 7.0+
  • Laravel 5.5+

通过 composer 安装:

1
composer require moecasts/laravel-wallet

如果你是用 Laravel 的版本 < 5.5,则需要手动将 provide 添加到 config/app.php providers 数组中

1
Moecasts\Laravel\Wallet\WalletServiceProvider,

发布迁移文件:

1
php artisan vendor:publish --tag=wallet-migrations

如果你想修改默认配置,可以运行下列命令发布配置文件后修改:

1
php artisan vendor:publish --tag=wallet-config

数据表迁移:

1
php artisan migrate

最后,添加 TraitUser Model 中:

1
2
3
4
5
6
use Moecasts\Laravel\Wallet\Traits\HasWallets;

class User extends Model
{
use HasWallets;
}

配置

货币类型

你可以设置允许使用的货币以及它们的系数和汇率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
return [
'currencies' => [
'POI',
'COI',
'CNY'
],
'coefficient' => [
'COI' => 100.,
'POI' => 1
],
'exchange' => [
'COI' => [
'POI' => 100,
'CNY' => 1
],
'CNY' => [
'COI' => 1
]
],
];

钱包

你可以按照下列的方式设置默认钱包。

1
2
3
4
5
6
7
8
9
10
return [
'wallet' => [
'table' => 'wallets',
'model' => \Moecasts\Laravel\Wallet\Models\Wallet::class,
'default' => [
'currency' => 'POI'
],
],
];

用法

获取钱包

如果货币在支持列表中,则会返回该货币类型的钱包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$wallet = $user->getWallet($currency)

$wallet->balance

// 返回用户转账记录
$user->transfers
// 返回钱包转账记录
$wallet->transfers

// 返回用户的收支记录
$user->transactions
// 返回钱包的收支记录
$wallet->transactions

充值

1
2
3
4
$wallet->deposit($amount, $meta = [], $confirmed = true)

$wallet->deposit(233)
$wallet->deposit(233, ['description' => 'Deposit Testing'])

取款

1
2
3
4
5
6
7
$wallet->withdraw($amount, $meta = [], $confirmed = true)

$wallet->withdraw(233)
$wallet->withdraw(233, ['description' => 'withdraw Testing'])

// 强制取款(即使余额不足也会生效)
$wallet->forceWithdraw(233)

兑换

添加 exchange 配置到 config/wallet.php.

1
2
3
4
5
6
7
8
9
10
return [
'exchange' => [
// To be exchanged cuurency
'COI' => [
// target currency => (one to be exchanged cuurency = ? target currency)
'POI' => 100,
'CNY' => 1
],
]
];

添加 Exchangeable Interface 到 User Model.

1
2
3
4
5
6
7
8
use Illuminate\Database\Eloquent\Model;
use Moecasts\Laravel\Wallet\Interfaces\Exchangeable;
use Moecasts\Laravel\Wallet\Traits\HasWallets;

class User extends Model implements Exchangeable
{
use HasWallets;
}

之后可以这么用:

1
2
3
4
5
6
7
8
$wallet = $userWallet->getWallet('COI')

// $wallet->exchange(string $currency, float $mouant)
$wallet->exchange('POI', 10)
// This will return null but not exception when it failed.
$wallet->safeExchange('POI', 10)
// This will exchange though balance is not enough
$wallet->forceExchange('POI', 10)

转账

添加 Transferable Interface 到 User Model.

1
2
3
4
5
6
7
use Moecasts\Laravel\Wallet\Interfaces\Transferable;
use Moecasts\Laravel\Wallet\Traits\HasWallets;

class User extends Model implements Transferable
{
use HasWallets;
}

之后可以这么用:

1
2
3
4
5
6
7
8
9
10
$user = User::find(1);
$transferable = User::find(2);

$wallet = $user->getWallet($currency);

// $wallet->transfer(Transferable $transferable,float $amount,?array $meta = [], string $action = 'transfer'))
$wallet->transfer($transferable, 233);

// 当有错误发生时返回 null
$wallet->safeTransfer($transferable, 233);

Transferable 所拥有的同类型货币的钱包将收到货款。

支付

添加 HasWallets Trait 和 ProductItem Model.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use Moecasts\Laravel\Wallet\Interfaces\Product;

class Item extends Model implements Product
{
use HasWallets;

public function canBePaid(string $action = 'paid'): bool
{
return true;
}

public function getProductAmount(string $action = 'paid'): float
{
switch ($action) {
case 'paid':
return 10;
break;

default:
return 23.3;
break;
}
}

public function getProductMeta(string $action = 'PAID'): ?array
{
return [
'title' => 'Paid for #' . $this->id
];
}

public function getReceiptWallet(string $currency): Wallet
{
return $this->getWallet($currency);
}
}

如果你想支付给作者,你可以这样做:

如果你不想让商品拥有钱包则可以移除 HasWallets Trait。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Moecasts\Laravel\Wallet\Interfaces\Product;

class Item extends Model implements Product
{
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}

public function canBePaid(string $action = 'paid'): bool
{
return true;
}

public function getProductAmount(string $action = 'paid'): float
{
switch ($action) {
case 'paid':
return 10;
break;

default:
return 23.3;
break;
}
}

public function getProductMeta(string $action = 'paid'): ?array
{
return [
'title' => 'Paid for #' . $this->id
];
}

public function getReceiptWallet(string $currency): Wallet
{
return $this->author->getWallet($currency);
}
}

之后可以这么用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$user = User::first();
$product = Item::first();

$wallet = $user->getWallet($currency);

// $wallet->pay(Product $item, string $action = 'paid', bool $force = false)
$wallet->pay($item)
$wallet->pay($item, 'read')

// This will return null but not exception when it failed.
$wallet->safePay($item, 'paid')

// return bool
$wallet->paid($item, $action = 'paid')

退款

添加 Refundable Interface 到 Item Model.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
use Moecasts\Laravel\Wallet\Interfaces\Product;
use Moecasts\Laravel\Wallet\Interfaces\Refundable;

class Item extends Model implements Product, Refundable
{
use HasWallets;

public function canBePaid(string $action = 'paid'): bool
{
return true;
}

public function getProductAmount(string $action = 'paid'): float
{
switch ($action) {
case 'paid':
return 10;
break;

default:
return 23.3;
break;
}
}

public function getProductMeta(string $action = 'PAID'): ?array
{
return [
'title' => 'Paid for #' . $this->id
];
}

public function getReceiptWallet(string $currency): Wallet
{
return $this->getWallet($currency);
}
}

之后可以这么用:

1
2
3
4
5
6
// $wallet->refund(Refundable $item, string $action = 'paid')
$wallet->refund($item)
$wallet->refund($item, 'read')

// This will return null but not exception when it failed.
$wallet->safeRefund($item, 'read')

Let’s enjoy coding!