From 4b537410e0759b510c6430d8b95dd8f69b8758bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=AF=E4=B9=90=E5=8A=A0=E5=86=B0?= <11869599+addie66@user.noreply.gitee.com> Date: Mon, 5 Oct 2026 10:47:41 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(shopro):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=86=E8=B4=A6=E8=AF=8A=E6=96=AD=E5=B7=A5=E5=85=B7=E7=9A=84?= =?UTF-8?q?=E4=BB=85=E7=A1=AE=E8=AE=A4=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 --confirm-only 参数用于排查 22000002 错误,该参数仅执行延时分账确认而不进行实际分账, 帮助识别错误是否来自分账接收方配置问题。 - 在 split_commission_verify.php 中新增 --confirm-only 命令行选项 - 实现 trySplitOrder 函数支持仅确认模式 - 更新帮助文档显示新的命令选项 fix(shopro_huifu): 优化分账确认请求处理 修复分账确认请求的参数处理逻辑,并添加调试功能以预览确认请求体内容。 - 新增 previewDelaytransConfirmExtend 方法预览确认请求 - 新增 executeConfirmOnlyByOrderId 方法仅执行确认操作 - 优化 confirm 方法的日志记录和参数处理 ``` --- .../shopro/tests/split_commission_verify.php | 36 ++++++-- .../library/HuifuSplitService.php | 90 ++++++++++++++++--- 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/addons/shopro/tests/split_commission_verify.php b/addons/shopro/tests/split_commission_verify.php index f1b30ea..0105a2e 100644 --- a/addons/shopro/tests/split_commission_verify.php +++ b/addons/shopro/tests/split_commission_verify.php @@ -9,6 +9,7 @@ * php addons/shopro/tests/split_commission_verify.php recent-success [limit=20] * php addons/shopro/tests/split_commission_verify.php diagnose-missing [limit=20] * php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --execute # 补跑汇付分账(慎用) + * php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --confirm-only # 仅延时分账确认、不分账(排查22000002) */ if (PHP_SAPI !== 'cli') { @@ -58,11 +59,12 @@ switch ($command) { case 'try-split': $sn = $argv[2] ?? ''; $execute = in_array('--execute', $argv, true); - trySplitOrder($sn, $execute); + $confirmOnly = in_array('--confirm-only', $argv, true); + trySplitOrder($sn, $execute, $confirmOnly); break; default: echo "未知命令: {$command}\n"; - echo "可用: summary | anomalies | order {order_sn} | recent-success [limit] | diagnose-missing [limit] | try-split {order_sn} --execute\n"; + echo "可用: summary | anomalies | order {order_sn} | recent-success [limit] | diagnose-missing [limit] | try-split {order_sn} [--execute|--confirm-only]\n"; exit(1); } @@ -567,7 +569,7 @@ function diagnoseMissingSplitOrders($limit) echo " php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --execute\n"; } -function trySplitOrder($orderSn, $execute) +function trySplitOrder($orderSn, $execute, $confirmOnly = false) { if ($orderSn === '') { fwrite(STDERR, "请指定订单号\n"); @@ -583,16 +585,22 @@ function trySplitOrder($orderSn, $execute) echo " · {$line}\n"; } printTrySplitChannelHints($order); - if (!$execute) { - echo "\n未执行分账。确认汇付控台分账关系 + 非 FREEZE 后再加 --execute\n"; - echo "(本单若佣金已入钱包,--execute 成功可能造成测试单双轨,正式单请先对齐 split_timing 与分销结算)\n"; - return; - } if (!class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) { fwrite(STDERR, "未安装 shopro_huifu\n"); exit(1); } $service = new \addons\shopro_huifu\library\HuifuSplitService(); + $preview = $service->previewDelaytransConfirmExtend($order['id']); + if ($preview) { + echo "\n【confirm 请求预览(写入日志 + 下方 JSON)】\n"; + echo json_encode($preview, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"; + } + if (!$execute && !$confirmOnly) { + echo "\n未执行分账。确认汇付控台分账关系 + 非 FREEZE 后再加 --execute\n"; + echo "排查 22000002 可先: try-split {$orderSn} --confirm-only(仅确认、不分账)\n"; + echo "(本单若佣金已入钱包,--execute 成功可能造成测试单双轨,正式单请先对齐 split_timing 与分销结算)\n"; + return; + } $payFee = number_format((float) $order['pay_fee'], 2, '.', ''); $confirmBase = $service->previewConfirmPayFee($order['order_sn'], $payFee); echo "\n汇付确认基数: pay_fee={$payFee} → confirm_base={$confirmBase}"; @@ -618,6 +626,18 @@ function trySplitOrder($orderSn, $execute) echo '查单摘要: 失败 ' . $e->getMessage() . "\n"; } + if ($confirmOnly) { + echo "\n>>> 执行 --confirm-only(不传 acct_split_bunch)\n"; + try { + $result = $service->executeConfirmOnlyByOrderId($order['id']); + echo "仅确认返回: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n"; + echo "若成功:说明延迟户/解冻正常,22000002 来自分账接收方 6666000245630872 未在控台绑定或 rule_origin≠01\n"; + } catch (\Throwable $e) { + echo "仅确认失败: " . $e->getMessage() . "\n"; + } + return; + } + $ok = $service->executeByOrderId($order['id']); if ($ok) { echo "\n分账执行返回: 成功\n"; diff --git a/addons/shopro_huifu/library/HuifuSplitService.php b/addons/shopro_huifu/library/HuifuSplitService.php index 648de9c..8e79ffc 100644 --- a/addons/shopro_huifu/library/HuifuSplitService.php +++ b/addons/shopro_huifu/library/HuifuSplitService.php @@ -136,6 +136,76 @@ class HuifuSplitService return $this->executeByOrderId($orderId); } + /** + * 预览延时分账 confirm 请求体(调试 22000002) + */ + public function previewDelaytransConfirmExtend($orderId) + { + $order = Order::get($orderId); + if (!$order || $order['pay_type'] !== 'huifu_wechat') { + return null; + } + $calculator = new SplitCalculator(); + $items = $calculator->calculate($orderId); + if (empty($items)) { + return ['error' => '分账明细为空']; + } + $payFee = number_format((float) $order['pay_fee'], 2, '.', ''); + $confirmBase = $this->resolveConfirmPayFee($order['order_sn'], $payFee); + $allocated = '0.00'; + foreach ($items as $item) { + $allocated = bcadd($allocated, number_format((float) $item['amount'], 2, '.', ''), 2); + } + if (bccomp($allocated, $confirmBase, 2) > 0) { + $items = $this->scaleSplitItemsToCap($items, $allocated, $confirmBase); + } + $acctInfos = $this->buildAcctInfos($items, $confirmBase); + $ext = HuifuPayService::getPayExt($orderId); + $bunch = json_encode(['acct_infos' => $acctInfos], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + return [ + 'confirm_base' => $confirmBase, + 'payment_huifu_id' => $this->config->get('huifu_id'), + 'org_req_date' => $ext['req_date'] ?? '', + 'org_req_seq_id' => $ext['req_seq_id'] ?? '', + 'org_hf_seq_id' => $ext['hf_seq_id'] ?? '', + 'acct_split_bunch' => $bunch, + ]; + } + + /** + * 仅交易确认、不传分账串(用于排查 22000002 是否来自分账接收方配置) + */ + public function executeConfirmOnlyByOrderId($orderId) + { + $order = Order::get($orderId); + if (!$order || $order['pay_type'] !== 'huifu_wechat') { + return false; + } + $ext = HuifuPayService::getPayExt($orderId); + if (!$ext) { + throw new \RuntimeException('无 shopro_huifu_pay_ext'); + } + $blockReason = $this->getDelayFundsBlockReason($order['order_sn']); + if ($blockReason !== null) { + throw new \RuntimeException($blockReason); + } + $outOrderNo = 'HFPC' . date('YmdHis') . $orderId; + SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmRequest.php'); + $request = new \BsPaySdk\request\V2TradePaymentDelaytransConfirmRequest(); + $request->setReqDate(date('Ymd')); + $request->setReqSeqId($outOrderNo); + $request->setHuifuId($this->config->get('huifu_id')); + $extend = [ + 'org_req_date' => $ext['req_date'], + 'org_req_seq_id' => $ext['req_seq_id'], + 'org_hf_seq_id' => $ext['hf_seq_id'] ?? '', + ]; + \think\Log::write('[shopro_huifu] delaytrans confirm-only extend=' . json_encode($extend, JSON_UNESCAPED_UNICODE), 'info'); + $request->setExtendInfo($extend); + $result = $this->client->postRequest($request); + return $this->client->parseResponse($result); + } + protected function confirm($ext, array $acctInfos, $confirmReqSeqId, $confirmBase = null) { SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmRequest.php'); @@ -144,22 +214,16 @@ class HuifuSplitService $request->setReqSeqId($confirmReqSeqId); $request->setHuifuId($this->config->get('huifu_id')); - $confirmBase = $confirmBase !== null - ? number_format((float) $confirmBase, 2, '.', '') - : null; $bunch = ['acct_infos' => $acctInfos]; - if ($confirmBase !== null && bccomp($confirmBase, '0.00', 2) > 0) { - // 本次交易确认总额(待确认全额);acct_infos 为向外分出的部分,其余留主商户 - $bunch['total_div_amt'] = $confirmBase; - } $acctSplitBunch = json_encode($bunch, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); - \think\Log::info('[shopro_huifu] delaytrans confirm acct_split_bunch=' . $acctSplitBunch); - $request->setExtendInfo([ - 'org_req_date' => $ext['req_date'], - 'org_req_seq_id' => $ext['req_seq_id'], - 'org_hf_seq_id' => $ext['hf_seq_id'] ?? '', + $extend = [ + 'org_req_date' => $ext['req_date'], + 'org_req_seq_id' => $ext['req_seq_id'], + 'org_hf_seq_id' => $ext['hf_seq_id'] ?? '', 'acct_split_bunch' => $acctSplitBunch, - ]); + ]; + \think\Log::write('[shopro_huifu] delaytrans confirm extend=' . json_encode($extend, JSON_UNESCAPED_UNICODE), 'info'); + $request->setExtendInfo($extend); $result = $this->client->postRequest($request); return $this->client->parseResponse($result);