mi-assessment/server/MiAssessment/src/MiAssessment.Core/Services/PaymentNotifyService.cs
zpc 21e8ff5372 refactor: 清理遗留实体和无效代码
- 删除无数据库表的实体: UserDetail, UserAddress, PaymentOrder, Admin, AdminLoginLog, AdminOperationLog, Picture, Delivery
- 删除关联服务: AddressService, PaymentService, PaymentOrderService, PaymentRewardDispatcher, DefaultPaymentRewardHandler
- 删除关联接口: IAddressService, IPaymentService, IPaymentOrderService, IPaymentRewardHandler, IPaymentRewardDispatcher
- 删除关联控制器: AddressController
- 删除关联DTO: AddressModels, CreatePaymentOrderRequest, PaymentOrderDto, PaymentOrderQueryRequest
- 删除关联测试: PaymentOrderServicePropertyTests, PaymentRewardDispatcherPropertyTests
- 修复实体字段映射: User, UserLoginLog, UserRefreshToken, Config, OrderNotify
- 更新 NotifyController 移除 IPaymentOrderService 依赖
- 更新 ServiceModule 移除已删除服务的DI注册
- 更新 MiAssessmentDbContext 移除已删除实体的DbSet和OnModelCreating配置
2026-02-20 20:29:34 +08:00

430 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.Json;
using MiAssessment.Core.Interfaces;
using MiAssessment.Model.Data;
using MiAssessment.Model.Entities;
using MiAssessment.Model.Models.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MiAssessment.Core.Services;
/// <summary>
/// 支付回调服务实现
/// 负责处理微信支付回调通知,验证签名,记录通知
/// 业务处理逻辑由具体业务模块实现
/// </summary>
public class PaymentNotifyService : IPaymentNotifyService
{
private readonly MiAssessmentDbContext _dbContext;
private readonly IWechatPayService _wechatPayService;
private readonly IWechatPayV3Service _wechatPayV3Service;
private readonly IWechatPayConfigService _wechatPayConfigService;
private readonly ILogger<PaymentNotifyService> _logger;
public PaymentNotifyService(
MiAssessmentDbContext dbContext,
IWechatPayService wechatPayService,
IWechatPayV3Service wechatPayV3Service,
IWechatPayConfigService wechatPayConfigService,
ILogger<PaymentNotifyService> logger)
{
_dbContext = dbContext;
_wechatPayService = wechatPayService;
_wechatPayV3Service = wechatPayV3Service;
_wechatPayConfigService = wechatPayConfigService;
_logger = logger;
}
/// <inheritdoc />
public async Task<NotifyResult> HandleWechatNotifyAsync(string notifyBody, WechatPayNotifyHeaders? headers = null)
{
// 自动识别回调格式
var version = _wechatPayV3Service.DetectNotifyVersion(notifyBody);
_logger.LogInformation("检测到微信支付回调版本: {Version}", version);
return version switch
{
NotifyVersion.V3 when headers != null => await HandleWechatV3NotifyAsync(notifyBody, headers),
NotifyVersion.V3 => new NotifyResult
{
Success = false,
Message = "V3 回调缺少请求头",
JsonResponse = JsonSerializer.Serialize(new WechatPayV3NotifyResponse { Code = "FAIL", Message = "缺少请求头" })
},
NotifyVersion.V2 => await HandleWechatV2NotifyAsync(notifyBody),
_ => new NotifyResult
{
Success = false,
Message = "无法识别的回调格式",
XmlResponse = _wechatPayService.GenerateNotifyResponseXml("FAIL", "无法识别的回调格式")
}
};
}
/// <inheritdoc />
public async Task<NotifyResult> HandleWechatV2NotifyAsync(string xmlData)
{
var successResponse = _wechatPayService.GenerateNotifyResponseXml("SUCCESS", "OK");
var failResponse = _wechatPayService.GenerateNotifyResponseXml("FAIL", "处理失败");
try
{
// 1. 检查XML数据是否为空
if (string.IsNullOrEmpty(xmlData))
{
_logger.LogWarning("微信支付 V2 回调数据为空");
return new NotifyResult
{
Success = false,
Message = "回调数据为空",
XmlResponse = failResponse
};
}
// 2. 解析XML数据
var notifyData = _wechatPayService.ParseNotifyXml(xmlData);
if (notifyData == null || string.IsNullOrEmpty(notifyData.OutTradeNo))
{
_logger.LogWarning("解析微信支付 V2 回调XML失败");
return new NotifyResult
{
Success = false,
Message = "解析回调数据失败",
XmlResponse = failResponse
};
}
var orderNo = notifyData.OutTradeNo;
var attach = notifyData.Attach;
_logger.LogInformation("收到微信支付 V2 回调: OrderNo={OrderNo}, Attach={Attach}, TotalFee={TotalFee}",
orderNo, attach, notifyData.TotalFee);
// 3. 验证签名
if (!_wechatPayService.VerifyNotifySign(notifyData))
{
_logger.LogWarning("微信支付 V2 回调签名验证失败: OrderNo={OrderNo}", orderNo);
return new NotifyResult
{
Success = false,
Message = "签名验证失败",
XmlResponse = failResponse
};
}
// 4. 检查返回状态
if (notifyData.ReturnCode != "SUCCESS" || notifyData.ResultCode != "SUCCESS")
{
_logger.LogWarning("微信支付回调状态异常: OrderNo={OrderNo}, ReturnCode={ReturnCode}, ResultCode={ResultCode}",
orderNo, notifyData.ReturnCode, notifyData.ResultCode);
// 即使支付失败,也返回成功响应,避免微信重复通知
return new NotifyResult
{
Success = true,
Message = "支付未成功",
XmlResponse = successResponse
};
}
// 5. 幂等性检查 - 检查订单是否已处理
if (await IsOrderProcessedAsync(orderNo))
{
_logger.LogInformation("订单已处理,跳过重复回调: OrderNo={OrderNo}", orderNo);
return new NotifyResult
{
Success = true,
Message = "订单已处理",
XmlResponse = successResponse
};
}
// 6. 记录回调通知
await RecordNotifyAsync(orderNo, notifyData);
// 7. 返回成功,业务处理由具体业务模块实现
_logger.LogInformation("微信支付 V2 回调记录成功: OrderNo={OrderNo}, Attach={Attach}", orderNo, attach);
return new NotifyResult
{
Success = true,
Message = "处理成功",
XmlResponse = successResponse,
OrderNo = orderNo,
Attach = attach,
NotifyData = notifyData
};
}
catch (Exception ex)
{
_logger.LogError(ex, "处理微信支付回调异常");
// 异常情况也返回成功,避免微信重复通知
return new NotifyResult
{
Success = false,
Message = $"处理异常: {ex.Message}",
XmlResponse = successResponse
};
}
}
/// <inheritdoc />
public async Task<NotifyResult> HandleWechatV3NotifyAsync(string jsonData, WechatPayNotifyHeaders headers)
{
var successResponse = JsonSerializer.Serialize(new WechatPayV3NotifyResponse { Code = "SUCCESS", Message = "成功" });
var failResponse = JsonSerializer.Serialize(new WechatPayV3NotifyResponse { Code = "FAIL", Message = "处理失败" });
try
{
// 1. 检查数据是否为空
if (string.IsNullOrEmpty(jsonData))
{
_logger.LogWarning("微信支付 V3 回调数据为空");
return new NotifyResult
{
Success = false,
Message = "回调数据为空",
JsonResponse = failResponse
};
}
// 2. 验证签名
if (!_wechatPayV3Service.VerifyNotifySignature(
headers.Timestamp,
headers.Nonce,
jsonData,
headers.Signature,
headers.Serial))
{
_logger.LogWarning("微信支付 V3 回调签名验证失败");
return new NotifyResult
{
Success = false,
Message = "签名验证失败",
JsonResponse = failResponse
};
}
// 3. 解析回调通知
var notification = JsonSerializer.Deserialize<WechatPayV3Notification>(jsonData);
if (notification == null || notification.Resource == null)
{
_logger.LogWarning("解析微信支付 V3 回调数据失败");
return new NotifyResult
{
Success = false,
Message = "解析回调数据失败",
JsonResponse = failResponse
};
}
_logger.LogInformation("收到微信支付 V3 回调: Id={Id}, EventType={EventType}",
notification.Id, notification.EventType);
// 4. 获取商户配置并解密数据
var merchantConfig = _wechatPayConfigService.GetDefaultConfig();
if (string.IsNullOrEmpty(merchantConfig.ApiV3Key))
{
_logger.LogError("APIv3 密钥未配置");
return new NotifyResult
{
Success = false,
Message = "APIv3 密钥未配置",
JsonResponse = failResponse
};
}
var decryptedJson = _wechatPayV3Service.DecryptNotifyResource(
notification.Resource.Ciphertext,
notification.Resource.Nonce,
notification.Resource.AssociatedData,
merchantConfig.ApiV3Key);
_logger.LogDebug("V3 回调解密成功: {DecryptedJson}", decryptedJson);
// 5. 解析支付结果
var paymentResult = JsonSerializer.Deserialize<WechatPayV3PaymentResult>(decryptedJson);
if (paymentResult == null || string.IsNullOrEmpty(paymentResult.OutTradeNo))
{
_logger.LogWarning("解析 V3 支付结果失败");
return new NotifyResult
{
Success = false,
Message = "解析支付结果失败",
JsonResponse = failResponse
};
}
var orderNo = paymentResult.OutTradeNo;
var attach = paymentResult.Attach;
_logger.LogInformation("V3 支付结果: OrderNo={OrderNo}, TradeState={TradeState}, Attach={Attach}",
orderNo, paymentResult.TradeState, attach);
// 6. 检查支付状态
if (paymentResult.TradeState != WechatPayV3TradeState.Success)
{
_logger.LogWarning("V3 支付状态非成功: OrderNo={OrderNo}, TradeState={TradeState}",
orderNo, paymentResult.TradeState);
// 即使支付失败,也返回成功响应,避免微信重复通知
return new NotifyResult
{
Success = true,
Message = "支付未成功",
JsonResponse = successResponse
};
}
// 7. 幂等性检查 - 检查订单是否已处理
if (await IsOrderProcessedAsync(orderNo))
{
_logger.LogInformation("订单已处理,跳过重复回调: OrderNo={OrderNo}", orderNo);
return new NotifyResult
{
Success = true,
Message = "订单已处理",
JsonResponse = successResponse
};
}
// 8. 记录回调通知(转换为 V2 格式以复用现有逻辑)
var notifyData = ConvertV3ToV2NotifyData(paymentResult);
await RecordNotifyAsync(orderNo, notifyData);
// 9. 返回成功,业务处理由具体业务模块实现
_logger.LogInformation("微信支付 V3 回调记录成功: OrderNo={OrderNo}, Attach={Attach}", orderNo, attach);
return new NotifyResult
{
Success = true,
Message = "处理成功",
JsonResponse = successResponse,
OrderNo = orderNo,
Attach = attach,
NotifyData = notifyData
};
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "V3 回调解密失败");
return new NotifyResult
{
Success = false,
Message = $"解密失败: {ex.Message}",
JsonResponse = failResponse
};
}
catch (Exception ex)
{
_logger.LogError(ex, "处理微信支付 V3 回调异常");
// 异常情况也返回成功,避免微信重复通知
return new NotifyResult
{
Success = false,
Message = $"处理异常: {ex.Message}",
JsonResponse = successResponse
};
}
}
/// <summary>
/// 将 V3 支付结果转换为 V2 格式(复用现有处理逻辑)
/// </summary>
private static WechatNotifyData ConvertV3ToV2NotifyData(WechatPayV3PaymentResult v3Result)
{
return new WechatNotifyData
{
ReturnCode = "SUCCESS",
ResultCode = v3Result.TradeState == WechatPayV3TradeState.Success ? "SUCCESS" : "FAIL",
OutTradeNo = v3Result.OutTradeNo,
TransactionId = v3Result.TransactionId,
TotalFee = v3Result.Amount.Total,
OpenId = v3Result.Payer.OpenId,
Attach = v3Result.Attach,
NonceStr = Guid.NewGuid().ToString("N")[..32]
};
}
/// <inheritdoc />
public async Task<bool> IsOrderProcessedAsync(string orderNo)
{
var notify = await _dbContext.OrderNotifies
.FirstOrDefaultAsync(n => n.OrderNo == orderNo && n.Status == 1);
return notify != null;
}
/// <inheritdoc />
public async Task<bool> RecordNotifyAsync(string orderNo, WechatNotifyData notifyData)
{
try
{
// 检查是否已存在记录
var existingNotify = await _dbContext.OrderNotifies
.FirstOrDefaultAsync(n => n.OrderNo == orderNo);
if (existingNotify != null)
{
// 更新现有记录
existingNotify.TransactionId = notifyData.TransactionId;
existingNotify.PayTime = DateTime.Now;
existingNotify.PayAmount = notifyData.TotalFee / 100m;
existingNotify.RawData = null; // 可选存储原始XML
existingNotify.UpdateTime = DateTime.Now;
}
else
{
// 创建新记录
var notify = new OrderNotify
{
OrderNo = orderNo,
TransactionId = notifyData.TransactionId,
NonceStr = notifyData.NonceStr,
PayTime = DateTime.Now,
PayAmount = notifyData.TotalFee / 100m,
Status = 0, // 待处理
Attach = notifyData.Attach,
OpenId = notifyData.OpenId,
RawData = null,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now
};
_dbContext.OrderNotifies.Add(notify);
}
await _dbContext.SaveChangesAsync();
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "记录支付回调通知失败: {OrderNo}", orderNo);
return false;
}
}
/// <inheritdoc />
public async Task<bool> UpdateNotifyStatusAsync(string orderNo, byte status, string? message = null)
{
try
{
var notify = await _dbContext.OrderNotifies
.FirstOrDefaultAsync(n => n.OrderNo == orderNo);
if (notify != null)
{
notify.Status = status;
notify.ErrorMessage = message;
notify.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
return true;
}
_logger.LogWarning("更新订单通知状态失败,未找到记录: OrderNo={OrderNo}", orderNo);
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "更新订单通知状态异常: OrderNo={OrderNo}", orderNo);
return false;
}
}
}