Parcourir la source

支付流程优化

shish il y a 1 mois
Parent
commit
772cc1e46a

+ 46 - 29
app-ghs/controllers/OrderController.php

@@ -735,7 +735,17 @@ class OrderController extends BaseController
         }
     }
 
-    //微信和支付宝付款码支付 ssh 2021.4.11
+    /**
+     * 微信和支付宝付款码支付
+     * 职责:处理商户扫描客户付款码(被扫支付)的请求,调用拉卡拉支付接口并更新本地订单状态
+     * 入参:GET 传参 id (订单ID), authCode (付款码)
+     * 返回:JSON 格式的支付结果(SUCCESS/FAILURE)
+     * 副作用:更新订单及采购单的支付状态、支付方式,记录支付日志,触发云打印、跑腿呼叫及语音播报
+     * 关键边界:
+     *   1. 验证订单及采购单的有效性与时效性
+     *   2. 校验付款码格式(微信/支付宝)
+     *   3. 优化:将拉卡拉外部网络请求移出数据库事务,避免因网络延迟长时间占用数据库连接和行锁
+     */
     public function actionCodePay()
     {
         ini_set('date.timezone', 'Asia/Shanghai');
@@ -817,8 +827,6 @@ class OrderController extends BaseController
         }
         $totalFee = $cg->actPrice ?? 0;
 
-        $connection = Yii::$app->db;
-        $transaction = $connection->beginTransaction();
         try {
             $subject = '购买花材 ' . $orderSn;
             $capitalType = dict::getDict('capitalType', 'xhPurchase', 'id');
@@ -842,39 +850,49 @@ class OrderController extends BaseController
                 'subject' => $subject,
                 'authCode' => $authCode,
             ];
+
+            // 复杂分支/关键逻辑:在调用外部支付接口前不开启事务,防止网络延迟卡死数据库连接
             $response = $laResource->scanPay($scanParams);
             if (isset($response['code']) && $response['code'] == 'BBS00000') {
 
-                $account_type = $response['resp_data']['account_type'] ?? '';
-                $payWayType = dict::getDict('payWay', 'wxPay');
-                if ($account_type == 'WECHAT') {
+                // 复杂分支/关键逻辑:支付成功后,开启数据库事务,确保本地订单状态更新的原子性
+                $connection = Yii::$app->db;
+                $transaction = $connection->beginTransaction();
+                try {
+                    $account_type = $response['resp_data']['account_type'] ?? '';
                     $payWayType = dict::getDict('payWay', 'wxPay');
-                }
-                if ($account_type == 'ALIPAY') {
-                    $payWayType = dict::getDict('payWay', 'alipay');
-                }
-                $order->payWay = $payWayType;
-                $order->save();
-                $cg->payWay = $payWayType;
-                $cg->save();
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+                    $order->payWay = $payWayType;
+                    $order->save();
+                    $cg->payWay = $payWayType;
+                    $cg->save();
 
-                $transactionId = $response['resp_data']['trade_no'] ?? '';
-                $openId = '';
-                $aliUserId = '';
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $openId = '';
+                    $aliUserId = '';
 
-                $cg->onlinePay = dict::getDict('onlinePay', 'yes');
-                $cg->codePay = 1;
-                $cg->payOpenId = $openId;
-                $cg->aliUserId = $aliUserId;
-                $cg->save();
+                    $cg->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $cg->codePay = 1;
+                    $cg->payOpenId = $openId;
+                    $cg->aliUserId = $aliUserId;
+                    $cg->save();
 
-                $order->onlinePay = dict::getDict('onlinePay', 'yes');
-                $order->codePay = 1;
-                $order->save();
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->codePay = 1;
+                    $order->save();
 
-                $attach = '';
-                payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
-                $transaction->commit();
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+                } catch (\Exception $e) {
+                    $transaction->rollBack();
+                    throw $e;
+                }
 
                 $saleId = $order->id ?? 0;
                 $order = OrderClass::getById($saleId, true);
@@ -922,7 +940,6 @@ class OrderController extends BaseController
                 util::success(['returnStatus' => 'FAILURE', 'returnCode' => $retCode], $msg);
             }
         } catch (\Exception $e) {
-            $transaction->rollBack();
             Yii::error("支付失败原因:" . $e->getMessage());
             util::fail('支付失败');
         }

+ 35 - 17
app-hd/controllers/OrderController.php

@@ -422,7 +422,17 @@ class OrderController extends BaseController
         }
     }
 
-    //微信和支付宝付款码支付 ssh 2021.4.11
+    /**
+     * 微信和支付宝付款码支付
+     * 职责:处理商户扫描客户付款码(被扫支付)的请求,调用拉卡拉支付接口并更新本地订单状态(零售端)
+     * 入参:GET 传参 id (订单ID), authCode (付款码), version (版本号)
+     * 返回:JSON 格式的支付结果(SUCCESS/FAILURE)
+     * 副作用:更新订单支付状态、支付方式,记录支付日志,触发云打印、跑腿呼叫及语音播报
+     * 关键边界:
+     *   1. 验证订单有效性与待付款状态
+     *   2. 校验付款码格式(微信/支付宝)
+     *   3. 优化:将拉卡拉外部网络请求移出数据库事务,避免因网络延迟长时间占用数据库连接和行锁
+     */
     public function actionCodePay()
     {
         ini_set('date.timezone', 'Asia/Shanghai');
@@ -491,8 +501,6 @@ class OrderController extends BaseController
         $totalFee = $order->mainPay ?? 0;
         $capitalType = dict::getDict('capitalType', 'xhOrder', 'id');
 
-        $connection = Yii::$app->db;
-        $transaction = $connection->beginTransaction();
         try {
 
             $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
@@ -515,25 +523,35 @@ class OrderController extends BaseController
                 'subject' => $subject,
                 'authCode' => $authCode,
             ];
+
+            // 复杂分支/关键逻辑:在调用外部支付接口前不开启事务,防止网络延迟卡死数据库连接
             $response = $laResource->scanPay($scanParams);
 
             if (isset($response['code']) && $response['code'] == 'BBS00000') {
 
-                $account_type = $response['resp_data']['account_type'] ?? '';
-                $payWayType = dict::getDict('payWay', 'wxPay');
-                if ($account_type == 'WECHAT') {
+                // 复杂分支/关键逻辑:支付成功后,开启数据库事务,确保本地订单状态更新的原子性
+                $connection = Yii::$app->db;
+                $transaction = $connection->beginTransaction();
+                try {
+                    $account_type = $response['resp_data']['account_type'] ?? '';
                     $payWayType = dict::getDict('payWay', 'wxPay');
+                    if ($account_type == 'WECHAT') {
+                        $payWayType = dict::getDict('payWay', 'wxPay');
+                    }
+                    if ($account_type == 'ALIPAY') {
+                        $payWayType = dict::getDict('payWay', 'alipay');
+                    }
+                    $order->payWay = $payWayType;
+                    $transactionId = $response['resp_data']['trade_no'] ?? '';
+                    $order->onlinePay = dict::getDict('onlinePay', 'yes');
+                    $order->save();
+                    $attach = '';
+                    payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
+                    $transaction->commit();
+                } catch (\Exception $e) {
+                    $transaction->rollBack();
+                    throw $e;
                 }
-                if ($account_type == 'ALIPAY') {
-                    $payWayType = dict::getDict('payWay', 'alipay');
-                }
-                $order->payWay = $payWayType;
-                $transactionId = $response['resp_data']['trade_no'] ?? '';
-                $order->onlinePay = dict::getDict('onlinePay', 'yes');
-                $order->save();
-                $attach = '';
-                payUtil::thirdPay($payWayType, $capitalType, $orderSn, $totalFee, $attach, $transactionId);
-                $transaction->commit();
 
                 //解决重复通知
                 $cacheKey = 'hd_shop_order_pay_' . $orderSn;
@@ -578,7 +596,7 @@ class OrderController extends BaseController
             }
 
         } catch (\Exception $e) {
-            $transaction->rollBack();
+            Yii::error("零售端支付失败原因:" . $e->getMessage());
             util::fail('支付失败');
         }
     }

+ 28 - 2
biz/shop/classes/ShopExtClass.php

@@ -206,7 +206,16 @@ class ShopExtClass extends BaseClass
         }
     }
 
-    //零售花店收款声音播放 ssh 20220423
+    /**
+     * 零售花店收款声音播放
+     * 职责:向外部音箱接口发送语音播报请求,提示商户收款成功(零售端)
+     * 入参:ActiveRecord $order (订单模型实例)
+     * 返回:bool (是否成功触发播报)
+     * 副作用:发送外部 HTTP 网络请求
+     * 关键边界:
+     *   1. 仅限门店订单、在线支付且已付款的订单播报
+     *   2. 优化:设置极短的 cURL 连接超时与执行超时限制,并强制使用 IPv4 解析,防止因外部音箱接口延迟拖垮收银支付主流程
+     */
     public static function hdGatheringReport($order)
     {
         if ($order->fromType != dict::getDict("fromType", "shop")) {
@@ -229,6 +238,10 @@ class ShopExtClass extends BaseClass
             $sound = $payWay == dict::getDict('payWay', 'wxPay') ? "微信收款{$actPrice}元,谢谢惠顾,欢迎下次光临" : "支付宝收款{$actPrice}元,谢谢惠顾,欢迎下次光临";
             $url = "https://speaker.17laimai.cn/notify.php?id={$shopExt->lbSn}&token=HK1626595800&version={$shopExt->lbVersion}&message=" . $sound;
             $curl = new curl\Curl();
+            // 复杂分支/关键逻辑:设置 cURL 连接和执行超时,并强制使用 IPv4,防止因外部音箱接口卡顿或 DNS 解析慢拖垮收银结账主进程
+            $curl->setOption(CURLOPT_CONNECTTIMEOUT, 1); // 连接超时限制为 1 秒,避免网络握手长时间卡死
+            $curl->setOption(CURLOPT_TIMEOUT, 1);        // 总执行时间限制为 1 秒,避免请求挂起时间过长
+            $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 强制 IPv4 解析,防止 IPv6 解析超时重试
             $curl->get($url);
         }
     }
@@ -275,7 +288,16 @@ class ShopExtClass extends BaseClass
         }
     }
 
-    //供货商收款声音播放 ssh 20230421
+    /**
+     * 供货商收款声音播放
+     * 职责:向外部音箱接口发送语音播报请求,提示商户收款成功
+     * 入参:ActiveRecord $order (订单模型实例)
+     * 返回:bool (是否成功触发播报)
+     * 副作用:发送外部 HTTP 网络请求
+     * 关键边界:
+     *   1. 仅限门店订单、在线支付且已付款的订单播报
+     *   2. 优化:设置极短的 cURL 连接超时与执行超时限制,并强制使用 IPv4 解析,防止因外部音箱接口延迟拖垮收银支付主流程
+     */
     public static function ghsGatheringReport($order)
     {
         if ($order->fromType != dict::getDict("fromType", "shop")) {
@@ -298,6 +320,10 @@ class ShopExtClass extends BaseClass
             $sound = $payWay == dict::getDict('payWay', 'wxPay') ? "微信收款{$actPrice}元,谢谢惠顾,欢迎下次光临" : "支付宝收款{$actPrice}元,谢谢惠顾,欢迎下次光临";
             $url = "https://speaker.17laimai.cn/notify.php?id={$shopExt->lbSn}&token=HK1626595800&version={$shopExt->lbVersion}&message=" . $sound;
             $curl = new curl\Curl();
+            // 复杂分支/关键逻辑:设置 cURL 连接和执行超时,并强制使用 IPv4,防止因外部音箱接口卡顿或 DNS 解析慢拖垮收银结账主进程
+            $curl->setOption(CURLOPT_CONNECTTIMEOUT, 1); // 连接超时限制为 1 秒,避免网络握手长时间卡死
+            $curl->setOption(CURLOPT_TIMEOUT, 1);        // 总执行时间限制为 1 秒,避免请求挂起时间过长
+            $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 强制 IPv4 解析,防止 IPv6 解析超时重试
             $curl->get($url);
         }
     }