钱包(可提现)付款
parent
2b215a615c
commit
2724be081a
|
|
@ -5,8 +5,16 @@ namespace App\Endpoint\Api\Http\Controllers\Account;
|
|||
use App\Endpoint\Api\Http\Controllers\Controller;
|
||||
use App\Endpoint\Api\Http\Resources\DistributionPreIncomeResource;
|
||||
use App\Endpoint\Api\Http\Resources\WalletLogResource;
|
||||
use App\Exceptions\BizException;
|
||||
use App\Helpers\Paginator as PaginatorHelper;
|
||||
use App\Models\Order;
|
||||
use App\Models\PayLog;
|
||||
use App\Models\WalletLog;
|
||||
use App\Services\PayService;
|
||||
use App\Services\WalletService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class WalletController extends Controller
|
||||
{
|
||||
|
|
@ -51,14 +59,10 @@ class WalletController extends Controller
|
|||
public function walletLogs(Request $request)
|
||||
{
|
||||
$perPage = PaginatorHelper::resolvePerPage('per_page', 20, 50);
|
||||
$walletLogs = $request->user()->wallet?->logs()
|
||||
|
||||
$walletLogs = $request->user()->walletLogs()
|
||||
->latest('id')
|
||||
->simplePaginate($perPage);
|
||||
if (is_null($walletLogs)) {
|
||||
return response()->json([
|
||||
'data'=>[],
|
||||
]);
|
||||
}
|
||||
|
||||
return WalletLogResource::collection($walletLogs);
|
||||
}
|
||||
|
|
@ -71,4 +75,53 @@ class WalletController extends Controller
|
|||
public function balanceLogs(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 钱包付款
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \App\Exceptions\BizException
|
||||
*/
|
||||
public function pay(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'pay_sn' => ['bail', 'required'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($user, $validated) {
|
||||
$payLog = PayLog::where('pay_sn', $validated['pay_sn'])->lockForUpdate()->firstOrFail();
|
||||
|
||||
$payable = $payLog->payable;
|
||||
|
||||
switch (get_class($payable)) {
|
||||
case Order::class:
|
||||
(new WalletService())->changeBalance($user, -$payable->total_amount, WalletLog::ACTION_ORDER_PAY, '订单支付扣款', $payable);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BizException('非法操作');
|
||||
break;
|
||||
}
|
||||
|
||||
(new PayService())->handleSuccess($payLog);
|
||||
});
|
||||
} catch (Throwable $e) {
|
||||
try {
|
||||
(new PayService())->handleFailedByPaySerialNumber($validated['pay_sn'], [
|
||||
'failed_reason' => $e->getMessage(),
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ class WalletLogResource extends JsonResource
|
|||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'remarks' => $this->remarks,
|
||||
'created_at' => $this->created_at->toDateTimeString(),
|
||||
'fee' => $this->fee_format,
|
||||
'remarks' => $this->remarks,
|
||||
'created_at' => $this->created_at->toDateTimeString(),
|
||||
'change_balance' => $this->change_balance,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ Route::group([
|
|||
Route::get('wallet', [WalletController::class, 'index']);
|
||||
Route::get('wallet/distribution-logs', [WalletController::class, 'distributionLogs']);
|
||||
Route::get('wallet/wallet-logs', [WalletController::class, 'walletLogs']);
|
||||
Route::post('wallet/pay', [WalletController::class, 'pay']);
|
||||
|
||||
//银行卡
|
||||
Route::get('user-bank', [UserBankController::class, 'show']);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class PayLog extends Model
|
|||
public const PAY_WAY_WXPAY_JSAPI = 'wxpay_jsapi'; // 微信支付 - JSAPI 支付
|
||||
public const PAY_WAY_WXPAY_MINI = 'wxpay_mini'; // 微信支付 - 小程序支付
|
||||
public const PAY_WAY_WXPAY_H5 = 'wxpay_h5'; // 微信支付 - H5 支付
|
||||
public const PAY_WAY_WALLET = 'wallet'; // 钱包付款(可提现)
|
||||
public const PAY_WAY_BALANCE = 'balance'; // 余额支付
|
||||
public const PAY_WAY_OFFLINE = 'offline'; // 线下支付
|
||||
|
||||
|
|
@ -86,4 +87,14 @@ class PayLog extends Model
|
|||
{
|
||||
return $this->pay_way === static::PAY_WAY_OFFLINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认支付方式是否是钱包付款
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWallet(): bool
|
||||
{
|
||||
return $this->pay_way === static::PAY_WAY_WALLET;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,14 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
|
|||
return $this->hasOne(Wallet::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户的钱包日志
|
||||
*/
|
||||
public function walletLogs()
|
||||
{
|
||||
return $this->hasMany(WalletLog::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户的预收益
|
||||
*/
|
||||
|
|
@ -270,6 +278,22 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此用户的钱包并加行锁
|
||||
*
|
||||
* @return \App\Models\Wallet
|
||||
*/
|
||||
public function lockWallet(): Wallet
|
||||
{
|
||||
if ($wallet = $this->wallet()->lockForUpdate()->first()) {
|
||||
return $wallet;
|
||||
}
|
||||
|
||||
$this->wallet()->create();
|
||||
|
||||
return $this->wallet()->lockForUpdate()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
*
|
||||
|
|
@ -290,8 +314,8 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
|
|||
'inviter_id' => null,
|
||||
'depth' => 1,
|
||||
'path' => '/',
|
||||
])
|
||||
;
|
||||
]
|
||||
);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ class Wallet extends Model
|
|||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'balance' => 0,
|
||||
'total_revenue' => 0,
|
||||
'total_expenses' => 0,
|
||||
'withdrawable' => true,
|
||||
];
|
||||
|
||||
|
|
@ -42,15 +45,6 @@ class Wallet extends Model
|
|||
'withdrawable' => 'bool',
|
||||
];
|
||||
|
||||
/**
|
||||
* 资产日志
|
||||
*
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
return $this->hasMany(WalletLog::class, 'wallet_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置此用户的安全密码
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,13 +7,28 @@ use Illuminate\Database\Eloquent\Model;
|
|||
|
||||
class WalletLog extends Model
|
||||
{
|
||||
public const ACTION_ORDER_PAY = 1;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'loggable_id',
|
||||
'loggable_type',
|
||||
'action',
|
||||
'before_balance',
|
||||
'change_balance',
|
||||
'remarks',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取变动金额
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFeeFormatAttribute()
|
||||
public function getChangeBalanceFormatAttribute()
|
||||
{
|
||||
return Numeric::trimTrailingZero(bcdiv($this->attributes['fee'], 100, 2));
|
||||
return Numeric::trimTrailingZero(bcdiv($this->attributes['change_balance'], 100, 2));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,8 +185,6 @@ class OrderService
|
|||
|
||||
$totalAmount += $shippingFee;
|
||||
|
||||
$totalAmount=0;
|
||||
|
||||
do {
|
||||
// 如果订单号重复,则直接重试
|
||||
try {
|
||||
|
|
@ -783,6 +781,11 @@ class OrderService
|
|||
'trade_type' => WeChatPayService::$tradeTypes[$payLog->pay_way],
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'pay_sn' => $payLog->pay_sn,
|
||||
'pay_way' => $payLog->pay_way,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class PayService
|
|||
public function handleSuccess(PayLog $payLog, array $params = []): PayLog
|
||||
{
|
||||
if (! $payLog->isPending()) {
|
||||
throw new BizException('支付记录状态异常');
|
||||
throw new BizException('支付异常');
|
||||
}
|
||||
|
||||
$payLog->update([
|
||||
|
|
@ -109,7 +109,7 @@ class PayService
|
|||
public function handleFailed(PayLog $payLog, array $params = []): PayLog
|
||||
{
|
||||
if (! $payLog->isPending()) {
|
||||
throw new BizException('支付记录状态异常');
|
||||
throw new BizException('支付异常');
|
||||
}
|
||||
|
||||
$payLog->update([
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Exceptions\BizException;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class WalletService
|
||||
{
|
||||
/**
|
||||
* 变更钱包余额
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param int $changeBalance
|
||||
* @param int $action
|
||||
* @param string|null $remarks
|
||||
* @param mixed $loggable
|
||||
* @return void
|
||||
*/
|
||||
public function changeBalance(User $user, int $changeBalance, int $action, ?string $remarks = null, $loggable = null)
|
||||
{
|
||||
$wallet = $user->lockWallet();
|
||||
|
||||
// 变更前余额
|
||||
$beforeBalance = $wallet->balance;
|
||||
$_balance = abs($changeBalance);
|
||||
|
||||
if ($changeBalance > 0) {
|
||||
// 收入
|
||||
$user->wallet()->update([
|
||||
'balance' => DB::raw("balance + {$_balance}"),
|
||||
'total_revenue' => DB::raw("total_revenue + {$_balance}"),
|
||||
]);
|
||||
} else {
|
||||
// 支出
|
||||
|
||||
if ($wallet->balance < $_balance) {
|
||||
throw new BizException('可提现金额不足');
|
||||
}
|
||||
|
||||
$user->wallet()->update([
|
||||
'balance' => DB::raw("balance - {$_balance}"),
|
||||
'total_expenses' => DB::raw("total_expenses + {$_balance}"),
|
||||
]);
|
||||
}
|
||||
|
||||
$user->walletLogs()->create([
|
||||
'loggable_id' => $loggable?->id,
|
||||
'loggable_type' => $loggable?->getMorphClass(),
|
||||
'before_balance' => $beforeBalance,
|
||||
'change_balance' => $changeBalance,
|
||||
'action' => $action,
|
||||
'remarks' => $remarks,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ class CreateOrderRefundLogsTable extends Migration
|
|||
$table->unsignedBigInteger('amount')->comment('退款金额');
|
||||
$table->tinyInteger('status')->default(0)->comment('状态');
|
||||
$table->string('reason')->nullable()->comment('退款原因');
|
||||
$table->string('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->text('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['order_id', 'after_sale_id']);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CreateDistributionPreIncomeJobsTable extends Migration
|
|||
$table->morphs('jobable');
|
||||
$table->tinyInteger('status')->default(0)->comment('状态');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
$table->string('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->text('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CreateWalletLogsTable extends Migration
|
|||
$table->nullableMorphs('loggable');
|
||||
$table->tinyInteger('action')->comment('操作类型');
|
||||
$table->unsignedBigInteger('before_balance')->default(0)->comment('变更前的余额');
|
||||
$table->bigInteger('fee')->default(0)->comment('变动金额(分)');
|
||||
$table->bigInteger('change_balance')->default(0)->comment('变动余额(分)');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
$table->timestamps();
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class CreatePayLogsTable extends Migration
|
|||
$table->string('out_trade_no')->nullable()->comment('外部交易单号');
|
||||
$table->timestamp('pay_at')->nullable()->comment('支付时间');
|
||||
$table->tinyInteger('status')->comment('状态: 0 待付款, 1 支付成功, 2 支付失败');
|
||||
$table->string('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->text('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue