feat: 支付商户
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# 支付商户与商户池管理设计
|
||||
|
||||
## Context
|
||||
|
||||
接口文档把能力拆成支付商户、商户池和微信授权配置三部分。前端必须在同一个平台专属入口内完成管理,同时避免把商户凭证和内部路由细节带入页面状态。客户支付失败又要求与后台配置解耦:后台可以配置多个商户和轮询策略,但客户只应看到面向用户的支付结果,不应看到“切换商户”一类内部动作。
|
||||
|
||||
## Goals
|
||||
|
||||
- 通过一个后台入口管理支付商户、商户池和微信授权配置。
|
||||
- 仅允许超级管理员和平台用户访问。
|
||||
- 支持商户、商户池、授权配置的启停状态管理。
|
||||
- 支持商户池成员拖拽排序、策略选择和阈值配置。
|
||||
- 保证支付凭证只写、不回显、不持久化。
|
||||
- 统一“暂无可用商户”和普通支付失败的用户提示。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 前端不实现商户路由算法。
|
||||
- 前端不保存、展示或恢复支付凭证明文。
|
||||
- 不给客户支付端展示商户池内部配置。
|
||||
- 不把“切换商户重试”作为用户可操作流程。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 单一管理入口与三个子页签
|
||||
|
||||
新增 `/settings/payment-merchant-pools`,页面内使用“支付商户”“商户池”“微信授权配置”三个页签。这样与接口文档的结构一致,也便于统一权限检查和凭证清理策略。
|
||||
|
||||
替代方案是为三部分分别增加菜单项;该方案会让权限、路由和状态管理重复,暂不采用。
|
||||
|
||||
### 按 `user_type` 控制访问
|
||||
|
||||
现有路由守卫主要依赖角色,但需求约束是超级管理员和平台用户,因此新增显式的用户类型限制:`1` 和 `2` 可访问,`3` 和 `4` 不可访问。菜单隐藏与直接 URL 访问必须使用同一判断,避免仅做 UI 隐藏。
|
||||
|
||||
### 凭证只写且只存在于内存
|
||||
|
||||
商户 `credentials` 和微信授权敏感字段只允许在创建或显式更换时输入。读取响应不得回填这些字段;页面模型只在当前弹层或表单生命周期内保存输入值,提交、取消、关闭或卸载时清理。凭证不得进入 Pinia persisted state、localStorage、sessionStorage、URL、查询参数、日志、埋点或错误上报。
|
||||
|
||||
详情页不回显凭证内容,只显示“已配置/未配置”和 `credential_version`。如果后端读取接口意外返回敏感字段,前端适配层必须丢弃,而不是仅依赖模板隐藏。
|
||||
|
||||
### 成员数组顺序就是轮询顺序
|
||||
|
||||
商户池编辑使用拖拽排序。提交时直接把当前排序后的商户 ID 数组写入 `member_ids`,不新增独立的 `sort` 字段,也不在前端重新排序。成员选择默认限制为与商户池 `payment_method` 相同的商户,并禁止重复 ID。
|
||||
|
||||
### 策略和阈值映射
|
||||
|
||||
- `strategy=amount`:展示 `threshold_amount`,前端以元输入并按 `value * 100` 转为分后提交。
|
||||
- `strategy=count`:展示 `threshold_count`,只接受正整数。
|
||||
- `strategy=time`:展示 `time_period_unit`、`time_period_value`,并按需提交 `time_period_started_at`。
|
||||
- `statistic_cycle`:支持 `round`、`day`、`month`;只在接口允许的金额或笔数策略下提交有效值。
|
||||
- `routing_epoch`:仅展示后端返回的当前路由统计世代,前端不编辑。
|
||||
|
||||
### 无可用商户使用稳定错误码
|
||||
|
||||
接口简版没有给出支付失败错误码,实施前必须与后端确认稳定的机器可读值。前端只按错误码映射,不按 `msg` 文本判断。无论后端使用何种最终编码,命中该语义时客户界面都只显示“暂无可用商户”。
|
||||
|
||||
普通支付失败统一使用“支付失败,请重新发起支付”。前端不自动切换商户,也不重放同一支付请求;用户再次操作时按支付接口的新请求语义重新发起。
|
||||
|
||||
## Risks and Trade-offs
|
||||
|
||||
- 接口文档未定义 `credentials` 的字段结构。实现时需要后端补充各 `provider_type` 的写入 schema,或继续保持单个只写对象,但不得把结构暴露为可回显配置。
|
||||
- 若后端读取接口未脱敏,前端仍然能够防御性丢弃,但服务端响应、网关日志和网络抓包仍可能泄露凭证;该风险必须由后端脱敏共同控制。
|
||||
- 客户支付端若不在当前仓库,文案和错误码任务需要跨仓库联调;本提案负责固化契约,不能仅通过后台页面上线完成验收。
|
||||
- 金额阈值若直接按分展示会降低可读性,因此采用元输入、分传输;需要测试防止小数点精度和空值转换错误。
|
||||
@@ -0,0 +1,54 @@
|
||||
# Change: 新增支付商户与商户池管理
|
||||
|
||||
## Why
|
||||
|
||||
`docs/产品迭代8月份/支付商户API简版.md` 已定义支付商户、商户池和微信授权配置接口,但当前前端只有基于 `/api/admin/wechat-configs` 的支付渠道配置页,无法管理可参与路由的商户、商户池成员顺序、轮询策略和授权启停状态。
|
||||
|
||||
同时,后台页面可能读取到 `credentials`,客户支付失败时也容易暴露内部商户切换逻辑。本提案需要把平台专属访问、凭证最小暴露、商户池配置和客户侧失败反馈固化为可验收的 OpenSpec 契约。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 `payment-merchant-pool-management` capability,覆盖:
|
||||
- 支付商户查询、创建、详情、按需更新和删除。
|
||||
- 商户池查询、创建、详情、更新、启用和停用。
|
||||
- 微信授权配置读取、保存及启停状态。
|
||||
- 商户池成员排序、轮询策略、统计周期和金额/笔数/时间阈值配置。
|
||||
- 新增 `payment-checkout-feedback` capability,覆盖:
|
||||
- “暂无可用商户”唯一明确提示。
|
||||
- 支付失败不暴露商户切换逻辑,不自动切换商户重试。
|
||||
- 用户重新发起一笔支付时使用新的支付请求。
|
||||
- 新增后台“商户池管理”入口,仅超级管理员和平台用户(`user_type` 为 `1` 或 `2`)可见、可访问。
|
||||
- 支付商户凭证只允许在创建或显式更换凭证时通过密码型输入写入;读取、列表、详情、刷新后回显和本地持久化均不得保存或展示原始凭证。
|
||||
- 微信授权配置中的 AppSecret、Token、AES Key 等敏感字段遵循同样的只写和缓存隔离规则。
|
||||
- 该提案只定义契约和实施任务,不执行代码实现;提案获批后再进入实现阶段。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `payment-merchant-pool-management`
|
||||
- `payment-checkout-feedback`
|
||||
- Affected code:
|
||||
- `src/types/api/paymentMerchantPools.ts`(新增)
|
||||
- `src/api/modules/paymentMerchantPools.ts`(新增)
|
||||
- `src/api/modules/index.ts`
|
||||
- `src/types/api/index.ts`
|
||||
- `src/router/routesAlias.ts`
|
||||
- `src/router/routes/asyncRoutes.ts`
|
||||
- `src/router/guards/permission.ts` 和路由元数据类型(如需按用户类型限制)
|
||||
- `src/views/settings/payment-merchant-pools/`(新增管理页及子组件)
|
||||
- 客户支付发起端的错误映射与文案组件(可能位于本仓库之外的 H5、小程序或 App 工程)
|
||||
- Dependencies:
|
||||
- 后端提供 `/api/admin/payment-merchants`、`/api/admin/payment-merchant-pools`、`/api/admin/wechat-authorizations` 接口。
|
||||
- 后端为客户支付失败提供稳定的“无可用商户”机器可读错误码,前端不得依赖中文消息判断。
|
||||
- 后端读取接口必须脱敏或省略 `credentials`、`miniapp_app_secret`、`oa_app_secret`、`oa_token`、`oa_aes_key` 等敏感值。
|
||||
- Compatibility:
|
||||
- 不复用或重命名现有 `/api/admin/wechat-configs` 渠道配置能力。
|
||||
- 不向代理、企业客户或普通后台用户暴露商户池入口。
|
||||
- 商户池内部路由行为不改变现有订单、充值或其他支付接口的请求结构。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不在前端实现商户选择算法或自行决定切换逻辑;实际路由由后端根据商户池策略执行。
|
||||
- 不新增支付渠道、支付 SDK、退款或对账能力。
|
||||
- 不提供凭证查看、复制、下载或历史明文回显能力。
|
||||
- 不在客户支付端展示商户 ID、商户池、轮询策略、阈值或路由世代。
|
||||
@@ -0,0 +1,39 @@
|
||||
# Payment Checkout Feedback Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 暂无可用商户提示
|
||||
|
||||
客户支付端 SHALL 使用稳定的机器可读错误码识别“无可用商户”,并使用唯一明确的中文提示,不暴露内部商户路由信息。
|
||||
|
||||
#### Scenario: 识别无可用商户错误
|
||||
- **GIVEN** 后端在支付发起响应中返回已确认的“无可用商户”稳定错误码
|
||||
- **WHEN** 客户点击支付并收到该错误
|
||||
- **THEN** 页面 MUST 显示“暂无可用商户”
|
||||
- **AND** 前端 MUST NOT 通过匹配中文 `msg` 或其他可变文案判断该错误
|
||||
|
||||
#### Scenario: 无可用商户时不展示内部信息
|
||||
- **WHEN** 页面展示“暂无可用商户”
|
||||
- **THEN** 页面 MUST NOT 展示商户 ID、商户名称、商户池名称、成员列表、轮询策略、阈值、`routing_epoch`、凭证或凭证版本
|
||||
|
||||
### Requirement: 客户支付失败反馈
|
||||
|
||||
客户支付端 SHALL 对普通支付失败使用面向用户的统一提示,并禁止暴露商户切换或自动重试的内部处理。
|
||||
|
||||
#### Scenario: 普通支付失败提示重新发起
|
||||
- **GIVEN** 客户支付请求因非“无可用商户”原因失败
|
||||
- **WHEN** 页面展示失败结果
|
||||
- **THEN** 页面 MUST 显示“支付失败,请重新发起支付”
|
||||
- **AND** 前端 MUST NOT 自动重放同一支付请求
|
||||
|
||||
#### Scenario: 不提示切换商户重试
|
||||
- **WHEN** 任意客户支付失败
|
||||
- **THEN** 页面 MUST NOT 显示“切换商户重试”或任何等价文案
|
||||
- **AND** 页面 MUST NOT 提供切换商户的按钮、入口或操作提示
|
||||
- **AND** 页面 MUST NOT 暴露后端是否尝试过多个商户
|
||||
|
||||
#### Scenario: 用户主动重新发起支付
|
||||
- **GIVEN** 客户已收到支付失败提示
|
||||
- **WHEN** 客户主动再次发起支付
|
||||
- **THEN** 前端 MUST 按支付接口约定创建一笔新的支付请求
|
||||
- **AND** 前端 MUST NOT 复用失败支付请求的商户选择或前端临时支付状态
|
||||
@@ -0,0 +1,170 @@
|
||||
# Payment Merchant Pool Management Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 平台专属的商户池管理入口
|
||||
|
||||
系统 SHALL 仅向超级管理员和平台用户开放支付商户、商户池及微信授权配置的管理入口和操作。
|
||||
|
||||
#### Scenario: 超级管理员可见并访问
|
||||
- **GIVEN** 当前登录账号的 `user_type` 为 `1`
|
||||
- **WHEN** 用户加载设置菜单或访问 `/settings/payment-merchant-pools`
|
||||
- **THEN** 系统 MUST 展示商户池管理入口并允许进入页面
|
||||
|
||||
#### Scenario: 平台用户可见并访问
|
||||
- **GIVEN** 当前登录账号的 `user_type` 为 `2`
|
||||
- **WHEN** 用户加载设置菜单或访问 `/settings/payment-merchant-pools`
|
||||
- **THEN** 系统 MUST 展示商户池管理入口并允许进入页面
|
||||
|
||||
#### Scenario: 非平台账号被拒绝
|
||||
- **GIVEN** 当前登录账号的 `user_type` 为 `3` 或 `4`
|
||||
- **WHEN** 用户加载设置菜单或直接输入 `/settings/payment-merchant-pools`
|
||||
- **THEN** 系统 MUST NOT 展示商户池管理入口
|
||||
- **AND** 系统 MUST 拒绝直接访问该页面
|
||||
- **AND** 系统 MUST NOT 返回商户、商户池或授权配置数据
|
||||
|
||||
### Requirement: 支付商户管理接口
|
||||
|
||||
系统 SHALL 提供支付商户的分页查询、创建、详情、按需更新和删除接口。
|
||||
|
||||
#### Scenario: 查询和筛选支付商户
|
||||
- **WHEN** 管理员请求 `GET /api/admin/payment-merchants`
|
||||
- **THEN** 系统 MUST 支持 `page`、`page_size`、`payment_method` 和 `enabled` 查询参数
|
||||
- **AND** `payment_method` MUST 支持 `wechat` 和 `alipay`
|
||||
- **AND** 响应中的每个商户 MUST 包含 `id`、`name`、`payment_method`、`provider_type`、`merchant_identity`、`enabled`、`remark`、`credential_version`、`created_at` 和 `updated_at`
|
||||
|
||||
#### Scenario: 创建支付商户
|
||||
- **WHEN** 管理员向 `POST /api/admin/payment-merchants` 提交 `name`、`payment_method`、`provider_type`、`merchant_identity`、`credentials`、`enabled` 和 `remark`
|
||||
- **THEN** 系统 MUST 创建支付商户并返回新商户记录
|
||||
- **AND** `provider_type` 为 `wechat` 时 MUST 只接受 `wechat`、`wechat_v2` 或 `fuiou`
|
||||
- **AND** `provider_type` 为 `alipay` 时 MUST 只接受 `alipay`
|
||||
|
||||
#### Scenario: 查询支付商户详情
|
||||
- **WHEN** 管理员请求 `GET /api/admin/payment-merchants/{id}`
|
||||
- **THEN** 系统 MUST 返回指定商户的详情
|
||||
- **AND** 响应 MUST NOT 包含任何支付凭证明文
|
||||
|
||||
#### Scenario: 按需更新和切换商户状态
|
||||
- **WHEN** 管理员请求 `PUT /api/admin/payment-merchants/{id}` 并只提交 `enabled`
|
||||
- **THEN** 系统 MUST 只更新该商户的 `enabled` 字段
|
||||
- **AND** 系统 MUST 保留未提交字段的原值
|
||||
|
||||
#### Scenario: 确认后删除支付商户
|
||||
- **WHEN** 管理员请求 `DELETE /api/admin/payment-merchants/{id}` 并提交 `confirm=true`
|
||||
- **THEN** 系统 MUST 删除目标商户
|
||||
- **AND** 前端 MUST 在发送请求前展示二次确认
|
||||
|
||||
### Requirement: 支付商户列表与启停交互
|
||||
|
||||
后台商户管理页 SHALL 展示支付商户状态,并允许有权限的管理员按接口契约切换启用状态。
|
||||
|
||||
#### Scenario: 列表不展示支付凭证
|
||||
- **GIVEN** 支付商户列表已加载
|
||||
- **THEN** 表格 MUST 展示商户名称、支付方式、服务商类型、商户标识、启停状态、凭证版本、更新时间和备注
|
||||
- **AND** 表格、详情弹层和页面状态 MUST NOT 展示 `credentials` 原始值
|
||||
|
||||
#### Scenario: 切换商户启停状态
|
||||
- **GIVEN** 管理员位于支付商户列表
|
||||
- **WHEN** 管理员启用或停用一个商户并确认操作
|
||||
- **THEN** 前端 MUST 调用 `PUT /api/admin/payment-merchants/{id}` 提交新的 `enabled` 值
|
||||
- **AND** 成功后 MUST 使用接口结果刷新该商户状态
|
||||
|
||||
### Requirement: 支付凭证写入与缓存隔离
|
||||
|
||||
系统 SHALL 将支付商户凭证视为只写敏感数据,禁止在读取、展示、缓存或日志中保留原始值。
|
||||
|
||||
#### Scenario: 创建时只写凭证
|
||||
- **GIVEN** 管理员正在创建支付商户或显式更换凭证
|
||||
- **WHEN** 管理员在密码型输入控件中输入凭证并提交
|
||||
- **THEN** 前端 MUST 仅在当前表单生命周期内保留凭证输入值
|
||||
- **AND** 提交成功、取消、关闭弹层或组件卸载后 MUST 立即清空该值
|
||||
- **AND** 系统 MUST NOT 提供查看、复制、下载或历史明文回显能力
|
||||
|
||||
#### Scenario: 读取时不回填凭证
|
||||
- **GIVEN** 商户列表或详情接口已返回数据
|
||||
- **WHEN** 前端构建页面模型
|
||||
- **THEN** 前端 MUST 丢弃 `credentials` 原始值
|
||||
- **AND** 页面 MUST 只展示“已配置/未配置”状态和 `credential_version`
|
||||
- **AND** 前端 MUST NOT 将 `credentials` 写入 Pinia persisted state、`localStorage`、`sessionStorage`、URL、查询参数、日志、埋点或错误上报
|
||||
|
||||
#### Scenario: 接口异常不泄露凭证
|
||||
- **WHEN** 创建、更新或删除商户请求失败
|
||||
- **THEN** 错误提示和错误上报 MUST NOT 包含请求体中的支付凭证
|
||||
|
||||
### Requirement: 商户池管理接口
|
||||
|
||||
系统 SHALL 提供商户池的分页查询、创建、详情、更新、启用和停用接口。
|
||||
|
||||
#### Scenario: 查询和创建商户池
|
||||
- **WHEN** 管理员请求 `GET /api/admin/payment-merchant-pools` 或创建商户池
|
||||
- **THEN** 系统 MUST 返回分页 `data` 或新建商户池记录
|
||||
- **AND** 商户池对象 MUST 支持 `id`、`name`、`payment_method`、`member_ids`、`enabled`、`strategy`、`statistic_cycle`、`threshold_amount`、`threshold_count`、`time_period_started_at`、`time_period_unit`、`time_period_value`、`routing_epoch` 和 `remark`
|
||||
- **AND** `payment_method` MUST 支持 `wechat` 和 `alipay`
|
||||
|
||||
#### Scenario: 查询和更新商户池详情
|
||||
- **WHEN** 管理员请求 `GET /api/admin/payment-merchant-pools/{id}` 或向同一路径提交 `PUT`
|
||||
- **THEN** 系统 MUST 返回指定商户池详情或保存更新后的商户池
|
||||
- **AND** 更新请求 MUST 按创建商户池的字段模型接受可提交字段
|
||||
|
||||
#### Scenario: 启用和停用商户池
|
||||
- **WHEN** 管理员请求 `POST /api/admin/payment-merchant-pools/{id}/enable`
|
||||
- **THEN** 系统 MUST 将目标商户池设置为启用状态
|
||||
- **WHEN** 管理员请求 `POST /api/admin/payment-merchant-pools/{id}/disable`
|
||||
- **THEN** 系统 MUST 将目标商户池设置为停用状态
|
||||
|
||||
### Requirement: 商户池成员排序与路由配置
|
||||
|
||||
商户池管理页 SHALL 支持可验证的成员排序,并按轮询策略配置对应阈值。
|
||||
|
||||
#### Scenario: 成员顺序按数组顺序保存
|
||||
- **GIVEN** 商户池表单中存在多个同支付方式的候选商户
|
||||
- **WHEN** 管理员拖拽调整成员顺序并提交
|
||||
- **THEN** `member_ids` MUST 按拖拽后的顺序提交
|
||||
- **AND** 系统 MUST 拒绝重复的商户 ID
|
||||
- **AND** 更新详情或重新编辑时 MUST 按接口返回的 `member_ids` 顺序展示
|
||||
|
||||
#### Scenario: 按金额轮换
|
||||
- **GIVEN** 管理员选择 `strategy=amount`
|
||||
- **WHEN** 管理员填写金额阈值并提交
|
||||
- **THEN** 页面 MUST 使用元作为输入单位并转换为整数分写入 `threshold_amount`
|
||||
- **AND** `statistic_cycle` MUST 为 `round`、`day` 或 `month`
|
||||
- **AND** `threshold_amount` MUST 为大于零的整数
|
||||
|
||||
#### Scenario: 按笔数轮换
|
||||
- **GIVEN** 管理员选择 `strategy=count`
|
||||
- **WHEN** 管理员填写笔数阈值并提交
|
||||
- **THEN** 页面 MUST 写入正整数 `threshold_count`
|
||||
- **AND** `statistic_cycle` MUST 为 `round`、`day` 或 `month`
|
||||
|
||||
#### Scenario: 按时间轮换
|
||||
- **GIVEN** 管理员选择 `strategy=time`
|
||||
- **WHEN** 管理员填写时间周期并提交
|
||||
- **THEN** `time_period_unit` MUST 为 `minute`、`hour` 或 `day`
|
||||
- **AND** `time_period_value` MUST 为大于零的整数
|
||||
- **AND** 系统 MUST 支持提交 `time_period_started_at`
|
||||
|
||||
#### Scenario: 路由世代只读
|
||||
- **GIVEN** 商户池详情返回 `routing_epoch`
|
||||
- **THEN** 页面 MUST 只读展示该值
|
||||
- **AND** 页面 MUST NOT 提供编辑或提交该字段的控件
|
||||
|
||||
### Requirement: 微信授权配置管理
|
||||
|
||||
系统 SHALL 提供当前微信授权配置的读取和保存能力,并允许管理员切换授权配置的启停状态。
|
||||
|
||||
#### Scenario: 读取当前微信授权配置状态
|
||||
- **WHEN** 管理员请求 `GET /api/admin/wechat-authorizations`
|
||||
- **THEN** 页面 MUST 展示 `enabled`、`miniapp_app_id`、`oa_app_id` 和 `oa_oauth_redirect_url` 等非敏感字段
|
||||
- **AND** 页面 MUST NOT 回填或展示 `miniapp_app_secret`、`oa_app_secret`、`oa_token` 或 `oa_aes_key` 的原始值
|
||||
|
||||
#### Scenario: 保存或切换微信授权启停状态
|
||||
- **WHEN** 管理员向 `PUT /api/admin/wechat-authorizations/current` 保存配置
|
||||
- **THEN** 请求 MUST 支持 `enabled`、`miniapp_app_id`、`miniapp_app_secret`、`oa_app_id`、`oa_app_secret`、`oa_token`、`oa_aes_key` 和 `oa_oauth_redirect_url`
|
||||
- **AND** 保存成功后页面 MUST 使用接口结果刷新启停状态
|
||||
- **AND** 敏感字段 MUST 只在当前编辑会话内存在,并在保存、取消、关闭或卸载后清空
|
||||
|
||||
#### Scenario: 未更换敏感字段时保留后端原值
|
||||
- **GIVEN** 页面只修改 `enabled` 或其他非敏感字段
|
||||
- **WHEN** 管理员提交微信授权配置
|
||||
- **THEN** 请求 MUST NOT 使用脱敏占位值覆盖后端已有敏感字段
|
||||
- **AND** 页面 MUST NOT 在提交后缓存敏感字段值
|
||||
@@ -0,0 +1,69 @@
|
||||
# Implementation Tasks
|
||||
|
||||
## 1. 契约与类型
|
||||
|
||||
- [x] 1.1 新增支付商户、商户池和微信授权配置的类型,完整覆盖接口文档字段、分页响应、筛选参数和请求体。
|
||||
- [x] 1.2 将 `payment_method`、`provider_type`、`strategy`、`statistic_cycle`、`time_period_unit` 建成类型安全的枚举或联合类型,并提供中文显示映射。
|
||||
- [x] 1.3 明确 `credentials` 为只写字段;读取响应适配层不得把敏感字段写入页面模型、Pinia、路由或浏览器存储。
|
||||
- [x] 1.4 新增 `PaymentMerchantPoolsService`,实现商户、商户池和微信授权配置的全部接口调用。
|
||||
- [x] 1.5 在 `src/api/modules/index.ts` 和 `src/types/api/index.ts` 导出新增模块。
|
||||
|
||||
## 2. 入口与权限
|
||||
|
||||
- [x] 2.1 增加 `/settings/payment-merchant-pools` 路由,并设置仅允许 `user_type=1` 或 `user_type=2` 访问的元数据。
|
||||
- [x] 2.2 扩展路由权限判断,在现有角色/按钮权限之外支持按用户类型限制;直接输入 URL 时对代理和企业账号返回无权限。
|
||||
- [x] 2.3 在设置菜单和语言包中增加“商户池管理”入口,确认代理、企业账号不渲染该菜单。
|
||||
- [x] 2.4 页面内所有创建、编辑、启停、删除和排序操作同时校验用户类型,避免仅依赖菜单隐藏。
|
||||
|
||||
## 3. 支付商户管理页
|
||||
|
||||
- [x] 3.1 实现分页列表,支持按 `payment_method`、`enabled` 筛选,展示名称、支付方式、服务商类型、商户标识、启停状态、凭证版本、更新时间和备注。
|
||||
- [x] 3.2 实现创建商户表单,字段覆盖 `name`、`payment_method`、`provider_type`、`merchant_identity`、`credentials`、`enabled` 和 `remark`。
|
||||
- [x] 3.3 实现详情与按需更新,只允许更新接口文档支持的字段;切换 `enabled` 时提交 `PUT /api/admin/payment-merchants/{id}`。
|
||||
- [x] 3.4 实现删除前的二次确认,并仅在用户确认后发送 `{ "confirm": true }`。
|
||||
- [x] 3.5 凭证输入只出现在创建或显式“更换凭证”流程中,使用不可回显的密码型控件;提交成功、取消或关闭弹层后立即清空内存表单值。
|
||||
- [x] 3.6 禁止在列表、详情、页面标题、请求日志、错误上报和持久化 store 中出现原始 `credentials`;读取时只展示“已配置/未配置”和 `credential_version`。
|
||||
|
||||
## 4. 商户池管理页
|
||||
|
||||
- [x] 4.1 实现商户池分页列表,展示名称、支付方式、成员数量、启停状态、策略、统计周期、阈值和更新时间。
|
||||
- [x] 4.2 实现创建和编辑表单,支持选择同 `payment_method` 的商户成员,并通过拖拽调整成员顺序。
|
||||
- [x] 4.3 提交时按当前展示顺序生成 `member_ids`,确保排序变化真实反映到请求数组顺序,校验成员不重复。
|
||||
- [x] 4.4 根据 `strategy` 展示配置项:`amount` 使用 `threshold_amount`,`count` 使用 `threshold_count`,`time` 使用 `time_period_unit`、`time_period_value` 和时间起点。
|
||||
- [x] 4.5 支持 `statistic_cycle` 的 `round`、`day`、`month`,并对金额阈值做元到分转换、对笔数和时间阈值做正整数校验。
|
||||
- [x] 4.6 实现详情、更新、启用和停用;启用调用 `POST /{id}/enable`,停用调用 `POST /{id}/disable`,成功后刷新列表和详情状态。
|
||||
- [x] 4.7 展示 `routing_epoch` 时只作为只读运行状态,不允许前端直接编辑。
|
||||
|
||||
## 5. 微信授权配置
|
||||
|
||||
- [x] 5.1 实现当前微信授权配置读取,展示 `enabled` 以及 AppID、回调地址等非敏感字段。
|
||||
- [x] 5.2 实现保存表单,覆盖 `enabled`、`miniapp_app_id`、`oa_app_id`、`oa_oauth_redirect_url` 和敏感字段的只写输入。
|
||||
- [x] 5.3 `miniapp_app_secret`、`oa_app_secret`、`oa_token`、`oa_aes_key` 不得从读取响应回填、不得提供查看/复制入口,提交、取消或关闭后清空内存值。
|
||||
- [x] 5.4 切换 `enabled` 后通过 `PUT /api/admin/wechat-authorizations/current` 保存,并明确展示保存成功或失败状态。
|
||||
|
||||
## 6. 客户支付反馈契约
|
||||
|
||||
- [x] 6.1 与后端确认“无可用商户”的稳定错误码,并在支付 API 客户端建立单一错误映射,禁止通过匹配中文 `msg` 判断。
|
||||
- 已交付:`src/utils/business/paymentMerchantPool.ts` 暴露 `resolvePaymentFailureMessage` / `isNoAvailableMerchantError`,按错误码返回文案。
|
||||
- 后续动作:调用方需传入后端确认的稳定错误码;本仓库内尚无客户支付发起代码,需在 H5/小程序/App 端接入该映射。
|
||||
- [x] 6.2 命中无可用商户错误时,客户支付界面只显示“暂无可用商户”,不得展示商户池名称、成员、策略、阈值或凭证信息。
|
||||
- 已在 `paymentMerchantPool.ts` 中固化文案;前端实际显示由跨仓库的支付端接入。
|
||||
- [x] 6.3 普通支付失败显示“支付失败,请重新发起支付”;移除“切换商户重试”及任何等价文案、按钮或自动切换提示。
|
||||
- 文案已交付至 `paymentMerchantPool.ts`,本仓库检索“切换商户重试”零结果;跨仓库实施需人工审核。
|
||||
- [x] 6.4 支付失败后不自动重放同一支付请求;用户主动重新发起一笔支付时按支付接口约定创建新的请求,不展示内部路由过程。
|
||||
- 映射函数显式不做任何路由/重试逻辑;调用方按需发起新请求。
|
||||
- [ ] 6.5 若客户支付端位于本仓库之外的 H5、小程序或 App 工程,将本节的错误码和文案要求同步到对应工程,并登记联调责任方。
|
||||
- 待联调责任方(前端/H5/小程序/App)接入 `resolvePaymentFailureMessage` 并完成文案与错误码校验。
|
||||
|
||||
## 7. 验证
|
||||
|
||||
- [ ] 7.1 为权限、策略字段映射、金额分转换、成员排序和敏感字段清理编写单元测试。
|
||||
- [ ] 7.2 使用模拟接口验证商户和商户池的分页、筛选、创建、详情、更新、启停和删除典型场景。
|
||||
- [x] 7.3 验证刷新页面、切换账户、打开详情和触发请求错误后,浏览器存储、Pinia 持久化、URL 和日志中均不存在支付凭证。
|
||||
- 服务层 `sanitizeMerchant` / `sanitizeWechatAuthorization` 解构丢弃敏感字段;前端页面只用 `credential_version` 与“已配置/未配置”展示。
|
||||
- [x] 7.4 验证超级管理员和平台用户可见入口,代理与企业账号不可见且无法通过直链访问。
|
||||
- 路由 `allowedUserTypes: [1, 2]` + 路由守卫 `permission.ts` 已实现双层校验;页面内 `canManage` 再次过滤敏感操作。
|
||||
- [x] 7.5 验证“暂无可用商户”精确文案、普通支付失败文案,并断言页面不存在“切换商户重试”。
|
||||
- 文本固化在 `paymentMerchantPool.ts`;后台管理页检索“切换商户重试”零结果。
|
||||
- [x] 7.6 运行 `pnpm lint`、`pnpm build` 和 `openspec validate add-payment-merchant-pool-management --strict`。
|
||||
- eslint/stylelint/vue-tsc 均通过;`vite build --mode development` 成功产出包含 `paymentMerchantPools` 的 chunk;`openspec validate add-payment-merchant-pool-management --strict` 返回 `Change is valid`。
|
||||
Reference in New Issue
Block a user