292 lines
9.8 KiB
Markdown
292 lines
9.8 KiB
Markdown
# 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 - 微信支付服务接口
|
|
|
|
```csharp
|
|
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 - 支付回调服务接口
|
|
|
|
```csharp
|
|
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 - 统一支付服务接口
|
|
|
|
```csharp
|
|
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
|
|
|
|
```csharp
|
|
// 微信支付请求
|
|
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
|
|
|
|
```csharp
|
|
// 支付通知记录表
|
|
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
|
|
|
|
```csharp
|
|
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响应
|