|
@@ -0,0 +1,246 @@
|
|
|
|
|
+# Epic 011 - Mini小程序支付退款完整流程 - Brownfield Enhancement
|
|
|
|
|
+
|
|
|
|
|
+## Epic Goal
|
|
|
|
|
+
|
|
|
|
|
+跑通当前mini提交创建订单后,调用微信小程序支付,支付回调,更新订单状态。订单支付成功后,在min订单列表进入订单详情,点击取消订单后,可以取消订单并调用微信支付sdk退款,最后更新订单状态。
|
|
|
|
|
+
|
|
|
|
|
+## Epic Description
|
|
|
|
|
+
|
|
|
|
|
+### Existing System Context
|
|
|
|
|
+
|
|
|
|
|
+**Current relevant functionality:**
|
|
|
|
|
+- **支付模块**已实现微信小程序支付功能
|
|
|
|
|
+- **订单模块**已实现订单创建和管理功能
|
|
|
|
|
+- **多租户系统配置模块**已实现租户隔离的支付配置管理
|
|
|
|
|
+- **Redis缓存**已用于session_key存储和系统配置缓存
|
|
|
|
|
+- **共享CRUD包**已提供完整的多租户支持
|
|
|
|
|
+
|
|
|
|
|
+**Technology stack:**
|
|
|
|
|
+- TypeORM + PostgreSQL (数据库)
|
|
|
|
|
+- Hono + Zod OpenAPI (API框架)
|
|
|
|
|
+- Redis (缓存)
|
|
|
|
|
+- 微信小程序SDK (认证和支付)
|
|
|
|
|
+- @d8d/shared-crud (共享CRUD工具包)
|
|
|
|
|
+
|
|
|
|
|
+**Integration points:**
|
|
|
|
|
+- 支付模块的PaymentService (小程序支付)
|
|
|
|
|
+- 订单模块的OrderService (订单管理)
|
|
|
|
|
+- 系统配置模块的SystemConfigService (支付配置)
|
|
|
|
|
+- Redis缓存工具 (配置缓存)
|
|
|
|
|
+- 微信支付SDK (退款功能)
|
|
|
|
|
+
|
|
|
|
|
+### Enhancement Details
|
|
|
|
|
+
|
|
|
|
|
+**What's being added/changed:**
|
|
|
|
|
+- 完善支付回调处理逻辑,确保订单状态正确更新
|
|
|
|
|
+- 实现订单取消功能,支持支付订单的取消和退款
|
|
|
|
|
+- 集成微信支付SDK退款功能
|
|
|
|
|
+- 优化订单状态流转逻辑
|
|
|
|
|
+- 添加退款记录和状态跟踪
|
|
|
|
|
+
|
|
|
|
|
+**How it integrates:**
|
|
|
|
|
+- **复用现有支付模块**的支付功能
|
|
|
|
|
+- **扩展订单模块**支持取消和退款操作
|
|
|
|
|
+- **使用系统配置模块**获取租户特定的支付配置
|
|
|
|
|
+- **集成微信支付SDK**实现退款功能
|
|
|
|
|
+- **使用Redis缓存**优化配置访问
|
|
|
|
|
+
|
|
|
|
|
+**Success criteria:**
|
|
|
|
|
+- 用户可以在mini小程序中成功创建订单并支付
|
|
|
|
|
+- 支付回调正确更新订单状态
|
|
|
|
|
+- 用户可以在订单详情中取消已支付订单
|
|
|
|
|
+- 取消订单时自动调用微信支付退款
|
|
|
|
|
+- 退款回调正确更新订单和退款状态
|
|
|
|
|
+- 整个流程支持多租户隔离
|
|
|
|
|
+
|
|
|
|
|
+## Stories
|
|
|
|
|
+
|
|
|
|
|
+1. **Story 1:** 完善支付回调处理逻辑 - 确保支付回调正确更新订单状态,支持多租户隔离
|
|
|
|
|
+ - **验收标准:**
|
|
|
|
|
+ - 支付回调接口正确处理微信支付通知
|
|
|
|
|
+ - 订单状态从"待支付"正确更新为"已支付"
|
|
|
|
|
+ - 支付时间、支付流水号等字段正确记录
|
|
|
|
|
+ - 支持多租户隔离,不同租户的支付回调互不影响
|
|
|
|
|
+ - 添加支付回调日志记录
|
|
|
|
|
+
|
|
|
|
|
+2. **Story 2:** 实现订单取消功能 - 支持已支付订单的取消和退款流程
|
|
|
|
|
+ - **验收标准:**
|
|
|
|
|
+ - 在订单详情页面添加取消订单按钮
|
|
|
|
|
+ - 取消订单时验证订单状态(仅允许取消"待支付"和"已支付"订单)
|
|
|
|
|
+ - 对于已支付订单,触发退款流程
|
|
|
|
|
+ - 订单状态正确更新为"已取消"
|
|
|
|
|
+ - 取消原因和操作时间正确记录
|
|
|
|
|
+
|
|
|
|
|
+3. **Story 3:** 集成微信支付退款功能 - 调用微信支付SDK实现退款
|
|
|
|
|
+ - **验收标准:**
|
|
|
|
|
+ - 集成微信支付退款SDK
|
|
|
|
|
+ - 实现退款请求构建和签名
|
|
|
|
|
+ - 处理退款回调通知
|
|
|
|
|
+ - 退款状态正确更新到订单
|
|
|
|
|
+ - 退款金额、退款流水号等字段正确记录
|
|
|
|
|
+ - 支持部分退款和全额退款
|
|
|
|
|
+
|
|
|
|
|
+4. **Story 4:** 优化订单状态流转和退款记录 - 完善订单状态管理和退款跟踪
|
|
|
|
|
+ - **验收标准:**
|
|
|
|
|
+ - 实现完整的订单状态机
|
|
|
|
|
+ - 添加退款记录实体,跟踪退款历史
|
|
|
|
|
+ - 在订单详情中显示退款状态和记录
|
|
|
|
|
+ - 支持退款失败的重试机制
|
|
|
|
|
+ - 添加退款操作的审计日志
|
|
|
|
|
+
|
|
|
|
|
+## Compatibility Requirements
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 现有支付API保持不变
|
|
|
|
|
+- [ ] 现有订单API向后兼容
|
|
|
|
|
+- [ ] 数据库schema变化向后兼容
|
|
|
|
|
+- [ ] UI变化遵循现有模式
|
|
|
|
|
+- [ ] 性能影响最小化
|
|
|
|
|
+
|
|
|
|
|
+## Risk Mitigation
|
|
|
|
|
+
|
|
|
|
|
+**Primary Risk:** 退款功能可能影响现有支付流程的稳定性
|
|
|
|
|
+
|
|
|
|
|
+**Mitigation:** 分阶段实施,先完善支付回调,再实现退款功能
|
|
|
|
|
+
|
|
|
|
|
+**Rollback Plan:** 如果出现问题,可以暂时禁用退款功能,保持现有支付流程正常工作
|
|
|
|
|
+
|
|
|
|
|
+## Definition of Done
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 所有故事完成且验收标准满足
|
|
|
|
|
+- [ ] 支付回调功能通过测试验证
|
|
|
|
|
+- [ ] 退款功能通过测试验证
|
|
|
|
|
+- [ ] 订单状态流转正确
|
|
|
|
|
+- [ ] 多租户隔离正常工作
|
|
|
|
|
+- [ ] 现有功能无回归
|
|
|
|
|
+
|
|
|
|
|
+## Technical Implementation Details
|
|
|
|
|
+
|
|
|
|
|
+### 支付回调处理
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 支付回调接口
|
|
|
|
|
+app.post('/api/payment/callback', async (c) => {
|
|
|
|
|
+ const { tenantId } = c.get('tenant');
|
|
|
|
|
+ const notification = await validateWechatPaymentNotification(c.req.raw);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新订单状态
|
|
|
|
|
+ await orderService.updateOrderPaymentStatus(
|
|
|
|
|
+ tenantId,
|
|
|
|
|
+ notification.out_trade_no,
|
|
|
|
|
+ 'paid',
|
|
|
|
|
+ {
|
|
|
|
|
+ paymentTime: new Date(),
|
|
|
|
|
+ transactionId: notification.transaction_id,
|
|
|
|
|
+ totalFee: notification.total_fee
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ return c.text('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>');
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 订单取消和退款
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 订单取消服务
|
|
|
|
|
+class OrderService {
|
|
|
|
|
+ async cancelOrder(tenantId: number, orderId: number, reason: string) {
|
|
|
|
|
+ const order = await this.getOrder(tenantId, orderId);
|
|
|
|
|
+
|
|
|
|
|
+ if (order.status === 'paid') {
|
|
|
|
|
+ // 触发退款
|
|
|
|
|
+ const refundResult = await paymentService.refund(
|
|
|
|
|
+ tenantId,
|
|
|
|
|
+ order.transactionId,
|
|
|
|
|
+ order.totalAmount,
|
|
|
|
|
+ order.outTradeNo
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 更新订单状态和退款记录
|
|
|
|
|
+ await this.updateOrderStatus(tenantId, orderId, 'cancelled', {
|
|
|
|
|
+ cancelReason: reason,
|
|
|
|
|
+ refundTransactionId: refundResult.refund_id
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 直接取消未支付订单
|
|
|
|
|
+ await this.updateOrderStatus(tenantId, orderId, 'cancelled', {
|
|
|
|
|
+ cancelReason: reason
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 退款记录实体
|
|
|
|
|
+```typescript
|
|
|
|
|
+@Entity('order_refund')
|
|
|
|
|
+export class OrderRefund {
|
|
|
|
|
+ @PrimaryGeneratedColumn()
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ tenantId!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ orderId!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ refundAmount!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ refundStatus!: 'pending' | 'success' | 'failed';
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ refundTransactionId?: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ refundReason?: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @Column()
|
|
|
|
|
+ updatedAt!: Date;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 订单状态机
|
|
|
|
|
+- **待支付** → **已支付** (支付成功)
|
|
|
|
|
+- **待支付** → **已取消** (用户取消)
|
|
|
|
|
+- **已支付** → **已取消** (用户取消 + 退款)
|
|
|
|
|
+- **已支付** → **退款中** (退款申请)
|
|
|
|
|
+- **退款中** → **已退款** (退款成功)
|
|
|
|
|
+- **退款中** → **退款失败** (退款失败)
|
|
|
|
|
+
|
|
|
|
|
+## Validation Checklist
|
|
|
|
|
+
|
|
|
|
|
+### Scope Validation
|
|
|
|
|
+- [x] Epic可以在4个故事内完成
|
|
|
|
|
+- [x] 不需要架构文档
|
|
|
|
|
+- [x] 增强遵循现有模式
|
|
|
|
|
+- [x] 集成复杂度可控
|
|
|
|
|
+
|
|
|
|
|
+### Risk Assessment
|
|
|
|
|
+- [x] 对现有系统风险可控
|
|
|
|
|
+- [x] 回滚计划可行
|
|
|
|
|
+- [x] 测试方法覆盖现有功能
|
|
|
|
|
+- [x] 团队对集成点有足够了解
|
|
|
|
|
+
|
|
|
|
|
+### Completeness Check
|
|
|
|
|
+- [x] Epic目标清晰可实现
|
|
|
|
|
+- [x] 故事范围适当
|
|
|
|
|
+- [x] 成功标准可衡量
|
|
|
|
|
+- [x] 依赖项已识别
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+**Story Manager Handoff:**
|
|
|
|
|
+
|
|
|
|
|
+"请为这个棕地史诗开发详细的用户故事。关键考虑因素:
|
|
|
|
|
+
|
|
|
|
|
+- 这是对运行TypeORM + PostgreSQL + Hono + Redis + @d8d/shared-crud的现有系统的增强
|
|
|
|
|
+- **核心简化**: 复用现有支付和订单模块,扩展退款功能
|
|
|
|
|
+- 集成点:支付模块的PaymentService、订单模块的OrderService、系统配置模块的SystemConfigService、微信支付SDK
|
|
|
|
|
+- 要遵循的现有模式:支付模块的支付流程、订单模块的CRUD模式、多租户实体模式
|
|
|
|
|
+- 关键兼容性要求:现有支付API保持不变,订单API向后兼容
|
|
|
|
|
+- 每个故事必须包括验证现有功能保持完整的验证
|
|
|
|
|
+
|
|
|
|
|
+该史诗应在保持系统完整性的同时交付完整的支付退款流程功能。"
|