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

5.3 KiB
Raw Permalink Blame History

name, overview, todos
name overview todos
修复支付业务逻辑问题 修复二次审查发现的支付业务问题:2 个资损级缺陷(虚拟支付订单取消漏洞、道具价格不同步)+ 5 个逻辑缺陷(iOS 最低价校验、退款状态判断、PaidTime 兜底等)+ 2 处文档同步,全部在现有支付代码上做定点修复。
id content status
fix-p0-1 修复取消漏洞:新增 70007 错误码、Cancel 拒绝虚拟支付订单、handleGoodsDeliver 补 Cancelled 补发货分支 completed
id content status
fix-p0-2 修复道具价格不同步:GoodsListVirtualSync 改为全量查询上架商品并更新同步注释与运营约定 completed
id content status
fix-p1 修复 P1 缺陷:新增 70008 与 iOS 最低金额校验、退款推送状态判断、PaidTime 兜底、策略注释 completed
id content status
sync-p2 同步 P2:OrderResponse 加 pay_channel 字段与映射,接口文档补退款路径、错误码与道具刷新说明 completed
id content status dependencies
verify 执行 go build、go vet、go test 与 read_lints 全量验证 completed
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)。