# 汇付侧已进件商户的纳管(两条进件路径并存) Status: ready-for-agent 更新日期:2026-10-09 规格来源:本会话已确认的业务决策,以及对进件、分阶段进度、收款闸门与汇付接口文档的只读核查。 测试边界:以接口为主入口、真实测试库与模拟汇付响应;自动化测试不得创建真实汇付商户。 **术语**:本规格把「商户在我们商户端自助进件」称为**自助进件**,把「商户已在汇付侧完成进件、 需要接进我们系统」称为**外部进件**。两者是同一件事的两条入口,最终收敛到同一套进件记录、 分阶段进度与**收款就绪**判定。 ## Problem Statement 项目上线后商户有两条进件路径:一条是在我们商户端自助进件(已有能力),另一条是商户直接在汇付侧 完成进件。第二条路径目前没有任何纳管手段——这些商户在汇付侧已经进件成功、微信支付业务也已开通, 但我们系统里没有它的进件记录,于是: - **收款被整体拦住。** 下单闸门要求该商家有一条已通过的进件,外部进件商户永远没有,报「商户进件 尚未审核通过,暂时无法收款」。 - **商户端看不到任何有意义的收款状态。** 进件页、收款能力页依赖同一条记录,没有记录就只能显示未提交。 - **运营只能靠手工操作**,且没有任何入口把汇付侧的商户号接到我们的商家上。 同时存在连锁场景:一个商家主体下有几个门店,希望共用一个收款主体(同一个汇付商户号), 而不是每个门店各自进一次件。本次确认的做法是**每个门店建成一个独立商家**,共用同一个汇付商户号, 不做「一个商家挂多个门店」的模型。 ## Solution 后台提供一个**纳管**入口:运营选定我方商家、填入汇付商户号与入驻申请单号,平台先做只读核验, 通过后为该商家建立结算卡记录与进件记录,并触发一次进度刷新,使分阶段进度与收款就绪在**现有模型内** 收敛。连锁的其余门店用同一入口各纳管一次、指向同一个汇付商户号即可,不需要额外机制。 同时提供**解绑**,作为挂错商户时的补救。解绑只解除我们这边的绑定,不回退通道侧的改动 (微信口径、人才端 AppID 绑定、延迟入账、实名都不是我们这次改的,也不会被解绑撤销)。 纳管全程**只查询、不下发**:不在纳管里补开微信、绑 AppID 或改口径。因为下发类操作不幂等—— 改支付场景会重建微信子商户号——而共用同一个汇付商户号的多个商家各自下发会造成重复下发。 外部进件商户的口径已由业务要求保持一致,纳管只需要核实,不需要改动。 ## User Stories 1. 作为平台运营人员,我希望在后台用一个入口把一个汇付侧已进件的商户接到我们的商家上,以便外部进件商户能在我方系统里收款。 2. 作为平台运营人员,我希望纳管时只需选定我方商家并填入汇付商户号与入驻申请单号,以便不必理解通道内部的字段结构。 3. 作为平台运营人员,我希望填错或不属于我们管辖的商户号在纳管时就被明确拒绝,以便不会挂到一个我们无权操作的商户上。 4. 作为平台运营人员,我希望纳管前看到通道侧返回的商户注册名称与商户简称,并能显式确认后才提交,以便与我们的商家名称核对,避免挂错主体。 5. 作为平台运营人员,我希望这个商户号已经绑给其他商家时得到提示而不是被拒绝,以便支持连锁多店共用同一个收款主体。 6. 作为平台运营人员,我希望同一入口对连锁的每个门店各执行一次就能共用同一个收款主体,以便不需要新机制。 7. 作为平台运营人员,我希望纳管完成后能立刻看到分阶段进度和收款就绪,以便知道还缺什么。 8. 作为平台运营人员,我希望纳管失败(通道查不到、核验不通过、写入冲突)时得到具体原因,以便判断是资料问题还是通道权限问题。 9. 作为平台运营人员,我希望重复纳管同一个商家得到幂等结果而不是产生第二条进件记录,以便重复点击不会制造重复数据。 10. 作为平台运营人员,我希望把挂错的商户解绑,以便纠正错误绑定。 11. 作为平台运营人员,我希望解绑前被告知该商户上是否还有在途订单或待结算的资金,以便不会切断正在进行的对账。 12. 作为平台运营人员,我希望解绑只解除我方绑定并明确告知通道侧不会被回退,以便不会误以为商户已恢复原样。 13. 作为平台运营人员,我希望解绑是可逆的(可以重新纳管),以便误操作后能恢复。 14. 作为平台运营人员,我希望外部进件商户的进件记录上标明它来自纳管,以便区分两条进件路径。 15. 作为平台运营人员,我希望纳管不自动下发任何通道改动,以便不会因重复下发重建微信子商户号。 16. 作为平台运营人员,我希望外部进件商户没有我方发起的申请单号时进度刷新仍能推进,以便不会永远停在未确认。 17. 作为外部进件商户,我希望登录商户端后能看到自己的收款状态和待办,以便知道现在能不能收款。 18. 作为外部进件商户,我希望首次纳管时若还没有商户端账号则被同时建好,以便能直接登录。 19. 作为外部进件商户,我希望已有商户端账号时纳管不覆盖我的密码,以便不会忽然登不上去。 20. 作为外部进件商户,我希望看到与下单闸门一致的「能否收款 + 原因 + 最近核实时间」,以便判断问题在自己还是在平台。 21. 作为外部进件商户,我希望点击「刷新进度」和「立即核实」推进状态,以便本地收不到回调时也能收敛。 22. 作为外部进件商户,我希望缺少实名或授权时看到办理入口(二维码),以便本人扫码完成。 23. 作为外部进件商户,我希望通道查询失败时保留上一次的进度而不是显示成失败,以便不会误以为进件出问题。 24. 作为连锁商户,我希望几个门店各自是一个独立店铺、共用同一个收款主体,以便门店各自经营但资金进一个主体。 25. 作为连锁商户的门店,我希望自己的订单、核销与门店维度都对得上,以便和共用同一收款主体的其他门店区分开。 26. 作为平台,我希望纳管前置校验商户是否落在我们**渠道商**的管辖范围内,以便不会对一个我们签名身份查不到的商户建记录。 27. 作为平台,我希望纳管复用既有的分阶段进度、能力快照与收款闸门判定,以便外部进件商户和自助进件商户走完全相同的收款就绪口径。 28. 作为平台,我希望纳管不新增任何定时任务,以便维持「进件进度由人工刷新推进」的既有取舍。 29. 作为平台,我希望纳管只在后台提供,不开放商户端自助认领,以便不会出现「填上别人的商户号就能挂靠」的冒领。 30. 作为平台,我希望纳管不影响自助进件的既有流程与存量商户,以便两条路径并存而不互相干扰。 31. 作为平台,我希望共用一个收款主体时,对账仍能按订单上的门店归属分摊到各商家,以便资金能说清是哪家门店的。 ## Implementation Decisions **范围与形状** - 只做**后台纳管入口 + 解绑**,外加支撑它们所需的最小改动。共用同一个汇付商户号本身不需要新机制: 现有表对 `provider_huifu_id` 没有唯一约束,也没有任何查重,多个商家指向同一个号本来就被允许。 - **连锁按「每个门店建成一个独立商家」实现**,不做「一个商家挂多个门店」。后者需要新做「给商家加门店」 的入口与商户端按门店进件,本次不做。 - 复用既有的进件记录、分阶段进度(六阶段)、**收款能力**快照与**收款就绪**判定,不新造状态、不新造并行链路。 **新写入口与来源标记** - `provider_huifu_id` 目前的三个写入口全部来自汇付回执(进件结果与绑卡回调)。纳管必须**新增一个写入口**。 - 进件记录新增一列标明来源(自助进件 / 纳管),用于进度刷新区分判定依据。迁移编号取 **V157** (当前已提交最大为 V156,动手前确认无人占号)。**落地时确实撞号了**:远端同期推送了 `V157__backfill_premium_service_merchant_types`,本票让号改为 **V158**(见迁移文件头的让号说明)。 - 进件记录的 `application_no` 与 `idempotency_key` 都是唯一键且非空,纳管生成的键必须可稳定复现 (由汇付商户号派生),使重复纳管幂等。 **结算卡是硬前提** - 分阶段进度只接管「已关联结算银行卡且未被整体拒绝」的申请;下单闸门另行要求该商家存在一张已通过的 结算卡。因此纳管**必须同时建立结算卡记录**,否则进度不会推进、闸门也不会放行。 - 卡号从通道的商户详细信息查询取回即可(该接口按 `huifu_id` 查询,返回结算卡列表与绑卡序列号)。 **已确认能拿到明文卡号**:现有的结算卡核实就是拿该查询返回的 `qry_cash_card_info_list[].card_no` 与本地卡号哈希做匹配的,且校验为 8-32 位纯数字(掩码含 `*` 会直接不通过),代码注释亦写明 「通道回执里的卡号是明文」。因此纳管表单**不需要**增加卡号输入。 - 结算卡记录沿用现有的字段级加密与卡号哈希算法,不得为此新造一套。 **进度刷新要能吃下「没有我方申请单号」的进件** - 现有刷新流程的第一步是按 `huifu_id` + `apply_no` 调申请单状态查询,且要求受理成功才继续。而 `apply_no` 在该接口的请求中是必填项,外部进件商户没有我方发起过的申请单号,这一步会直接失败, 整段刷新只写一条同步错误就跳过——基础审核阶段永远无法确认,进件状态永远进不了通过。 - 做法:**纳管时把汇付侧那个入驻申请单号一起录入**(控台的入驻申请列表里可见,运营能取到)。该字段 **必填**,刷新流程因此无需改动即可跑通。 - **不做**不依赖申请单号的备用判定分支:取不到申请单号的商户不予纳管,提示运营去控台取,而不是为它 另开一条并行的判定路径。 **防绑错:主体名只做人工核对,不做程序判定** - 通道的商户详细信息查询返回**商户注册名称**(要求与证照一致,即工商全称)与**商户简称**两个名字; 而我们这边 `merchant` 只有 `merchant_name` 一个名字,且往往是服务点名或品牌简称,与通道的全称形态 不对应。因此**不能用字符串比较判定「对得上」**——误拒会把运营卡死且无路可绕,误放等于没设防。 - 程序只判确定性的事:用我们的签名身份查得到该商户(查不到即拒)。 - 名称改为**展示 + 显式确认**:纳管对话框列出通道返回的商户注册名称、商户简称与主体类型(企业/个人), 由运营与所选商家核对并显式勾选确认后才能提交。 - 两个名称与所选商家名**连一个字符都不重叠**时给出醒目警示,但不硬拒(可能是简称、别名或服务点名)。 - 通道侧的主体名写入进件记录(进件记录本来就有主体名字段),使进件列表能同时看到我方商家名与通道主体名。 **纳管记录本身要填什么、以及冲突处理** - 进件记录里有几个非空字段(主体类型、主体名、联系人、联系电话、申请单号、幂等键)。纳管时这些 **从通道的商户详细信息查询反填**(该接口按 `huifu_id` 返回客户类型与主体资料),拿不到的(如联系人) 再从我们的商家记录取。不得留空导致后续读取失败。 - 纳管前要检查**该商家、该门店**是否已经有一条进行中或已通过的进件。已有则拒绝并说明(同一门店出现两个 收款主体会在下单时打架)。这与「重复纳管同一商家幂等」是两件事:前者是防冲突,后者是防重复点击。 - 纳管成功后跑一次进度刷新,由刷新决定这条记录最终是通过还是待办。 **商户端账号不在纳管里处理** - 纳管只管收款绑定,**不创建商户端登录账号**。账号由「商家查询」的商家编辑负责(该处已经有创建/更新 商户端账号的能力)。 - 但纳管时若该商家还没有登录账号、或未开启商户端登录,应给出提示,让运营知道商户还登不进来。 - 解绑**不动**商户端账号:解绑只是解除收款绑定,商家的登录与资料照旧。 **共用同一个汇付商户号** - 入口对「该商户号已被其他商家绑定」**只提示不拦截**,提示文案列出已绑的商家,由运营确认。 - 每个商家各自有一条进件记录、各自一份进度与能力快照。这是本次有意接受的代价:体量下并发副本很少, 不一致是瞬时的,会在下一次成功刷新后自动收敛。**不做挂靠表**。 **解绑** - 解绑清理该商家的进件记录与结算卡记录,并回收商户端收款相关权限。 - 解绑前必须检查该商户号上是否还有在途订单或待结算资金,有则拒绝并说明原因。 - 解绑只解除我方绑定:不上报通道、不回退通道侧的任何改动。这一点必须在界面上写明。 **后台页面:入口放哪** - **纳管入口**放在「财务管理 → 结算记录 → 商户进件」页签的筛选栏里,作为该页签的主按钮(位置沿用「商家查询」 页「新建」按钮的既有习惯)。理由是纳管产出的就是一条进件记录,这个页签正是进件记录的列表,纳管结果当场可见。 点开后是一个**对话框**(选我方商家 + 汇付商户号 + 入驻申请单号;卡号不用填,从通道查回来), 对话框里同时列出通道返回的商户注册名称、商户简称与主体类型供运营核对并显式确认; 提交后刷新列表,并可直接进入该进件详情。 - **解绑入口**放在「商户进件详情」页,与既有的「刷新进度 / 重试失败步骤 / 查询通道状态 / 发起微信实名」并列, 作为**危险操作**(二次确认,并在确认前说明在途订单与待结算资金的拦截条件)。解绑成功后该进件记录消失, 页面需回到列表;该商家的结算卡也会同时从「银行卡申请」页签消失。 - 不新增路由:对话框沿用现有页面,避免改动共享的前端路由文件。若团队更看重与「新建商家」(独立路由页) 的一致,也可以做页面,但那会新增一条路由。 - 只在后台提供,**不开放商户端自助认领**(避免「填上别人的商户号就能挂靠」的冒领)。 - **权限不新增**:纳管与解绑的端点放在既有的 `/merchant-settlement/onboardings/...` 之下,即可沿用现有的 `admin.merchant-settlement.edit`(该前缀已按「GET 为 view、其余为 edit」统一判定),前端按钮用同一个 权限码控制。这样不需要新增权限码,也不需要改共享的权限映射。 - 商家查询页**不放**这个入口:它是商家资料域,且每行已有禁用/编辑/团购管理三个操作,再加会挤。若运营的心智 是「这家商家要收款」而希望从商家列表进入,可另议,但同一功能不要两处都放。 **既有取舍的继承(不得违背)** - 不新增进件定时任务或页面自动轮询;进度由人工刷新与回调推进(既有 ADR)。 - 商户端只展示派生结果(能否收款 + 原因 + 最近核实时间),不回传通道原始值(既有 ADR)。 - 需要通道推进的状态必须有查询对等物;纳管触发的刷新就是这条查询路径。 - 不得为了开发便利提供「跳过真实通道」的捷径。 ## Testing Decisions - **测什么**:只测对外可见的行为——纳管与解绑接口的输入校验与结果、纳管后该商家能通过下单闸门、 共用同一商户号的多个商家各自可用、重复纳管幂等、解绑的在途资金拦截、进度刷新在无申请单号时 仍能收敛到收款就绪。 - **不测什么**:不测内部实现细节(具体 SQL、私有方法调用顺序、列名)。不测通道返回的原始字段如何 被解析——那属于进件解析已有测试的范围。 - **在哪测(接缝)**:沿用现有的**接口层 + 真实测试库 + 模拟汇付响应**这一条最高接缝,与 `merchant-wechat-onboarding` 的既有测试同源(该目录下已有进件、补开、能力、绑卡等接口级测试)。 本功能**不需要新接缝**;纳管只是进件这条链路上新增的两个入口,复用同一条接缝即可。 - **先验一项**:动手前先用一次只读的通道查询确认两件事——我们用现有签名身份能否查到目标商户、 详情查询是否返回明文卡号。这两个答案决定入口字段是三个还是四个。 - **回归面**:改动落在进件记录写入、进度刷新判定与收款闸门读取上,属于支付链路核心,必须跑通全量 支付相关测试(仓库既有基线失败除外)。 ## Out of Scope - **通道侧能力补齐**:纳管只查询不下发,不做补开微信、绑定人才端 AppID、改支付场景/费率、开启延迟 入账。这些如需处理,另行走自助进件的既有路径。 - **「一个商家挂多个门店」**:不做给商家加门店的入口,不做商户端按门店进件。 - **分账**:通道的收款方 + 分账接收方能力本身不在本次范围。 - **不依赖申请单号的备用判定分支**:纳管时入驻申请单号必填,取不到就不纳管。 - **商户端自助认领**:不做,纳管只放后台。 - **批量/自动纳管**:不做一个「扫一遍控台把商户批量接进来」的能力;本次是逐个纳管。 - **对账分摊逻辑**:共用同一个收款主体时,靠订单上已记录的门店归属分摊到各商家;本次不新增分摊逻辑。 - **挂靠表**:不做「一份收款主体 + N 个引用」的模型。 - **存量/自助进件商户的行为**:不改变它们的任何现有流程。 ## Further Notes - **关于二清**:能共用一个汇付商户号,这件事本身就意味着只有一个主体,共用不引入资金二次清算, 不需要再单独确认「是不是同一法人」。真正的风险是**绑错主体**——把别家的商家接到这个商户号上—— 由纳管时的主体名核对拦截。 - **前提(通道权限)**:纳管的第一步是用我们自己的签名身份查一次;查不到说明该商户不在我们 **渠道商**的管辖范围内,纳管无法进行。注意按领域上下文,数据权限由签名身份决定,**把商户改成挂在 平台自己名下既解决不了权限失败、还会平白多出材料要求**,所以不要把它当成「迁一下归属就能解决」的事。 - **待确认**:纳管入口的商户端账号处理——若该商家已有登录账号,只做启用、不重置密码。 - **不接受的做法**:为了快速看到效果而伪造通道成功状态、或在纳管里自动下发通道改动。 - **建议的落地节奏**:先拿**一个**外部进件商户端到端走通(纳管 → 刷新 → 商户端可登录且显示收款就绪 → 真实收一笔),再铺开给其余商户,不要一次性做完再验。