6.7 KiB
6.7 KiB
R3 订单闭环 + 支付接口占位(feat/r3-order-pay)
Goal: 打通“设计清单 → 结算确认 → 创建订单 → 订单列表/详情 → 状态流转”的服务端闭环, 微信支付只留接口与配置位,不填真实密钥;支付不可用时流程能明确回到“待付款/支付未配置”。
Architecture: 前端 api/order.ts 承接下单与查询,checkout/orders/orderDetail 三个页面
改为服务端数据源;后端订单创建在事务内重算金额、快照地址、生成幂等的订单号并创建 PENDING
支付记录;payments 在密钥未配置时返回显式占位响应,不做假成功。
Tech Stack: Taro 3.6 + React 18 + TypeScript;NestJS 11 + Prisma/PostgreSQL + Redis/BullMQ(可选)。
1. 本分支要开发的内容
前端 wechat_wc
| 文件 | 职责 |
|---|---|
src/utils/api/order.ts |
createOrder/fetchOrders/fetchOrderDetail/payOrder;请求只带商品项、地址 id、designListId,不带金额 |
src/types/index.ts |
服务端 Order 类型:orderNo/status/totalAmount/items/addressSnapshot/createdAt/paidAt |
src/pages/checkout/index.tsx |
从服务端读设计清单,选择地址(用 R2 的地址接口),提交下单;展示服务端总价;支付未配置时提示后回订单列表 |
src/pages/orders/index.tsx |
订单列表按状态 Tab 拉取;付款/确认收货等按钮调用对应接口或显示未配置 |
src/pages/orderDetail/index.tsx |
按订单 id 拉详情;地址快照、商品、状态条、物流占位 |
src/utils/store/order.ts |
标记 deprecated,仅保留为本地兜底,不再被 checkout 主流程调用 |
src/pages/profile/index.tsx |
状态统计改为基于服务端订单列表(R2/R3 合入后统一收尾) |
后端 wxmp_backend
| 模块 | 内容 |
|---|---|
orders |
POST /api/orders:服务端按 Product/design-list 重算金额、事务创建订单+items+Payment、生成唯一 orderNo、快照地址 |
orders 查询 |
GET /api/orders(状态筛选/分页)、GET /api/orders/:id(仅本人)、PATCH /api/orders/:id/confirm、POST /api/orders/:id/cancel(按需) |
payments |
POST /api/payments/:orderId/pay 与 POST /api/payments/notify 保留签名;密钥为空时返回 { configured:false, message:'支付未配置' } |
wechat |
createUnifiedOrder/verifyPayNotify 保持占位,不填入假商户参数 |
queue |
可选:订单创建后入队定制任务,处理器先只更新 CustomizationTask 状态 |
docs/api-contract-v1.md |
订单创建请求/响应、状态机、金额字段精度、错误码、幂等键 |
词云项目 wordcloud
不参与本分支。
2. 订单状态机
服务端 OrderStatus 与前端展示映射:
| 后端 | 前端 Tab | 说明 |
|---|---|---|
| PENDING | 待付款 | 允许取消;支付未配置时长期停留 |
| PAID | 待发货 | 由支付回调推进,占位期不出现 |
| PROCESSING | 待发货 | 定制生产中 |
| SHIPPED | 待收货 | 需要发货/物流数据,本分支可展示占位 |
| COMPLETED | 已完成 | 用户确认收货推进 |
| CANCELLED | 已取消 | 待付款状态用户取消 |
前端只读状态数组,不做状态转移判断;转移一律走后端接口或支付回调。
3. 接口设计要点
创建订单请求:
{
designListId: string,
addressId: string,
items: [{ productId, quantity }]
}
服务端响应:
{
id: string,
orderNo: string,
status: "PENDING",
totalAmount: number,
items: [],
addressSnapshot: {},
createdAt: string
}
4. 设计注意事项
DO
- DO 订单金额、单价、总价全部由服务端计算,前端提交只给
productId + quantity。 - DO 创建订单使用
prisma.$transaction,订单、明细、支付记录任一步失败整体回滚。 - DO 给创建订单接口支持幂等键(客户端
requestId或designListId防重复下单),双击提交只产生一单。 - DO 订单号
orderNo唯一,生成规则包含日期与随机位并在冲突时重试。 - DO 收货地址在创建订单时直接快照到
addressSnapshot,订单创建后不再跟随地址变更。 - DO 支付占位返回结构化
configured:false,前端据此显示“暂不支持支付”而不是错误弹窗。 - DO 后端从配置读取微信支付密钥,密钥缺失时支付接口直接返回占位,不使用默认值或硬编码。
- DO 订单列表/详情都校验当前用户 openid,越权访问返回 403。
- DO 金额相关字段用 Decimal 精确类型持久化,序列化时统一成字符串或 number,避免浮点误差。
DON'T
- DON'T 信任前端传的
totalAmount/price,即使前端为了展示计算过也一律忽略。 - DON'T 让客户端参数直接决定
status,状态只能由服务端接口和支付回调推进。 - DON'T 在代码里填占位商户号、密钥、证书路径;密钥缺失是合法运行状态而非 bug。
- DON'T 模拟支付成功,包括开发环境;未配置就是未配置,避免上线前“假流程”掩盖问题。
- DON'T 支付回调先解锁订单再验签;本分支只留入口,不实现任何未验签的落库逻辑。
- DON'T 在本分支删除
store/order.ts,把它标记 deprecated 即可,本地兜底退出要留到全链路验证后。 - DON'T 修改 R2 的地址/设计清单接口签名,R3 只消费。
5. 补充内容
验收标准
- 指定一个已登录用户,从设计清单进入结算页,选地址后创建订单;服务端返回的
totalAmount与商品单价×数量一致。 - 双击提交只生成一单,订单号唯一。
- 订单列表/详情展示服务端数据,越权用户拿不到他人订单。
- 支付按钮在密钥未配置时返回
configured:false并给出明确文案,不调用微信、不写成功记录。 - 后端
npm run build、前端npm run build:weapp通过。
合并与依赖
- 分支名:前端与后端均为
feat/r3-order-pay。 - 依赖 R1 的商品表和 R2 的地址/设计清单接口,合并顺序放在 R1、R2 之后。
- 支付实现作为一个独立后续里程碑,不阻塞本分支交付。
风险
- 微信小程序对订单支付有场景与类目要求,目前未备案时不要尝试真支付,容易触发审核风险。
- 物流信息当前为假数据,SHIPPED/COMPLETED 的物流时间轴要标注占位。
- 若 R2 未按约定提供地址接口,checkout 集成会被卡住;R3 开发时先按
api-contract-v1.md编写,联调阶段再对齐实现。
测试要求
- 后端:订单创建事务、金额重算、重复提交幂等、越权查询、状态流转、支付未配置占位响应。
- 前端:结算页创建订单、双击防抖、订单空态、状态 Tab 筛选、越权场景提示、支付未配置提示。