```
feat(shopro): 完善汇付分账功能的错误处理和调试信息 - 添加主商户号验证逻辑,当platform_huifu_id为空时使用插件主商户 - 改进分账剩余金额显示逻辑,区分主商户留存和平台分账情况 - 更新错误提示信息,明确2200002错误的处理方式 - 新增acct_infos预览功能,便于调试分账配置 - 优化分账服务中的商户号处理,避免主商户重复分账 fix(shopro_huifu): 修复汇付分账配置错误提示和分账逻辑 - 更新22000002错误排查说明,简化表述并突出关键点 - 修复分账服务中构建分账信息的逻辑,确保不包含收款主商户 - 添加日志记录,当跳过分账方为自己时进行警告 - 优化剩余金额处理,当日志平台即收款主商户时剩余款项保留在主商户 - 增强分账验证,当无有效分账接收方时抛出异常 ```
这个提交包含在:
@@ -428,13 +428,18 @@ function diagnoseOrderSplitBlockers($orderId)
|
||||
);
|
||||
}
|
||||
$payFee = number_format((float) $order['pay_fee'], 2, '.', '');
|
||||
$payHuifuId = trim((string) Db::name('shopro_huifu_config')->where('name', 'huifu_id')->value('value'));
|
||||
$platformId = trim((string) ($sc['platform_huifu_id'] ?? ''));
|
||||
if ($platformId === '') {
|
||||
$platformId = $payHuifuId;
|
||||
}
|
||||
$remain = bcsub($payFee, number_format($sum, 2, '.', ''), 2);
|
||||
if (bccomp($remain, '0.00', 2) > 0) {
|
||||
$reasons[] = ' → platform 剩余 huifu_id=' . ($platformId !== '' ? $platformId : '(未配 platform_huifu_id,将用插件主商户)') . ' 金额=' . $remain;
|
||||
}
|
||||
if ($platformId === '') {
|
||||
$reasons[] = ' ⚠ platform_huifu_id 为空,请与汇付收款主商户 huifu_id 一致,否则易 22000002';
|
||||
if ($platformId !== '' && $platformId === $payHuifuId) {
|
||||
$reasons[] = ' → 主商户留存(不写 acct_infos)huifu_id=' . $payHuifuId . ' 金额=' . $remain;
|
||||
} else {
|
||||
$reasons[] = ' → 平台分账 huifu_id=' . $platformId . ' 金额=' . $remain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +493,7 @@ function printTrySplitChannelHints(array $order)
|
||||
if ($latest && intval($latest['status']) === 4 && !empty($latest['wx_result'])) {
|
||||
$raw = is_string($latest['wx_result']) ? $latest['wx_result'] : json_encode($latest['wx_result'], JSON_UNESCAPED_UNICODE);
|
||||
if (strpos($raw, '22000002') !== false || strpos($raw, '分账配置') !== false) {
|
||||
echo "\n【最近失败】id={$latest['id']} → 22000002 分账配置有误:控台绑定上述 huifu_id 后再重试\n";
|
||||
echo "\n【最近失败】id={$latest['id']} → 22000002:若已部署「主商户不写 acct_infos」仍失败,请在控台绑定子商户分账关系\n";
|
||||
} elseif (strpos($raw, 'FREEZE') !== false) {
|
||||
echo "\n【最近失败】id={$latest['id']} → 资金冻结,见上查单\n";
|
||||
}
|
||||
@@ -498,6 +503,44 @@ function printTrySplitChannelHints(array $order)
|
||||
if ($payHuifuId) {
|
||||
echo " · 插件支付 huifu_id={$payHuifuId}(应与 platform_huifu_id / 控台主商户一致)\n";
|
||||
}
|
||||
|
||||
printHuifuAcctInfosPreview($order);
|
||||
}
|
||||
|
||||
function printHuifuAcctInfosPreview(array $order)
|
||||
{
|
||||
if (!class_exists('\\addons\\shopro\\library\\split\\SplitCalculator')
|
||||
|| !class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
|
||||
return;
|
||||
}
|
||||
$items = (new \addons\shopro\library\split\SplitCalculator())->calculate($order['id']);
|
||||
if (empty($items)) {
|
||||
return;
|
||||
}
|
||||
$service = new \addons\shopro_huifu\library\HuifuSplitService();
|
||||
$payFee = number_format((float) $order['pay_fee'], 2, '.', '');
|
||||
$confirmBase = $service->previewConfirmPayFee($order['order_sn'], $payFee);
|
||||
$ref = new \ReflectionClass($service);
|
||||
$method = $ref->getMethod('buildAcctInfos');
|
||||
$method->setAccessible(true);
|
||||
try {
|
||||
$acctInfos = $method->invoke($service, $items, $confirmBase);
|
||||
} catch (\Throwable $e) {
|
||||
echo "\n【acct_infos 预览】组装失败: " . $e->getMessage() . "\n";
|
||||
return;
|
||||
}
|
||||
echo "\n【将提交汇付 acct_infos】confirm_base={$confirmBase}\n";
|
||||
foreach ($acctInfos as $row) {
|
||||
echo ' · huifu_id=' . ($row['huifu_id'] ?? '-') . ' div_amt=' . ($row['div_amt'] ?? '-') . "\n";
|
||||
}
|
||||
$splitSum = '0.00';
|
||||
foreach ($acctInfos as $row) {
|
||||
$splitSum = bcadd($splitSum, number_format((float) ($row['div_amt'] ?? 0), 2, '.', ''), 2);
|
||||
}
|
||||
$stay = bcsub($confirmBase, $splitSum, 2);
|
||||
if (bccomp($stay, '0.00', 2) > 0) {
|
||||
echo " · (其余 {$stay} 元留在收款主商户,不写入分账串)\n";
|
||||
}
|
||||
}
|
||||
|
||||
function diagnoseMissingSplitOrders($limit)
|
||||
|
||||
@@ -399,7 +399,7 @@ class HuifuClient
|
||||
|
||||
if ($respCode === '22000002') {
|
||||
|
||||
$parts[] = '排查: 1) 汇付控台主商户已开通延时分账/分账业务 2) acct_split_bunch 中每个 huifu_id 已在控台绑定为分账接收方 3) 商城「平台汇付商户号 platform_huifu_id」= 收款主商户 huifu_id 4) 分账接收方账号为进件 huifu_id(非微信 openid)5) 推广员进件成功且与 receiver 一致';
|
||||
$parts[] = '排查: 1) 控台主商户已开通延时分账 2) 子商户 huifu_id 已绑为分账接收方 3) acct_infos 勿含收款主商户(剩余款留主商户)4) platform_huifu_id=主商户时仅分出推广员等子户 5) receiver 为进件 huifu_id';
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,13 @@ class HuifuSplitService
|
||||
|
||||
protected function buildAcctInfos(array $items, $payFee)
|
||||
{
|
||||
$payFee = number_format((float) $payFee, 2, '.', '');
|
||||
$paymentHuifuId = trim((string) $this->config->get('huifu_id'));
|
||||
$platformHuifuId = trim((string) ((new SplitConfig())->all()['platform_huifu_id'] ?? $paymentHuifuId));
|
||||
if ($platformHuifuId === '') {
|
||||
$platformHuifuId = $paymentHuifuId;
|
||||
}
|
||||
|
||||
$infos = [];
|
||||
$allocated = '0.00';
|
||||
foreach ($items as $item) {
|
||||
@@ -165,28 +172,45 @@ class HuifuSplitService
|
||||
if (bccomp($amt, '0.00', 2) <= 0) {
|
||||
continue;
|
||||
}
|
||||
$receiverHuifuId = trim((string) ($item['receiver_account'] ?? ''));
|
||||
if ($receiverHuifuId === '') {
|
||||
continue;
|
||||
}
|
||||
// 汇付要求:分账方勿与收款主商户相同(剩余款留在主商户,勿写入 acct_infos)
|
||||
if ($paymentHuifuId !== '' && $receiverHuifuId === $paymentHuifuId) {
|
||||
\think\Log::warning('[shopro_huifu] split skip self receiver in acct_infos huifu_id=' . $receiverHuifuId);
|
||||
continue;
|
||||
}
|
||||
$infos[] = [
|
||||
'huifu_id' => $item['receiver_account'],
|
||||
'huifu_id' => $receiverHuifuId,
|
||||
'div_amt' => $amt,
|
||||
];
|
||||
$allocated = bcadd($allocated, $amt, 2);
|
||||
}
|
||||
|
||||
$platformHuifuId = (new SplitConfig())->all()['platform_huifu_id'] ?? $this->config->get('huifu_id');
|
||||
$remain = bcsub($payFee, $allocated, 2);
|
||||
if (bccomp($remain, '0.00', 2) > 0) {
|
||||
$infos[] = [
|
||||
'huifu_id' => $platformHuifuId,
|
||||
'div_amt' => $remain,
|
||||
];
|
||||
$platformIsCollector = ($platformHuifuId === $paymentHuifuId);
|
||||
if (!$platformIsCollector) {
|
||||
$infos[] = [
|
||||
'huifu_id' => $platformHuifuId,
|
||||
'div_amt' => $remain,
|
||||
];
|
||||
} else {
|
||||
\think\Log::info('[shopro_huifu] split platform remainder stays on collector: remain=' . $remain
|
||||
. ' huifu_id=' . $paymentHuifuId);
|
||||
}
|
||||
}
|
||||
|
||||
$sum = '0.00';
|
||||
foreach ($infos as $info) {
|
||||
$sum = bcadd($sum, $info['div_amt'], 2);
|
||||
}
|
||||
if (bccomp($sum, $payFee, 2) !== 0) {
|
||||
throw new \RuntimeException('分账金额之和必须等于订单实付');
|
||||
if (empty($infos)) {
|
||||
throw new \RuntimeException('分账明细为空:无有效分账接收方(勿将主商户号作为分账方)');
|
||||
}
|
||||
if (bccomp($sum, $payFee, 2) > 0) {
|
||||
throw new \RuntimeException('分账金额之和不能超过待确认金额');
|
||||
}
|
||||
return $infos;
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户