diff --git a/app/Command/MineCommand.php b/app/Command/MineCommand.php index b0453d1..ef5ed72 100644 --- a/app/Command/MineCommand.php +++ b/app/Command/MineCommand.php @@ -43,6 +43,8 @@ class MineCommand extends HyperfCommand public function handle(): void { + $this->refundConfirm(); + return; $this->rsyncUser('4673f922e30cfd2efeeb992ff6a32ece', '202308070000001', 'ELF1990'); $this->rsyncUser('247eda1b7de0c76603123c477f2072fa', '202308070000001', 'RLX1990'); $this->rsyncUser('13f366c46bb842b6cb732e6a30836a9b', '202308070000001', '30684'); @@ -151,6 +153,16 @@ class MineCommand extends HyperfCommand echo $url; } + protected function unbindCard() + { + /** + * @var UserService $userService + */ + $userService = $this->container->make(UserService::class); + $result = $userService->unbindCard(['userId' => 'ELF1990', 'agreementNo' => '312023080400002047764'], $this->getApp()); + var_dump($result); + } + protected function payment() { /** @@ -159,7 +171,7 @@ class MineCommand extends HyperfCommand $paymentService = $this->container->make(PaymentService::class); $data = [ 'userId' => 'ELF1990', - 'agreementNo' => '312023080400002047764', + 'agreementNo' => '312023081200002052866', 'notifyUrl' => 'http://www.baidu.com', 'returnUrl' => 'http://www.baidu.com', 'amount' => 100, @@ -209,4 +221,51 @@ class MineCommand extends HyperfCommand $userId = $requestLog->getDataValue('userId'); var_dump($userId); } + + public function getBalance() + { + /** + * @var UserService $userService + */ + $userService = $this->container->make(UserService::class); + $result = $userService->getBalance('1274207'); + var_dump($result); + } + + public function refund() + { + /** + * @var PaymentService $paymentService + */ + $paymentService = $this->container->make(PaymentService::class); + $data = [ + 'userId' => 'ELF1990', + 'outOrderNo' => '16918297262208', + 'notifyUrl' => 'http://www.baidu.com', + 'returnUrl' => 'http://www.baidu.com', + 'outRefundNo' => time() . rand(1000, 9999), + 'refundAmount' => 100, + 'refundReason' => '不想买了', + 'refundSplitInfoList' => [ + ] + ]; + $result = $paymentService->refundApply($data, $this->getApp()); + var_dump($result); + } + + public function refundConfirm() + { + /** + * @var PaymentService $paymentService + */ + $paymentService = $this->container->make(PaymentService::class); + $data = [ + 'userId' => 'ELF1990', + 'outOrderNo' => '16918297262208', + 'outRefundNo' => '16919422375337', + 'notifyUrl' => 'http://www.baidu.com', + 'refundAmount' => 100, + ]; + $paymentService->refundConfirm($data, $this->getApp(), $this->getToken()); + } } \ No newline at end of file diff --git a/app/Controller/Payment/NotifyController.php b/app/Controller/Payment/NotifyController.php index 965974f..2f8fc3b 100644 --- a/app/Controller/Payment/NotifyController.php +++ b/app/Controller/Payment/NotifyController.php @@ -104,14 +104,14 @@ class NotifyController extends AbstractController $info = [ 'member_id' => $params['loginId'], - 'order_no' => $params['orderId'], - 'third_order_no' => $params['tradeId'], + 'order_no' => $params['tradeId'], + 'third_order_no' => $params['orderId'], 'status' => $params['orderStatus'], 'amount' => $params['orderMoney'], 'finished_at' => $params['finishTime'], 'error_message' => $params['errorMsg'], - 'transaction_id' => $params['transactionId'], - 'out_transaction_id' => $params['outTransactionId'], + 'transaction_id' => $params['transactionId'] ?? '', + 'out_transaction_id' => $params['outTransactionId'] ?? '', ]; $order = $this->paymentService->updateOrder($info); if (empty($order)) { diff --git a/app/Controller/Payment/PaymentController.php b/app/Controller/Payment/PaymentController.php index 931e444..5f163e4 100644 --- a/app/Controller/Payment/PaymentController.php +++ b/app/Controller/Payment/PaymentController.php @@ -75,8 +75,8 @@ class PaymentController extends AbstractController public function payment(RequestInterface $request) { [$app, $data, $token] = $this->parseReqest($request, UnbindCardRequest::class); - $url = $this->paymentService->payment($data, $app, $token); - return $this->success(['url' => $url]); + $this->paymentService->payment($data, $app, $token); + return $this->success(); } public function refundApply(RequestInterface $request) @@ -89,8 +89,8 @@ class PaymentController extends AbstractController public function refundConfirm(RequestInterface $request) { [$app, $data, $token] = $this->parseReqest($request, UnbindCardRequest::class); - $data = $this->paymentService->refundApply($app, $data); - return $this->success($data); + $this->paymentService->refundConfirm($app, $data, $token); + return $this->success(); } public function refundCancel(RequestInterface $request) diff --git a/app/Helper/Baofu/Baofu.php b/app/Helper/Baofu/Baofu.php index 9d34758..1103dfe 100644 --- a/app/Helper/Baofu/Baofu.php +++ b/app/Helper/Baofu/Baofu.php @@ -329,7 +329,7 @@ class Baofu $data['provinceName'] = $info['provinceName']; $data['cityName'] = $info['cityName']; $data['notifyUrl'] = NotifyList::getNotifyUrl('apply-bind-card', $token); - return $this->api('apply-bind-card' ,$data); + return $this->api('apply-bind-card', $data); } public function openAccount($loginNo, $info) @@ -375,14 +375,14 @@ class Baofu return $this->api('profit-share-refund-apply', $data); } - public function profitShareRefundConfirm($params){ + public function profitShareRefundConfirm($params, $token){ $data = []; $data['loginNo'] = $params['loginNo'] ?: '';// 匿名支付不需要传 $data['refundTradeId'] = $params['refundTradeId']; // 与退款申请中的退款请求订单号一致 $data['refundTradeType'] = 'REFUND_CONFIRM'; $data['orgTradeId'] = $params['orgTradeId']; // 原始支付商户订单号 $data['refundAmount'] = $params['refundAmount']; // 单位:分 - $data['notifyUrl'] = $params['notifyUrl']; + $data['notifyUrl'] = NotifyList::getNotifyUrl('confirm-split', $token); return $this->api('profit-share-refund-confirm', $data); } diff --git a/app/Helper/Baofu/SignatureUtil.php b/app/Helper/Baofu/SignatureUtil.php index 4e0e443..f3e2734 100644 --- a/app/Helper/Baofu/SignatureUtil.php +++ b/app/Helper/Baofu/SignatureUtil.php @@ -53,7 +53,6 @@ class SignatureUtil $pubKey = file_get_contents($cerPath); $certs = openssl_get_publickey($pubKey); $ok = openssl_verify($data, hex2bin($signature), $certs); - var_dump($cerPath, $signature); if ($ok == 1) { return true; } diff --git a/app/Model/Order.php b/app/Model/Order.php index c19d6d4..909be51 100644 --- a/app/Model/Order.php +++ b/app/Model/Order.php @@ -7,6 +7,8 @@ namespace App\Model; class Order extends Model { public const STATUS_PREPARE = 'PREPARE'; + public const STATUS_APPLY_SUCCESS = 'APPLY_SUCCESS'; + public const STATUS_APPLY_FAILED = 'APPLY_FAILED'; public const STATUS_INIT = 'INIT'; public const STATUS_PROCESS = 'PROCESS'; public const STATUS_SUCCESS = 'SUCCESS'; @@ -21,4 +23,9 @@ class Order extends Model public function getExpiresIn() { return strtotime($this->expired_at) - time(); } + + public function orderSplitInfos() + { + return $this->hasMany(OrderSplitInfo::class, 'order_no', 'order_no'); + } } \ No newline at end of file diff --git a/app/Model/Refund.php b/app/Model/Refund.php index aab0743..34048dd 100644 --- a/app/Model/Refund.php +++ b/app/Model/Refund.php @@ -6,7 +6,11 @@ namespace App\Model; class Refund extends Model { + public const STATUS_PREPARE = 'PREPARE'; + public const STATUS_APPLY_SUCCESS = 'APPLY_SUCCESS'; + public const STATUS_APPLY_FAILED = 'APPLY_FAILED'; + public const STATUS_CONFIRM_FAILED = 'CONFIRM_FAILED'; public const STATUS_INIT = 'INIT'; public const STATUS_PROCESS = 'PROCESS'; public const STATUS_SUCCESS = 'SUCCESS'; @@ -14,6 +18,10 @@ class Refund extends Model protected $table = 'refunds'; + protected $casts = [ + 'org_refund_split_info_list' => 'array', + ]; + public static $shortStatusAndStatusMap = [ 'I' => self::STATUS_INIT, 'P' => self::STATUS_PROCESS, diff --git a/app/Model/RefundSplitInfo.php b/app/Model/RefundSplitInfo.php new file mode 100644 index 0000000..bc8caaf --- /dev/null +++ b/app/Model/RefundSplitInfo.php @@ -0,0 +1,10 @@ +org_split_info_list = $params['splitInfoList']; $order->save(); - $fee = floor($order->amount * 0.007); + $platformAccount = User::getPlatformAccount(); + $fee = $platformAccount ? floor($order->amount * 0.01) : 0; $orderSplitInfos = []; $splitUserIds = array_column($params['splitInfoList'], 'userId'); $users = User::where('app_id', $app->app_id)->whereIn('user_id', $splitUserIds)->get()->keyBy('user_id'); - $platformAccount = User::getPlatformAccount(); foreach ($params['splitInfoList'] as $splitInfo) { $splitUser = $users[$splitInfo['userId']]; $splitAmount = $splitInfo['sellerFlag'] == 1 ? ($splitInfo['amount'] - $fee): $splitInfo['amount']; @@ -151,7 +152,7 @@ class PaymentService extends AbstractService } public function updateOrder($params) { - $order = Order::where('order_no', $params['order_no'])->where('status', 'APPLY_FAILED')->first(); + $order = Order::where('order_no', $params['order_no'])->where('status', 'APPLY_SUCCESS')->first(); if (empty($order)) { return null; } @@ -167,17 +168,17 @@ class PaymentService extends AbstractService public function refundApply(array $data, App $app) { $user = User::where('app_id', $app->app_id)->where('user_id', $data['userId'])->first(); - if ($user) { - throw new BusinessException('用户已存在'); + if (!$user) { + throw new BusinessException('用户不存在'); } $order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first(); if (empty($order)) { throw new BusinessException('订单号不存在'); } - if ($order->status != 'FINISH') { + if ($order->status != 'SUCCESS') { throw new BusinessException('该订单状态不能发起退款'); } - $refund = Refund::where('app_id', $app->app_id)->where('refund_no', $data['refundNo'])->first(); + $refund = Refund::where('app_id', $app->app_id)->where('out_refund_no', $data['outRefundNo'])->first(); if ($refund) { throw new BusinessException('该退款单已经存在'); } @@ -189,19 +190,20 @@ class PaymentService extends AbstractService $refund->order_no = $order->order_no; $refund->out_order_no = $order->out_order_no; $refund->refund_no = StringHelper::generateOrderNo(StringHelper::ORDER_NO_TYPE_REFUND); + $refund->out_refund_no = $data['outRefundNo']; $refund->refund_amount = $data['refundAmount']; $refund->refund_reason = $data['refundReason']; $refund->notify_url = $data['notifyUrl'] ?? ''; - $refund->org_refund_split_info_list = $data['refundSplitInfoList']; - $refund->remark = $data['remark'] ?: ''; + $refund->org_refund_split_info_list = $data['refundSplitInfoList'] ?? []; + $refund->remark = $data['remark'] ?? ''; $refund->applied_at = date('Y-m-d H:i:s'); $refundSplitInfoList = []; $refundSplitInfos = []; - foreach ($order->order_split_infos as $splitInfo) { + foreach ($order->orderSplitInfos as $splitInfo) { $refundSplitInfoList[] = [ 'orgSubOutOrderNo' => $splitInfo->sub_order_no, - 'refundAmount' => $splitInfo->split_amount, + 'refundAmount' => (string)$splitInfo->split_amount, ]; $refundSplitInfos[$splitInfo->sub_order_no] = [ 'app_id' => $splitInfo->user_id, @@ -210,12 +212,14 @@ class PaymentService extends AbstractService 'user_id' => $splitInfo->user_id, 'split_member_id' => $splitInfo->split_member_id, 'split_user_id' => $splitInfo->split_user_id, + 'refund_no' => $refund->refund_no, + 'out_refund_no' => $refund->out_refund_no, 'order_no' => $splitInfo->order_no, 'out_order_no' => $splitInfo->out_order_no, 'sub_order_no' => $splitInfo->sub_order_no, 'sub_out_order_no' => $splitInfo->sub_out_order_no, 'refund_amount' => $splitInfo->split_amount, - 'status' => 'P', + 'status' => Refund::STATUS_PREPARE, 'message' => '', ]; } @@ -235,18 +239,69 @@ class PaymentService extends AbstractService $refund->real_refund_amount = $result['refundAmount']; foreach ($result['refundSplitResultList'] as $refundSplitResult) { $subOrderNo = $refundSplitResult['orgSubOutOrderNo']; - $refundSplitResult[$subOrderNo]['status'] = $refundSplitResult['status']; - $refundSplitResult[$subOrderNo]['message'] = $refundSplitResult['message']; + $refundSplitInfos[$subOrderNo]['status'] = Refund::getStatusByShortStatus($refundSplitResult['status']); + $refundSplitInfos[$subOrderNo]['message'] = $refundSplitResult['message'] ?: ''; } + RefundSplitInfo::insert($refundSplitInfos); + $refund->save(); + } catch (BasicException $e) { $refund->status = 'APPLY_FAILED'; $refund->error_code = $e->getCode(); $refund->error_message = $e->getMessage(); + foreach ($refundSplitInfos as $key => $refundSplitInfo) { + $refundSplitInfos[$key]['status'] = 'APPLY_FAILED'; + $refundSplitInfos[$key]['message'] = $e->getMessage(); + } + RefundSplitInfo::insert($refundSplitInfos); + $refund->save(); throw $e; } + } - $refund->save(); + public function refundConfirm(array $data, App $app, string $token) + { + $user = User::where('app_id', $app->app_id)->where('user_id', $data['userId'])->first(); + if (!$user) { + throw new BusinessException('用户不存在'); + } + $order = Order::where('app_id', $app->app_id)->where('out_order_no', $data['outOrderNo'])->first(); + if (empty($order)) { + throw new BusinessException('订单号不存在'); + } + if ($order->status != 'SUCCESS') { + throw new BusinessException('该订单状态不能发起退款'); + } + $refund = Refund::where('app_id', $app->app_id)->where('out_refund_no', $data['outRefundNo'])->first(); + if (empty($refund)) { + throw new BusinessException('退款申请不存在'); + } + if ($refund->status != Refund::STATUS_APPLY_SUCCESS || $refund->status != Refund::STATUS_CONFIRM_FAILED) { + throw new BusinessException('该退款单未申请成功'); + } + + try { + $baofu = new Baofu(); + $result = $baofu->profitShareRefundConfirm([ + 'loginNo' => $user->member_id, + 'refundTradeId' => $refund->refund_no, + 'refundTradeType' => 'REFUND_APPLY', + 'orgTradeId' => $refund->order_no, + 'refundAmount' => $refund->refund_amount, + 'notifyUrl' => $data['notifyUrl'], + ], $token); + $refund->status = Refund::getStatusByShortStatus($result['refundStatus']); + $refund->third_refund_no = $result['refundOrderId']; + $refund->save(); + + } catch (ApiException $e) { + $refund->status = Refund::STATUS_CONFIRM_FAILED; + $refund->error_code = $e->getCode(); + $refund->error_message = $e->getMessage(); + $refund->save(); + throw $e; + } } public function updateRefund($params) diff --git a/app/Service/UserService.php b/app/Service/UserService.php index e0ba698..bcb0da3 100644 --- a/app/Service/UserService.php +++ b/app/Service/UserService.php @@ -93,6 +93,13 @@ class UserService extends AbstractService 'loginNo' => $memberId, 'contractNo' => $user->contract_no, ]); + + $oldAgreementNos = BankCard::where('member_id', $memberId)->get()->pluck('agreement_no')->toArray(); + $agreementNos = array_column($bankCards, 'agreementNo'); + + $deletedAgreementNos = array_diff($oldAgreementNos, $agreementNos); + BankCard::where('member_id', $memberId)->whereIn('agreement_no', $deletedAgreementNos)->delete(); + foreach ($bankCards as $bankCard) { $this->saveBankCard($bankCard, $user); } @@ -169,7 +176,7 @@ class UserService extends AbstractService $baofu = new Baofu(); $result = $baofu->cardUnbind($user->member_id, $data['agreementNo']); - if ($result && $result != 'false') { + if ($result) { $this->rsyncBankCards($user->member_id) ; return true; } @@ -186,7 +193,8 @@ class UserService extends AbstractService return $baofu->pwdForget($user->member_id, $token); } - public function pwdModify(array $data, App $app, $token) { + public function pwdModify(array $data, App $app, $token) + { $user = User::where('app_id', $app->app_id)->where('user_id', $data['userId'])->first(); if (empty($user)) { throw new BusinessException('用户不存在'); @@ -195,4 +203,10 @@ class UserService extends AbstractService $baofu = new Baofu(); return $baofu->pwdModify($user->member_id, $token); } + + public function getBalance($memberId) + { + $baofu = new Baofu(); + return $baofu->getBalance($memberId); + } } \ No newline at end of file diff --git a/payment.sql b/payment.sql index 28dd4b0..8cb5c4a 100644 --- a/payment.sql +++ b/payment.sql @@ -168,7 +168,7 @@ CREATE TABLE `refunds` ( `out_order_no` varchar(32) NOT NULL, `refund_no` varchar(32) NOT NULL, `out_refund_no` varchar(32) NOT NULL, - `third_refund_no` varchar(32) NOT NULL, + `third_refund_no` varchar(32) NOT NULL DEFAULT '', `refund_amount` int(11) NOT NULL DEFAULT 0, `refund_reason` varchar(255) NOT NULL DEFAULT '', `refund_type` varchar(16) NOT NULL DEFAULT '', @@ -200,6 +200,8 @@ CREATE TABLE `refund_split_infos` ( `out_order_no` varchar(32) NOT NULL, `sub_order_no` varchar(32) NOT NULL, `sub_out_order_no` varchar(32) NOT NULL, + `refund_no` varchar(32) NOT NULL, + `out_refund_no` varchar(32) NOT NULL, `refund_amount` int(11) NOT NULL DEFAULT 0, `status` varchar(16) NOT NULL DEFAULT '', `message` varchar(32) NOT NULL DEFAULT '',