Files
zt-closet-backend/.codebuddy/plans/修复支付业务逻辑问题_cb2e4aae.md
2026-09-11 01:41:52 +08:00

93 lines
5.3 KiB
Markdown
Raw Permalink 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.
---
name: 修复支付业务逻辑问题
overview: 修复二次审查发现的支付业务问题:2 个资损级缺陷(虚拟支付订单取消漏洞、道具价格不同步)+ 5 个逻辑缺陷(iOS 最低价校验、退款状态判断、PaidTime 兜底等)+ 2 处文档同步,全部在现有支付代码上做定点修复。
todos:
- id: fix-p0-1
content: 修复取消漏洞:新增 70007 错误码、Cancel 拒绝虚拟支付订单、handleGoodsDeliver 补 Cancelled 补发货分支
status: completed
- id: fix-p0-2
content: 修复道具价格不同步:GoodsListVirtualSync 改为全量查询上架商品并更新同步注释与运营约定
status: completed
- id: fix-p1
content: 修复 P1 缺陷:新增 70008 与 iOS 最低金额校验、退款推送状态判断、PaidTime 兜底、策略注释
status: completed
- id: sync-p2
content: 同步 P2:OrderResponse 加 pay_channel 字段与映射,接口文档补退款路径、错误码与道具刷新说明
status: completed
- id: verify
content: 执行 go build、go vet、go test 与 read_lints 全量验证
status: completed
dependencies:
- fix-p0-1
- fix-p0-2
- fix-p1
- sync-p2
---
## 产品概述
对支付业务逻辑二次审查发现的 2 个资损级问题、5 个逻辑缺陷与 2 个文档问题做定点修复,不改变支付链路整体架构与权益发放回退幂等机制。
## 核心修复内容
### P0 资损级
- P0-1 虚拟支付订单取消漏洞:虚拟支付订单(pay_channel=2)不可再取消(新增 70007 错误码);发货推送补「已取消订单补发货」防御分支,杜绝「用户取消后继续支付、钱已扣但权益永不发放」。
- P0-2 道具价格不同步:道具同步改为全量刷新全部上架商品(含已发布),每次启动重新上传发布道具信息与价格,保证微信侧道具价格与本地改价一致,避免金额校验失败导致付款不发货。
### P1 逻辑缺陷
- iOS 下单校验最低 1 元(100 分),低于此金额拒绝(70008)。
- 退款推送仅「成功」状态才回退权益,防中间状态误回退。
- 发货推送 PaidTime 缺失或异常时兜底为当前时间。
- iOS 退款问询保持默认建议退款,补充策略注释。
### P2 文档与提示
- 订单响应增加 pay_channel 字段(1=微信支付 2=虚拟支付)。
- 接口文档补充虚拟支付退款操作路径(虚拟支付后台 Midas)、道具价格刷新运营约定、新错误码说明。
## 技术方案
### 修复策略
定点修复,复用现有架构与幂等机制:不动 `benefit_grant_tasks` 发放凭证、唯一索引幂等、`settleRefundSuccess`/`revokeOrderBenefits` 回退链路;不新增表结构与迁移 SQL。
### 关键改动
**1. 错误码(pkg/errcode/order_code.go)**
- 新增 `VirtualOrderCancelNotAllowed = NewError(70007, "虚拟支付订单不支持取消")`
- 新增 `VirtualPayAmountTooLow = NewError(70008, "iOS 虚拟支付最低 1 元")`
- 70007/70008 空闲无冲突(70005/70006 已占用)。
**2. 取消漏洞(internal/service/order.go + virtualpay.go)**
- `Cancel`:`getOwnedOrder` 后加 `if order.PayChannel == PayChannelVirtual { return nil, errcode.VirtualOrderCancelNotAllowed }`(置于状态校验前,统一拒绝提示);同时避免对虚拟订单误调 JSAPI `CloseOrder`。
- `handleGoodsDeliver`:switch 增加 `case OrderStatusCancelled:`,条件流转 `Cancelled → Paid` 成功后 `finalizeVirtualPaidOrder` 补发货,与 `Expired` 分支并列(防御历史数据与竞态)。
**3. 道具全量同步(internal/mysqldao/goods.go + virtualpay.go)**
- `GoodsListVirtualSync` 查询条件改为 `Where("status = ?", 1)`,返回全部上架商品(含已发布)。
- `SyncVirtualGoods`/`syncOneGoods` 注释更新为全量同步语义:重复 upload+publish 同 goods_id 视为更新道具信息与价格;商品 SKU 少(5 类),启动一次开销可忽略;失败仍置 `GoodsProductPublishFail`。
**4. P1 系列(internal/service/virtualpay.go)**
- `VirtualOrderCreate`:goods 上架校验后、落单前加 `if request.Platform == XPayPlatformIOS && payAmount < 100` → 返回 70008。
- `handleRefundNotify`:仅 `RefundStatus == ""` 或 `== "SUCCESS"` 时调用 `settleRefundSuccess`,其他状态 `slog.WarnContext` 记录并返回(枚举联调确认)。
- `finalizeVirtualPaidOrder`:`paidTime <= 0` 时兜底 `nowUnix()`。
- `handleIOSRefundQuery`:补充策略注释(默认建议退款、已用权益不回退、后续可按权益使用情况细化拒绝)。
**5. 文档与 DTO(internal/dto/order.go + service/order.go + 接口文档.md)**
- `OrderResponse` 加 `PayChannel int8 json:"pay_channel"`;`buildOrderResponse` 加映射。
- 接口文档:订单对象(4.5 节)补 pay_channel 字段;退款章节补「虚拟支付订单请在虚拟支付后台(Midas)发起退款」;虚拟支付下单章节补 70007/70008 说明与「改价后重启服务自动刷新道具价格」运营约定;错误码表补 70007/70008。
## 验证
- `go build ./...`、`go vet ./...`、`go test ./pkg/wechat/` 通过。
- read_lints 检查改动文件无新增错误。
## 约束
- 不改支付架构、权益幂等机制、表结构;不修改 configs/config.yaml(本地文件,含非法 YAML)。