epic-011-mini-payment-refund-flow.md 24 KB

Epic 011 - Mini小程序支付退款完整流程 - Brownfield Enhancement

Epic Status

进度: 100% (4/4 故事完成) 当前状态: Story 1、Story 2、Story 3 和 Story 4 全部完成,完整的支付退款流程已实现并通过测试 完成时间: 2025-11-21

Epic Goal

跑通当前mini提交创建订单后,调用微信小程序支付,支付回调,更新订单状态。订单支付成功后,在min订单列表进入订单详情,点击取消订单后,可以取消订单并调用微信支付sdk退款,最后更新订单状态。

Epic Description

Existing System Context

Current relevant functionality:

  • 多租户支付模块已实现微信小程序支付功能,支持租户隔离和系统配置集成
  • 多租户订单模块已实现订单创建和管理功能,支持租户隔离
  • 多租户退款模块已实现退款记录管理,但缺少与微信支付SDK的集成
  • 多租户系统配置模块已实现租户隔离的支付配置管理,支付模块已集成
  • Redis缓存已用于session_key存储和系统配置缓存
  • 共享CRUD包已提供完整的多租户支持
  • Mini小程序前端已实现:
    • 订单提交页面(/pages/order-submit/index.tsx)- 支持订单创建,跳转到订单详情
    • 订单列表页面(/pages/order-list/index.tsx)- 支持订单状态显示和筛选,有"去支付"按钮
    • 订单详情页面(/pages/order-detail/index.tsx)- 支持取消订单和申请退款UI
    • 支付工具函数(/utils/payment.ts)- 完整的微信支付工具类,包含支付调用、验证、重试、状态管理等

Technology stack:

  • TypeORM + PostgreSQL (数据库)
  • Hono + Zod OpenAPI (API框架)
  • Redis (缓存)
  • 微信小程序SDK (认证和支付)
  • @d8d/shared-crud (共享CRUD工具包)

Integration points:

  • 多租户支付模块的PaymentMtService (小程序支付,已集成系统配置)
  • 多租户订单模块的OrderMtService (订单管理)
  • 多租户退款模块的UserRefundsMtService (退款记录管理)
  • 多租户系统配置模块的SystemConfigServiceMt (支付配置)
  • Redis缓存工具 (配置缓存)
  • 微信支付SDK (退款功能 - 需要集成)

Enhancement Details

What's being added/changed:

  • 完善支付回调处理逻辑,确保订单状态正确更新(支付模块已有TODO注释)
  • 实现订单取消功能,支持支付订单的取消和退款(当前缺少取消订单功能)
  • 集成微信支付SDK退款功能(支付模块缺少退款功能)
  • 优化订单状态流转逻辑
  • 完善退款记录和状态跟踪(退款模块已有基础功能)
  • Mini小程序前端增强:
    • 添加支付页面(/pages/payment/index.tsx)- 当前缺少支付页面
    • 集成支付工具函数到订单流程中
    • 完善取消订单和退款的前端交互

How it integrates:

  • 复用多租户支付模块的支付功能,扩展退款功能
  • 扩展多租户订单模块支持取消和退款操作
  • 使用多租户系统配置模块获取租户特定的支付配置(已集成)
  • 集成微信支付SDK实现退款功能
  • 使用Redis缓存优化配置访问(已集成)
  • Mini小程序前端集成:
    • 订单提交页面调用支付API获取支付参数
    • 支付页面调用微信支付SDK完成支付
    • 订单详情页面集成取消订单和退款功能
    • 使用现有的支付工具函数库

Success criteria:

  • 用户可以在mini小程序中成功创建订单并支付
  • 支付回调正确更新订单状态
  • 用户可以在订单详情中取消已支付订单
  • 取消订单时自动调用微信支付退款
  • 退款回调正确更新订单和退款状态
  • 整个流程支持多租户隔离

Stories

  1. Story 1: 完善支付回调处理逻辑 - 确保支付回调正确更新订单状态,支持多租户隔离

    • 修改位置:
      • packages/mini-payment-mt/src/services/payment.mt.service.ts - 修复TODO注释,实现订单状态更新
      • packages/mini-payment-mt/src/routes/payment/callback.mt.ts - 支付回调接口
    • 验收标准:
      • ✅ 支付回调接口正确处理微信支付通知
      • ✅ 订单状态从"待支付"正确更新为"已支付"(支付状态从0更新为2)
      • ✅ 支付时间、支付流水号等字段正确记录
      • ✅ 支持多租户隔离,不同租户的支付回调互不影响
      • ✅ 添加支付回调日志记录
      • ✅ 修复支付模块中的TODO:更新订单状态
    • 完成状态: 所有7个集成测试通过,包括多租户数据隔离验证
  2. Story 2: 实现订单取消功能 - 支持已支付订单的取消和退款流程

    • 修改位置:
      • packages/orders-module-mt/src/services/order.mt.service.ts - 添加cancelOrder方法
      • packages/orders-module-mt/src/routes/user/orders.mt.ts - 添加取消订单API
      • mini/src/pages/order-detail/index.tsx - 集成取消订单UI
    • 验收标准:
      • ✅ 在订单详情页面添加取消订单按钮
      • ✅ 取消订单时验证订单状态(仅允许取消"待支付"和"已支付"订单)
      • ✅ 对于已支付订单,触发退款流程
      • ✅ 订单状态正确更新为"已取消"(支付状态更新为5)
      • ✅ 取消原因和操作时间正确记录
      • ✅ 在OrderMtService中添加cancelOrder方法
    • 完成状态: 所有12个集成测试通过,包括多租户数据隔离和用户权限验证
  3. Story 3: 集成微信支付退款功能 - 调用微信支付SDK实现退款

    • 修改位置:
      • packages/mini-payment-mt/src/services/payment.mt.service.ts - 添加refund方法
      • 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
      • ✅ 实现退款请求构建和签名
      • ✅ 处理退款回调通知
      • ✅ 退款状态正确更新到订单和退款记录
      • ✅ 退款金额、退款流水号等字段正确记录
      • ✅ 支持部分退款和全额退款
      • ✅ 多租户退款数据隔离验证
      • ✅ 完整的退款集成测试覆盖
    • 完成状态: 所有6个集成测试通过,包括多租户数据隔离验证
  4. Story 4: Mini小程序前端支付页面和流程集成 - 创建支付页面并集成支付流程

    • 修改位置:
      • mini/src/pages/payment/index.tsx - 创建支付页面
      • mini/src/pages/payment-success/index.tsx - 创建支付成功页面
      • mini/src/pages/order-submit/index.tsx - 集成支付页面跳转
      • mini/src/pages/order-list/index.tsx - 集成"去支付"按钮功能
      • mini/src/pages/order-detail/index.tsx - 集成取消订单功能
      • mini/src/utils/payment.ts - 增强支付状态管理
      • mini/src/app.config.ts - 添加页面路由
    • 验收标准:
      • ✅ 创建完整的支付页面,支持微信支付调用
      • ✅ 订单提交页面成功跳转到支付页面
      • ✅ 订单列表页面的"去支付"按钮正常工作
      • ✅ 订单详情页面支持取消订单操作
      • ✅ 支付成功/失败有相应的页面跳转和提示
      • ✅ 复用现有的支付工具函数库
      • ✅ 实现完整的订单状态机流转
      • ✅ 支持退款失败的重试机制
    • 完成状态: 所有功能实现完成,类型检查通过,样式统一使用Tailwind 4

Compatibility Requirements

  • 现有支付API保持不变
  • 现有订单API向后兼容
  • 数据库schema变化向后兼容
  • UI变化遵循现有模式
  • 性能影响最小化

Risk Mitigation

Primary Risk: 退款功能可能影响现有支付流程的稳定性

Mitigation: 分阶段实施,先完善支付回调,再实现退款功能

Rollback Plan: 如果出现问题,可以暂时禁用退款功能,保持现有支付流程正常工作

Definition of Done

  • Story 1完成且验收标准满足
  • Story 2完成且验收标准满足
  • Story 3完成且验收标准满足
  • Story 4完成且验收标准满足
  • 支付回调功能通过测试验证
  • 订单取消功能通过测试验证
  • 退款功能通过测试验证
  • 前端支付流程完整实现
  • 订单状态流转正确
  • 多租户隔离正常工作
  • 现有功能无回归
  • 前端样式统一使用Tailwind 4
  • 类型检查通过,无支付相关错误

Technical Implementation Details

支付回调处理

// 支付回调接口 - 修复PaymentMtService中的TODO
class PaymentMtService {
  async handlePaymentCallback(callbackData: any, headers: any, rawBody: string): Promise<void> {
    // ... 现有验证逻辑 ...

    // 根据回调结果更新支付状态
    if (parsedData.trade_state === 'SUCCESS') {
      payment.paymentStatus = PaymentStatus.PAID;
      payment.wechatTransactionId = parsedData.transaction_id;

      // TODO: 更新订单状态 - 需要实现
      await this.updateOrderPaymentStatus(payment.tenantId, payment.externalOrderId, 2); // 支付成功
    } else if (parsedData.trade_state === 'FAIL') {
      payment.paymentStatus = PaymentStatus.FAILED;
      await this.updateOrderPaymentStatus(payment.tenantId, payment.externalOrderId, 4); // 支付失败
    } else if (parsedData.trade_state === 'REFUND') {
      payment.paymentStatus = PaymentStatus.REFUNDED;
      await this.updateOrderPaymentStatus(payment.tenantId, payment.externalOrderId, 3); // 已退款
    }

    await paymentRepository.save(payment);
  }
}

订单取消和退款

// 订单取消服务 - 需要在OrderMtService中添加
class OrderMtService {
  async cancelOrder(tenantId: number, orderId: number, reason: string, userId: number) {
    const order = await this.repository.findOne({
      where: { id: orderId, tenantId }
    });

    if (!order) {
      throw new Error('订单不存在');
    }

    // 验证订单状态
    if (order.payState !== 0 && order.payState !== 2) {
      throw new Error('该订单状态不允许取消');
    }

    if (order.payState === 2) {
      // 已支付订单,触发退款
      const paymentService = new PaymentMtService(this.dataSource);
      const refundResult = await paymentService.refund(
        tenantId,
        order.orderNo,
        order.payAmount
      );

      // 创建退款记录
      const refundService = new UserRefundsMtService(this.dataSource);
      await refundService.createUserRefund({
        orderNo: order.orderNo,
        refundOrderNo: refundResult.refund_id,
        refundAmount: order.payAmount,
        state: 1 // 退款中
      }, userId, tenantId);
    }

    // 更新订单状态
    order.payState = 5; // 订单关闭
    order.closeTime = new Date();
    order.remark = `用户取消: ${reason}`;
    order.updatedBy = userId;

    await this.repository.save(order);
  }
}

退款功能集成

// 在PaymentMtService中添加退款功能 - 实际实现
class PaymentMtService {
  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 outRefundNo = `REFUND_${outTradeNo}_${Date.now()}`;

    // 调用微信支付退款API
    const result = await this.wxPay.refunds({
      out_trade_no: outTradeNo,
      out_refund_no: outRefundNo,
      amount: {
        refund: 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 {
      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);
    }
  }
}

订单状态机(基于现有PayStatus枚举)

  • 0未支付2支付成功 (支付成功)
  • 0未支付5订单关闭 (用户取消)
  • 2支付成功5订单关闭 (用户取消 + 退款)
  • 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) (退款失败)

支付退款流程时序图

sequenceDiagram
    participant User as 用户
    participant Mini as Mini小程序
    participant OrderAPI as 订单API
    participant PaymentAPI as 支付API
    participant WechatPay as 微信支付
    participant RefundAPI as 退款API

    %% 支付流程
    User->>Mini: 提交订单
    Mini->>OrderAPI: 创建订单
    OrderAPI-->>Mini: 返回订单ID
    Mini->>PaymentAPI: 请求支付参数
    PaymentAPI-->>Mini: 返回支付参数
    Mini->>WechatPay: 调用微信支付
    WechatPay-->>Mini: 支付结果

    %% 支付回调
    WechatPay->>PaymentAPI: 支付回调通知
    PaymentAPI->>OrderAPI: 更新订单状态(已支付)
    OrderAPI-->>PaymentAPI: 状态更新成功
    PaymentAPI-->>WechatPay: 回调响应

    %% 退款流程
    User->>Mini: 取消订单
    Mini->>OrderAPI: 请求取消订单
    OrderAPI->>RefundAPI: 发起退款
    RefundAPI->>WechatPay: 调用退款API
    WechatPay-->>RefundAPI: 退款结果
    RefundAPI->>OrderAPI: 更新订单状态(已退款)
    OrderAPI-->>Mini: 取消成功
    Mini-->>User: 显示取消结果

    %% 退款回调
    WechatPay->>PaymentAPI: 退款回调通知
    PaymentAPI->>PaymentAPI: 验证签名和解密数据
    PaymentAPI->>PaymentAPI: 更新支付记录退款状态
    PaymentAPI-->>WechatPay: 回调响应

Mini小程序前端实现

// 支付页面 - 需要创建 /pages/payment/index.tsx
class PaymentPage {
  async handlePayment(orderId: number) {
    // 1. 调用后端API获取支付参数
    const paymentParams = await orderClient['create-payment']['$post']({
      json: { orderId }
    });

    // 2. 使用现有支付工具函数调用微信支付
    const result = await requestWechatPayment(paymentParams);

    // 3. 处理支付结果
    if (result.success) {
      // 支付成功,跳转到支付成功页面
      Taro.redirectTo({ url: '/pages/payment-success/index' });
    } else {
      // 支付失败,显示错误信息
      Taro.showToast({ title: result.message, icon: 'none' });
    }
  }
}

// 订单详情页面 - 集成取消订单功能
class OrderDetailPage {
  async handleCancelOrder(orderId: number, reason: string) {
    try {
      // 调用后端取消订单API
      await orderClient['cancel-order']['$post']({
        json: { orderId, reason }
      });

      Taro.showToast({ title: '订单取消成功', icon: 'success' });
      // 刷新页面或返回订单列表
    } catch (error) {
      Taro.showToast({ title: error.message, icon: 'none' });
    }
  }
}

Validation Checklist

Scope Validation

  • Epic可以在5个故事内完成
  • 不需要架构文档
  • 增强遵循现有模式
  • 集成复杂度可控

Risk Assessment

  • 对现有系统风险可控
  • 回滚计划可行
  • 测试方法覆盖现有功能
  • 团队对集成点有足够了解

Completeness Check

  • Epic目标清晰可实现
  • 故事范围适当
  • 成功标准可衡量
  • 依赖项已识别

Story Manager Handoff:

"请为这个棕地史诗开发详细的用户故事。关键考虑因素:

  • 这是对运行TypeORM + PostgreSQL + Hono + Redis + @d8d/shared-crud的多租户系统的增强
  • 核心简化: 复用多租户支付模块和订单模块,扩展退款功能
  • Mini小程序前端: 已有订单提交、列表、详情页面和支付工具函数,需要添加支付页面和集成支付流程
  • 集成点:多租户支付模块的PaymentMtService、多租户订单模块的OrderMtService、多租户退款模块的UserRefundsMtService、多租户系统配置模块的SystemConfigServiceMt、微信支付SDK、Mini小程序前端
  • 要遵循的现有模式:多租户支付模块的支付流程、多租户订单模块的CRUD模式、多租户实体模式、Mini小程序前端架构
  • 关键兼容性要求:现有支付API保持不变,订单API向后兼容,Mini小程序现有页面功能无影响
  • 每个故事必须包括验证现有功能保持完整的验证
  • 特别注意:支付模块中已有TODO需要更新订单状态,需要实现
  • Mini小程序前端关键点:
    • 订单提交页面(mini/src/pages/order-submit/index.tsx)需要集成支付页面跳转
    • 订单列表页面(mini/src/pages/order-list/index.tsx)的"去支付"按钮需要集成支付功能
    • 需要创建支付页面(mini/src/pages/payment/index.tsx
    • 订单详情页面(mini/src/pages/order-detail/index.tsx)需要集成取消订单功能
    • 复用现有的支付工具函数库(mini/src/utils/payment.ts

该史诗应在保持系统完整性的同时交付完整的支付退款流程功能。"

Epic Completion Summary

Story 1 完成情况总结

  • 支付回调处理逻辑 - 完全实现并通过所有测试
  • 多租户数据隔离 - 验证通过,不同租户支付回调互不影响
  • 订单状态同步 - 支付成功/失败/退款状态正确更新到订单
  • 日志记录 - 完整的调试日志记录,便于问题排查
  • 测试覆盖 - 7个集成测试全部通过,覆盖各种场景

Story 2 完成情况总结

  • 订单取消功能 - 完全实现并通过所有测试
  • 订单状态验证 - 仅允许取消"待支付"和"已支付"订单
  • 退款流程集成 - 已支付订单触发退款流程占位符
  • 多租户数据隔离 - 验证通过,不同租户订单数据完全隔离
  • 用户权限验证 - 用户只能取消自己的订单
  • 测试覆盖 - 12个集成测试全部通过,覆盖各种场景

技术实现亮点

  1. 实体元数据修复 - 解决了DeliveryAddressMt、UserEntityMt、AreaEntityMt等实体的依赖问题
  2. 模块导入优化 - 确保多租户版本正确导入,避免路径冲突
  3. 测试数据工厂 - 创建了PaymentTestFactory和OrdersTestFactory简化测试数据创建
  4. 回调数据处理 - 修复了商户订单号获取顺序和订单状态更新逻辑
  5. 多租户隔离验证 - 通过特定回调数据确保租户间数据完全隔离
  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 4 完成情况总结

  • 支付页面创建 - 创建完整的支付页面,支持微信支付调用
  • 支付流程集成 - 订单提交、订单列表、订单详情页面完整集成支付流程
  • 支付状态管理 - 实现完整的支付状态机流转,支持重试机制
  • 用户界面优化 - 支付成功页面、错误处理、状态提示完整
  • 样式统一 - 所有页面使用Tailwind 4,清理SCSS文件
  • 类型安全 - 支付相关代码类型检查通过,无类型错误

技术实现亮点

  1. 支付页面架构 - 完整的支付流程,包括支付中、成功、失败状态管理
  2. 状态流转控制 - PaymentStateFlowManager实现完整的支付状态流转验证
  3. 错误处理机制 - 支付失败重试、频率限制、超时处理
  4. 样式系统统一 - 从SCSS迁移到Tailwind 4,保持设计一致性
  5. API集成优化 - 使用Hono RPC客户端,状态码验证正确
  6. 用户体验完善 - 支付成功页面、操作按钮、温馨提示完整

史诗完成总结

Epic 011 - Mini小程序支付退款完整流程已100%完成,所有4个故事全部实现并通过验证。完整的支付退款流程包括:

  1. 后端支付退款功能 - Story 1-3完成支付回调、订单取消、退款功能
  2. 前端支付流程集成 - Story 4完成支付页面和流程集成
  3. 多租户数据隔离 - 所有功能支持多租户数据隔离
  4. 完整测试覆盖 - 后端集成测试通过,前端类型检查通过

完整流程验证:

  • 用户创建订单 → 跳转支付页面 → 微信支付 → 支付回调 → 订单状态更新
  • 用户取消订单 → 调用退款API → 微信退款 → 退款回调 → 订单状态更新

Epic目标已完全达成,系统现在支持完整的支付退款业务流程。