Files
junhong_cmp_fiber/openspec/changes/add-payment-merchant-pools/tasks.md
2026-09-10 10:53:07 +08:00

21 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 1. 数据、配置与双读基础
- [x] 1.1 冻结现状契约与新旧分流边界:追踪 `tb_wechat_config`、订单/充值、`tb_payment`、支付加载器、微信/支付宝/富友回调、查单和现有退款处理的 `payment_config_id` 读写链路,形成文件/符号级实现清单。明确新单以 `merchant_id` 非空走商户加载,历史 `merchant_id` 为空继续走旧 `payment_config_id`;明确三类新支付仅为 C 端套餐购买、C 端资产钱包充值、代理在线预存款充值,后台线下订单、后台钱包余额支付和员工线下代充值不经过商户池;列出敏感字段、富友 CommonQuery 接缝及退款 A仅商户加载/能力判定)的边界。不得修改源码。
- [x] 1.2 新增成对 Schema 迁移:创建商户、商户池、成员、微信授权配置、凭证版本、成功累计唯一事实及支付路由/统计世代快照所需表列;建立每种支付方式最多一个启用池、成员支付方式一致、历史引用保护、唯一累计事实和查询索引约束。不得修改既有迁移,不把迁移文件编号写死在任务或实现契约中。
- [x] 1.3 实现商户、商户池与微信授权配置管理交付模型、Query、Handler、可执行 RouteSpec 及管理审计写操作仅限超级管理员和平台用户实现启停、成员顺序、引用字段锁定、移除后二次确认删除、唯一启用池和全局唯一微信授权配置。C 端微信登录、OpenID、JSSDK 和支付 AppID 只读授权配置;普通 DTO、错误、日志、审计、快照、导出不得返回或保存敏感凭证。
- [x] 1.4 先行实现商户双读与凭证版本化加载:在三类新支付接入前,使商户/微信授权凭证更新在事务提交时递增版本;支付创建、回调验签、查单和退款 A 先从主库取得当前版本,再按“配置 ID + 版本”读取缓存,旧版本提交后不得被新读取命中,缓存不可用时回源当前数据库记录。`merchant_id` 非空的新单走商户服务商凭证,空值历史单走 `payment_config_id`;不得按当前启用池推断历史商户。
- [x] 1.5 实现上线迁移与零 active A 规则:存在唯一当前生效综合支付配置时,仅复制完整凭证;按完整微信/支付宝凭证分别创建商户及单成员启用池,微信授权字段完整时创建全局授权配置;凭证不完整的方式不建池,授权字段不完整不建授权配置。零条 active 综合支付配置时仅创建新 Schema 和管理入口,绝不插入商户、商户池或微信授权业务行;新线上支付明确按“暂无可用商户”失败且不回退旧配置。多条 active 配置时迁移失败且不选择来源。敏感数据仅在数据库复制,迁移日志不得输出凭证,不批量回填既有支付单 `merchant_id`
## 2. 商户池与支付链路
- [x] 2.1 实现商户池选择器与统计世代规则:支持金额、笔数、时间轮询;金额/笔数支持每轮累计、自然日累计、自然月累计,时间周期最小 1 分钟并带单位和起始时间。支付创建事务内选择启用且未达阈值成员,冻结商户、池、轮询快照和 `routing_epoch`;修改阈值保留当前统计,修改统计周期、策略、时间周期、起始时间或每轮排序按规则开启新世代;自然周期仅排序调整时保留未移除成员累计;停用成员即时跳过但不改写既有支付。不得预下单预占。
- [x] 2.2 接入支付成功首次生效统计:以支付 ID 唯一事实按支付创建时冻结的商户和 `routing_epoch` 累计金额/笔数;迟到首次成功仍写入冻结世代,不改写当前选路世代;失败、关闭、预下单和退款不计入;重复回调、重复事件和重复执行不得重复累计。
- [x] 2.3 接入三类新支付创建并保持代理边界:在双读/版本加载和选择器完成后,将商户池选择接入 C 端套餐购买、C 端资产钱包充值、代理在线预存款充值。每笔支付在同一业务事务内冻结 merchant/pool/支付方式/服务商/非敏感身份/轮询及统计世代快照,再使用所选商户凭证创建支付。三类后续新支付始终只走商户池;无池、无可用成员或池停用时明确失败,预下单失败不得回退旧综合配置、不得自动换商户重试。后续 Apply 必须删除商户池新支付创建开关、所有引用及任何旧综合配置创建回退。仅代理在线预存款充值走池;员工线下代充值、后台线下订单、后台钱包余额支付及其它代理后台钱包操作不得经过商户池。
- [x] 2.4 改造回调、查单及退款 A 的商户加载:微信、支付宝、富友回调和代理在线查单对 `merchant_id` 非空新单使用商户当前凭证,对空值历史单继续按 `payment_config_id` 双读;停用商户不得阻断已冻结历史单的本地回调/查询/退款 A 路径。退款 A 仅实现依据冻结商户加载当前凭证并作退款能力/必需凭证完整性判断、接入既有退款流程;不得新增或验收微信/支付宝/富友实际渠道退款 API、退款请求、渠道退款回调或外部退款成功保留客户凭证退款、代理钱包回退及既有幂等语义。不得按当前池推断历史商户。
## 3. 文档、渠道核验与发布控制
- [x] 3.1 更新支付、商户、商户池和微信授权管理接口 OpenAPI、可执行路由说明及生成入口覆盖权限、启停、成员排序、引用锁定、二次确认删除、零 active 失败提示、三类新支付和后台排除边界。敏感凭证的脱敏/禁止泄露仅适用于普通 DTO、错误、日志、审计、支付快照、导出及非专用管理响应超级管理员和平台用户的专用管理列表、详情响应继续允许返回完整凭证。文档与 OpenAPI 示例只能使用占位值,绝不写入真实凭据。不得把具体迁移编号或 candidate 目录写入契约,不新增测试体系;不得暗示本 Change 实现渠道退款 API。
- [x] 3.2 富友 B保持现有 `CommonQuery` 请求格式、签名算法、验签、状态映射和恢复语义;仅改本地双读配置来源与商户加载,使双读查单可按商户当前凭证调用。不得改变协议、验签、状态解释、恢复规则或业务能力,不得增加退款能力;未第三方实测可记录但不得作为任务或归档阻塞。
- [x] 3.3 在本地工作区以明确 `DB_*` 指向维护者提供的 `junhong_cmp_test` PostgreSQL 与 Redis DB6完成 migration up/down/up 和数据行为验证:零/一/多 active、支付/授权凭证缺失、三类新支付始终走商户池、后台 wallet/offline 排除、缺失配置、三种轮询、统计世代与迟到成功、凭证轮换及缓存一致性、并发成功累计、商户停用后的历史回调/查单/退款 A、merchant ID 为空旧单兼容、敏感字段不泄露和重复处理幂等。允许仅为当前 Change 创建/删除 fixtures禁止重置整个库仅连接、迁移或实际行为失败时阻塞对应场景。完成后才提交推送 Iteration/8-11每个场景提供可观察状态/错误/快照/统计事实证据;不新增测试体系,不验收实际渠道退款成功。
- [x] 3.4 Iteration/8-11 分支 Gitea 以本次提交 SHA 仅构建/部署 `cmp-test` 测试镜像并检查 migration version不自动执行 migration up/down 或重置整库;记录 API/Worker 容器状态、健康检查和 `/opt/junhong_cmp/logs` 的有限日志不建运行时开关且不是生产发布。部署后验证三类后续新支付立即走商户池并冻结路由无池、成员或停用池明确失败且无旧创建回退merchant ID 为空历史支付仍读旧路径。故障仅在双读版本上前向修复;存在非空 `merchant_id` 支付、成功事实或新配置时,禁止部署不识别新路由的旧二进制和破坏性 down。后续 Apply 必须删除商户池新支付创建开关、所有引用及任何关闭后恢复旧创建的代码。