|
|
@@ -9,9 +9,10 @@
|
|
|
### Existing System Context
|
|
|
|
|
|
**Current relevant functionality:**
|
|
|
-- **支付模块**已实现微信小程序支付功能
|
|
|
-- **订单模块**已实现订单创建和管理功能
|
|
|
-- **多租户系统配置模块**已实现租户隔离的支付配置管理
|
|
|
+- **多租户支付模块**已实现微信小程序支付功能,支持租户隔离和系统配置集成
|
|
|
+- **多租户订单模块**已实现订单创建和管理功能,支持租户隔离
|
|
|
+- **多租户退款模块**已实现退款记录管理,但缺少与微信支付SDK的集成
|
|
|
+- **多租户系统配置模块**已实现租户隔离的支付配置管理,支付模块已集成
|
|
|
- **Redis缓存**已用于session_key存储和系统配置缓存
|
|
|
- **共享CRUD包**已提供完整的多租户支持
|
|
|
|
|
|
@@ -23,27 +24,28 @@
|
|
|
- @d8d/shared-crud (共享CRUD工具包)
|
|
|
|
|
|
**Integration points:**
|
|
|
-- 支付模块的PaymentService (小程序支付)
|
|
|
-- 订单模块的OrderService (订单管理)
|
|
|
-- 系统配置模块的SystemConfigService (支付配置)
|
|
|
+- 多租户支付模块的PaymentMtService (小程序支付,已集成系统配置)
|
|
|
+- 多租户订单模块的OrderMtService (订单管理)
|
|
|
+- 多租户退款模块的UserRefundsMtService (退款记录管理)
|
|
|
+- 多租户系统配置模块的SystemConfigServiceMt (支付配置)
|
|
|
- Redis缓存工具 (配置缓存)
|
|
|
-- 微信支付SDK (退款功能)
|
|
|
+- 微信支付SDK (退款功能 - 需要集成)
|
|
|
|
|
|
### Enhancement Details
|
|
|
|
|
|
**What's being added/changed:**
|
|
|
-- 完善支付回调处理逻辑,确保订单状态正确更新
|
|
|
-- 实现订单取消功能,支持支付订单的取消和退款
|
|
|
-- 集成微信支付SDK退款功能
|
|
|
+- 完善支付回调处理逻辑,确保订单状态正确更新(支付模块已有TODO注释)
|
|
|
+- 实现订单取消功能,支持支付订单的取消和退款(当前缺少取消订单功能)
|
|
|
+- 集成微信支付SDK退款功能(支付模块缺少退款功能)
|
|
|
- 优化订单状态流转逻辑
|
|
|
-- 添加退款记录和状态跟踪
|
|
|
+- 完善退款记录和状态跟踪(退款模块已有基础功能)
|
|
|
|
|
|
**How it integrates:**
|
|
|
-- **复用现有支付模块**的支付功能
|
|
|
-- **扩展订单模块**支持取消和退款操作
|
|
|
-- **使用系统配置模块**获取租户特定的支付配置
|
|
|
+- **复用多租户支付模块**的支付功能,扩展退款功能
|
|
|
+- **扩展多租户订单模块**支持取消和退款操作
|
|
|
+- **使用多租户系统配置模块**获取租户特定的支付配置(已集成)
|
|
|
- **集成微信支付SDK**实现退款功能
|
|
|
-- **使用Redis缓存**优化配置访问
|
|
|
+- **使用Redis缓存**优化配置访问(已集成)
|
|
|
|
|
|
**Success criteria:**
|
|
|
- 用户可以在mini小程序中成功创建订单并支付
|
|
|
@@ -58,32 +60,35 @@
|
|
|
1. **Story 1:** 完善支付回调处理逻辑 - 确保支付回调正确更新订单状态,支持多租户隔离
|
|
|
- **验收标准:**
|
|
|
- 支付回调接口正确处理微信支付通知
|
|
|
- - 订单状态从"待支付"正确更新为"已支付"
|
|
|
+ - 订单状态从"待支付"正确更新为"已支付"(支付状态从0更新为2)
|
|
|
- 支付时间、支付流水号等字段正确记录
|
|
|
- 支持多租户隔离,不同租户的支付回调互不影响
|
|
|
- 添加支付回调日志记录
|
|
|
+ - 修复支付模块中的TODO:更新订单状态
|
|
|
|
|
|
2. **Story 2:** 实现订单取消功能 - 支持已支付订单的取消和退款流程
|
|
|
- **验收标准:**
|
|
|
- 在订单详情页面添加取消订单按钮
|
|
|
- 取消订单时验证订单状态(仅允许取消"待支付"和"已支付"订单)
|
|
|
- 对于已支付订单,触发退款流程
|
|
|
- - 订单状态正确更新为"已取消"
|
|
|
+ - 订单状态正确更新为"已取消"(支付状态更新为5)
|
|
|
- 取消原因和操作时间正确记录
|
|
|
+ - 在OrderMtService中添加cancelOrder方法
|
|
|
|
|
|
3. **Story 3:** 集成微信支付退款功能 - 调用微信支付SDK实现退款
|
|
|
- **验收标准:**
|
|
|
+ - 在PaymentMtService中添加退款功能
|
|
|
- 集成微信支付退款SDK
|
|
|
- 实现退款请求构建和签名
|
|
|
- 处理退款回调通知
|
|
|
- - 退款状态正确更新到订单
|
|
|
+ - 退款状态正确更新到订单和退款记录
|
|
|
- 退款金额、退款流水号等字段正确记录
|
|
|
- 支持部分退款和全额退款
|
|
|
|
|
|
4. **Story 4:** 优化订单状态流转和退款记录 - 完善订单状态管理和退款跟踪
|
|
|
- **验收标准:**
|
|
|
- 实现完整的订单状态机
|
|
|
- - 添加退款记录实体,跟踪退款历史
|
|
|
+ - 完善退款记录实体,跟踪退款历史
|
|
|
- 在订单详情中显示退款状态和记录
|
|
|
- 支持退款失败的重试机制
|
|
|
- 添加退款操作的审计日志
|
|
|
@@ -117,98 +122,113 @@
|
|
|
|
|
|
### 支付回调处理
|
|
|
```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
|
|
|
+// 支付回调接口 - 修复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); // 已退款
|
|
|
}
|
|
|
- );
|
|
|
|
|
|
- return c.text('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>');
|
|
|
-});
|
|
|
+ await paymentRepository.save(payment);
|
|
|
+ }
|
|
|
+}
|
|
|
```
|
|
|
|
|
|
### 订单取消和退款
|
|
|
```typescript
|
|
|
-// 订单取消服务
|
|
|
-class OrderService {
|
|
|
- async cancelOrder(tenantId: number, orderId: number, reason: string) {
|
|
|
- const order = await this.getOrder(tenantId, orderId);
|
|
|
+// 订单取消服务 - 需要在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.status === 'paid') {
|
|
|
- // 触发退款
|
|
|
+ if (order.payState === 2) {
|
|
|
+ // 已支付订单,触发退款
|
|
|
+ const paymentService = new PaymentMtService(this.dataSource);
|
|
|
const refundResult = await paymentService.refund(
|
|
|
tenantId,
|
|
|
- order.transactionId,
|
|
|
- order.totalAmount,
|
|
|
- order.outTradeNo
|
|
|
+ order.orderNo,
|
|
|
+ order.payAmount
|
|
|
);
|
|
|
|
|
|
- // 更新订单状态和退款记录
|
|
|
- await this.updateOrderStatus(tenantId, orderId, 'cancelled', {
|
|
|
- cancelReason: reason,
|
|
|
- refundTransactionId: refundResult.refund_id
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 直接取消未支付订单
|
|
|
- await this.updateOrderStatus(tenantId, orderId, 'cancelled', {
|
|
|
- cancelReason: reason
|
|
|
- });
|
|
|
+ // 创建退款记录
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 退款记录实体
|
|
|
+### 退款功能集成
|
|
|
```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;
|
|
|
+// 在PaymentMtService中添加退款功能
|
|
|
+class PaymentMtService {
|
|
|
+ async refund(tenantId: number, orderNo: string, refundAmount: number): Promise<{
|
|
|
+ refund_id: string;
|
|
|
+ out_refund_no: string;
|
|
|
+ }> {
|
|
|
+ await this.initializeWxPay(tenantId);
|
|
|
+
|
|
|
+ const result = await this.wxPay.refund({
|
|
|
+ out_trade_no: orderNo,
|
|
|
+ out_refund_no: `REFUND_${orderNo}_${Date.now()}`,
|
|
|
+ amount: {
|
|
|
+ refund: refundAmount,
|
|
|
+ total: refundAmount,
|
|
|
+ currency: 'CNY'
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ refund_id: result.refund_id,
|
|
|
+ out_refund_no: result.out_refund_no
|
|
|
+ };
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 订单状态机
|
|
|
-- **待支付** → **已支付** (支付成功)
|
|
|
-- **待支付** → **已取消** (用户取消)
|
|
|
-- **已支付** → **已取消** (用户取消 + 退款)
|
|
|
-- **已支付** → **退款中** (退款申请)
|
|
|
-- **退款中** → **已退款** (退款成功)
|
|
|
-- **退款中** → **退款失败** (退款失败)
|
|
|
+### 订单状态机(基于现有PayStatus枚举)
|
|
|
+- **0未支付** → **2支付成功** (支付成功)
|
|
|
+- **0未支付** → **5订单关闭** (用户取消)
|
|
|
+- **2支付成功** → **5订单关闭** (用户取消 + 退款)
|
|
|
+- **2支付成功** → **3已退款** (退款成功)
|
|
|
+- **1支付中** → **4支付失败** (支付失败)
|
|
|
|
|
|
## Validation Checklist
|
|
|
|
|
|
@@ -236,11 +256,12 @@ export class OrderRefund {
|
|
|
|
|
|
"请为这个棕地史诗开发详细的用户故事。关键考虑因素:
|
|
|
|
|
|
-- 这是对运行TypeORM + PostgreSQL + Hono + Redis + @d8d/shared-crud的现有系统的增强
|
|
|
-- **核心简化**: 复用现有支付和订单模块,扩展退款功能
|
|
|
-- 集成点:支付模块的PaymentService、订单模块的OrderService、系统配置模块的SystemConfigService、微信支付SDK
|
|
|
-- 要遵循的现有模式:支付模块的支付流程、订单模块的CRUD模式、多租户实体模式
|
|
|
+- 这是对运行TypeORM + PostgreSQL + Hono + Redis + @d8d/shared-crud的多租户系统的增强
|
|
|
+- **核心简化**: 复用多租户支付模块和订单模块,扩展退款功能
|
|
|
+- 集成点:多租户支付模块的PaymentMtService、多租户订单模块的OrderMtService、多租户退款模块的UserRefundsMtService、多租户系统配置模块的SystemConfigServiceMt、微信支付SDK
|
|
|
+- 要遵循的现有模式:多租户支付模块的支付流程、多租户订单模块的CRUD模式、多租户实体模式
|
|
|
- 关键兼容性要求:现有支付API保持不变,订单API向后兼容
|
|
|
- 每个故事必须包括验证现有功能保持完整的验证
|
|
|
+- 特别注意:支付模块中已有TODO需要更新订单状态,需要实现
|
|
|
|
|
|
该史诗应在保持系统完整性的同时交付完整的支付退款流程功能。"
|