HaniBlindBox/.kiro/specs/payment-integration/design.md
2026-01-02 22:34:41 +08:00

9.8 KiB

Design Document: 支付集成迁移

Overview

本设计文档描述了将PHP支付系统迁移到.NET 8的技术方案。支付系统主要包括微信支付统一下单、支付回调处理、余额/积分/哈尼券支付、以及订单发货通知等功能。

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      API Layer                               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │PayController│  │NotifyController│ │OrderController     │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
└─────────┼────────────────┼─────────────────────┼────────────┘
          │                │                     │
┌─────────▼────────────────▼─────────────────────▼────────────┐
│                    Service Layer                             │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────┐  │
│  │WechatPayService │  │PaymentService   │  │NotifyService│  │
│  └────────┬────────┘  └────────┬────────┘  └──────┬──────┘  │
└───────────┼────────────────────┼──────────────────┼─────────┘
            │                    │                  │
┌───────────▼────────────────────▼──────────────────▼─────────┐
│                    Data Layer                                │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────────┐  │
│  │  Order   │  │   User   │  │ProfitPay │  │OrderNotify  │  │
│  └──────────┘  └──────────┘  └──────────┘  └─────────────┘  │
└─────────────────────────────────────────────────────────────┘

Components and Interfaces

1. IWechatPayService - 微信支付服务接口

public interface IWechatPayService
{
    /// <summary>
    /// 创建微信支付统一下单
    /// </summary>
    Task<WechatPayResult> CreatePaymentAsync(WechatPayRequest request);
    
    /// <summary>
    /// 验证支付签名
    /// </summary>
    bool VerifySign(Dictionary<string, string> parameters, string sign);
    
    /// <summary>
    /// 生成支付签名
    /// </summary>
    string MakeSign(Dictionary<string, string> parameters);
    
    /// <summary>
    /// 发送订单发货通知到微信
    /// </summary>
    Task<bool> PostOrderShippingAsync(string orderNo, string openId);
}

2. IPaymentNotifyService - 支付回调服务接口

public interface IPaymentNotifyService
{
    /// <summary>
    /// 处理微信支付回调
    /// </summary>
    Task<NotifyResult> HandleWechatNotifyAsync(string xmlData);
    
    /// <summary>
    /// 处理一番赏订单支付成功
    /// </summary>
    Task<bool> ProcessLotteryOrderAsync(int orderId, int userId, int goodsId, int num);
    
    /// <summary>
    /// 处理无限赏订单支付成功
    /// </summary>
    Task<bool> ProcessInfiniteOrderAsync(int orderId, int userId, int goodsId);
    
    /// <summary>
    /// 处理充值订单支付成功
    /// </summary>
    Task<bool> ProcessRechargeOrderAsync(string orderNo);
    
    /// <summary>
    /// 处理发货运费支付成功
    /// </summary>
    Task<bool> ProcessShippingFeeOrderAsync(string orderNo);
}

3. IPaymentService - 统一支付服务接口

public interface IPaymentService
{
    /// <summary>
    /// 扣减用户余额
    /// </summary>
    Task<bool> DeductBalanceAsync(int userId, decimal amount, string content);
    
    /// <summary>
    /// 扣减用户积分
    /// </summary>
    Task<bool> DeductIntegralAsync(int userId, decimal amount, string content);
    
    /// <summary>
    /// 扣减用户哈尼券
    /// </summary>
    Task<bool> DeductMoney2Async(int userId, decimal amount, string content);
    
    /// <summary>
    /// 记录支付流水
    /// </summary>
    Task<bool> RecordPaymentAsync(int userId, string orderNo, decimal amount, int payType, string content);
}

Data Models

Request/Response Models

// 微信支付请求
public class WechatPayRequest
{
    public string OrderNo { get; set; }
    public decimal Amount { get; set; }
    public string Body { get; set; }
    public string Attach { get; set; }
    public string OpenId { get; set; }
    public int UserId { get; set; }
}

// 微信支付结果
public class WechatPayResult
{
    public int Status { get; set; }
    public string Msg { get; set; }
    public WechatPayData Data { get; set; }
}

public class WechatPayData
{
    [JsonPropertyName("appId")]
    public string AppId { get; set; }
    
    [JsonPropertyName("timeStamp")]
    public string TimeStamp { get; set; }
    
    [JsonPropertyName("nonceStr")]
    public string NonceStr { get; set; }
    
    [JsonPropertyName("package")]
    public string Package { get; set; }
    
    [JsonPropertyName("signType")]
    public string SignType { get; set; }
    
    [JsonPropertyName("paySign")]
    public string PaySign { get; set; }
    
    [JsonPropertyName("is_weixin")]
    public int IsWeixin { get; set; }
}

// 支付回调通知结果
public class NotifyResult
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public string XmlResponse { get; set; }
}

Database Entities

// 支付通知记录表
public class OrderNotify
{
    public int Id { get; set; }
    public string OrderNo { get; set; }
    public string NotifyUrl { get; set; }
    public string NonceStr { get; set; }
    public DateTime PayTime { get; set; }
    public decimal PayAmount { get; set; }
    public int Status { get; set; }
    public int RetryCount { get; set; }
    public DateTime CreateTime { get; set; }
    public DateTime UpdateTime { get; set; }
}

Correctness Properties

A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.

Property 1: 支付签名正确性

For any set of payment parameters and secret key, generating a signature and then verifying it with the same parameters should return true.

Validates: Requirements 1.4, 7.1, 7.2

Property 2: 支付回调幂等性

For any payment notification, processing it multiple times should produce the same result as processing it once (order status should not change after first successful processing).

Validates: Requirements 2.8

Property 3: 资产扣减原子性

For any mixed payment transaction, either all asset deductions succeed or none of them succeed (transaction rollback on failure).

Validates: Requirements 6.7

Property 4: 余额扣减正确性

For any balance payment, the user's balance after payment should equal the original balance minus the payment amount.

Validates: Requirements 3.3, 3.4

Property 5: 支付记录完整性

For any successful payment, there should be a corresponding record in the profit_pay table with correct order number, amount, and payment type.

Validates: Requirements 8.1, 8.2

Error Handling

Payment Errors

Error Code Description Handling
NOTENOUGH 余额不足 返回错误提示,不扣款
SIGNERROR 签名错误 拒绝请求,记录日志
ORDERPAID 订单已支付 返回成功,不重复处理
SYSTEMERROR 系统错误 重试或返回错误

Transaction Handling

public async Task<bool> ProcessPaymentAsync(PaymentRequest request)
{
    using var transaction = await _context.Database.BeginTransactionAsync();
    try
    {
        // 1. 验证订单状态
        // 2. 扣减用户资产
        // 3. 更新订单状态
        // 4. 记录支付流水
        // 5. 触发后续业务(抽奖等)
        
        await transaction.CommitAsync();
        return true;
    }
    catch (Exception ex)
    {
        await transaction.RollbackAsync();
        _logger.LogError(ex, "Payment processing failed");
        throw;
    }
}

Testing Strategy

Unit Tests

  • 测试签名生成和验证逻辑
  • 测试余额/积分/哈尼券扣减逻辑
  • 测试支付参数构建逻辑

Property-Based Tests

  • 使用FsCheck或类似库测试签名的正确性
  • 测试支付回调的幂等性
  • 测试资产扣减的原子性

Integration Tests

  • 测试完整的支付流程
  • 测试支付回调处理流程
  • 测试混合支付场景

Test Configuration

  • 使用InMemory数据库进行单元测试
  • 使用测试商户配置进行集成测试
  • 模拟微信支付API响应