diff --git a/honey_box/common/env.js b/honey_box/common/env.js index 6ee2ac79..d004875c 100644 --- a/honey_box/common/env.js +++ b/honey_box/common/env.js @@ -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', diff --git a/server/HoneyBox/src/HoneyBox.Api/Controllers/PayController.cs b/server/HoneyBox/src/HoneyBox.Api/Controllers/PayController.cs index 37f4f96b..fc6646bf 100644 --- a/server/HoneyBox/src/HoneyBox.Api/Controllers/PayController.cs +++ b/server/HoneyBox/src/HoneyBox.Api/Controllers/PayController.cs @@ -18,18 +18,84 @@ public class PayController : ControllerBase { private readonly IWechatPayService _wechatPayService; private readonly IRechargeService _rechargeService; + private readonly IPaymentNotifyService _paymentNotifyService; private readonly ILogger _logger; public PayController( IWechatPayService wechatPayService, IRechargeService rechargeService, + IPaymentNotifyService paymentNotifyService, ILogger logger) { _wechatPayService = wechatPayService; _rechargeService = rechargeService; + _paymentNotifyService = paymentNotifyService; _logger = logger; } + /// + /// 微信支付回调接口(兼容旧路由) + /// POST /api/pay/notify + /// 接收微信支付结果通知,处理订单状态更新 + /// + [HttpPost("pay/notify")] + public async Task 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 ?? "", "application/xml"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "处理微信支付回调异常"); + + // 返回成功响应,避免微信重复通知 + var successResponse = ""; + return Content(successResponse, "application/xml"); + } + } + /// /// 微信支付统一下单接口 /// POST /api/pay diff --git a/server/HoneyBox/src/HoneyBox.Core/Services/PaymentNotifyService.cs b/server/HoneyBox/src/HoneyBox.Core/Services/PaymentNotifyService.cs index cace7fc3..bbbe17d9 100644 --- a/server/HoneyBox/src/HoneyBox.Core/Services/PaymentNotifyService.cs +++ b/server/HoneyBox/src/HoneyBox.Core/Services/PaymentNotifyService.cs @@ -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 } } + /// + /// 处理钻石商品订单 + /// + /// 订单号 + /// 用户ID + /// 是否处理成功 + public async Task 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; + } + } + + /// + /// 发放钻石商品奖励 + /// + private async Task GrantDiamondRewardsAsync(int userId, DiamondProduct product, bool isFirstCharge) + { + var rewardLogs = new List(); + + 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); + } + + /// + /// 解析奖励配置字符串 + /// + 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; + } + + /// + /// 发放单个奖励 + /// + private async Task 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; + } + } + /// public async Task IsOrderProcessedAsync(string orderNo) { diff --git a/server/HoneyBox/src/HoneyBox.Model/Models/Payment/PaymentModels.cs b/server/HoneyBox/src/HoneyBox.Model/Models/Payment/PaymentModels.cs index 71eaaaaf..8cf22c90 100644 --- a/server/HoneyBox/src/HoneyBox.Model/Models/Payment/PaymentModels.cs +++ b/server/HoneyBox/src/HoneyBox.Model/Models/Payment/PaymentModels.cs @@ -776,6 +776,11 @@ public static class OrderAttachType /// 发货运费 /// public const string OrderListSend = "order_list_send"; + + /// + /// 钻石商品订单 + /// + public const string OrderProduct = "order_product"; } #endregion