```
feat(shopro_huifu): 优化分账确认金额计算逻辑并增强调试功能 - 添加previewConfirmPayFee方法用于预览汇付确认基数 - 实现更精确的分账确认金额计算,支持多种场景:待确认金额、结算金额、交易金额减手续费 - 新增辅助方法pickTransAmt、pickFeeAmt、isPositiveMoney和queryFieldRecursive - 支持递归查询嵌套数据结构中的字段值 - 在测试脚本中添加详细的调试输出,包括汇付确认基数对比和查单摘要 - 增强错误处理,显示最新的分账记录详情便于问题排查 - 改进分账执行结果的反馈信息,提供更清晰的成功/失败状态说明 ```
这个提交包含在:
@@ -467,8 +467,49 @@ function trySplitOrder($orderSn, $execute)
|
||||
fwrite(STDERR, "未安装 shopro_huifu\n");
|
||||
exit(1);
|
||||
}
|
||||
$ok = (new \addons\shopro_huifu\library\HuifuSplitService())->executeByOrderId($order['id']);
|
||||
echo $ok ? "\n分账执行返回: 成功\n" : "\n分账执行返回: 失败(查日志 / shopro_split_order)\n";
|
||||
$service = new \addons\shopro_huifu\library\HuifuSplitService();
|
||||
$payFee = number_format((float) $order['pay_fee'], 2, '.', '');
|
||||
$confirmBase = $service->previewConfirmPayFee($order['order_sn'], $payFee);
|
||||
echo "\n汇付确认基数: pay_fee={$payFee} → confirm_base={$confirmBase}";
|
||||
if (bccomp($confirmBase, $payFee, 2) < 0) {
|
||||
echo "(已扣手续费/待确认金额)";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
try {
|
||||
$query = (new \addons\shopro_huifu\library\HuifuQueryService())->queryByOrderSn($order['order_sn']);
|
||||
$snippet = [
|
||||
'trans_amt' => $query['trans_amt'] ?? null,
|
||||
'unconfirm_amt' => $query['unconfirm_amt'] ?? null,
|
||||
'settlement_amt' => $query['settlement_amt'] ?? null,
|
||||
'delay_acct_flag'=> $query['delay_acct_flag'] ?? null,
|
||||
'trans_stat' => $query['trans_stat'] ?? null,
|
||||
];
|
||||
echo '查单摘要: ' . json_encode($snippet, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo '查单摘要: 失败 ' . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
$ok = $service->executeByOrderId($order['id']);
|
||||
if ($ok) {
|
||||
echo "\n分账执行返回: 成功\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$latest = Db::name('shopro_split_order')
|
||||
->where('order_id', $order['id'])
|
||||
->where('channel', 'huifu')
|
||||
->order('id', 'desc')
|
||||
->find();
|
||||
echo "\n分账执行返回: 失败\n";
|
||||
if ($latest) {
|
||||
echo "最新分账记录 id={$latest['id']} status={$latest['status']} split_amount={$latest['split_amount']}\n";
|
||||
if (!empty($latest['wx_result'])) {
|
||||
echo "wx_result: {$latest['wx_result']}\n";
|
||||
}
|
||||
} else {
|
||||
echo "未写入 shopro_split_order(可能明细为空或 pay_type 非 huifu_wechat)\n";
|
||||
}
|
||||
}
|
||||
|
||||
function printOrderDetail($orderSn)
|
||||
|
||||
@@ -189,34 +189,45 @@ class HuifuSplitService
|
||||
/**
|
||||
* 延时分账确认总额须与汇付「待确认/交易金额」一致;内扣手续费时往往小于商城 pay_fee。
|
||||
*/
|
||||
public function previewConfirmPayFee($orderSn, $orderPayFee)
|
||||
{
|
||||
return $this->resolveConfirmPayFee($orderSn, $orderPayFee);
|
||||
}
|
||||
|
||||
protected function resolveConfirmPayFee($orderSn, $orderPayFee)
|
||||
{
|
||||
$fallback = number_format((float) $orderPayFee, 2, '.', '');
|
||||
try {
|
||||
$query = (new HuifuQueryService())->queryByOrderSn($orderSn);
|
||||
$unconfirm = trim((string) ($query['unconfirm_amt'] ?? ''));
|
||||
if ($unconfirm !== '' && is_numeric($unconfirm)) {
|
||||
$amt = number_format((float) $unconfirm, 2, '.', '');
|
||||
if (bccomp($amt, '0.00', 2) > 0) {
|
||||
return $amt;
|
||||
$delayAcct = strtoupper(trim((string) ($this->queryFieldRecursive($query, 'delay_acct_flag') ?? ''))) === 'Y';
|
||||
|
||||
$unconfirm = $this->queryFieldRecursive($query, 'unconfirm_amt');
|
||||
if ($this->isPositiveMoney($unconfirm)) {
|
||||
return number_format((float) $unconfirm, 2, '.', '');
|
||||
}
|
||||
|
||||
$settlement = $this->queryFieldRecursive($query, 'settlement_amt');
|
||||
if ($this->isPositiveMoney($settlement) && bccomp((string) $settlement, $fallback, 2) < 0) {
|
||||
return number_format((float) $settlement, 2, '.', '');
|
||||
}
|
||||
|
||||
$transAmt = $this->pickTransAmt($query);
|
||||
$feeAmt = $this->pickFeeAmt($query);
|
||||
if ($transAmt !== null && $feeAmt !== null) {
|
||||
$net = bcsub($transAmt, $feeAmt, 2);
|
||||
if (bccomp($net, '0.00', 2) > 0 && bccomp($net, $fallback, 2) <= 0) {
|
||||
\think\Log::info('[shopro_huifu] split confirm base from trans-fee: trans=' . $transAmt . ' fee=' . $feeAmt . ' net=' . $net);
|
||||
return $net;
|
||||
}
|
||||
}
|
||||
$trans = trim((string) ($query['trans_amt'] ?? ''));
|
||||
if ($trans !== '' && is_numeric($trans)) {
|
||||
$transAmt = number_format((float) $trans, 2, '.', '');
|
||||
foreach (['fee_amt', 'fee_amount', 'trans_fee'] as $feeKey) {
|
||||
$feeRaw = trim((string) ($query[$feeKey] ?? ''));
|
||||
if ($feeRaw === '' || !is_numeric($feeRaw)) {
|
||||
continue;
|
||||
}
|
||||
$net = bcsub($transAmt, number_format((float) $feeRaw, 2, '.', ''), 2);
|
||||
if (bccomp($net, '0.00', 2) > 0 && bccomp($net, $fallback, 2) < 0) {
|
||||
return $net;
|
||||
}
|
||||
}
|
||||
if (bccomp($transAmt, '0.00', 2) > 0) {
|
||||
return $transAmt;
|
||||
}
|
||||
|
||||
if ($transAmt !== null && !$delayAcct && bccomp($transAmt, '0.00', 2) > 0) {
|
||||
return $transAmt;
|
||||
}
|
||||
|
||||
if ($delayAcct && $transAmt !== null && bccomp($transAmt, $fallback, 2) === 0) {
|
||||
\think\Log::warning('[shopro_huifu] split confirm base: delay_acct=Y but unconfirm/fee missing, still using pay_fee=' . $fallback
|
||||
. ' query_keys=' . implode(',', array_slice(array_keys($query), 0, 20)));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
\think\Log::warning('[shopro_huifu] split confirm amount query fallback: ' . $e->getMessage());
|
||||
@@ -224,6 +235,67 @@ class HuifuSplitService
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
protected function pickTransAmt(array $query)
|
||||
{
|
||||
$raw = $this->queryFieldRecursive($query, 'trans_amt');
|
||||
if (!$this->isPositiveMoney($raw)) {
|
||||
return null;
|
||||
}
|
||||
return number_format((float) $raw, 2, '.', '');
|
||||
}
|
||||
|
||||
protected function pickFeeAmt(array $query)
|
||||
{
|
||||
foreach (['actual_fee_amt', 'receivable_fee_amt', 'fee_amt', 'fee_amount', 'trans_fee', 'combinedpay_fee_amt'] as $key) {
|
||||
$raw = $this->queryFieldRecursive($query, $key);
|
||||
if ($this->isPositiveMoney($raw)) {
|
||||
return number_format((float) $raw, 2, '.', '');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function isPositiveMoney($value)
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return false;
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
return bccomp(number_format((float) $value, 2, '.', ''), '0.00', 2) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string|mixed $data
|
||||
*/
|
||||
protected function queryFieldRecursive($data, $key)
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$trim = trim($data);
|
||||
if ($trim !== '' && ($trim[0] === '{' || $trim[0] === '[')) {
|
||||
$decoded = json_decode($trim, true);
|
||||
if (is_array($decoded)) {
|
||||
return $this->queryFieldRecursive($decoded, $key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
if (array_key_exists($key, $data) && $data[$key] !== '' && $data[$key] !== null) {
|
||||
return $data[$key];
|
||||
}
|
||||
foreach ($data as $value) {
|
||||
$found = $this->queryFieldRecursive($value, $key);
|
||||
if ($found !== null && $found !== '') {
|
||||
return $found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function scaleSplitItemsToCap(array $items, $allocated, $cap)
|
||||
{
|
||||
if (empty($items) || bccomp($allocated, '0.00', 2) <= 0) {
|
||||
|
||||
在新工单中引用
屏蔽一个用户