fix: 修复微信支付回调无法接收的问题
1. 添加 /api/pay/notify 回调路由(兼容微信配置的回调地址)
2. 修复 attach 值匹配逻辑,支持 order_{type} 和 infinite_{type} 格式
3. 添加钻石订单 (order_product) 的回调处理逻辑
4. 添加 OrderAttachType.OrderProduct 常量
This commit is contained in:
parent
e67602b3c4
commit
e4a1f055c1
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
// 测试环境配置 - .NET 10 后端
|
||||
const testing = {
|
||||
baseUrl: 'https://app.zpc-xy.com/honey/api',
|
||||
// baseUrl: 'https://app.zpc-xy.com/honey/api',
|
||||
baseUrl: 'https://api.hanimanghe.top',
|
||||
// baseUrl: 'http://192.168.1.24:5238',
|
||||
// baseUrl: 'http://192.168.195.15:2822',
|
||||
imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com',
|
||||
|
|
|
|||
|
|
@ -18,18 +18,84 @@ public class PayController : ControllerBase
|
|||
{
|
||||
private readonly IWechatPayService _wechatPayService;
|
||||
private readonly IRechargeService _rechargeService;
|
||||
private readonly IPaymentNotifyService _paymentNotifyService;
|
||||
private readonly ILogger<PayController> _logger;
|
||||
|
||||
public PayController(
|
||||
IWechatPayService wechatPayService,
|
||||
IRechargeService rechargeService,
|
||||
IPaymentNotifyService paymentNotifyService,
|
||||
ILogger<PayController> logger)
|
||||
{
|
||||
_wechatPayService = wechatPayService;
|
||||
_rechargeService = rechargeService;
|
||||
_paymentNotifyService = paymentNotifyService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信支付回调接口(兼容旧路由)
|
||||
/// POST /api/pay/notify
|
||||
/// 接收微信支付结果通知,处理订单状态更新
|
||||
/// </summary>
|
||||
[HttpPost("pay/notify")]
|
||||
public async Task<IActionResult> PayNotify()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取请求体
|
||||
using var reader = new StreamReader(Request.Body);
|
||||
var notifyBody = await reader.ReadToEndAsync();
|
||||
|
||||
_logger.LogInformation("收到微信支付回调请求 [/api/pay/notify],数据长度: {Length}, ContentType: {ContentType}",
|
||||
notifyBody?.Length ?? 0, Request.ContentType);
|
||||
|
||||
// 提取 V3 回调请求头(如果存在)
|
||||
WechatPayNotifyHeaders? headers = null;
|
||||
if (Request.Headers.TryGetValue("Wechatpay-Timestamp", out var timestamp) &&
|
||||
Request.Headers.TryGetValue("Wechatpay-Nonce", out var nonce) &&
|
||||
Request.Headers.TryGetValue("Wechatpay-Signature", out var signature) &&
|
||||
Request.Headers.TryGetValue("Wechatpay-Serial", out var serial))
|
||||
{
|
||||
headers = new WechatPayNotifyHeaders
|
||||
{
|
||||
Timestamp = timestamp.ToString(),
|
||||
Nonce = nonce.ToString(),
|
||||
Signature = signature.ToString(),
|
||||
Serial = serial.ToString()
|
||||
};
|
||||
_logger.LogDebug("检测到 V3 回调请求头: Timestamp={Timestamp}, Serial={Serial}",
|
||||
headers.Timestamp, headers.Serial);
|
||||
}
|
||||
|
||||
// 调用服务处理回调(自动识别 V2/V3 格式)
|
||||
var result = await _paymentNotifyService.HandleWechatNotifyAsync(notifyBody ?? string.Empty, headers);
|
||||
|
||||
_logger.LogInformation("微信支付回调处理完成: Success={Success}, Message={Message}",
|
||||
result.Success, result.Message);
|
||||
|
||||
// 根据回调版本返回对应格式的响应
|
||||
if (!string.IsNullOrEmpty(result.JsonResponse))
|
||||
{
|
||||
// V3 返回 JSON
|
||||
return Content(result.JsonResponse, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
// V2 返回 XML
|
||||
return Content(result.XmlResponse ?? "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>", "application/xml");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "处理微信支付回调异常");
|
||||
|
||||
// 返回成功响应,避免微信重复通知
|
||||
var successResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
|
||||
return Content(successResponse, "application/xml");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信支付统一下单接口
|
||||
/// POST /api/pay
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using HoneyBox.Core.Interfaces;
|
|||
using HoneyBox.Model.Data;
|
||||
using HoneyBox.Model.Entities;
|
||||
using HoneyBox.Model.Models.Lottery;
|
||||
using HoneyBox.Model.Models.Mall;
|
||||
using HoneyBox.Model.Models.Payment;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -418,31 +419,60 @@ public class PaymentNotifyService : IPaymentNotifyService
|
|||
await RecordPaymentAsync(user.Id, orderNo, notifyData.TotalFee / 100m, attach);
|
||||
|
||||
// 根据attach类型路由处理
|
||||
// attach格式: order_{type} 或 infinite_{type} 或固定字符串
|
||||
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 if (attach == OrderAttachType.OrderProduct)
|
||||
{
|
||||
// 钻石商品订单
|
||||
return await ProcessDiamondOrderAsync(orderNo, user.Id);
|
||||
}
|
||||
else if (attach.StartsWith("order_"))
|
||||
{
|
||||
// 一番赏类订单 (order_1, order_3, order_4, order_5, order_6, order_10, order_11 等)
|
||||
// 提取类型数字
|
||||
var typeStr = attach.Replace("order_", "");
|
||||
if (int.TryParse(typeStr, out var orderType))
|
||||
{
|
||||
// 类型 4 是抽卡机
|
||||
if (orderType == 4)
|
||||
{
|
||||
return await ProcessCardExtractorOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
// 其他类型按一番赏处理
|
||||
return await ProcessLotteryOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
// 如果不是数字格式,尝试匹配旧格式
|
||||
if (LotteryOrderTypes.Contains(attach))
|
||||
{
|
||||
return await ProcessLotteryOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
_logger.LogWarning("未知的order_类型: Attach={Attach}, OrderNo={OrderNo}", attach, orderNo);
|
||||
return false;
|
||||
}
|
||||
else if (attach.StartsWith("infinite_"))
|
||||
{
|
||||
// 无限赏类订单 (infinite_2, infinite_7, infinite_8, infinite_9 等)
|
||||
return await ProcessInfiniteOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
else if (LotteryOrderTypes.Contains(attach))
|
||||
{
|
||||
// 兼容旧格式 order_yfs, order_lts 等
|
||||
return await ProcessLotteryOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
else if (InfiniteOrderTypes.Contains(attach))
|
||||
{
|
||||
// 兼容旧格式 order_wxs, order_fbs 等
|
||||
return await ProcessInfiniteOrderByOrderNoAsync(orderNo);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("未知的订单类型: Attach={Attach}, OrderNo={OrderNo}", attach, orderNo);
|
||||
|
|
@ -998,6 +1028,192 @@ public class PaymentNotifyService : IPaymentNotifyService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理钻石商品订单
|
||||
/// </summary>
|
||||
/// <param name="orderNo">订单号</param>
|
||||
/// <param name="userId">用户ID</param>
|
||||
/// <returns>是否处理成功</returns>
|
||||
public async Task<bool> ProcessDiamondOrderAsync(string orderNo, int userId)
|
||||
{
|
||||
using var transaction = await _dbContext.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
// 1. 查找钻石订单
|
||||
var diamondOrder = await _dbContext.DiamondOrders
|
||||
.FirstOrDefaultAsync(o => o.OrderNo == orderNo && o.Status == DiamondOrderStatus.Pending);
|
||||
|
||||
if (diamondOrder == null)
|
||||
{
|
||||
_logger.LogWarning("未找到待支付的钻石订单: OrderNo={OrderNo}", orderNo);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 查找钻石商品配置
|
||||
var diamondProduct = await _dbContext.DiamondProducts
|
||||
.FirstOrDefaultAsync(p => p.Id == diamondOrder.DiamondId);
|
||||
|
||||
if (diamondProduct == null)
|
||||
{
|
||||
_logger.LogWarning("钻石商品不存在: DiamondId={DiamondId}", diamondOrder.DiamondId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 更新订单状态为已支付
|
||||
diamondOrder.Status = DiamondOrderStatus.Success;
|
||||
diamondOrder.PaidAt = DateTime.Now;
|
||||
|
||||
// 4. 解析并发放奖励
|
||||
var rewardLog = await GrantDiamondRewardsAsync(userId, diamondProduct, diamondOrder.IsFirstCharge == 1);
|
||||
diamondOrder.RewardLog = rewardLog;
|
||||
|
||||
// 5. 更新订单通知状态
|
||||
await UpdateOrderNotifyStatusAsync(orderNo, 1, "处理成功");
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
|
||||
_logger.LogInformation("钻石订单处理成功: OrderNo={OrderNo}, UserId={UserId}, RewardLog={RewardLog}",
|
||||
orderNo, userId, rewardLog);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "处理钻石订单失败: OrderNo={OrderNo}", orderNo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放钻石商品奖励
|
||||
/// </summary>
|
||||
private async Task<string> GrantDiamondRewardsAsync(int userId, DiamondProduct product, bool isFirstCharge)
|
||||
{
|
||||
var rewardLogs = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// 解析基础奖励 (格式: "type:value,type:value" 如 "1:100,2:50")
|
||||
if (!string.IsNullOrEmpty(product.BaseReward))
|
||||
{
|
||||
var baseRewards = ParseRewards(product.BaseReward);
|
||||
foreach (var reward in baseRewards)
|
||||
{
|
||||
var log = await GrantSingleRewardAsync(userId, reward.Type, reward.Value, "购买钻石商品");
|
||||
if (!string.IsNullOrEmpty(log))
|
||||
rewardLogs.Add(log);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是首充,发放首充额外奖励
|
||||
if (isFirstCharge && !string.IsNullOrEmpty(product.FirstBonusReward))
|
||||
{
|
||||
var bonusRewards = ParseRewards(product.FirstBonusReward);
|
||||
foreach (var reward in bonusRewards)
|
||||
{
|
||||
var log = await GrantSingleRewardAsync(userId, reward.Type, reward.Value, "首充奖励");
|
||||
if (!string.IsNullOrEmpty(log))
|
||||
rewardLogs.Add(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "发放钻石奖励异常: UserId={UserId}, ProductId={ProductId}", userId, product.Id);
|
||||
rewardLogs.Add($"发放异常: {ex.Message}");
|
||||
}
|
||||
|
||||
return string.Join("; ", rewardLogs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析奖励配置字符串
|
||||
/// </summary>
|
||||
private static List<(int Type, decimal Value)> ParseRewards(string rewardStr)
|
||||
{
|
||||
var rewards = new List<(int Type, decimal Value)>();
|
||||
|
||||
if (string.IsNullOrEmpty(rewardStr))
|
||||
return rewards;
|
||||
|
||||
var parts = rewardStr.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var kv = part.Split(':');
|
||||
if (kv.Length == 2 && int.TryParse(kv[0], out var type) && decimal.TryParse(kv[1], out var value))
|
||||
{
|
||||
rewards.Add((type, value));
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放单个奖励
|
||||
/// </summary>
|
||||
private async Task<string> GrantSingleRewardAsync(int userId, int rewardType, decimal value, string source)
|
||||
{
|
||||
var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
if (user == null)
|
||||
return string.Empty;
|
||||
|
||||
switch (rewardType)
|
||||
{
|
||||
case 1: // 钻石 (Money2)
|
||||
user.Money2 = (user.Money2 ?? 0) + value;
|
||||
// 记录钻石变动
|
||||
_dbContext.ProfitMoney2s.Add(new ProfitMoney2
|
||||
{
|
||||
UserId = userId,
|
||||
ChangeMoney = value,
|
||||
Money = user.Money2 ?? 0,
|
||||
Type = 1,
|
||||
Content = source,
|
||||
ShareUid = 0,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
return $"钻石+{value}";
|
||||
|
||||
case 2: // UU币/余额 (Money)
|
||||
user.Money += value;
|
||||
// 记录余额变动
|
||||
_dbContext.ProfitMoneys.Add(new ProfitMoney
|
||||
{
|
||||
UserId = userId,
|
||||
ChangeMoney = value,
|
||||
Money = user.Money,
|
||||
Type = 1,
|
||||
Content = source,
|
||||
ShareUid = 0,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
return $"余额+{value}";
|
||||
|
||||
case 3: // 哈尼券/达达券 - 暂不支持,记录到积分
|
||||
case 4: // 积分 (Integral)
|
||||
user.Integral += value;
|
||||
// 记录积分变动
|
||||
_dbContext.ProfitIntegrals.Add(new ProfitIntegral
|
||||
{
|
||||
UserId = userId,
|
||||
ChangeMoney = value,
|
||||
Money = user.Integral,
|
||||
Type = 1,
|
||||
Content = source,
|
||||
ShareUid = 0,
|
||||
CreatedAt = DateTime.Now
|
||||
});
|
||||
return rewardType == 3 ? $"哈尼券+{value}" : $"积分+{value}";
|
||||
|
||||
default:
|
||||
_logger.LogWarning("未知的奖励类型: Type={Type}", rewardType);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> IsOrderProcessedAsync(string orderNo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -776,6 +776,11 @@ public static class OrderAttachType
|
|||
/// 发货运费
|
||||
/// </summary>
|
||||
public const string OrderListSend = "order_list_send";
|
||||
|
||||
/// <summary>
|
||||
/// 钻石商品订单
|
||||
/// </summary>
|
||||
public const string OrderProduct = "order_product";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user