소스 검색

📝 docs(prd): update epic-011 payment refund flow status

- update epic progress from 50% to 75% (3/4 stories completed)
- mark Story 3 as completed with all acceptance criteria met
- update current status to include refund functionality implementation
- update next step to Story 4 - Mini app frontend payment page integration
- add detailed implementation of Story 3 including service methods and test coverage
- add refund status machine and payment status enum documentation
- add Story 3 completion summary and technical implementation highlights
- update timeline and task tracking information
yourname 1 개월 전
부모
커밋
9aa2bf1629
1개의 변경된 파일123개의 추가작업 그리고 30개의 파일을 삭제
  1. 123 30
      docs/prd/epic-011-mini-payment-refund-flow.md

+ 123 - 30
docs/prd/epic-011-mini-payment-refund-flow.md

@@ -1,9 +1,9 @@
 # Epic 011 - Mini小程序支付退款完整流程 - Brownfield Enhancement
 
 ## Epic Status
-**进度:** 50% (2/4 故事完成)
-**当前状态:** Story 1 和 Story 2 已完成,支付回调和订单取消功能已实现并通过测试
-**下一步:** 开始 Story 3 - 集成微信支付退款功能
+**进度:** 75% (3/4 故事完成)
+**当前状态:** Story 1、Story 2 和 Story 3 已完成,支付回调、订单取消和退款功能已实现并通过测试
+**下一步:** 开始 Story 4 - Mini小程序前端支付页面和流程集成
 
 ## Epic Goal
 
@@ -103,19 +103,24 @@
      - ✅ 在OrderMtService中添加cancelOrder方法
    - **完成状态:** 所有12个集成测试通过,包括多租户数据隔离和用户权限验证
 
-3. **Story 3:** 集成微信支付退款功能 - 调用微信支付SDK实现退款
+3. **Story 3:** 集成微信支付退款功能 - 调用微信支付SDK实现退款
    - **修改位置:**
      - `packages/mini-payment-mt/src/services/payment.mt.service.ts` - 添加refund方法
-     - `packages/mini-payment-mt/src/routes/payment/create.mt.ts` - 添加退款API
-     - `packages/mini-payment-mt/src/entities/payment.mt.entity.ts` - 可能扩展退款相关字段
+     - `packages/mini-payment-mt/src/entities/payment.mt.entity.ts` - 扩展退款相关字段
+     - `packages/mini-payment-mt/src/entities/payment.types.ts` - 扩展退款相关类型定义
+     - `packages/orders-module-mt/src/services/order.mt.service.ts` - 集成退款功能到订单取消流程
+     - `packages/mini-payment-mt/tests/integration/payment-refund.integration.test.ts` - 创建退款集成测试
    - **验收标准:**
-     - 在PaymentMtService中添加退款功能
-     - 集成微信支付退款SDK
-     - 实现退款请求构建和签名
-     - 处理退款回调通知
-     - 退款状态正确更新到订单和退款记录
-     - 退款金额、退款流水号等字段正确记录
-     - 支持部分退款和全额退款
+     - ✅ 在PaymentMtService中添加退款功能
+     - ✅ 集成微信支付退款SDK
+     - ✅ 实现退款请求构建和签名
+     - ✅ 处理退款回调通知
+     - ✅ 退款状态正确更新到订单和退款记录
+     - ✅ 退款金额、退款流水号等字段正确记录
+     - ✅ 支持部分退款和全额退款
+     - ✅ 多租户退款数据隔离验证
+     - ✅ 完整的退款集成测试覆盖
+   - **完成状态:** 所有6个集成测试通过,包括多租户数据隔离验证
 
 4. **Story 4:** Mini小程序前端支付页面和流程集成 - 创建支付页面并集成支付流程
    - **修改位置:**
@@ -153,9 +158,10 @@
 
 - [x] Story 1完成且验收标准满足
 - [x] Story 2完成且验收标准满足
+- [x] Story 3完成且验收标准满足
 - [x] 支付回调功能通过测试验证
 - [x] 订单取消功能通过测试验证
-- [ ] 退款功能通过测试验证
+- [x] 退款功能通过测试验证
 - [x] 订单状态流转正确
 - [x] 多租户隔离正常工作
 - [x] 现有功能无回归
@@ -239,29 +245,93 @@ class OrderMtService {
 
 ### 退款功能集成
 ```typescript
-// 在PaymentMtService中添加退款功能
+// 在PaymentMtService中添加退款功能 - 实际实现
 class PaymentMtService {
-  async refund(tenantId: number, orderNo: string, refundAmount: number): Promise<{
-    refund_id: string;
-    out_refund_no: string;
+  async refund(tenantId: number, outTradeNo: string, refundAmount: number, refundDesc?: string): Promise<{
+    refundId: string;
+    outRefundNo: string;
+    refundStatus: string;
+    refundAmount: number;
+    refundTime: string;
   }> {
+    // 验证支付记录存在且状态正确
+    const payment = await this.findPaymentByOutTradeNo(tenantId, outTradeNo);
+    if (!payment) {
+      throw new Error(`支付记录不存在,订单号: ${outTradeNo}`);
+    }
+
+    if (payment.paymentStatus !== PaymentStatus.PAID) {
+      throw new Error(`订单支付状态不正确,当前状态: ${payment.paymentStatus}`);
+    }
+
+    if (refundAmount <= 0 || refundAmount > payment.totalAmount) {
+      throw new Error(`退款金额无效,退款金额: ${refundAmount}, 支付金额: ${payment.totalAmount}`);
+    }
+
+    // 初始化微信支付SDK
     await this.initializeWxPay(tenantId);
 
-    const result = await this.wxPay.refund({
-      out_trade_no: orderNo,
-      out_refund_no: `REFUND_${orderNo}_${Date.now()}`,
+    // 生成退款订单号
+    const outRefundNo = `REFUND_${outTradeNo}_${Date.now()}`;
+
+    // 调用微信支付退款API
+    const result = await this.wxPay.refunds({
+      out_trade_no: outTradeNo,
+      out_refund_no: outRefundNo,
       amount: {
         refund: refundAmount,
-        total: refundAmount,
+        total: payment.totalAmount,
         currency: 'CNY'
-      }
+      },
+      notify_url: `${this.config['wx.payment.notify.url']}/refund`
     });
 
+    // 更新支付记录退款状态
+    payment.refundStatus = PaymentStatus.REFUNDED;
+    payment.refundTransactionId = result.id;
+    payment.refundAmount = refundAmount;
+    payment.refundTime = new Date();
+
+    await this.paymentRepository.save(payment);
+
     return {
-      refund_id: result.refund_id,
-      out_refund_no: result.out_refund_no
+      refundId: result.id,
+      outRefundNo: outRefundNo,
+      refundStatus: result.status,
+      refundAmount: refundAmount,
+      refundTime: payment.refundTime.toISOString()
     };
   }
+
+  // 处理退款回调
+  async handleRefundCallback(callbackData: any, headers: any, rawBody: string): Promise<void> {
+    // 验证签名
+    const isValid = await this.wxPay.verifySign(headers, rawBody);
+    if (!isValid) {
+      throw new Error('退款回调签名验证失败');
+    }
+
+    // 解密回调数据
+    const decryptedData = this.wxPay.decipher_gcm(
+      callbackData.resource.ciphertext,
+      callbackData.resource.associated_data,
+      callbackData.resource.nonce
+    );
+
+    const parsedData = JSON.parse(decryptedData);
+
+    // 根据退款回调更新支付状态
+    const payment = await this.findPaymentByRefundOrderNo(parsedData.out_refund_no);
+    if (payment) {
+      if (parsedData.refund_status === 'SUCCESS') {
+        payment.refundStatus = PaymentStatus.REFUNDED;
+      } else if (parsedData.refund_status === 'FAIL') {
+        payment.refundStatus = PaymentStatus.REFUND_FAILED;
+      }
+
+      await this.paymentRepository.save(payment);
+    }
+  }
 }
 ```
 
@@ -272,6 +342,13 @@ class PaymentMtService {
 - **2支付成功** → **3已退款** (退款成功)
 - **1支付中** → **4支付失败** (支付失败)
 
+### 支付状态机(PaymentStatus枚举)
+- **PENDING(0)** → **PROCESSING(1)** (发起支付)
+- **PROCESSING(1)** → **PAID(2)** (支付成功)
+- **PROCESSING(1)** → **FAILED(4)** (支付失败)
+- **PAID(2)** → **REFUNDED(3)** (退款成功)
+- **PAID(2)** → **REFUND_FAILED(6)** (退款失败)
+
 ### 支付退款流程时序图
 
 ```mermaid
@@ -309,10 +386,10 @@ sequenceDiagram
     Mini-->>User: 显示取消结果
 
     %% 退款回调
-    WechatPay->>RefundAPI: 退款回调通知
-    RefundAPI->>OrderAPI: 确认退款状态
-    OrderAPI-->>RefundAPI: 状态确认成功
-    RefundAPI-->>WechatPay: 回调响应
+    WechatPay->>PaymentAPI: 退款回调通知
+    PaymentAPI->>PaymentAPI: 验证签名和解密数据
+    PaymentAPI->>PaymentAPI: 更新支付记录退款状态
+    PaymentAPI-->>WechatPay: 回调响应
 ```
 
 ### Mini小程序前端实现
@@ -426,6 +503,22 @@ class OrderDetailPage {
 6. **TypeScript类型安全** - 修复了测试工厂中的字段定义错误,确保类型安全
 7. **API响应验证** - 使用parseWithAwait确保API响应格式与Schema一致
 
+### Story 3 完成情况总结
+- ✅ **微信支付退款功能** - 完全实现并通过所有测试
+- ✅ **退款请求构建** - 正确构建微信支付退款请求参数和签名
+- ✅ **退款回调处理** - 完整处理微信支付退款回调通知
+- ✅ **退款状态同步** - 退款成功/失败状态正确更新到支付记录
+- ✅ **多租户数据隔离** - 验证通过,不同租户退款数据完全隔离
+- ✅ **错误处理** - 完善的错误验证和异常处理机制
+- ✅ **测试覆盖** - 6个集成测试全部通过,覆盖各种场景
+
+### 技术实现亮点
+1. **退款字段扩展** - 在PaymentMtEntity中添加退款状态、退款流水号、退款金额、退款时间字段
+2. **微信支付SDK集成** - 完整集成wechatpay-node-v3 SDK的退款功能
+3. **退款回调处理** - 实现退款回调的签名验证、数据解密和状态更新
+4. **订单服务集成** - 在OrderMtService中集成PaymentMtService退款方法
+5. **测试数据工厂** - 创建完整的退款测试数据,支持多租户场景
+6. **边界条件验证** - 测试不存在的支付记录、未支付订单、无效退款金额等边界情况
+
 ### 下一步计划
-- **Story 3**: 集成微信支付退款功能,调用微信支付SDK实现退款
 - **Story 4**: Mini小程序前端支付页面和流程集成