feat(shopro_huifu): 新增汇付个人进件自动提交功能

新增以下命令:
- php think shopro_huifu:onboard-auto-submit(每 5-10 分钟自动提交)
- php think shopro_huifu:onboard-poll(每 10 分钟轮询审核状态)
- php think shopro_huifu:diagnose-freeze-flow(冻结/解冻链路诊断)

完善了汇付插件的 README 文档,包含新功能说明和安装验证步骤。
```
这个提交包含在:
可乐加冰
2026-10-08 14:02:12 +08:00
父节点 727d8586e9
当前提交 48cde5f891
共有 5 个文件被更改,包括 49 次插入 和 7 次删除

查看文件

@@ -53,6 +53,19 @@ php think shopro_huifu:compensate
php think shopro_huifu:compensate --order_sn=2026111111220944091001100
php think shopro_huifu:split-retry
# 每 5-10 分钟:汇付个人进件自动提交(业务已通过且从未提交→立即;失败满 24h→重提)
php think shopro_huifu:onboard-auto-submit
# 每 10 分钟:轮询进件审核中状态
php think shopro_huifu:onboard-poll
```
已有站点若后台无「自动提交进件」开关,执行 SQL:`addons/shopro_huifu/upgrade_huifu_onboard_auto.sql`(替换表前缀)。
```bash
# 试跑(不调用汇付)
php think shopro_huifu:onboard-auto-submit --dry-run
```
## 安装验证
@@ -121,6 +134,13 @@ php think shopro_huifu:import-bank-branch --file=addons/shopro_huifu/data/cnaps.
支付成功通知会优先落库微信 `transaction_id`(`wx_response.transaction_id` / `out_trans_id`),供 `shopro_wechat_trade` 插件调用 `upload_shipping_info`。汇付流水仍保存在 `shopro_huifu_pay_ext.hf_seq_id`。
冻结/解冻链路只读诊断(服务器有 `.env` 时):
```bash
php think shopro_huifu:diagnose-freeze-flow
# 或 mysql < addons/shopro_huifu/docs/diagnose-freeze-flow.sql
```
## 支付故障排查
| 报错 | 处理 |

查看文件

@@ -60,7 +60,9 @@ php think shopro_wechat_trade:retry-failed
## 触发点
- `order_send_after` → 异步 `upload_shipping_info`
- `order_confirm_after`(全部 item 已收货)→ `notify_confirm_receive`
- `order_confirm_after`(全部 item 已收货)→ `notify_confirm_receive`(须已有 shipping 同步成功;`received_time` 为秒级时间戳)
微信错误 **47001 data format error** 多为确认收货 JSON 格式不符(勿用 RFC3339 字符串作为 `received_time`)。
## 小程序跳转

查看文件

@@ -70,4 +70,13 @@ class SyncLogger
->where('status', 'success')
->find();
}
public static function hasSuccessfulShipping($orderId)
{
return (bool) Db::name('shopro_wechat_trade_sync')
->where('order_id', $orderId)
->where('sync_type', 'shipping')
->where('status', 'success')
->find();
}
}

查看文件

@@ -75,13 +75,20 @@ class WxShippingPayload
$confirmTime = intval($ext['confirm_time']);
}
return [
'order_key' => [
'order_number_type' => 2,
'transaction_id' => $transactionId,
],
'received_time' => self::formatUploadTime($confirmTime),
// 官方 POST /wxa/sec/order/notify_confirm_receive:received_time 为秒级时间戳(number),非 RFC3339
$payload = [
'transaction_id' => $transactionId,
'received_time' => intval($confirmTime),
];
$mchid = WxOrderContext::resolveMchid($order);
if ($mchid !== '') {
$payload['merchant_id'] = $mchid;
}
$orderSn = trim((string) ($order['order_sn'] ?? ''));
if ($orderSn !== '') {
$payload['merchant_trade_no'] = $orderSn;
}
return $payload;
}
protected static function buildShippingListItem(array $item, $order)

查看文件

@@ -72,6 +72,10 @@ class WxShippingService
return true;
}
if (!SyncLogger::hasSuccessfulShipping($orderId)) {
throw new \RuntimeException('须先成功 upload_shipping_info 再同步确认收货(notify_confirm_receive)');
}
$order = Order::with('item')->where('id', $orderId)->find();
if (!$order || !Bridge::shouldHandleOrder($order)) {
return false;