# 11 统一订单与支付（for AI Agents）

> 关联代码：internal/service/order/（order.go/sweeper.go）、internal/wechat/pay/、internal/handler/wechat/notify.go
> 管理端页面：web/src/views/order/OrderList.vue；小程序页面：pages/mall/orders/、pages/mall/order-detail/

====================================================================
1. 功能概述
====================================================================

全站统一一张 Order 表承载所有需要微信支付的交易（11 种商品类型），统一支付回调按类型分发履约，统一订单管理页提供人工兜底（标记已支付/发货/退款/物流）。钱包提现不走订单（见 10）。

====================================================================
2. 底层逻辑
====================================================================

2.1 订单模型
- Order：status = pending（待支付）→ paid（已支付）→ refunding（退款中）→ refunded（已退款）/ closed（超时关闭）。AddressSnapshot 为加密 JSON 快照（AES，见 01）。
- ProductType（11 种）与回调后动作：
  - lottery_entry 抽奖报名费：标记参与资格
  - virtual_goods / physical_goods 虚拟/实物商品
  - promo_slot 展示券套餐：发券到库存
  - mall_product / mall_cart 商城现金单/购物车合并单：扣 SKU 库存（乐观锁）、核销优惠券、销量自增
  - mall_product_points 积分兑换单（0 元占位或混合）
  - ad_recharge 广告余额充值：入账 AdAccount（幂等键 ad_recharge_paid:<orderID>）
  - lottery_prize_settlement 抽奖商城奖品结算单：推进待发货
  - subscription 高级版订阅：开通商户订阅
  - points_recharge 好运币充值：积分入账（默认 100:1 汇率）
- 佣金溯源字段：CommissionRateBps / CPSClickID / CommissionSettledAt（CPS 供应商佣金，见 17）；InviterIDSnapshot（推广员佣金快照——注意：该快照当前从未写入，推广员佣金归因恒空，见 14）。
- OrderStatusLog：状态审计（paid/shipped/received/refunded/closed/refunding/refund_failed/pay_compensate_* 仅追加）。

2.2 支付主流程
- 下单 → 返回 wx.requestPayment 参数（utils/pay.ts 统一调起；取消支付订单保留待支付可重付）。
- 回调 POST /api/wechat/pay/notify：验签 → MarkPaidChecked（校验金额一致）→ 事务内按 ProductType 分发（上述动作）→ 推广员佣金记录（best-effort）。回调非 200 时微信自动重试。
- 资损接口防护：confirm/repay 挂 SignMW 签名防重放（见 01）。

2.3 兜底任务（order Sweeper，60s）
- 超时关单：pending 超过 order.pay_timeout_minutes（默认 150 分钟，刻意大于微信 2 小时回调窗口）自动 closed。
- 超期自动确认收货；优惠券过期标记。
- 人工补偿：后台「标记已支付」写 pay_compensate_* 状态日志（跳过微信支付直接履约，高敏）。

2.4 退款
- 白名单类型才可退：mall_product / mall_cart / mall_product_points / promo_slot / ad_recharge。
- 发起：POST /admin/orders/:id/refund（必填原因，独立高敏权限 order:refund）→ 微信退款接口 → 回调 /api/wechat/refund/notify 更新 refunding→refunded。
- 积分订单退款 = 退回积分 + 回补库存。

2.5 物流
- 发货：POST /admin/orders/:id/ship（物流公司 + 运单号）。
- 物流轨迹：GET/POST /admin/orders/:id/logistics/track（查询 + 手动添加轨迹节点：状态 5 选/位置/备注）；用户端 GET /mp/orders/:id/logistics。

====================================================================
3. 后台操作（订单管理 /orders）
====================================================================

web/src/views/order/OrderList.vue（全站最重的列表页）：
- 表格列：订单ID/用户/商品/金额(元)/状态/物流公司/运单号/买家备注/邀请人/path快照/创建时间/操作。
- 筛选器：订单号、用户、金额(元)、状态（pending/paid/refunding/refunded/closed）、物流状态、创建时间（日期范围）、邀请人、搜索用户、搜索邀请人。
- 「导出 CSV」：GET /api/admin/orders/export，blob 下载。
- 行操作（按条件显隐）：
  - 「标记已支付」（仅 pending；高敏弹窗确认；跳过微信支付直接履约，用于回调丢失的人工补偿——先在微信商户后台核实真实付款再操作）
  - 「发货」（已支付 + 可发货类型 + 待发货；弹窗录物流公司 + 运单号）
  - 「物流」（已发货/已收货；时间线弹窗 + 「添加轨迹」二级弹窗）
  - 「退款」（仅白名单类型；弹窗必填原因）
- 权限：order:*；退款独立权限 order:refund。

3.1 后台操作逻辑备注（隐藏约束，UI 不直接体现）
- 订单状态流转：pending→paid→refunding→refunded/closed；pending 可超时自动 closed（order.pay_timeout_minutes，默认 150 分钟，大于微信 2 小时回调窗口）。
- 「标记已支付」真实履约：POST /orders/:id/mark-paid 跳过微信支付直接触发回调履约逻辑（发券/入账/扣库存等），操作前必须在微信商户后台确认款项已到账；此操作写入审计日志。
- 退款白名单：仅 mall_product/mall_cart/mall_product_points/promo_slot/ad_recharge 五种类型可退；抽奖报名费（lottery_entry）、结算单（lottery_prize_settlement）、订阅（subscription）、充值（points_recharge）不可退——处理客诉走线下或钱包 adjust。
- 发货约束：仅 paid 状态 + 可发货商品类型（实物/商城商品）+ 待发货（未发货）时可操作发货；发货后状态变为 shipped，物流轨迹支持手动补录节点。
- 积分订单退款特殊：退回积分 + 回补库存同事务完成；现金订单退款走微信退款接口，回调异步更新状态。
- 人工补偿审计：mark-paid 和 refund 操作均写入 OrderStatusLog（仅追加不可修改），支持事后审计。

====================================================================
4. 小程序端操作
====================================================================

- 订单列表 pages/mall/orders/index：待支付单可「重付」（repay，带签名）/「取消」；已发货可「查看物流」「确认收货」。
- 订单详情 pages/mall/order-detail/index：物流轨迹、支付/收货/取消操作。
- 下单入口分散在各业务：商城（见 12）、抽奖结算（见 03）、广告充值（见 15）、订阅（见 19）、充值（见 13）、命运盒子购买权（见 08）。支付统一 utils/pay.ts requestPayment；取消支付提示「订单待支付可重付」。
- 确认收货 POST /mp/orders/:id/confirm（SignMW 签名防重放）。

====================================================================
5. API 速查
====================================================================

小程序端（/api/mp）：
GET  /api/mp/orders(/:id)                       我的订单
POST /api/mp/orders/:id/confirm                 确认收货（SignMW）
POST /api/mp/orders/:id/repay                   重新支付（SignMW）
POST /api/mp/orders/:id/cancel                  取消订单
GET  /api/mp/orders/:id/logistics               物流轨迹

回调（公开，微信验签）：
POST /api/wechat/pay/notify | transfer/notify | refund/notify

管理端（/api/admin，权限 order:*，退款 order:refund）：
GET  /api/admin/orders                          订单列表（全量筛选）
GET  /api/admin/orders/export                   CSV 导出
POST /api/admin/orders/:id/mark-paid            人工标记已支付（高敏）
POST /api/admin/orders/:id/ship                 发货
POST /api/admin/orders/:id/refund               退款（白名单类型）
GET/POST /api/admin/orders/:id/logistics/track  物流轨迹查询/补录

====================================================================
6. 注意事项
====================================================================

- 「标记已支付」会真实履约（发券/入账/扣库存），操作前必须在微信商户后台核实款项到账；此操作进审计日志。
- 超时关单时间必须大于微信回调重试窗口（2 小时），调整 pay_timeout_minutes 时不要低于该值。
- 退款仅白名单类型；抽奖报名费、结算单等不可退，处理客诉时走线下/人工调整（钱包 adjust）路径。
- InviterIDSnapshot 从未写入是已知缺口：推广员佣金归因、订单按邀请人筛选、漏斗归因均为空（见 14/17），不要基于这些字段做运营决策。
- 订单金额与回调金额校验在 MarkPaidChecked；金额不一致不会履约，排查「支付了没发货」先看回调日志与订单状态日志。
