From 2c09bd9fba6780df2acf0bfe628ca8ca7077f145 Mon Sep 17 00:00:00 2001 From: BrettonYe <867057410@qq.com> Date: Mon, 20 Nov 2023 23:51:44 +0800 Subject: [PATCH] Rewrite Alipay Notify logic --- app/Utils/Library/AlipayF2F.php | 44 ++++++++++++++++-------- app/Utils/Payments/F2Fpay.php | 59 ++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/app/Utils/Library/AlipayF2F.php b/app/Utils/Library/AlipayF2F.php index 90847273..ae21187e 100644 --- a/app/Utils/Library/AlipayF2F.php +++ b/app/Utils/Library/AlipayF2F.php @@ -1,8 +1,8 @@ rsaVerify($response[$resKey], $response['sign'])) { + if (! $this->validate_response_sign($response[$resKey], $response['sign'])) { throw new RuntimeException('验签错误-'.$response[$resKey]['msg'].' | '.($response[$resKey]['sub_msg'] ?? var_export($response, true))); } @@ -113,14 +113,14 @@ class AlipayF2F private function buildParams(): array { $params = [ - 'app_id' => $this->config['app_id'] ?? '', - 'method' => $this->config['method'] ?? '', + 'app_id' => $this->config['app_id'], + 'method' => $this->config['method'], 'charset' => 'utf-8', 'sign_type' => 'RSA2', 'timestamp' => date('Y-m-d H:m:s'), - 'biz_content' => $this->config['biz_content'] ?? [], + 'biz_content' => $this->config['biz_content'], 'version' => '1.0', - 'notify_url' => $this->config['notify_url'] ?? '', + 'notify_url' => $this->config['notify_url'], ]; $params = array_filter($params); $params['sign'] = $this->encrypt($this->buildQuery($params)); @@ -155,21 +155,39 @@ class AlipayF2F } /** - * RSA2验签. + * 同步返回验签. * - * @param array $data 待签名数据 + * @param array $body 待签名数据 * * @throws RuntimeException */ - public function rsaVerify(array $data, string $sign): bool + public function validate_response_sign(array $body, string $sign): bool { - unset($data['sign'], $data['sign_type']); + unset($body['sign'], $body['sign_type']); $publicKey = openssl_pkey_get_public($this->config['public_key']); if (empty($publicKey)) { throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确'); } - return (bool) openssl_verify(json_encode($data), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256); + return (bool) openssl_verify(json_encode($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256); + } + + /** + * 异步通知验签. + * + * @param array $body 待签名数据 + * + * @throws RuntimeException + */ + public function validate_notification_sign(array $body, string $sign): bool + { + unset($body['sign'], $body['sign_type']); + $publicKey = openssl_pkey_get_public($this->config['public_key']); + if (empty($publicKey)) { + throw new RuntimeException('支付宝RSA公钥错误。请检查公钥文件格式是否正确'); + } + + return (bool) openssl_verify($this->buildQuery($body), base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256); } public function qrCharge(array $content): array diff --git a/app/Utils/Payments/F2Fpay.php b/app/Utils/Payments/F2Fpay.php index 4d094f5d..0c9404c8 100644 --- a/app/Utils/Payments/F2Fpay.php +++ b/app/Utils/Payments/F2Fpay.php @@ -2,6 +2,7 @@ namespace App\Utils\Payments; +use App\Models\Payment; use App\Services\PaymentService; use App\Utils\Library\AlipayF2F; use App\Utils\Library\Templates\Gateway; @@ -14,16 +15,16 @@ use Response; class F2Fpay extends PaymentService implements Gateway { - private static array $aliConfig; + private static AlipayF2F $aliClient; public function __construct() { - self::$aliConfig = [ + self::$aliClient = new AlipayF2F([ 'app_id' => sysConfig('f2fpay_app_id'), 'ali_public_key' => sysConfig('f2fpay_public_key'), 'rsa_private_key' => sysConfig('f2fpay_private_key'), 'notify_url' => route('payment.notify', ['method' => 'f2fpay']), - ]; + ]); } public function purchase(Request $request): JsonResponse @@ -37,8 +38,7 @@ class F2Fpay extends PaymentService implements Gateway ]; try { - $gateWay = new AlipayF2F(self::$aliConfig); - $result = $gateWay->qrCharge($data); + $result = self::$aliClient->qrCharge($data); $payment->update(['qr_code' => 1, 'url' => $result['qr_code']]); } catch (Exception $e) { Log::alert('【支付宝当面付】支付错误: '.$e->getMessage()); @@ -52,26 +52,47 @@ class F2Fpay extends PaymentService implements Gateway public function notify(Request $request): void { try { - $result = (new AlipayF2F(self::$aliConfig))->tradeQuery($request->only('out_trade_no', 'trade_no')); - Log::notice('【支付宝当面付】回调验证验证:'.var_export($result, true)); + if (sysConfig('f2fpay_app_id') === $request->input('app_id') && self::$aliClient->validate_notification_sign($request->except('method'), $request->input('sign'))) { + $payment = Payment::whereTradeNo($request->input('out_trade_no'))->with('order')->first(); + if ($payment && abs($payment->amount - $request->input('total_amount')) < 0.01 && in_array($request->input('trade_status'), ['TRADE_FINISHED', 'TRADE_SUCCESS']) && $this->paymentReceived($request->input('out_trade_no'))) { + exit('success'); + } + } + Log::error('【支付宝当面付】异步验证失败,尝试订单查询'); + if ($this->capture($request->input('out_trade_no'), $request->input('trade_no'))) { + exit('success'); + } + + Log::notice('【支付宝当面付】异步验证失败:'.var_export($request->all(), true)); } catch (Exception $e) { Log::alert('【支付宝当面付】回调信息错误: '.$e->getMessage()); exit; } - if ($result['code'] === '10000' && $result['msg'] === 'Success') { - if ($request->has('out_trade_no') && in_array($request->input('trade_status'), ['TRADE_FINISHED', 'TRADE_SUCCESS'])) { - if ($this->paymentReceived($request->input('out_trade_no'))) { - exit('success'); - } - } else { - Log::error('【支付宝当面付】交易失败:'.var_export($request->all(), true)); - } - } else { - Log::error('【支付宝当面付】验证失败:'.var_export($result, true)); - } - // 返回验证结果 exit('fail'); } + + public function capture(string $trade_no = null, string $ali_trade_no = null): bool + { + $result = self::$aliClient->tradeQuery(array_filter([ + 'out_trade_no' => $trade_no, + 'trade_no' => $ali_trade_no, + ])); + + if ($result['code'] === '10000' && $result['msg'] === 'Success') { + if ($result['out_trade_no'] && in_array($result['trade_status'], ['TRADE_FINISHED', 'TRADE_SUCCESS'])) { + if ($this->paymentReceived($result['out_trade_no'])) { + return true; + } + Log::error('【支付宝当面付】收单交易订单结算失败:'.var_export($result, true)); + + return false; + } + } else { + Log::error('【支付宝当面付】收单交易查询失败:'.var_export($result, true)); + } + + return false; + } }