From e17a2b9357ae2265db2b7c31f2c9947fd950af5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9D=99?= Date: Tue, 12 Apr 2022 14:38:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/WalletToBankLogController.php | 2 +- app/Admin/Forms/WalletToBankLogVerify.php | 56 +++++++++--- app/Console/Commands/WalletToBankCommand.php | 86 +++++++++++++++++++ .../Controllers/YeePayNotifyController.php | 13 +++ app/Endpoint/Callback/routes.php | 3 + app/Enums/WalletToBankLogPayWay.php | 38 ++++++++ app/Enums/WalletToBankLogStatus.php | 9 ++ app/Exceptions/YeePayException.php | 9 ++ app/Models/WalletToBankLog.php | 7 ++ app/Services/YeePayService.php | 82 ++++++++++++++++++ config/services.php | 6 ++ ...y_columns_to_wallet_to_bank_logs_table.php | 38 ++++++++ resources/lang/zh_CN/wallet-to-bank-log.php | 3 + 13 files changed, 339 insertions(+), 13 deletions(-) create mode 100644 app/Console/Commands/WalletToBankCommand.php create mode 100644 app/Endpoint/Callback/Http/Controllers/YeePayNotifyController.php create mode 100644 app/Enums/WalletToBankLogPayWay.php create mode 100644 app/Exceptions/YeePayException.php create mode 100644 app/Services/YeePayService.php create mode 100644 database/migrations/2022_04_12_110138_add_pay_columns_to_wallet_to_bank_logs_table.php diff --git a/app/Admin/Controllers/WalletToBankLogController.php b/app/Admin/Controllers/WalletToBankLogController.php index f14326b6..bb42b499 100644 --- a/app/Admin/Controllers/WalletToBankLogController.php +++ b/app/Admin/Controllers/WalletToBankLogController.php @@ -51,7 +51,7 @@ class WalletToBankLogController extends AdminController $grid->model()->orderBy('created_at', 'desc'); $grid->actions(function (Grid\Displayers\Actions $actions) { - if ($actions->row->status == WalletToBankLogStatus::Pending && Admin::user()->can('dcat.admin.wallet_to_bank_logs.verify')) { + if (in_array($actions->row->status, [WalletToBankLogStatus::Pending, WalletToBankLogStatus::Failed]) && Admin::user()->can('dcat.admin.wallet_to_bank_logs.verify')) { $actions->append(new WalletToBankLogVerify()); } }); diff --git a/app/Admin/Forms/WalletToBankLogVerify.php b/app/Admin/Forms/WalletToBankLogVerify.php index 0d3f4134..9ab00ecd 100644 --- a/app/Admin/Forms/WalletToBankLogVerify.php +++ b/app/Admin/Forms/WalletToBankLogVerify.php @@ -2,6 +2,9 @@ namespace App\Admin\Forms; +use App\Enums\WalletToBankLogPayWay; +use App\Enums\WalletToBankLogStatus; +use App\Exceptions\BizException; use App\Models\WalletLog; use App\Models\WalletToBankLog; use App\Services\WalletService; @@ -34,15 +37,37 @@ class WalletToBankLogVerify extends Form implements LazyRenderable */ public function handle(array $input) { - $id = $this->payload['id'] ?? 0; - $log = WalletToBankLog::findOrFail($id); try { DB::beginTransaction(); - $log->update($input); - //如果拒绝,重新添加对应可提金额 - if ($log->status == 2) { - $walletService = new WalletService(); - $walletService->changeBalance($log->user, $log->amount, WalletLog::ACTION_WITHDRAW_FAILED, '提现-失败', $log); + + $log = WalletToBankLog::lockForUpdate()->findOrFail($this->payload['id']); + + if (! in_array($log->status, [WalletToBankLogStatus::Pending, WalletToBankLogStatus::Failed])) { + throw new BizException('提现记录状态异常'); + } + + if ($input['status'] == 1) { + $log->pay_way = $input['pay_way']; + + if ($log->pay_way === WalletToBankLogPayWay::Offline) { + $log->status = WalletToBankLogStatus::Success; + $log->pay_at = now(); + } else { + $log->pay_sn = serial_number(); + $log->status = WalletToBankLogStatus::Passed; + } + + $log->failed_reason = null; + } else { + $log->status = WalletToBankLogStatus::Refused; + } + + $log->remarks = $input['remarks']; + $log->save(); + + // 如果拒绝,退换扣除金额 + if ($log->status === WalletToBankLogStatus::Refused) { + (new WalletService())->changeBalance($log->user, $log->amount, WalletLog::ACTION_WITHDRAW_FAILED, '提现-失败', $log); } DB::commit(); @@ -65,7 +90,6 @@ class WalletToBankLogVerify extends Form implements LazyRenderable $id = $this->payload['id'] ?? 0; $log = WalletToBankLog::findOrFail($id); - $this->text('bank_name')->value($log->bank_name)->disable(); $this->text('bank_number')->value($log->bank_number)->disable(); $this->text('username')->value($log->username)->disable(); @@ -75,11 +99,19 @@ class WalletToBankLogVerify extends Form implements LazyRenderable $this->currency('service_amount')->symbol('¥')->value(bcdiv($log->service_amount, 100, 2))->disable(); $this->currency('account_amount')->symbol('¥')->value(bcdiv($log->account_amount, 100, 2))->disable(); - $this->radio('status')->options([ - 1 => '通过', - 2 => '拒绝', - ])->default(1); + if ($log->status === WalletToBankLogStatus::Failed) { + $this->textarea('failed_reason')->value($log->failed_reason ?: '')->disable(); + } + $this->radio('status') + ->options([ + 1 => '通过', + 2 => '拒绝', + ]) + ->default(1); + $this->select('pay_way') + ->options(WalletToBankLogPayWay::texts()) + ->rules('required_if:status,1', ['required_if'=>'请选择支付方式']); $this->text('remarks')->rules('required_if:status,2', ['required_if'=>'拒绝时需要填写备注']); } } diff --git a/app/Console/Commands/WalletToBankCommand.php b/app/Console/Commands/WalletToBankCommand.php new file mode 100644 index 00000000..850d9dce --- /dev/null +++ b/app/Console/Commands/WalletToBankCommand.php @@ -0,0 +1,86 @@ +chunkById(1, function ($logs) use ($yeePayService) { + foreach ($logs as $log) { + try { + $result = $yeePayService->request('accountpay.behalf.Pay', [ + 'payerOutUserId' => '21102510220227100003' ?: config('services.yeepay.partner_id'), + 'merchOrderNo' => $log->pay_sn, + 'tradeName' => '余额提现', + 'payeeUserName' => $log->username, + 'bankCardNo' => $log->bank_number, + 'bankCode' => Bank::tryFromBankName($log->bank_name)?->name, + 'bankCardType' => 'DEBIT_CARD', + 'amount' => bcdiv($log->account_amount, 100, 2), + 'feeRole' => 'PAYER', + 'tradeMemo' => '商城余额提现', + ]); + + if ($result['orderStatus'] === 'SUCCESS') { + $log->update([ + 'status' => WalletToBankLogStatus::Success, + 'pay_at' => now(), + 'failed_reason' => null, + ]); + } elseif ($result['orderStatus'] === 'FAIL') { + $log->update([ + 'status' => WalletToBankLogStatus::Failed, + 'failed_reason' => '交易失败', + ]); + } else { + $log->update([ + 'status' => WalletToBankLogStatus::Paying, + 'failed_reason' => null, + ]); + } + } catch (YeePayException $e) { + $log->update([ + 'status' => WalletToBankLogStatus::Failed, + 'failed_reason' => $e->getMessage(), + ]); + } catch (Throwable $e) { + throw $e; + } + } + }); + + sleep(60); + } + } +} diff --git a/app/Endpoint/Callback/Http/Controllers/YeePayNotifyController.php b/app/Endpoint/Callback/Http/Controllers/YeePayNotifyController.php new file mode 100644 index 00000000..d5e81843 --- /dev/null +++ b/app/Endpoint/Callback/Http/Controllers/YeePayNotifyController.php @@ -0,0 +1,13 @@ +debug('yeepay notify', $request->input()); + } +} diff --git a/app/Endpoint/Callback/routes.php b/app/Endpoint/Callback/routes.php index 50d2f0f3..b6679783 100644 --- a/app/Endpoint/Callback/routes.php +++ b/app/Endpoint/Callback/routes.php @@ -3,6 +3,7 @@ use App\Endpoint\Callback\Http\Controllers\AlipayController; use App\Endpoint\Callback\Http\Controllers\Kuaidi100Controller; use App\Endpoint\Callback\Http\Controllers\WxpayController; +use App\Endpoint\Callback\Http\Controllers\YeePayNotifyController; use Illuminate\Support\Facades\Route; //快递100物流推送 @@ -13,3 +14,5 @@ Route::post('wxpay/{payment}/paid-notify', [WxpayController::class, 'paidNotify' Route::post('wxpay/{payment}/order-refund-notify', [WxpayController::class, 'orderRefundedNotify'])->name('wxpay.order_refund_notify'); Route::post('alipay', AlipayController::class)->name('alipay.notify'); + +Route::post('yeepay-notify', YeePayNotifyController::class)->name('yeepay.notify'); diff --git a/app/Enums/WalletToBankLogPayWay.php b/app/Enums/WalletToBankLogPayWay.php new file mode 100644 index 00000000..91e13686 --- /dev/null +++ b/app/Enums/WalletToBankLogPayWay.php @@ -0,0 +1,38 @@ + '#5b69bc', + static::Behalf => '#21b978', + }; + } + + /** + * @return string + */ + public function text(): string + { + return static::texts()[$this->value]; + } + + /** + * @return array + */ + public static function texts(): array + { + return [ + static::Offline->value => '线下', + static::Behalf->value => '代付', + ]; + } +} diff --git a/app/Enums/WalletToBankLogStatus.php b/app/Enums/WalletToBankLogStatus.php index 9a56a25b..c98b5e69 100644 --- a/app/Enums/WalletToBankLogStatus.php +++ b/app/Enums/WalletToBankLogStatus.php @@ -6,6 +6,9 @@ enum WalletToBankLogStatus: int { case Pending = 0; case Success = 1; case Refused = 2; + case Passed = 3; // 审核通过 - 等待付款 + case Paying = 4; // 审核通过 - 付款中 + case Failed = 9; /** * @return string @@ -14,7 +17,10 @@ enum WalletToBankLogStatus: int { { return match ($this) { static::Pending => '#5b69bc', + static::Passed => '#dda451', + static::Paying => '#3085d6', static::Success => '#21b978', + static::Failed => '#ea5455', static::Refused => '#b3b9bf', }; } @@ -34,7 +40,10 @@ enum WalletToBankLogStatus: int { { return [ static::Pending->value => '待处理', + static::Passed->value => '待付款', + static::Paying->value => '付款中', static::Success->value => '成功', + static::Failed->value => '失败', static::Refused->value => '拒绝', ]; } diff --git a/app/Exceptions/YeePayException.php b/app/Exceptions/YeePayException.php new file mode 100644 index 00000000..b3b7c702 --- /dev/null +++ b/app/Exceptions/YeePayException.php @@ -0,0 +1,9 @@ + WalletToBankLogStatus::class, + 'pay_way' => WalletToBankLogPayWay::class, + 'pay_at' => 'datetime', ]; /** @@ -31,6 +34,10 @@ class WalletToBankLog extends Model 'rate', 'service_amount', 'account_amount', + 'pay_sn', + 'pay_way', + 'pay_at', + 'failed_reason', ]; /** diff --git a/app/Services/YeePayService.php b/app/Services/YeePayService.php new file mode 100644 index 00000000..454ecdf9 --- /dev/null +++ b/app/Services/YeePayService.php @@ -0,0 +1,82 @@ + Str::uuid()->getHex()->toString(), + 'service' => $service, + 'partnerId' => $this->config['partner_id'], + 'signType' => 'MD5', + 'notifyUrl' => route('yeepay.notify'), + ], $data); + + $data['sign'] = $this->sign($data); + + $response = Http::asForm()->post($this->config['gateway_url'], $data); + + $response->throw(); + + $result = $response->json(); + + if ($result['success'] && in_array($result['resultCode'], ['EXECUTE_SUCCESS', 'EXECUTE_PROCESSING'])) { + return $result; + } + + $message = $result['resultDetail'] ?? $result['resultMessage']; + + throw new YeePayException($message); + } + + /** + * 生成签名 + * + * @param array $params + * @return string + */ + protected function sign(array $params): string + { + unset($params['sign']); + + ksort($params); + + $str = ''; + + foreach ($params as $key => $value) { + if (blank($value)) { + continue; + } + + if ($str !== '') { + $str .= '&'; + } + + if (is_array($value)) { + $value = json_encode($value); + } + + $str .= "{$key}={$value}"; + } + + $str .= $this->config['secret_key']; + + return md5($str); + } +} diff --git a/config/services.php b/config/services.php index 2a1d616c..0541871e 100644 --- a/config/services.php +++ b/config/services.php @@ -30,4 +30,10 @@ return [ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], + 'yeepay' => [ + 'partner_id' => env('YEEPAY_PARTNER_ID'), + 'secret_key' => env('YEEPAY_SECRET_KEY'), + 'gateway_url' => env('YEEPAY_GATEWAY_URL'), + ], + ]; diff --git a/database/migrations/2022_04_12_110138_add_pay_columns_to_wallet_to_bank_logs_table.php b/database/migrations/2022_04_12_110138_add_pay_columns_to_wallet_to_bank_logs_table.php new file mode 100644 index 00000000..7878fcdc --- /dev/null +++ b/database/migrations/2022_04_12_110138_add_pay_columns_to_wallet_to_bank_logs_table.php @@ -0,0 +1,38 @@ +string('pay_sn')->nullable()->comment('支付单号'); + $table->tinyInteger('pay_way')->default(WalletToBankLogPayWay::Offline->value)->comment('支付方式: 1 线下, 2 代付'); + $table->timestamp('pay_at')->nullable()->comment('支付时间'); + $table->text('failed_reason')->nullable()->comment('失败原因'); + + $table->unique('pay_sn'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('wallet_to_bank_logs', function (Blueprint $table) { + $table->dropColumn(['pay_sn', 'pay_way', 'failed_reason', 'pay_at']); + }); + } +} diff --git a/resources/lang/zh_CN/wallet-to-bank-log.php b/resources/lang/zh_CN/wallet-to-bank-log.php index c98af7bb..5ab0879a 100644 --- a/resources/lang/zh_CN/wallet-to-bank-log.php +++ b/resources/lang/zh_CN/wallet-to-bank-log.php @@ -17,6 +17,9 @@ return [ 'status' => '状态', 'remarks' => '备注', 'created_at'=> '申请时间', + 'pay_way' => '支付方式', + 'pay_at' => '支付时间', + 'failed_reason' => '失败原因', 'user'=>[ 'phone' => '申请人', ],