docs/huifu/08-上线与环境.md
@@ -67,8 +67,12 @@
| `app.payment.stranded-refund.enabled` | `true` | **悬账**自动退款(退回付款人);通道拒绝则升级告警转人工 |
| `app.payment.stranded-refund.fixed-delay-ms` | `300000` | 5 分钟一轮 |
| `...huifu.merchant-use-chains-flag` | 空(不下发) | **不要下发 `3`**:那要求上级本身有商户级微信配置,我们的上级是渠道商,会被拒「失败:复用交易信息,上级商户号为空」【实测】 |
| `...huifu.merchant-wechat-fee-rate` | `0.70` | 微信费率(Java 字段默认值是 `0.60`,与 yml 不一致) |
| `...huifu.merchant-wechat-fee-rule-id` / `-individual` / `-sole-proprietor` | `756` / `775` / `761` | CID 按「支付场景 × 业务开通类型 × **主体类型**」三维定位,不能全平台共用一个号(Java 里企业那个默认值为空) |
| `...huifu.merchant-wechat-scenes` | `1,2,3` | 微信支付场景:线下反扫/线下公众号/线下小程序。**2026-09-24 起统一走线下**;回退线上配 `4,5,12`(场景、CID、费率、fee-type、进件场景是一套口径,要一起换) |
| `...huifu.merchant-wechat-fee-type` | `02` | 业务开通类型:线下 `02`/线上 `01`。它同时决定 AppID 绑定的 `fee_type`、实名与实名查询的 `pay_scene`、以及判定时按 `fee_type` 匹配配置项的口径——四处必须同一个值 |
| `...huifu.merchant-wechat-fee-rate` | `0.30` | 微信费率(线下)。Java 字段默认值已与 yml 统一为 `0.30`(线上是 `0.70`)。实际生效值以商户详情 `qry_wx_conf_list[].fee_rate` **读回为准** |
| `...huifu.merchant-wechat-fee-rule-id` / `-individual` / `-sole-proprietor` | `758` / `770` / `762` | CID 按「支付场景 × 业务开通类型 × **主体类型**」三维定位:**线下(类型 02)企业 758/小微 770/个体户 762**;线上(回退用)是 756/775/761。不能全平台共用一个号 |
| `...huifu.merchant-onboarding-scene-type` | `OFFLINE` | 进件的 `scene_type`:线下报备口径(要实体经营场所材料)。回退线上配 `ONLINE` |
| `...huifu.merchant-micro-business-type` | `MICRO_TYPE_STORE` | 小微经营类型(个人主体开通微信业务必填):线下门店场所取 `MICRO_TYPE_STORE`,线上商品服务交易才是 `MICRO_TYPE_ONLINE` |
| `app.order.pending-expiry.fixed-delay-ms` | **yml 里没有这一行** | 订单到期清理,靠注解默认 `60000` 生效;它**没有** `enabled` 开关 |
两处过期的注释要当心:`enabled: true` 上方仍写着 `Keep disabled until the Huifu test/production product, merchant and keys are provisioned.`,
@@ -155,7 +159,7 @@
### A. 进件能走通(8 项)
- [ ] 企业主体与个人(小微)主体各提交一条,均不再报 `Unknown column ...`(`merchant_onboarding_application` 有行、无 500)
- [ ] 个人主体能提交门店照片(F22 门头照 / F24 内景照),且资料真的下发到通道
- [ ] 企业主体与个人主体各自都能提交门店材料(门头照 F22 / 内景照 F24 / 收银台 F105),且**三张都真的下发到通道**(进件请求的 `store_*_pic` 与业务开通的 `extended_material_list`)
- [ ] 进件审核回调能到达并落库(`payment_callback_event` 出现 `merchant-onboarding-notify` 的请求流水号)
- [ ] 商户业务开通被受理时**不会**被记成失败(`90000000` 是「受理/审核中」,「微信支付」阶段不落在 `failed`)
- [ ] 「微信小程序配置」排在「商户业务开通」之后执行,不被驳「根据渠道号或微信子商户号未查到入驻记录」
@@ -203,11 +207,11 @@
## 5. 迁移编号规约
- **编号全仓共享**。新增迁移前**先看** `talent-card-server/src/main/resources/db/migration/` 当前的**最大版本号**
  (截至本提交是 `V154`)。本仓库由多人按业务域分工,两人从同一祖先各自独立占同一段编号时 **git 不报冲突,
  (截至本提交是 `V155`)。本仓库由多人按业务域分工,两人从同一祖先各自独立占同一段编号时 **git 不报冲突,
  Flyway 会直接报「同一版本两个迁移」**。
- **最大版本号 ≠ 迁移条数**。当前是 **148 个**:147 个 `.sql` + **1 个 Java 版迁移**
- **最大版本号 ≠ 迁移条数**。当前是 **149 个**:148 个 `.sql` + **1 个 Java 版迁移**
  (`src/main/java/db/migration/V125__parking_lot_registration_snapshot.java`,包名 `db.migration`)。
  `1..154` 里真正的空档只有 **6 个**:97 / 105 / 109 / 110 / 111 / 126。
  `1..155` 里真正的空档只有 **6 个**:97 / 105 / 109 / 110 / 111 / 126。
  **数迁移时不要只数 `resources/db/migration/*.sql`**——那样会漏掉 Java 版迁移,把 `V125` 误判成空档
  (这个错 2026-09-22 真的发生过)。要核条数就看 Flyway 启动日志里的
  `Successfully validated N migrations`。经过见 §8.2。
@@ -331,6 +335,12 @@
它覆盖本机随机端口的调试来源、**与精确白名单是两个键**,覆盖 `CORS_ALLOWED_ORIGINS` 不会动它——
所以本地 vite / HBuilderX 调试不受影响。**上线时把真实域名加进 `CORS_ALLOWED_ORIGINS` 即可。**
**这条坑会跟着域名的每一次变化重演。** origin 由「协议 + 主机 + 端口」三者共同决定,**任何一个变了
都是一个新 origin,都要单独加**。2026-09-22 一天里演了两次:先用 IP 访问(`http://117.172.73.106:8081`)
踩了一次;把域名 `www.dqrcfwpt.com` 解析好、**改用域名访问又踩了一次**(登录再次 403,表现与第一次
一模一样)。所以:**换域名、换端口、上 HTTPS 都要同步改这个变量**;`http://host:8081` 与 `https://host`
是两个不同 origin,端口不同也是两个。
**4. 凭据要逐条过一遍「弱口令」口径。** 政务云的安全扫描不是形式主义——2026-09-22 它**真的登进来了一次**
(抓到请求体 `{"account":"admin","password":"123456"}`,响应 200 带 `accessToken`)。部署时以下三件事一起做:
@@ -380,3 +390,30 @@
唯一要守住的是 **`app.payment.huifu.reconciliation.enabled` 保持默认 `true`**:它是支付与退款唯一的后台兜底,
关掉就只剩「用户点了取消 / 重申支付」这一个入口(见 §1.2)。
## 9. 存量商户迁移到线下口径(2026-09-24)
微信支付口径整体从线上切到线下(见 §1 的环境变量表:场景 `1,2,3`、类型 `02`、费率 `0.30`、
CID `758/770/762`、进件 `scene_type=OFFLINE`)。**存量商户是在控台手工改的口径**,所以发布后要逐个迁移:
1. 部署新代码,在商户详情点一次「刷新进度」。
2. 跑下面这条 SQL(开发库,需 MySQL 5.7+)。列出来的就是「通道侧仍是线上 4/5/12、按线下判定会变
   `missing`」的商户 —— 也就是**收款闸门会挡住收款**的那批:
```sql
select a.provider_huifu_id as huifu_id,
       a.merchant_name,
       json_extract(p.stages_json, '$.wechat') as wechat_stage,
       p.last_synced_at
from merchant_onboarding_application a
join merchant_onboarding_progress p on p.application_id = a.id
where a.provider_huifu_id is not null and a.provider_huifu_id <> ''
  and json_extract(p.stages_json, '$.wechat') <> 'confirmed';
```
3. 在汇付控台把每条商户的微信场景改成线下(`1/2/3`),并**确认人才端小程序 AppID 绑到了该商户实际收款的
   那个微信子商户**(改场景会重建微信子商户号,见 `05` 第 19c 条)—— 不绑就会出现「业务执行失败[失败]」。
4. 回商户详情再点「刷新进度」,`wechat` 回到 `confirmed` 即恢复收款。
**注意**:判定完全跟随配置,所以回退线上要连场景、CID、费率、`fee-type`、进件 `scene_type` **一起**改回去
(`05` 第 19b 条),只改一处会出现「通道已开通、阶段永远 missing」。