# 支付配置动态加载能力规范

## ADDED Requirements

### Requirement: 从支付配置 ID 动态加载支付实例

系统在处理支付验签（订单支付回调、充值确认等）时，应根据订单/充值记录中的 `payment_config_id` 字段动态加载对应的支付配置，而不是使用全局单例 `s.wechatPayment`。

#### Scenario: 订单支付回调验签

- **WHEN** 微信支付回调到达 `/api/callback/wechat`，系统解析回调中的商户号和订单数据
- **THEN** 系统根据订单表中的 `payment_config_id` 从 Redis（TTL 1h）或数据库加载对应的支付配置
- **THEN** 系统使用该配置的私钥和证书验签回调数据
- **THEN** 若配置不存在或当前用户无权限访问该配置，返回 `{code: 1103, msg: "支付配置不存在或无权限"}`

#### Scenario: 充值订单确认支付

- **WHEN** 代理用户在充值页面点击"确认支付"，提交 `recharge_order_id` 和 `amount`
- **THEN** 系统根据充值订单表中的 `payment_config_id` 动态加载支付配置
- **THEN** 系统调用该配置对应的支付 SDK 创建预支付单（微信 JSAPI 或 H5）
- **THEN** 若配置无效或超配额，返回 `{code: 1103, msg: "支付配置无效，请联系商户"}`

### Requirement: 支付配置 Redis 缓存

系统应缓存支付配置到 Redis，减少数据库查询。

#### Scenario: 首次加载配置

- **WHEN** 调用 `PaymentConfigLoader.LoadConfig(ctx, configID)` 且 Redis 中不存在该配置
- **THEN** 系统从数据库查询配置，存入 Redis（key: `payment:config:{configID}`，TTL: 1 小时）
- **THEN** 返回加载的配置对象

#### Scenario: 配置缓存命中

- **WHEN** 调用 `PaymentConfigLoader.LoadConfig(ctx, configID)` 且 Redis 中存在该配置
- **THEN** 系统直接返回 Redis 中缓存的配置
- **THEN** 不查询数据库

#### Scenario: 配置变更后清除缓存

- **WHEN** 支付配置被修改（通过管理后台）
- **THEN** 系统主动删除 Redis 中的该配置缓存（`DEL payment:config:{configID}`）
- **THEN** 下次加载时重新从数据库读取最新配置

### Requirement: 支付配置访问控制

系统在加载支付配置时，根据调用场景采用不同的校验策略：

> **场景分类说明**：
> - **回调场景**：微信支付回调到达 `/api/callback/wechat`，请求来自微信服务器，无登录态，无用户身份上下文
> - **用户操作场景**：代理用户在前端主动发起的支付相关操作（如创建充值单、查询支付配置等），有完整的登录态和用户上下文

#### Scenario: 回调场景 — 商户号一致性校验

- **WHEN** 微信支付回调到达，系统解析回调中的商户号（`mchid`）
- **THEN** 系统根据订单的 `payment_config_id` 加载配置，比较配置中存储的商户号与回调携带的商户号是否一致
- **THEN** 若不一致，记录告警日志并拒绝处理，返回非 2xx 状态码（微信会重试）
- **NOTE** 此场景**不做用户身份鉴权**，无"代理 A/B"概念，只做商户号匹配验证

#### Scenario: 用户操作场景 — 归属权限校验

- **WHEN** 代理用户在前端主动操作（如创建充值单）需加载支付配置
- **THEN** 系统检查该配置的归属（`shop_id` 或 `enterprise_id`）与当前登录用户是否匹配
- **THEN** 若当前用户无权访问该配置，返回 `{code: 403, msg: "无权限访问该支付配置"}`

#### Scenario: 平台用户无限制访问

- **WHEN** 平台管理员的操作需要加载任意支付配置
- **THEN** 系统跳过归属权限检查，允许访问所有配置（仅限用户操作场景）
