```
feat(shopro_huifu): 增强分账配置查询命令的诊断能力 - 改进 QuerySplitConfig 命令的错误提示信息,提供更详细的分账配置状态说明 - 新增商户详细信息查询功能,可获取 delay_flag 等业务开通字段 - 添加嵌套键值扫描工具方法,用于解析汇付应答中的嵌套JSON数据 - 扩展分账配置查询接口,支持传递 product_id 获取更完整的商户信息 - 优化命令行输出,提供分账配置问题的解决方案指导 ```
这个提交包含在:
@@ -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 字符串)
|
||||
*
|
||||
|
||||
在新工单中引用
屏蔽一个用户