```
feat(shopro): 添加分账诊断工具的仅确认模式 添加 --confirm-only 参数用于排查 22000002 错误,该参数仅执行延时分账确认而不进行实际分账, 帮助识别错误是否来自分账接收方配置问题。 - 在 split_commission_verify.php 中新增 --confirm-only 命令行选项 - 实现 trySplitOrder 函数支持仅确认模式 - 更新帮助文档显示新的命令选项 fix(shopro_huifu): 优化分账确认请求处理 修复分账确认请求的参数处理逻辑,并添加调试功能以预览确认请求体内容。 - 新增 previewDelaytransConfirmExtend 方法预览确认请求 - 新增 executeConfirmOnlyByOrderId 方法仅执行确认操作 - 优化 confirm 方法的日志记录和参数处理 ```
这个提交包含在:
@@ -9,6 +9,7 @@
|
|||||||
* php addons/shopro/tests/split_commission_verify.php recent-success [limit=20]
|
* 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 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} --execute # 补跑汇付分账(慎用)
|
||||||
|
* php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --confirm-only # 仅延时分账确认、不分账(排查22000002)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (PHP_SAPI !== 'cli') {
|
if (PHP_SAPI !== 'cli') {
|
||||||
@@ -58,11 +59,12 @@ switch ($command) {
|
|||||||
case 'try-split':
|
case 'try-split':
|
||||||
$sn = $argv[2] ?? '';
|
$sn = $argv[2] ?? '';
|
||||||
$execute = in_array('--execute', $argv, true);
|
$execute = in_array('--execute', $argv, true);
|
||||||
trySplitOrder($sn, $execute);
|
$confirmOnly = in_array('--confirm-only', $argv, true);
|
||||||
|
trySplitOrder($sn, $execute, $confirmOnly);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
echo "未知命令: {$command}\n";
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +569,7 @@ function diagnoseMissingSplitOrders($limit)
|
|||||||
echo " php addons/shopro/tests/split_commission_verify.php try-split {order_sn} --execute\n";
|
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 === '') {
|
if ($orderSn === '') {
|
||||||
fwrite(STDERR, "请指定订单号\n");
|
fwrite(STDERR, "请指定订单号\n");
|
||||||
@@ -583,16 +585,22 @@ function trySplitOrder($orderSn, $execute)
|
|||||||
echo " · {$line}\n";
|
echo " · {$line}\n";
|
||||||
}
|
}
|
||||||
printTrySplitChannelHints($order);
|
printTrySplitChannelHints($order);
|
||||||
if (!$execute) {
|
|
||||||
echo "\n未执行分账。确认汇付控台分账关系 + 非 FREEZE 后再加 --execute\n";
|
|
||||||
echo "(本单若佣金已入钱包,--execute 成功可能造成测试单双轨,正式单请先对齐 split_timing 与分销结算)\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
|
if (!class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
|
||||||
fwrite(STDERR, "未安装 shopro_huifu\n");
|
fwrite(STDERR, "未安装 shopro_huifu\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
$service = new \addons\shopro_huifu\library\HuifuSplitService();
|
$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, '.', '');
|
$payFee = number_format((float) $order['pay_fee'], 2, '.', '');
|
||||||
$confirmBase = $service->previewConfirmPayFee($order['order_sn'], $payFee);
|
$confirmBase = $service->previewConfirmPayFee($order['order_sn'], $payFee);
|
||||||
echo "\n汇付确认基数: pay_fee={$payFee} → confirm_base={$confirmBase}";
|
echo "\n汇付确认基数: pay_fee={$payFee} → confirm_base={$confirmBase}";
|
||||||
@@ -618,6 +626,18 @@ function trySplitOrder($orderSn, $execute)
|
|||||||
echo '查单摘要: 失败 ' . $e->getMessage() . "\n";
|
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']);
|
$ok = $service->executeByOrderId($order['id']);
|
||||||
if ($ok) {
|
if ($ok) {
|
||||||
echo "\n分账执行返回: 成功\n";
|
echo "\n分账执行返回: 成功\n";
|
||||||
|
|||||||
@@ -136,6 +136,76 @@ class HuifuSplitService
|
|||||||
return $this->executeByOrderId($orderId);
|
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)
|
protected function confirm($ext, array $acctInfos, $confirmReqSeqId, $confirmBase = null)
|
||||||
{
|
{
|
||||||
SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmRequest.php');
|
SdkLoader::requireRequest('V2TradePaymentDelaytransConfirmRequest.php');
|
||||||
@@ -144,22 +214,16 @@ class HuifuSplitService
|
|||||||
$request->setReqSeqId($confirmReqSeqId);
|
$request->setReqSeqId($confirmReqSeqId);
|
||||||
$request->setHuifuId($this->config->get('huifu_id'));
|
$request->setHuifuId($this->config->get('huifu_id'));
|
||||||
|
|
||||||
$confirmBase = $confirmBase !== null
|
|
||||||
? number_format((float) $confirmBase, 2, '.', '')
|
|
||||||
: null;
|
|
||||||
$bunch = ['acct_infos' => $acctInfos];
|
$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);
|
$acctSplitBunch = json_encode($bunch, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
\think\Log::info('[shopro_huifu] delaytrans confirm acct_split_bunch=' . $acctSplitBunch);
|
$extend = [
|
||||||
$request->setExtendInfo([
|
'org_req_date' => $ext['req_date'],
|
||||||
'org_req_date' => $ext['req_date'],
|
'org_req_seq_id' => $ext['req_seq_id'],
|
||||||
'org_req_seq_id' => $ext['req_seq_id'],
|
'org_hf_seq_id' => $ext['hf_seq_id'] ?? '',
|
||||||
'org_hf_seq_id' => $ext['hf_seq_id'] ?? '',
|
|
||||||
'acct_split_bunch' => $acctSplitBunch,
|
'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);
|
$result = $this->client->postRequest($request);
|
||||||
return $this->client->parseResponse($result);
|
return $this->client->parseResponse($result);
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户