feat(shopro_huifu): 增强分账配置查询命令的诊断能力

- 改进 QuerySplitConfig 命令的错误提示信息,提供更详细的分账配置状态说明
- 新增商户详细信息查询功能,可获取 delay_flag 等业务开通字段
- 添加嵌套键值扫描工具方法,用于解析汇付应答中的嵌套JSON数据
- 扩展分账配置查询接口,支持传递 product_id 获取更完整的商户信息
- 优化命令行输出,提供分账配置问题的解决方案指导
```
这个提交包含在:
可乐加冰
2026-10-05 17:17:18 +08:00
父节点 cd559ca80d
当前提交 1182faca30
共有 2 个文件被更改,包括 113 次插入 和 2 次删除

查看文件

@@ -79,8 +79,46 @@ class QuerySplitConfig extends Command
$items = HuifuClient::parseSplitBillResult($data['split_bill_result'] ?? null);
if ($items === []) {
$output->writeln('');
$output->writeln('[WARN] 未返回 split_bill_result:主商户可能未开通分账/延时分账,或 product 无权限');
$output->writeln('可加 --json 查看完整应答');
$output->writeln('[结论] split_bill_result 为空 = 汇付侧「分账配置申请」尚未生效(或未提交)');
$output->writeln(' 这与支付查单 delay_acct_flag=Y(延时分账/延迟入账)不是同一开关。');
$output->writeln(' 商城 confirm 带 acct_split_bunch 时,无生效分账配置 + 未绑定接收方 → 典型 22000002。');
$output->writeln('');
$output->writeln('应答顶层字段: ' . implode(', ', array_keys($data)));
$nested = HuifuClient::collectNestedKeys($data, ['rule_origin', 'div_flag', 'apply_ratio', 'delay_flag']);
if ($nested !== []) {
$output->writeln('嵌套扫描: ' . json_encode($nested, JSON_UNESCAPED_UNICODE));
}
$output->writeln('');
$output->writeln('--- 商户详情补充 (v2/merchant/basicdata/query + product_id) ---');
$basic = $client->queryMerchantBasicdata($huifuId, true);
if ($basic['ok']) {
$bd = $basic['data'] ?? [];
$bCode = (string) ($bd['resp_code'] ?? '');
$delay = (string) ($bd['delay_flag'] ?? '');
$forced = (string) ($bd['forced_delay_flag'] ?? '');
if ($delay !== '' || $forced !== '') {
$output->writeln('delay_flag(延迟入账): ' . ($delay !== '' ? $delay : '(未返回)'));
$output->writeln('forced_delay_flag: ' . ($forced !== '' ? $forced : '(未返回)'));
} else {
$scan = HuifuClient::collectNestedKeys($bd, ['delay_flag', 'forced_delay_flag', 'rule_origin', 'div_flag']);
if ($scan !== []) {
$output->writeln('详情嵌套: ' . json_encode($scan, JSON_UNESCAPED_UNICODE));
} else {
$output->writeln('未读到 delay_flag(可 --json 看 basicdata 全文)');
}
}
if ($bCode !== '' && $bCode !== '00000000') {
$output->writeln('basicdata resp_code=' . $bCode . ' ' . ($bd['resp_desc'] ?? ''));
}
} else {
$output->writeln('[SKIP] ' . $basic['message']);
}
$output->writeln('');
$output->writeln('下一步(汇付实施/工单):');
$output->writeln(' 1) v2/merchant/split/config 提交:rule_origin=01, div_flag=Y, apply_ratio≥业务比例(如30)');
$output->writeln(' 2) 控台/接口绑定分账接收方 6666000245630872(进件通过 ≠ 分账关系已配)');
$output->writeln(' 3) 生效后再跑本命令,应出现 rule_origin;新订单测分账,勿重试 unconfirm_amt=0 的旧单');
$output->writeln('完整 JSON: php think shopro_huifu:query-split-config --json');
return 2;
}

查看文件

@@ -579,12 +579,85 @@ class HuifuClient
$request->setHuifuId($huifuId);
$request->setReqDate(date('Ymd'));
$request->setReqSeqId('SPLQ' . date('YmdHis') . mt_rand(1000, 9999));
$productId = trim((string) $this->config->get('product_id'));
if ($productId !== '') {
$request->setExtendInfo(['product_id' => $productId]);
}
return $this->safeApiResult($this->postRequest($request));
}
/**
* 商户详细信息查询(传 product_id 时可含 delay_flag 等业务开通字段)
*/
public function queryMerchantBasicdata($huifuId = '', $withProductId = true)
{
$huifuId = trim((string) $huifuId);
if ($huifuId === '') {
$huifuId = trim((string) $this->config->get('huifu_id'));
}
if ($huifuId === '') {
return ['ok' => false, 'message' => '缺少 huifu_id', 'data' => null];
}
SdkLoader::requireRequest('V2MerchantBasicdataQueryRequest.php');
$request = new \BsPaySdk\request\V2MerchantBasicdataQueryRequest();
$request->setHuifuId($huifuId);
$request->setReqDate(date('Ymd'));
$request->setReqSeqId('MBDQ' . date('YmdHis') . mt_rand(1000, 9999));
if ($withProductId) {
$productId = trim((string) $this->config->get('product_id'));
if ($productId !== '') {
$request->setExtendInfo(['product_id' => $productId]);
}
}
return $this->safeApiResult($this->postRequest($request));
}
/**
* 在汇付应答中递归查找指定键(用于 split 配置可能在嵌套 JSON 字符串中)
*
* @return array<string, mixed>
*/
public static function collectNestedKeys($payload, array $keys)
{
$found = [];
self::walkNestedPayload($payload, $keys, $found);
return $found;
}
protected static function walkNestedPayload($node, array $keys, array &$found)
{
if (is_string($node)) {
$trim = trim($node);
if ($trim !== '' && ($trim[0] === '{' || $trim[0] === '[')) {
$decoded = json_decode($trim, true);
if (is_array($decoded)) {
self::walkNestedPayload($decoded, $keys, $found);
}
}
return;
}
if (!is_array($node)) {
return;
}
foreach ($node as $k => $v) {
if (in_array($k, $keys, true) && $v !== '' && $v !== null && !isset($found[$k])) {
$found[$k] = $v;
}
self::walkNestedPayload($v, $keys, $found);
}
}
/**
* 解析 split_bill_result(可能是 array 或 JSON 字符串)
*