1151 lines
43 KiB
C#
1151 lines
43 KiB
C#
using System.Text.Json;
|
||
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Entities;
|
||
using HoneyBox.Model.Models.Lottery;
|
||
using HoneyBox.Model.Models.Payment;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace HoneyBox.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 支付回调服务实现
|
||
/// </summary>
|
||
public class PaymentNotifyService : IPaymentNotifyService
|
||
{
|
||
private readonly HoneyBoxDbContext _dbContext;
|
||
private readonly IWechatPayService _wechatPayService;
|
||
private readonly IWechatPayV3Service _wechatPayV3Service;
|
||
private readonly IWechatPayConfigService _wechatPayConfigService;
|
||
private readonly IPaymentService _paymentService;
|
||
private readonly ILotteryEngine _lotteryEngine;
|
||
private readonly ILogger<PaymentNotifyService> _logger;
|
||
|
||
/// <summary>
|
||
/// 一番赏类订单类型列表
|
||
/// </summary>
|
||
private static readonly string[] LotteryOrderTypes = new[]
|
||
{
|
||
OrderAttachType.OrderYfs,
|
||
OrderAttachType.OrderLts,
|
||
OrderAttachType.OrderZzs,
|
||
OrderAttachType.OrderFlw,
|
||
OrderAttachType.OrderScs
|
||
};
|
||
|
||
/// <summary>
|
||
/// 无限赏类订单类型列表
|
||
/// </summary>
|
||
private static readonly string[] InfiniteOrderTypes = new[]
|
||
{
|
||
OrderAttachType.OrderWxs,
|
||
OrderAttachType.OrderFbs
|
||
};
|
||
|
||
public PaymentNotifyService(
|
||
HoneyBoxDbContext dbContext,
|
||
IWechatPayService wechatPayService,
|
||
IWechatPayV3Service wechatPayV3Service,
|
||
IWechatPayConfigService wechatPayConfigService,
|
||
IPaymentService paymentService,
|
||
ILotteryEngine lotteryEngine,
|
||
ILogger<PaymentNotifyService> logger)
|
||
{
|
||
_dbContext = dbContext;
|
||
_wechatPayService = wechatPayService;
|
||
_wechatPayV3Service = wechatPayV3Service;
|
||
_wechatPayConfigService = wechatPayConfigService;
|
||
_paymentService = paymentService;
|
||
_lotteryEngine = lotteryEngine;
|
||
_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. 根据订单类型路由处理
|
||
var processResult = await RouteOrderProcessingAsync(orderNo, attach, notifyData);
|
||
|
||
if (processResult)
|
||
{
|
||
_logger.LogInformation("微信支付回调处理成功: OrderNo={OrderNo}", orderNo);
|
||
return new NotifyResult
|
||
{
|
||
Success = true,
|
||
Message = "处理成功",
|
||
XmlResponse = successResponse
|
||
};
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("微信支付回调处理失败: OrderNo={OrderNo}", orderNo);
|
||
// 处理失败也返回成功,避免微信重复通知,后续通过其他机制处理
|
||
return new NotifyResult
|
||
{
|
||
Success = false,
|
||
Message = "处理失败",
|
||
XmlResponse = successResponse
|
||
};
|
||
}
|
||
}
|
||
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. 根据订单类型路由处理
|
||
var processResult = await RouteOrderProcessingAsync(orderNo, attach, notifyData);
|
||
|
||
if (processResult)
|
||
{
|
||
_logger.LogInformation("微信支付 V3 回调处理成功: OrderNo={OrderNo}", orderNo);
|
||
return new NotifyResult
|
||
{
|
||
Success = true,
|
||
Message = "处理成功",
|
||
JsonResponse = successResponse
|
||
};
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("微信支付 V3 回调处理失败: OrderNo={OrderNo}", orderNo);
|
||
// 处理失败也返回成功,避免微信重复通知
|
||
return new NotifyResult
|
||
{
|
||
Success = false,
|
||
Message = "处理失败",
|
||
JsonResponse = successResponse
|
||
};
|
||
}
|
||
}
|
||
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]
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据订单类型路由到对应的处理方法
|
||
/// </summary>
|
||
/// <param name="orderNo">订单号</param>
|
||
/// <param name="attach">附加数据(订单类型)</param>
|
||
/// <param name="notifyData">回调数据</param>
|
||
/// <returns>是否处理成功</returns>
|
||
private async Task<bool> RouteOrderProcessingAsync(string orderNo, string attach, WechatNotifyData notifyData)
|
||
{
|
||
try
|
||
{
|
||
// 获取用户信息
|
||
var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.OpenId == notifyData.OpenId);
|
||
if (user == null)
|
||
{
|
||
_logger.LogWarning("未找到用户: OpenId={OpenId}", notifyData.OpenId);
|
||
return false;
|
||
}
|
||
|
||
// 记录支付流水
|
||
await RecordPaymentAsync(user.Id, orderNo, notifyData.TotalFee / 100m, attach);
|
||
|
||
// 根据attach类型路由处理
|
||
if (attach == OrderAttachType.UserRecharge)
|
||
{
|
||
// 余额充值
|
||
return await ProcessRechargeOrderAsync(orderNo);
|
||
}
|
||
else if (LotteryOrderTypes.Contains(attach))
|
||
{
|
||
// 一番赏类订单
|
||
return await ProcessLotteryOrderByOrderNoAsync(orderNo);
|
||
}
|
||
else if (InfiniteOrderTypes.Contains(attach))
|
||
{
|
||
// 无限赏类订单
|
||
return await ProcessInfiniteOrderByOrderNoAsync(orderNo);
|
||
}
|
||
else if (attach == OrderAttachType.OrderCkj)
|
||
{
|
||
// 抽卡机订单
|
||
return await ProcessCardExtractorOrderByOrderNoAsync(orderNo);
|
||
}
|
||
else if (attach == OrderAttachType.OrderListSend)
|
||
{
|
||
// 发货运费订单
|
||
return await ProcessShippingFeeOrderAsync(orderNo);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("未知的订单类型: Attach={Attach}, OrderNo={OrderNo}", attach, orderNo);
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "路由订单处理异常: OrderNo={OrderNo}, Attach={Attach}", orderNo, attach);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录支付流水(委托给PaymentService)
|
||
/// </summary>
|
||
private async Task RecordPaymentAsync(int userId, string orderNo, decimal amount, string content)
|
||
{
|
||
// 根据content确定支付说明
|
||
var paymentContent = string.IsNullOrEmpty(content) ? "微信支付" : content;
|
||
|
||
// 委托给PaymentService处理
|
||
var result = await _paymentService.RecordPaymentAsync(userId, orderNo, amount, PaymentType.WechatPay, paymentContent);
|
||
|
||
if (!result)
|
||
{
|
||
_logger.LogWarning("记录支付流水失败: UserId={UserId}, OrderNo={OrderNo}", userId, orderNo);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据订单号处理一番赏订单
|
||
/// </summary>
|
||
private async Task<bool> ProcessLotteryOrderByOrderNoAsync(string orderNo)
|
||
{
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.OrderNum == orderNo && o.Status == 0);
|
||
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的一番赏订单: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
|
||
return await ProcessLotteryOrderAsync(order.Id, order.UserId, order.GoodsId, order.Num);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据订单号处理无限赏订单
|
||
/// </summary>
|
||
private async Task<bool> ProcessInfiniteOrderByOrderNoAsync(string orderNo)
|
||
{
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.OrderNum == orderNo && o.Status == 0);
|
||
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的无限赏订单: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
|
||
return await ProcessInfiniteOrderAsync(order.Id, order.UserId, order.GoodsId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据订单号处理抽卡机订单
|
||
/// </summary>
|
||
private async Task<bool> ProcessCardExtractorOrderByOrderNoAsync(string orderNo)
|
||
{
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.OrderNum == orderNo && o.Status == 0 && o.OrderType == 4);
|
||
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的抽卡机订单: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
|
||
return await ProcessCardExtractorOrderAsync(order.Id, order.UserId, order.GoodsId);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ProcessLotteryOrderAsync(int orderId, int userId, int goodsId, int num)
|
||
{
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 1. 查找订单 - 支持一番赏类订单类型 (1=一番赏, 3=擂台赏, 5=转转赏, 6=福利屋, 11=商城赏, 15=福利屋特殊, 10=商城赏特殊)
|
||
var validOrderTypes = new byte[] { 1, 3, 5, 6, 11, 15, 10 };
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.Id == orderId
|
||
&& o.UserId == userId
|
||
&& o.GoodsId == goodsId
|
||
&& o.Num == num
|
||
&& o.Status == 0
|
||
&& validOrderTypes.Contains(o.OrderType));
|
||
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的一番赏订单: OrderId={OrderId}, UserId={UserId}, GoodsId={GoodsId}, Num={Num}",
|
||
orderId, userId, goodsId, num);
|
||
return false;
|
||
}
|
||
|
||
var goodsTitle = order.GoodsTitle ?? "商品";
|
||
var content = $"购买盒子{goodsTitle}";
|
||
|
||
// 2. 更新订单状态为已支付
|
||
order.Status = 1;
|
||
order.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
order.UpdatedAt = DateTime.Now;
|
||
|
||
// 3. 扣减用户资产(余额、积分、哈尼券)
|
||
// 扣减余额
|
||
if (order.UseMoney > 0)
|
||
{
|
||
var balanceResult = await _paymentService.DeductBalanceAsync(order.UserId, order.UseMoney, content, order.OrderNum);
|
||
if (!balanceResult.Success)
|
||
{
|
||
_logger.LogWarning("扣减余额失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseMoney, balanceResult.Message);
|
||
// 继续处理,不中断流程(PHP代码也是这样处理的)
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减余额成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney);
|
||
}
|
||
}
|
||
|
||
// 扣减积分(吧唧币)
|
||
if (order.UseIntegral > 0)
|
||
{
|
||
var integralResult = await _paymentService.DeductIntegralAsync(order.UserId, order.UseIntegral, content, order.OrderNum);
|
||
if (!integralResult.Success)
|
||
{
|
||
_logger.LogWarning("扣减积分失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseIntegral, integralResult.Message);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减积分成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseIntegral);
|
||
}
|
||
}
|
||
|
||
// 扣减哈尼券
|
||
if (order.UseMoney2 > 0)
|
||
{
|
||
var money2Result = await _paymentService.DeductMoney2Async(order.UserId, order.UseMoney2, content, order.OrderNum);
|
||
if (!money2Result.Success)
|
||
{
|
||
_logger.LogWarning("扣减哈尼券失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseMoney2, money2Result.Message);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减哈尼券成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney2);
|
||
}
|
||
}
|
||
|
||
// 4. 更新优惠券状态为已使用
|
||
if (order.CouponId.HasValue && order.CouponId.Value > 0)
|
||
{
|
||
await UpdateCouponStatusAsync(order.CouponId.Value);
|
||
}
|
||
|
||
// 5. 更新订单通知状态
|
||
await UpdateOrderNotifyStatusAsync(order.OrderNum, 1, "处理成功");
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
_logger.LogInformation("一番赏订单支付处理成功: OrderId={OrderId}, UserId={UserId}, GoodsId={GoodsId}, Num={Num}, OrderType={OrderType}",
|
||
orderId, userId, goodsId, num, order.OrderType);
|
||
|
||
// 6. 执行普通抽奖逻辑
|
||
await ExecuteLotteryAsync(userId, orderId, goodsId, order.OrderType, num, order.PrizeNum, order.OrderNum);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "处理一番赏订单失败: OrderId={OrderId}", orderId);
|
||
|
||
// 标记订单为卡单状态
|
||
await MarkOrderAsStuckAsync(orderId);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行普通抽奖逻辑(一番赏、擂台赏、转转赏等)
|
||
/// </summary>
|
||
private async Task ExecuteLotteryAsync(int userId, int orderId, int goodsId, int orderType, int num, int prizeNum, string orderNum)
|
||
{
|
||
try
|
||
{
|
||
var drawRequest = new LotteryDrawRequest
|
||
{
|
||
UserId = userId,
|
||
GoodsId = goodsId,
|
||
Num = num,
|
||
OrderId = orderId,
|
||
OrderNum = orderNum,
|
||
OrderType = orderType,
|
||
Source = 1 // 抽奖获得
|
||
};
|
||
|
||
// 执行多次抽奖
|
||
var results = await _lotteryEngine.DrawMultipleAsync(drawRequest, prizeNum);
|
||
|
||
var successCount = results.Count(r => r.Success);
|
||
_logger.LogInformation("普通抽奖完成: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}, Num={Num}, PrizeNum={PrizeNum}, SuccessCount={SuccessCount}",
|
||
userId, orderId, goodsId, num, prizeNum, successCount);
|
||
|
||
if (successCount == 0)
|
||
{
|
||
_logger.LogWarning("普通抽奖全部失败: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}",
|
||
userId, orderId, goodsId);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "执行普通抽奖失败: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}",
|
||
userId, orderId, goodsId);
|
||
// 抽奖失败不影响支付回调处理,但需要记录日志以便后续处理
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ProcessInfiniteOrderAsync(int orderId, int userId, int goodsId)
|
||
{
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 1. 查找订单 - 支持无限赏类订单类型
|
||
// 无限赏订单类型: 2=无限赏, 7=翻倍赏, 8=领主赏, 9=秘宝赏, 10=商城赏, 17=特殊无限赏
|
||
var validOrderTypes = new byte[] { 2, 7, 8, 9, 10, 17 };
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.Id == orderId
|
||
&& o.UserId == userId
|
||
&& o.GoodsId == goodsId
|
||
&& o.Status == 0);
|
||
|
||
// PHP逻辑: 如果num=0找不到订单,尝试num=1(针对order_type=10的商城赏)
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的无限赏订单: OrderId={OrderId}, UserId={UserId}, GoodsId={GoodsId}",
|
||
orderId, userId, goodsId);
|
||
return false;
|
||
}
|
||
|
||
var goodsTitle = order.GoodsTitle ?? "商品";
|
||
var content = $"购买盒子{goodsTitle}";
|
||
|
||
// 2. 更新订单状态为已支付
|
||
order.Status = 1;
|
||
order.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
order.UpdatedAt = DateTime.Now;
|
||
|
||
// 3. 扣减用户资产(余额、积分、哈尼券)
|
||
// 扣减余额
|
||
if (order.UseMoney > 0)
|
||
{
|
||
var balanceResult = await _paymentService.DeductBalanceAsync(order.UserId, order.UseMoney, content, order.OrderNum);
|
||
if (!balanceResult.Success)
|
||
{
|
||
_logger.LogWarning("扣减余额失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseMoney, balanceResult.Message);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减余额成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney);
|
||
}
|
||
}
|
||
|
||
// 扣减积分(吧唧币)
|
||
if (order.UseIntegral > 0)
|
||
{
|
||
var integralResult = await _paymentService.DeductIntegralAsync(order.UserId, order.UseIntegral, content, order.OrderNum);
|
||
if (!integralResult.Success)
|
||
{
|
||
_logger.LogWarning("扣减积分失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseIntegral, integralResult.Message);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减积分成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseIntegral);
|
||
}
|
||
}
|
||
|
||
// 扣减哈尼券
|
||
if (order.UseMoney2 > 0)
|
||
{
|
||
var money2Result = await _paymentService.DeductMoney2Async(order.UserId, order.UseMoney2, content, order.OrderNum);
|
||
if (!money2Result.Success)
|
||
{
|
||
_logger.LogWarning("扣减哈尼券失败: UserId={UserId}, Amount={Amount}, Message={Message}",
|
||
order.UserId, order.UseMoney2, money2Result.Message);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogDebug("扣减哈尼券成功: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney2);
|
||
}
|
||
}
|
||
|
||
// 4. 更新优惠券状态为已使用
|
||
if (order.CouponId.HasValue && order.CouponId.Value > 0)
|
||
{
|
||
await UpdateCouponStatusAsync(order.CouponId.Value);
|
||
}
|
||
|
||
// 5. 更新订单通知状态
|
||
await UpdateOrderNotifyStatusAsync(order.OrderNum, 1, "处理成功");
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
_logger.LogInformation("无限赏订单支付处理成功: OrderId={OrderId}, UserId={UserId}, GoodsId={GoodsId}, OrderType={OrderType}, PrizeNum={PrizeNum}",
|
||
orderId, userId, goodsId, order.OrderType, order.PrizeNum);
|
||
|
||
// 6. 执行无限赏抽奖逻辑
|
||
await ExecuteInfiniteLotteryAsync(userId, orderId, goodsId, order.OrderType, order.PrizeNum, order.OrderNum);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "处理无限赏订单失败: OrderId={OrderId}", orderId);
|
||
|
||
// 标记订单为卡单状态
|
||
await MarkOrderAsStuckAsync(orderId);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行无限赏抽奖逻辑
|
||
/// </summary>
|
||
private async Task ExecuteInfiniteLotteryAsync(int userId, int orderId, int goodsId, int orderType, int prizeNum, string orderNum)
|
||
{
|
||
try
|
||
{
|
||
var drawRequest = new LotteryDrawRequest
|
||
{
|
||
UserId = userId,
|
||
GoodsId = goodsId,
|
||
Num = 0, // 无限赏固定为0
|
||
OrderId = orderId,
|
||
OrderNum = orderNum,
|
||
OrderType = orderType,
|
||
Source = 1 // 抽奖获得
|
||
};
|
||
|
||
// 执行多次抽奖
|
||
var results = await _lotteryEngine.DrawInfiniteMultipleAsync(drawRequest, prizeNum);
|
||
|
||
var successCount = results.Count(r => r.Success);
|
||
_logger.LogInformation("无限赏抽奖完成: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}, PrizeNum={PrizeNum}, SuccessCount={SuccessCount}",
|
||
userId, orderId, goodsId, prizeNum, successCount);
|
||
|
||
if (successCount == 0)
|
||
{
|
||
_logger.LogWarning("无限赏抽奖全部失败: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}",
|
||
userId, orderId, goodsId);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "执行无限赏抽奖失败: UserId={UserId}, OrderId={OrderId}, GoodsId={GoodsId}",
|
||
userId, orderId, goodsId);
|
||
// 抽奖失败不影响支付回调处理,但需要记录日志以便后续处理
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ProcessRechargeOrderAsync(string orderNo)
|
||
{
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 1. 查找充值订单 - 状态为1(待支付)
|
||
var rechargeOrder = await _dbContext.UserRecharges
|
||
.FirstOrDefaultAsync(r => r.OrderNum == orderNo && r.Status == 1);
|
||
|
||
if (rechargeOrder == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的充值订单: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
|
||
var userId = rechargeOrder.UserId;
|
||
var money = rechargeOrder.Money;
|
||
|
||
// 2. 更新充值订单状态为已完成
|
||
rechargeOrder.Status = 2; // 2=已完成
|
||
rechargeOrder.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
|
||
// 3. 增加用户余额
|
||
var addBalanceResult = await _paymentService.AddBalanceAsync(userId, money, "在线充值", orderNo);
|
||
if (!addBalanceResult)
|
||
{
|
||
_logger.LogWarning("增加用户余额失败: UserId={UserId}, Amount={Amount}, OrderNo={OrderNo}",
|
||
userId, money, orderNo);
|
||
await transaction.RollbackAsync();
|
||
return false;
|
||
}
|
||
|
||
// 4. 记录微信支付流水
|
||
var profitPay = new ProfitPay
|
||
{
|
||
UserId = userId,
|
||
OrderNum = orderNo,
|
||
ChangeMoney = money,
|
||
Content = "微信支付",
|
||
PayType = (byte)PaymentType.WechatPay,
|
||
CreatedAt = DateTime.Now
|
||
};
|
||
|
||
_dbContext.ProfitPays.Add(profitPay);
|
||
|
||
// 5. 更新订单通知状态
|
||
await UpdateOrderNotifyStatusAsync(orderNo, 1, "处理成功");
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
_logger.LogInformation("充值订单处理成功: OrderNo={OrderNo}, UserId={UserId}, Money={Money}",
|
||
orderNo, userId, money);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "处理充值订单失败: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ProcessShippingFeeOrderAsync(string orderNo)
|
||
{
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 1. 查找发货记录
|
||
var sendRecord = await _dbContext.OrderItemsSends
|
||
.FirstOrDefaultAsync(s => s.SendNum == orderNo && s.Status == 0);
|
||
|
||
if (sendRecord == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的发货记录: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
|
||
// 2. 更新发货记录状态为待发货
|
||
sendRecord.Status = 1; // 0待支付 -> 1待发货
|
||
sendRecord.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
sendRecord.UpdatedAt = DateTime.Now;
|
||
|
||
// 3. 更新关联的订单项状态
|
||
var orderItems = await _dbContext.OrderItems
|
||
.Where(oi => oi.UserId == sendRecord.UserId
|
||
&& oi.Status == 0
|
||
&& oi.GoodslistType == 1
|
||
&& oi.SendNum == sendRecord.SendNum)
|
||
.ToListAsync();
|
||
|
||
foreach (var item in orderItems)
|
||
{
|
||
item.Status = 2; // 选择发货
|
||
item.ChoiceTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
}
|
||
|
||
// 4. 更新订单通知状态
|
||
await UpdateOrderNotifyStatusAsync(orderNo, 1, "处理成功");
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
_logger.LogInformation("发货运费订单处理成功: OrderNo={OrderNo}, UserId={UserId}",
|
||
orderNo, sendRecord.UserId);
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "处理发货运费订单失败: OrderNo={OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> ProcessCardExtractorOrderAsync(int orderId, int userId, int goodsId)
|
||
{
|
||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||
try
|
||
{
|
||
// 1. 查找订单
|
||
var order = await _dbContext.Orders
|
||
.FirstOrDefaultAsync(o => o.Id == orderId
|
||
&& o.UserId == userId
|
||
&& o.GoodsId == goodsId
|
||
&& o.Status == 0
|
||
&& o.OrderType == 4);
|
||
|
||
if (order == null)
|
||
{
|
||
_logger.LogWarning("未找到待支付的抽卡机订单: OrderId={OrderId}, UserId={UserId}", orderId, userId);
|
||
return false;
|
||
}
|
||
|
||
// 2. 更新订单状态为已支付
|
||
order.Status = 1;
|
||
order.PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
order.UpdatedAt = DateTime.Now;
|
||
|
||
// 3. 扣减用户资产
|
||
await DeductUserAssetsAsync(order);
|
||
|
||
// 4. 更新优惠券状态
|
||
if (order.CouponId.HasValue && order.CouponId.Value > 0)
|
||
{
|
||
await UpdateCouponStatusAsync(order.CouponId.Value);
|
||
}
|
||
|
||
// 5. 更新订单通知状态
|
||
await UpdateOrderNotifyStatusAsync(order.OrderNum, 1, "处理成功");
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
await transaction.CommitAsync();
|
||
|
||
_logger.LogInformation("抽卡机订单处理成功: OrderId={OrderId}, UserId={UserId}", orderId, userId);
|
||
|
||
// TODO: 触发抽卡机抽奖逻辑
|
||
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
await transaction.RollbackAsync();
|
||
_logger.LogError(ex, "处理抽卡机订单失败: OrderId={OrderId}", orderId);
|
||
|
||
// 标记订单为卡单状态
|
||
await MarkOrderAsStuckAsync(orderId);
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <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.UpdatedAt = 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,
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
};
|
||
|
||
_dbContext.OrderNotifies.Add(notify);
|
||
}
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "记录支付回调通知失败: {OrderNo}", orderNo);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
#region Private Helper Methods
|
||
|
||
/// <summary>
|
||
/// 扣减用户资产(余额、积分、哈尼券)
|
||
/// </summary>
|
||
private async Task DeductUserAssetsAsync(Order order)
|
||
{
|
||
var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == order.UserId);
|
||
if (user == null)
|
||
{
|
||
_logger.LogWarning("扣减资产时未找到用户: UserId={UserId}", order.UserId);
|
||
return;
|
||
}
|
||
|
||
var goodsTitle = order.GoodsTitle ?? "商品";
|
||
var content = $"购买盒子{goodsTitle}";
|
||
|
||
// 扣减余额
|
||
if (order.UseMoney > 0)
|
||
{
|
||
await _paymentService.DeductBalanceAsync(order.UserId, order.UseMoney, content);
|
||
_logger.LogDebug("扣减余额: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney);
|
||
}
|
||
|
||
// 扣减积分(吧唧币)
|
||
if (order.UseIntegral > 0)
|
||
{
|
||
await _paymentService.DeductIntegralAsync(order.UserId, order.UseIntegral, content);
|
||
_logger.LogDebug("扣减积分: UserId={UserId}, Amount={Amount}", order.UserId, order.UseIntegral);
|
||
}
|
||
|
||
// 扣减哈尼券
|
||
if (order.UseMoney2 > 0)
|
||
{
|
||
await _paymentService.DeductMoney2Async(order.UserId, order.UseMoney2, content);
|
||
_logger.LogDebug("扣减哈尼券: UserId={UserId}, Amount={Amount}", order.UserId, order.UseMoney2);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新优惠券状态为已使用
|
||
/// </summary>
|
||
private async Task UpdateCouponStatusAsync(int couponId)
|
||
{
|
||
var coupon = await _dbContext.CouponReceives
|
||
.FirstOrDefaultAsync(c => c.Id == couponId && c.Status == 0);
|
||
|
||
if (coupon != null)
|
||
{
|
||
coupon.Status = 1; // 已使用
|
||
_logger.LogDebug("更新优惠券状态: CouponId={CouponId}", couponId);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新订单通知状态
|
||
/// </summary>
|
||
private async Task UpdateOrderNotifyStatusAsync(string orderNo, byte status, string? message = null)
|
||
{
|
||
var notify = await _dbContext.OrderNotifies
|
||
.FirstOrDefaultAsync(n => n.OrderNo == orderNo);
|
||
|
||
if (notify != null)
|
||
{
|
||
notify.Status = status;
|
||
notify.ErrorMessage = message;
|
||
notify.UpdatedAt = DateTime.Now;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标记订单为卡单状态
|
||
/// </summary>
|
||
private async Task MarkOrderAsStuckAsync(int orderId)
|
||
{
|
||
try
|
||
{
|
||
var order = await _dbContext.Orders.FirstOrDefaultAsync(o => o.Id == orderId);
|
||
if (order != null)
|
||
{
|
||
order.KdIs = 1; // 标记为卡单
|
||
order.UpdatedAt = DateTime.Now;
|
||
await _dbContext.SaveChangesAsync();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "标记订单卡单状态失败: OrderId={OrderId}", orderId);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|