first commit

This commit is contained in:
2026-09-11 01:41:52 +08:00
commit 9f22e6ccae
263 changed files with 28935 additions and 0 deletions
@@ -0,0 +1,93 @@
---
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)。