diff --git a/app/Controller/Payment/NotifyController.php b/app/Controller/Payment/NotifyController.php index 79b34ce..e8f77d7 100644 --- a/app/Controller/Payment/NotifyController.php +++ b/app/Controller/Payment/NotifyController.php @@ -249,5 +249,71 @@ class NotifyController extends AbstractController { $params = $request->all(); Log::info('NOTIFY_DATA: ' . $request->getUri() . ':', $params); + + $result = json_decode($params['data_content'], true); + $info = [ + 'member_id' => $params['loginId'], + 'order_no' => $params['tradeId'], + 'third_order_no' => $params['orderId'], + 'status' => $params['orderStatus'], + 'amount' => $params['orderMoney'], + 'finished_at' => $params['finishTime'], + ]; + $apply = $this->paymentService->updateWithdrawApply($info, ['APPLY_SUCCESS']); + if (empty($apply)) { + return 'OK'; + } + + $result = $this->notify( + $apply->notify_url, + $apply->app, + [ + 'amount' => $apply->amount, + 'status' => $apply->status, + 'userId' => $apply->user_id, + 'outWithdrawNo' => $apply->out_withdraw_no, + ] + ); + + return 'OK'; + } + + public function withdraw(RequestInterface $request) + { + [$token, $params] = $this->getTokenAndParams($request); + + $baofu = new Baofu(); + if (!$baofu->notifyVerify($params, 'withdraw')) { + Log::info('withdrawNotifyVerifyFail: ', $params); + return $baofu->notifySuccess(); + } + + $requestLog = $this->requestService->getRequestLogByToken($token); + + $info = [ + 'member_id' => $params['loginId'], + 'order_no' => $params['tradeId'], + 'third_order_no' => $params['orderId'], + 'status' => $params['orderStatus'], + 'amount' => $params['orderMoney'], + 'finished_at' => $params['finishTime'], + ]; + $withdraw = $this->paymentService->updateWithdraw($info, ['APPLY_SUCCESS']); + if (empty($withdraw)) { + return $baofu->notifySuccess(); + } + + $result = $this->notify( + $requestLog->getDataValue('notifyUrl'), + $requestLog->app, + [ + 'amount' => $withdraw->amount, + 'status' => $withdraw->status, + 'userId' => $withdraw->user_id, + 'outWithdrawNo' => $withdraw->out_withdraw_no, + ] + ); + + $baofu->notifySuccess(); } } diff --git a/app/Controller/Payment/PaymentController.php b/app/Controller/Payment/PaymentController.php index 63b4f2c..550fe96 100644 --- a/app/Controller/Payment/PaymentController.php +++ b/app/Controller/Payment/PaymentController.php @@ -161,24 +161,14 @@ class PaymentController extends AbstractController public function withdraw(RequestInterface $request) { [$app, $data, $token] = $this->parseReqest($request, UnbindCardRequest::class); - $order = $this->paymentService->queryOrder($data, $app); - $result = [ - 'finishTime' => $order->finished_at, - 'amount' => $order->amount, - 'status' => $order->status, - ]; - return $this->success($result); + $url = $this->paymentService->withdraw($data, $app, $token); + return $this->success(['url' => $url]); } public function withdrawApply(RequestInterface $request) { [$app, $data, $token] = $this->parseReqest($request, UnbindCardRequest::class); - $order = $this->paymentService->queryOrder($data, $app); - $result = [ - 'finishTime' => $order->finished_at, - 'amount' => $order->amount, - 'status' => $order->status, - ]; - return $this->success($result); + $data = $this->paymentService->withdrawApply($data, $app); + return $this->success($data); } } diff --git a/app/Helper/Baofu/Baofu.php b/app/Helper/Baofu/Baofu.php index 01069aa..1e2669c 100644 --- a/app/Helper/Baofu/Baofu.php +++ b/app/Helper/Baofu/Baofu.php @@ -2,6 +2,7 @@ namespace App\Helper\Baofu; +use App\Exception\BusinessException; use App\Helper\StringHelper; class Baofu @@ -69,8 +70,8 @@ class Baofu $data['loginNo'] = $params['loginNo']; $data['agreementNo'] = Rsa::encryptByCERFile($params['agreementNo'], $this->getCerFilePath()); $data['outOrderNo'] = $params['outOrderNo']; - $data['notifyUrl'] = NotifyList::getNotifyUrl($params['notifyUrl'], $token); - return $this->withReturnUrl($this->api('withdraw', $data, $token), $token); + $data['notifyUrl'] = NotifyList::getNotifyUrl('withdraw', $token); + return $this->withReturnUrl($this->api('withdraw', $data), $token); } public function assurePaymentSplit($params, $token) @@ -475,12 +476,6 @@ class Baofu } public function transferWithdraw($params) { - $header = []; - $header['memberId'] = $this->getMerchantNo(); - $header['terminalId'] = $this->getTerminalNo(); - $header['serviceTp'] = 'T-1001-003-01'; - $header['verifyType'] = 1; - $items = []; foreach ($params['transContent'] as $transItem) { $item = []; @@ -505,6 +500,29 @@ class Baofu } $body = ['transContent' => $items]; + return $this->unionPayPost($body, 'T-1001-003-01'); + } + + public function transferWithdrawQuery($params) { + $items = []; + foreach ($params['transContent'] as $transItem) { + $item = []; + $item['transNo'] = $transItem['transNo']; + $item['transBatchId'] = $transItem['transBatchId']; + ksort($item); + $items[] = $item; + } + + $body = ['transContent' => $items]; + return $this->unionPayPost($body, 'T-1001-003-02'); + } + + private function unionPayPost($body, $serviceTp) { + $header = []; + $header['memberId'] = $this->getMerchantNo(); + $header['terminalId'] = $this->getTerminalNo(); + $header['serviceTp'] = $serviceTp; + $header['verifyType'] = 1; $content = [ 'header' => $header, 'body' => $body, @@ -527,6 +545,12 @@ class Baofu echo $result; echo PHP_EOL; $result = base64_decode($result); - return json_decode($result, true); + $result = json_decode($result, true); + if ($result['header']['sysRespCode'] == 'S_0000' && $result['body']['transHeader']['returnCode'] == '0000') { + return $result['body']['transContent']; + } else { + $msg = $result['header']['sysRespCode'] == 'S_0000' ? $result['body']['transHeader']['returnMsg'] : $result['header']['sysRespDesc']; + throw new BusinessException($msg); + } } } diff --git a/app/Model/WithdrawApply.php b/app/Model/WithdrawApply.php index 50525f9..c519d08 100644 --- a/app/Model/WithdrawApply.php +++ b/app/Model/WithdrawApply.php @@ -6,10 +6,30 @@ namespace App\Model; class WithdrawApply extends Model { + public const STATUS_PREPARE = 'PREPARE'; + public const STATUS_APPLY_SUCCESS = 'APPLY_SUCCESS'; + public const STATUS_APPLY_FAILED = 'APPLY_FAILED'; + public const STATUS_REFUND = 'REFUND'; + public const STATUS_PROCESS = 'PROCESS'; + public const STATUS_SUCCESS = 'SUCCESS'; + public const STATUS_FAILED = 'FAILED'; + protected $table = 'withdraw_apply'; public function app() { return $this->belongsTo(App::class, 'app_id', 'app_id'); } + + public static $shortStatusAndStatusMap = [ + '0' => self::STATUS_PROCESS, + '1' => self::STATUS_SUCCESS, + '-1' => self::STATUS_FAILED, + '2' => self::STATUS_REFUND, + ]; + + public static function getStatusByShortStatus($shortStatus) + { + return self::$shortStatusAndStatusMap[$shortStatus] ?: null; + } } \ No newline at end of file diff --git a/app/Service/PaymentService.php b/app/Service/PaymentService.php index 659e867..4c3cf89 100644 --- a/app/Service/PaymentService.php +++ b/app/Service/PaymentService.php @@ -15,6 +15,7 @@ use App\Model\OrderSplitInfo; use App\Model\Refund; use App\Model\RefundSplitInfo; use App\Model\User; +use App\Model\Withdraw; use App\Model\WithdrawApply; class PaymentService extends AbstractService @@ -481,6 +482,27 @@ class PaymentService extends AbstractService public function queryOrder(array $data, App $app) { + $type = $data['type'] ?? 'PAYMENT'; + $orderNo = null; + $memberId = null; + if ($type == 'WITHDRAW') { + $withdraw = Withdraw::where('app_id', $app->app_id)->where('out_withdraw_no', $data['outOrderNo'])->first(); + if (empty($withdraw)) { + throw new BusinessException('提现记录不存在'); + } + $memberId = $withdraw->member_id; + $orderNo = $withdraw->withdraw_no; + } elseif ($type == 'PAYMENT') { + $order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first(); + if (empty($order)) { + throw new BusinessException('订单不存在'); + } + $memberId = $order->member_id; + $orderNo = $order->order_no; + } else { + throw new BusinessException('查询类型错误'); + } + $order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first(); if (empty($order)) { throw new BusinessException('订单不存在'); @@ -488,23 +510,30 @@ class PaymentService extends AbstractService $baofu = new Baofu(); $result = $baofu->queryOrder([ - 'loginNo' => $order->member_id, - 'tradeId' => $order->order_no, + 'loginNo' => $orderNo, + 'tradeId' => $memberId, ]); $info = [ - 'member_id' => $order->member_id, - 'order_no' => $order->order_no, + 'member_id' => $memberId, + 'order_no' => $orderNo, 'third_order_no' => $result['requestNo'] ?? '', 'status' => $result['status'], 'amount' => $result['amount'], + 'fee' => $result['feeAmt'] ?? 0, + 'fee_acc' => $result['feeAcc'] ?? 0, 'finished_at' => $result['finishDate'], 'error_message' => $result['errorMsg'] ?? '', 'transaction_id' => $result['transactionId'] ?? '', 'out_transaction_id' => $result['outTransactionId'] ?? '', ]; - return $this->updateOrder($info); - + if ($result['tradeType'] == 'WITHDRAW') { + return $this->updateWithdraw($info); + } elseif (in_array($result['tradeType'], ['ASSURE_RECHARGE', 'PAYMENT_SPLIT_API', 'ASSURE_PORTFOLIO_PAY'])) { + return $this->updateOrder($info); + } else { + return true; + } } public function withdrawApply(array $data, App $app) { @@ -519,8 +548,8 @@ class PaymentService extends AbstractService $apply->account_name = $data['accountName']; $apply->batch_num = StringHelper::generateOrderNo(StringHelper::ORDER_NO_TYPE_WITHDRAW_APPLY_BATCH_NUM); $apply->apply_no = StringHelper::generateOrderNo(StringHelper::ORDER_NO_TYPE_WITHDRAW_APPLY_NO); - $apply->out_apply_no = $params['outApplyNo']; - $apply->amount = $params['amount']; + $apply->out_apply_no = $data['outApplyNo']; + $apply->amount = $data['amount']; $apply->status = 'PREPARE'; $apply->summary = $data['summary'] ?? '转账'; $apply->save(); @@ -538,23 +567,127 @@ class PaymentService extends AbstractService ] ] ]; - $result = $baofu->transferWithdraw($data); - if ($result['header']['sysRespCode'] == 'S_0000' && $result['body']['transHeader']['returnCode'] == '0000') { + try { + $result = $baofu->transferWithdraw($data); + $apply->third_apply_no = $result[0]['transOrderId']; + $apply->third_batch_num = $result[0]['transBatchId']; $apply->status = 'APPLY_SUCCESS'; - } else { + $apply->save(); + + return ['batchNum' => $apply->batch_num, 'applyNo' => $apply->apply_no]; + } catch (ApiException $e) { $apply->status = 'APPLY_FAILED'; + $apply->error_code = $e->getCode(); + $apply->error_message = $e->getMessage(); + $apply->save(); + throw $e; } - $apply->save(); } - public function withdraw(array $data, App $app, string $token) { + public function withdrawApplyQuery(array $data, App $app) { + $user = User::where('app_id', $app->app_id)->where('user_id', $data['userId'])->first(); + if (empty($user)) { + throw new BusinessException('用户不存在'); + } + + $apply = WithdrawApply::where('app_id', $app->app_id)->where('out_apply_no', $data['outApplyNo'])->first(); $baofu = new Baofu(); - return $baofu->withdraw([ - 'amount' => $data['amount'], - 'loginNo' => $data['amount'], - 'agreementNo' => $data['agreementNo'], - 'outOrderNo' => $data['outOrderNo'], - ], $token); + $data = [ + 'transContent' => [ + [ + 'transNo' => $apply->apply_no, + 'transBatchId' => $apply->batch_num, + ] + ] + ]; + try { + $result = $baofu->transferWithdrawQuery($data); + $item = $result[0]; + $apply->fee = intval($item['transFee'] * 100); + $apply->third_apply_no = $item['transOrderId']; + $apply->third_batch_num = $item['transBatchId']; + $apply->status = WithdrawApply::getStatusByShortStatus($item['state']); + $apply->save(); + + return ['fee' => $apply->fee, 'status' => $apply->status, 'amount' => $apply->amount]; + } catch (ApiException $e) { + throw $e; + } + } + + public function withdraw(array $data, App $app, string $token) { + $user = User::where('app_id', $app->app_id)->where('user_id', $data['userId'])->first(); + if (empty($user)) { + throw new BusinessException('用户不存在'); + } + $withdraw = new Withdraw(); + $withdraw->app_id = $app->app_id; + $withdraw->user_id = $user->user_id; + $withdraw->member_id = $user->member_id; + $withdraw->agreement_no = $data['agreementNo']; + $withdraw->withdraw_no = StringHelper::generateOrderNo(StringHelper::ORDER_NO_TYPE_WITHDRAW_NO); + $withdraw->out_withdraw_no = $data['outWithdrawNo']; + $withdraw->amount = $data['amount']; + $withdraw->status = 'PREPARE'; + $withdraw->notify_url = $data['notifyUrl']; + $withdraw->save(); + + try { + $baofu = new Baofu(); + $result = $baofu->withdraw([ + 'amount' => $data['amount'], + 'loginNo' => $user->member_id, + 'agreementNo' => $data['agreementNo'], + 'outOrderNo' => $data['outWithdrawNo'], + ], $token); + + $withdraw->status = 'APPLY_SUCCESS'; + $withdraw->withdraw_url = $result; + $withdraw->save(); + return $result; + } catch (ApiException $e) { + $withdraw->status = 'APPLY_FAILED'; + $withdraw->error_code = $e->getCode(); + $withdraw->error_message = $e->getMessage(); + $withdraw->save(); + throw $e; + } + } + + public function updateWithdraw($params, array $statusList = null) { + $withdraw = Withdraw::where('withdraw_no', $params['order_no'])->first(); + if (empty($withdraw)) { + return null; + } + + if ($statusList && !in_array($withdraw->status, $statusList)) { + return null; + } + + $withdraw->third_order_no = $params['third_order_no']; + $withdraw->status = $params['status']; + $withdraw->fee = $params['fee']; + $withdraw->fee_acc = $params['fee_acc'] ?? ''; + $withdraw->save(); + return $withdraw; + } + + public function updateWithdrawApply($params, array $statusList = null) { + $apply = WithdrawApply::where('apply_no', $params['transNo'])->first(); + if (empty($apply)) { + return null; + } + + if ($statusList && !in_array($apply->status, $statusList)) { + return null; + } + + $apply->fee = intval($params['transFee'] * 100); + $apply->third_apply_no = $params['transOrderId']; + $apply->third_batch_num = $params['transBatchId']; + $apply->status = WithdrawApply::getStatusByShortStatus($params['state']); + $apply->save(); + return $apply; } } diff --git a/config/routes.php b/config/routes.php index d92f7a9..628d1fd 100644 --- a/config/routes.php +++ b/config/routes.php @@ -36,6 +36,7 @@ Router::addGroup('/notify',function () { Router::addRoute(['GET', 'POST'], '/bind-card/{token}', [NotifyController::class, 'bindCard']); Router::addRoute(['GET', 'POST'], '/payment/{token}', [NotifyController::class, 'payment']); Router::addRoute(['GET', 'POST'], '/refund/{token}', [NotifyController::class, 'refund']); + Router::addRoute(['GET', 'POST'], '/withdraw/{token}', [NotifyController::class, 'withdraw']); Router::addRoute(['GET', 'POST'], '/transfer-withdraw', [NotifyController::class, 'transferWithdraw']); Router::addRoute(['POST'], '/test-notify', [NotifyController::class, 'testNotify']); Router::addRoute(['GET', 'POST'], '/confirm-assure-portfolio-pay/{token}', [NotifyController::class, 'confirmAssurePortfolioPay']);