适用提交: 6c42f8f | 最后验证: 2026-09-18 | 何时会失效: 新增端侧入口、新增通道能力、或闸门/资金链路的判定被改动时
这份文档回答三件事:**整条链路长什么样**、**代码在哪**、**我要改 X 该去哪**。末尾有本节最该记住的 6 条硬规定。
flowchart TD
subgraph ONBOARD["① 进件(商户端发起)"]
A1["商户提交资料<br/>MerchantWorkController"] --> A2["MerchantSettlementService<br/>提交企业/个人进件"]
A2 --> A3["汇付进件接入<br/>同步回 huifu_id / apply_no"]
A3 --> A4["通道异步回调<br/>MerchantSettlementService.handleOnboardingCallback"]
A4 --> A5["business_open_status<br/>分阶段:微信支付 → appId → 实名 → 延迟入账 → 自动结算"]
end
subgraph READY["② 收款就绪(闸门)"]
B1["MerchantPaymentReadinessService<br/>唯一判定点"] --> B2{"本地快照 + 通道能力<br/>都正常?"}
B2 -->|否| B3["拒绝下单<br/>同源同文案"]
B2 -->|是| B4["允许下单"]
end
subgraph PAY["③ 支付"]
C1["人才端下单<br/>PendingOrderPortalController"] --> C2["订单固定收款子商户<br/>collect_onboarding_id / collect_huifu_id"]
C2 --> C3["HuifuPaymentService<br/>落 payment_transaction + 调网关"]
C3 --> C4["微信收银台"]
C4 --> C5["通道异步回调<br/>PaymentCallbackController"]
C4 -.回调可能丢.-> C6["对账兜底<br/>PaymentReconciliationService<br/>每分钟一轮"]
C5 --> C7["PaymentSettlementService<br/>唯一幂等结算入口"]
C6 --> C7
end
subgraph FUND["④ 核销与资金"]
D1["核销<br/>三条入口收敛"] --> D2["交易确认<br/>PaymentFundConfirmationService"]
D2 --> D3["钱从延迟户进商户基本户<br/>funds_confirmation_status=confirmed"]
D4["撤销核销"] --> D5["交易确认退款<br/>钱退回延迟户"]
end
subgraph SETTLE["⑤ 结算"]
E1["斗拱按 settle_config 批次自动结算<br/>平台不生成提现记录"] --> E2["后台结算记录页<br/>实时查通道,本地无表"]
end
ONBOARD --> READY --> PAY --> FUND --> SETTLE
PAY -.取消/关单.-> C8["关单 + 关单查询<br/>PaymentReconciliationService"]
C8 -.悬账.-> C9["StrandedPaymentRefundRetry<br/>自动原路退回"]
图的文字兜底:商户先进件 → 通道异步回调推进「业务开通/实名/延迟入账/自动结算」五项能力 → 能力齐了才允许人才端下单 → 订单在创建时就**固定收款子商户**(防止支付请求漂移到别家门店)→ 支付成功靠回调推进,**回调丢了由每分钟一轮的对账兜底**(两者最终都走 PaymentSettlementService 这**唯一**的幂等结算入口)→ 核销时才把延迟户的钱确认给商户 → 撤销核销要把钱退回去 → 结算由斗拱按周期自动做,本地只有查询没有表。
talent-card-server这条链路**没有 MyBatis 实体与 Mapper**(mapper/ 目录只服务别的业务),全部用 JdbcTemplate 直接写 SQL。通道调用统一封在 business/payment/huifu/ 内。
business/
payment/ 支付链路编排层(与具体通道无关)
PaymentCallbackController.java 汇付异步通知总入口,5 个回调端点
HuifuPaymentService.java 小程序下单:校验订单/幂等键、落 payment_transaction、调网关、状态映射
HuifuPaymentCallbackService.java 支付回调:先验签、再解析、去重,交给 PaymentSettlementService
PaymentSettlementService.java ★ 唯一幂等结算入口(回调与查单都走这里,比对请求号/商户号/金额后才改本地状态)
PaymentReconciliationService.java 第二确认路径:查单、关单、关单查询、取消前确认、退款收敛、问题记录
PaymentReconciliationScheduler.java 对账定时任务(默认 1 分钟一轮)
HuifuRefundService.java 退款受理/查询/回调收敛,含悬账判定
PaymentFundConfirmationService.java 延迟入账资金确认(核销时交易确认)与撤销核销的资金回退
PaymentFundConfirmationRetry.java 资金确认补偿(已核销但资金未确认的订单)
StrandedPaymentRefundRetry.java 悬账(订单已取消但通道收到钱)自动原路退回
PaymentIssueRecorder.java 差错台账 payment_reconciliation_issue
HuifuCallbackUrlAudit.java 启动时逐个打印五个回调地址的结论
huifu/ ★ 斗拱 SDK 封装层(唯一直连通道的地方)
HuifuPaymentProperties.java @ConfigurationProperties("app.payment.huifu")
HuifuSignatureService.java ★ 唯一的验签实现(SHA256WithRSA + 平台公钥)
HuifuPayload.java 回执统一解析:字段名、元→分、TransStatus、业务码语义
HuifuMiniProgramPaymentGateway.java 下单 + 关单 + 关单查询 + 查单
HuifuRefundGateway.java 退款与退款查询
HuifuDelayAccountGateway.java 延迟交易确认与交易确认退款(回退)
HuifuMerchantCapabilityGateway.java 查商户状态/自动结算/取现开关
HuifuSettlementRecordGateway.java 结算记录查询(pageSize 上限 50)
Huifu*OnboardingGateway.java 企业/个人进件提交
HuifuMerchantBusinessOpenGateway.java 商户业务开通(微信支付能力)
HuifuMerchantWechatConfigGateway.java 微信商户配置下发
HuifuMerchantRealnameGateway.java 微信实名认证申请
HuifuPictureUploadGateway.java ★ 唯一直接用 RestTemplate 出站的地方(图片取 file_id)
merchant/
MerchantSettlementService.java 商户端进件提交/刷新/业务开通/银行卡/回调处理的总编排
MerchantOnboardingProgressService.java 六阶段进度模型 + 人工刷新
MerchantPaymentReadinessService.java ★ 唯一「这家商户能不能收款」判定点 + 能力快照读写
MerchantPaymentCapabilityScheduler.java 能力快照定时刷新
MerchantRealnameService.java 微信实名认证申请(法人路径)
MerchantOnboardingSchemaInitializer.java 无 Flyway 部署的补列 best-effort
MerchantWorkController.java 商户端入口
admin/
MerchantSettlementAdminService.java 后台进件/银行卡/结算记录查询、代发起实名、能力刷新
AdminManagementController.java 后台管理端点入口
order/
OrderService.java 订单写路径
OrderFundBoundaryService.java ★ 订单资金边界(核销/退款/提现锁定串行化,SELECT FOR UPDATE)
portal/
PendingOrderPortalController.java 人才端入口:支付、取消
AppOrderPortalService.java 人才端创建订单(绑定收款子商户)
注意:后台**没有人工「进件审核」动作**。merchant_onboarding_application.status 的 approved/rejected 由**通道回调**写入;后台只有查看 + 刷新 + 代发起实名。
talent-card-admin(Vue)| 文件 | 作用 |
|---|---|
src/views/MerchantSettlementView.vue |
核心页,3 个 tab:结算记录 / 商户进件 / 银行卡 |
src/views/MerchantOnboardingDetailView.vue |
进件详情:进度、收款能力、实名、银行卡、二维码 |
src/views/OrderView.vue / OrderDetailView.vue |
订单列表/详情,含「立即对账」 |
src/views/RefundRecordView.vue |
退款记录与「刷新退款状态」 |
src/api/admin.ts、src/types/admin.ts |
端点与类型 |
后台没有「支付对账问题台账」页面:GET/PATCH /api/admin/management/payment-reconciliation/issues 已实现,但前端只用了「立即对账」按钮——台账目前只能查库或直连接口。
| 目录 | 端 |
|---|---|
rencaika_merchant/pages/merchant-onboarding/、merchant-onboarding-pending/、payment-capability/、bank-card-add/ |
商户端:进件、待审、收款能力、银行卡 |
rencaika_merchant/services/merchant-management.uts、settlement.uts、onboarding-draft.uts |
商户端服务层 |
rencaika/pages/pending-payment/ |
人才端:待支付 |
uvue 客户端没有可编译的反馈回路(本机没有 HBuilderX / uni CLI),改前端**只能真机验证**。06 第 46 条那个「待支付页每秒闪一次」就是这么产生的——客户端 loadOrder ↔ checkPaymentStatus 互相触发形成无限循环,静态读代码看不出来。改这两端之前,先确认你有真机调试环境。
| 我想改… | 去哪 | 顺带看 |
|---|---|---|
| 进件表单字段/校验 | rencaika_merchant/pages/merchant-onboarding/ + MerchantSettlementService |
06 §2 |
| 进件后要开哪些能力 | MerchantSettlementService + MerchantOnboardingProgressService.LABELS |
06 第 28 条(加第 7 项会破坏完成判定) |
| 谁能收款(闸门) | MerchantPaymentReadinessService |
01 §4 硬规定第 1 条;ADR-0002 |
| 下单/支付参数 | HuifuPaymentService + HuifuMiniProgramPaymentGateway |
05 §2.1 签名规则 |
| 支付回调校验 | PaymentSettlementService |
01 §4 硬规定第 5 条 |
| 关单/取消 | PaymentReconciliationService |
06 第 36/38/39/40/41/42/43 条一串 |
| 退款 | HuifuRefundService + OrderFundBoundaryService |
06 第 56/57 条 |
| 核销与资金确认 | PaymentFundConfirmationService + OrderFundBoundaryService |
06 第 60/64/66 条 |
| 结算记录页 | MerchantSettlementAdminService + MerchantSettlementView.vue |
06 第 67/68/69 条 |
| 对账节奏/开关 | PaymentReconciliationScheduler + application.yml |
08-上线与环境.md |
| 通道配置/费率/CID | application.yml 的 app.payment.huifu |
05 §3 第 18/19 条;reference/official/wx-fee-rule-id.xlsx |
| 差错台账 | PaymentIssueRecorder + payment_reconciliation_issue |
06 第 53 条 |
| 排查线上问题 | 07-排障手册.md + talent-card-server/scripts/probes/ |
— |
这六条**不是在描述现状,是在约束改动**。它们各自都有一次踩坑换来。
商户端页面说「能收款」、下单却被拒——这个 bug 出现过一次,根因是两处读了不同的数据。**任何新增「能不能收款」的展示,都必须调 MerchantPaymentReadinessService 的同一个入口,不许另写一份判定。** 有测试 merchantViewReusesTheGateMessage 钉住。
PREPAY_CREATED + CANCEL_UNCONFIRMED一单一活预支付单。这个集合**已经改过四轮**(PREPAY_CREATED → +CLOSING → 只留 PREPAY_CREATED → +CANCEL_UNCONFIRMED),每次都是因为把「关单可能已生效」和「关单明确没生效」混在一起:
CLOSING = 关单**可能**已生效 → 不可复用CANCEL_UNCONFIRMED = 通道明确说这次关单**没生效**(拒绝关单,或关单请求本身失败 90000000)→ 凭证**还活着**,**正是最该复用的一张**通道的 23000003 会**同时带 trans_stat=F**,但它在交易确认路径上是**幂等成功**(「已无未确认金额」)。先看交易状态再看返回码,必然把幂等成功判成失败——补偿任务会每隔几分钟无意义地打一次通道,永远不自愈。见 HuifuDelayAccountGateway.java:223-239。
LABELS 同时是展示列表与完成判定的键集MerchantOnboardingProgressService.LABELS 既驱动进件页的六项阶段展示,**又是完成判定的键集**。往里加第 7 项(例如把 bank/merchantNormal 加进去)会两次破坏完成判定:这些是布尔值,而其余六项必须等于字符串 "confirmed",加进去进件会**永远无法 approved**。ADR-0002 的「考虑过但没选的」记的就是这条。
回调会丢、会重、本地与无公网环境根本收不到。 任何一个「只有回调能推进」的状态都是 bug。新增通道能力时,必须同时回答:「回调丢了,谁把它推进?」ADR-0004 里那张「谁触发查询」的边界表,每次新增能力都要补一行。
不看 merchant_stat 的实时值做业务判定,也不把它混进六项阶段——一律读能力快照,与下单闸门同源。快照的时效靠定时核实 + 手动刷新冷却来保证,见 02-进件与收款能力.md。
| 文档 | 讲什么 |
|---|---|
README.md |
导航、术语对照、权威来源地图、维护契约 |
02-进件与收款能力.md |
进件、业务开通、实名、能力快照、收款闸门 |
03-支付与资金.md |
下单、回调、关单、取消、延迟入账、交易确认、退款 |
04-结算与对账.md |
斗拱自动结算、结算记录页、差错台账、退役提现 |
05-汇付对接注意事项.md |
汇付侧协议契约与 28 条反直觉行为 |
06-踩坑清单.md |
96 条我方踩坑 + 待确认清单 |
07-排障手册.md |
症状 → 排查路径 → 探针 |
08-上线与环境.md |
环境、凭据、回调地址、开关、上线验收、迁移规约 |
docs/adr/0001~0004 |
四份架构决策记录 |
docs/agents/testing.md |
测试环境与测试库纪律 |
talent-card-server/docs/huifu-payment-integration.md |
附录 A:英文实现说明,细节细节以它为准 |