Переглянути джерело

优化微信支付V3回调

萤火科技 1 рік тому
батько
коміт
9cf28f0631

+ 2 - 2
app/api/controller/Notify.php

@@ -23,7 +23,7 @@ use app\api\service\Notify as NotifyService;
 class Notify extends BaseController
 {
     /**
-     * 支付成功异步通知 (微信支付v2)
+     * 支付成功异步通知 (微信支付V2)
      * @return string
      */
     public function wechatV2(): string
@@ -37,7 +37,7 @@ class Notify extends BaseController
     }
 
     /**
-     * 支付成功异步通知 (微信支付)
+     * 支付成功异步通知 (微信支付V3)
      * @return string
      */
     public function wechatV3(): string

+ 50 - 26
app/api/service/Notify.php

@@ -15,8 +15,8 @@ namespace app\api\service;
 use app\api\model\Payment as PaymentModel;
 use app\api\model\PaymentTrade as PaymentTradeModel;
 use app\api\model\PaymentTemplate as PaymentTemplateModel;
-use app\api\service\order\PaySuccess as OrderPaySuccesService;
-use app\api\service\recharge\PaySuccess as RechargePaySuccesService;
+use app\api\service\order\PaySuccess as OrderPaySuccessService;
+use app\api\service\recharge\PaySuccess as RechargePaySuccessService;
 use app\common\enum\OrderType as OrderTypeEnum;
 use app\common\enum\payment\Method as PaymentMethodEnum;
 use app\common\library\Log;
@@ -33,6 +33,9 @@ use cores\exception\BaseException;
  */
 class Notify
 {
+    // 异步通知的请求参数 (由第三方支付发送)
+    private array $notifyParams;
+
     /**
      * 支付成功异步通知 (微信支付V2)
      * @return string
@@ -46,7 +49,7 @@ class Notify
             $Payment = $this->getPayment($tradeInfo);
             // 验证异步通知参数是否合法
             if (!$Payment->notify()) {
-                throwError($Payment->getError() ?: '异步通知验证未通过');
+                throwError($Payment->getError() ?: '微信支付V2异步通知验证未通过');
             }
             // 订单支付成功事件
             $this->orderPaySucces($tradeInfo, $Payment->getNotifyParams());
@@ -64,28 +67,14 @@ class Notify
     public function wechatV3(): string
     {
         try {
-            // 通过微信支付v3平台证书序号或微信支付公钥ID 获取支付模板
-            $platformCertificateSerialOrPublicKeyId = \request()->header('wechatpay-serial');
-            $templateInfo = PaymentTemplateModel::findByWechatpaySerial($platformCertificateSerialOrPublicKeyId);
-            empty($templateInfo) && throwError("未找到该平台证书序号或微信支付公钥ID:$platformCertificateSerialOrPublicKeyId");
-
-            // 从支付模板中取出v3apikey 用于解密异步通知的密文
-            $apiv3Key = $templateInfo['config']['wechat']['mchType'] === 'provider'
-                ? $templateInfo['config']['wechat']['provider']['spApiKey']
-                : $templateInfo['config']['wechat']['normal']['apiKey'];
-
-            // 获取「微信支付平台证书」或者「微信支付平台公钥」的路径
-            $platformCertificateOrPublicKeyFilePath = $this->getPlatformCertificateOrPublicKeyFilePath($templateInfo);
-
-            // 验证异步通知是否合法并获取第三方支付交易订单号
-            $V3 = new WechatPaymentV3();
-            $outTradeNo = $V3->notify($apiv3Key, $platformCertificateOrPublicKeyFilePath);
-            empty($outTradeNo) && throwError('异步通知验证未通过');
-
+            // 微信支付V3异步回调验证
+            $this->wechatV3Notify();
+            // 获取异步回调的请求参数
+            $notifyParams = $this->getNotifyParams();
             // 获取第三方交易记录
-            $tradeInfo = PaymentTradeModel::detailByOutTradeNo($outTradeNo);
+            $tradeInfo = PaymentTradeModel::detailByOutTradeNo($notifyParams['outTradeNo']);
             // 订单支付成功事件
-            $this->orderPaySucces($tradeInfo, $V3->getNotifyParams());
+            $this->orderPaySucces($tradeInfo, $notifyParams);
         } catch (\Throwable $e) {
             // 记录错误日志
             Log::append('Notify-wechat', ['errMessage' => $e->getMessage()]);
@@ -93,6 +82,41 @@ class Notify
         return '';
     }
 
+    /**
+     * 微信支付V3异步回调验证
+     * @return void
+     * @throws BaseException
+     */
+    private function wechatV3Notify()
+    {
+        // 通过微信支付v3平台证书序号或微信支付公钥ID 获取支付模板
+        $platformCertificateSerialOrPublicKeyId = \request()->header('wechatpay-serial');
+        $templateInfo = PaymentTemplateModel::findByWechatpaySerial($platformCertificateSerialOrPublicKeyId);
+        empty($templateInfo) && throwError("未找到该平台证书序号或微信支付公钥ID:$platformCertificateSerialOrPublicKeyId");
+        // 从支付模板中取出v3apikey 用于解密异步通知的密文
+        $apiV3Key = $templateInfo['config']['wechat']['mchType'] === 'provider'
+            ? $templateInfo['config']['wechat']['provider']['spApiKey']
+            : $templateInfo['config']['wechat']['normal']['apiKey'];
+        // 获取「微信支付平台证书」或者「微信支付平台公钥」的路径
+        $platformCertificateOrPublicKeyFilePath = $this->getPlatformCertificateOrPublicKeyFilePath($templateInfo);
+        // 验证异步通知参数是否合法
+        $V3 = new WechatPaymentV3();
+        if (!$V3->notify($apiV3Key, $platformCertificateOrPublicKeyFilePath)) {
+            throwError($V3->getError() ?: '异步通知验证未通过');
+        }
+        // 获取异步回调的请求参数
+        $this->notifyParams = $V3->getNotifyParams();
+    }
+
+    /**
+     * 获取异步回调的请求参数
+     * @return array
+     */
+    public function getNotifyParams(): array
+    {
+        return $this->notifyParams;
+    }
+
     /**
      * 获取「微信支付平台证书」或者「微信支付平台公钥」的路径
      * @param $templateInfo
@@ -125,7 +149,7 @@ class Notify
             $Payment = $this->getPayment($tradeInfo);
             // 验证异步通知参数是否合法
             if (!$Payment->notify()) {
-                throwError($Payment->getError() ?: '异步通知验证未通过');
+                throwError($Payment->getError() ?: '支付宝异步通知验证未通过');
             }
             // 订单支付成功事件
             $this->orderPaySucces($tradeInfo, $Payment->getNotifyParams());
@@ -151,7 +175,7 @@ class Notify
         try {
             // 订单支付成功业务处理 (商城订单)
             if ($tradeInfo['order_type'] == OrderTypeEnum::ORDER) {
-                $service = new OrderPaySuccesService;
+                $service = new OrderPaySuccessService;
                 $service->setOrderNo($tradeInfo['order_no'])
                     ->setMethod($tradeInfo['pay_method'])
                     ->setTradeId($tradeInfo['trade_id'])
@@ -160,7 +184,7 @@ class Notify
             }
             // 订单支付成功业务处理 (余额充值订单)
             if ($tradeInfo['order_type'] == OrderTypeEnum::RECHARGE) {
-                $service = new RechargePaySuccesService;
+                $service = new RechargePaySuccessService;
                 $service->setOrderNo($tradeInfo['order_no'])
                     ->setMethod($tradeInfo['pay_method'])
                     ->setTradeId($tradeInfo['trade_id'])

+ 5 - 9
app/common/library/payment/gateway/driver/wechat/V3.php

@@ -179,30 +179,26 @@ class V3
 
     /**
      * 支付成功后的异步通知
-     * @param string $apiv3Key 微信支付v3秘钥
+     * @param string $apiV3Key 微信支付v3秘钥
      * @param string $platformCertificateOrPublicKeyFilePath 微信支付公钥或平台证书路径
      * @return bool|string
      */
-    public function notify(string $apiv3Key, string $platformCertificateOrPublicKeyFilePath)
+    public function notify(string $apiV3Key, string $platformCertificateOrPublicKeyFilePath)
     {
         // 微信异步通知参数
         $header = \request()->header();
         $inBody = file_get_contents('php://input');
-
         // 微信支付平台证书
         $platformPublicKeyInstance = Rsa::from("file://{$platformCertificateOrPublicKeyFilePath}", Rsa::KEY_TYPE_PUBLIC);
-
         // 检查通知时间偏移量,允许5分钟之内的偏移
         // $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
         $timeOffsetStatus = true;
-
         $verifiedStatus = Rsa::verify(
         // 构造验签名串
             Formatter::joinedByLineFeed($header['wechatpay-timestamp'], $header['wechatpay-nonce'], $inBody),
             $header['wechatpay-signature'],
             $platformPublicKeyInstance
         );
-
         if ($timeOffsetStatus && $verifiedStatus) {
             // 转换通知的JSON文本消息为PHP Array数组
             $inBodyArray = (array)json_decode($inBody, true);
@@ -213,12 +209,12 @@ class V3
                 'associated_data' => $aad
             ]] = $inBodyArray;
             // 加密文本消息解密
-            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
+            $inBodyResource = AesGcm::decrypt($ciphertext, $apiV3Key, $nonce, $aad);
             // 把解密后的文本转换为PHP Array数组
             $this->notifyParams = helper::jsonDecode($inBodyResource);
             // 记录日志
-            Log::append('Wechat-notify', ['message' => '微信异步回调验证成功']);
-            return $this->notifyParams['out_trade_no'];
+            Log::append('Wechat-notify', ['message' => '微信异步回调验证成功', 'notifyParams' => $this->notifyParams]);
+            return true;
         }
         return false;
     }