diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs
index 2aa15dd..05b5263 100644
--- a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs
+++ b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Options;
namespace MiAssessment.Core.Services;
///
-/// ��֧������ʵ��
+/// 微信支付服务实现
///
public class WechatPayService : IWechatPayService
{
@@ -29,29 +29,29 @@ public class WechatPayService : IWechatPayService
private readonly Lazy? _v3ServiceLazy;
///
- /// ��ͳһ�µ�API��ַ
+ /// 微信统一下单API地址
///
private const string UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
///
- /// �ŷ���֪ͨAPI��ַ
+ /// 微信发货通知API地址
///
private const string SHIPPING_NOTIFY_URL = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info";
///
- /// ����ʧ�ܶ���Redis��ǰ
+ /// 发货失败订单Redis键前缀
///
private const string FAILED_SHIPPING_KEY_PREFIX = "post_order:";
///
- /// ����ʧ�ܶ���Redis����ʱ�䣨3�죩
+ /// 发货失败订单Redis过期时间(3天)
///
private static readonly TimeSpan FAILED_SHIPPING_EXPIRY = TimeSpan.FromDays(3);
///
- /// �����û�֧�����֣�
+ /// 测试用户支付金额(分)
///
- private const int TEST_USER_PAY_AMOUNT = 1; // 0.01Ԫ = 1��
+ private const int TEST_USER_PAY_AMOUNT = 1; // 0.01元 = 1分
public WechatPayService(
MiAssessmentDbContext dbContext,
@@ -80,58 +80,58 @@ public class WechatPayService : IWechatPayService
{
try
{
- _logger.LogInformation("��ʼ������֧������: OrderNo={OrderNo}, UserId={UserId}, Amount={Amount}",
+ _logger.LogInformation("开始创建微信支付订单: OrderNo={OrderNo}, UserId={UserId}, Amount={Amount}",
request.OrderNo, request.UserId, request.Amount);
- // 1. ���ݶ����Ż�ȡ�̻����ã����֧���汾
+ // 1. 根据订单号获取商户配置,判断支付版本
var merchantConfig = _configService.GetMerchantByOrderNo(request.OrderNo);
- // 2. �汾·�ɣ��������Ϊ V3 �� V3 ������ã���ʹ�� V3 ����
+ // 2. 版本路由:如果配置为 V3 且 V3 服务可用,则使用 V3 流程
if (merchantConfig.PayVersion == "V3" && _v3ServiceLazy != null)
{
- _logger.LogInformation("�̻�����Ϊ V3 �汾��·�ɵ� V3 ����: MchId={MchId}", merchantConfig.MchId);
+ _logger.LogInformation("商户配置为 V3 版本,路由到 V3 流程: MchId={MchId}", merchantConfig.MchId);
return await _v3ServiceLazy.Value.CreateJsapiOrderAsync(request);
}
- // 3. ʹ�� V2 ����
- _logger.LogDebug("ʹ�� V2 ֧������: MchId={MchId}, PayVersion={PayVersion}",
+ // 3. 使用 V2 流程
+ _logger.LogDebug("使用 V2 支付流程: MchId={MchId}, PayVersion={PayVersion}",
merchantConfig.MchId, merchantConfig.PayVersion);
- // 4. ��ȡ�û���Ϣ��OpenId
+ // 4. 获取用户信息和OpenId
var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == request.UserId);
if (user == null)
{
- _logger.LogWarning("�û�������: UserId={UserId}", request.UserId);
+ _logger.LogWarning("用户不存在: UserId={UserId}", request.UserId);
return new WechatPayResult
{
Status = 0,
- Msg = "�û�������"
+ Msg = "用户不存在"
};
}
var openId = string.IsNullOrEmpty(request.OpenId) ? user.OpenId : request.OpenId;
if (string.IsNullOrEmpty(openId))
{
- _logger.LogWarning("�û�OpenIdΪ��: UserId={UserId}", request.UserId);
+ _logger.LogWarning("用户OpenId为空: UserId={UserId}", request.UserId);
return new WechatPayResult
{
Status = 0,
- Msg = "�û�OpenId������"
+ Msg = "用户OpenId不存在"
};
}
- // 5. ʹ���ѻ�ȡ���̻�����
+ // 5. 使用已获取的商户配置
var appId = merchantConfig.AppId;
var mchId = merchantConfig.MchId;
var merchantKey = merchantConfig.Key;
- _logger.LogDebug("ʹ���̻�����: MchId={MchId}, AppId={AppId}", mchId, appId);
+ _logger.LogDebug("使用商户配置: MchId={MchId}, AppId={AppId}", mchId, appId);
- // 3. ��������ַ���
+ // 6. 生成随机字符串
var nonceStr = GenerateNonceStr();
var callbackNonceStr = GenerateNonceStr();
- // 4. ���ɻص�֪ͨURL
+ // 7. 生成回调通知URL
var notifyUrl = GenerateNotifyUrl(request.Attach, request.UserId, request.OrderNo, callbackNonceStr);
// 验证回调通知URL
@@ -141,17 +141,17 @@ public class WechatPayService : IWechatPayService
return new WechatPayResult { Status = 0, Msg = "支付回调URL未配置,请联系管理员在后台配置" };
}
- // 5. ����֪ͨ��¼��order_notify��
+ // 8. 保存通知记录(order_notify)
await SaveOrderNotifyAsync(request.OrderNo, notifyUrl, callbackNonceStr, request.Amount, request.Attach, openId);
- // 6. ����ͳһ�µ�����
+ // 9. 构建统一下单参数
var body = TruncateBody(request.Body, 30);
- var totalFee = (int)Math.Round(request.Amount * 100); // ת��Ϊ��
+ var totalFee = (int)Math.Round(request.Amount * 100); // 转换为分
- // ���Ի����£�IsTest=2 ���û�֧������Ϊ 0.01 Ԫ
+ // 测试环境下,IsTest=2 的用户支付金额为 0.01 元
if (_appSettings.IsTestEnvironment && user.IsTest == 2)
{
- _logger.LogInformation("�����û�֧��������: UserId={UserId}, ԭ���={OriginalAmount}��, ����Ϊ={TestAmount}��",
+ _logger.LogInformation("测试用户支付金额调整: UserId={UserId}, 原金额={OriginalAmount}分, 调整为={TestAmount}分",
request.UserId, totalFee, TEST_USER_PAY_AMOUNT);
totalFee = TEST_USER_PAY_AMOUNT;
}
@@ -171,64 +171,64 @@ public class WechatPayService : IWechatPayService
{ "openid", openId }
};
- // 7. ����ǩ��
+ // 10. 生成签名
unifiedOrderParams["sign"] = MakeSign(unifiedOrderParams, merchantKey);
- // 8. ת��ΪXML��������API
+ // 11. 转换为XML并调用微信API
var requestXml = DictionaryToXml(unifiedOrderParams);
- _logger.LogDebug("ͳһ�µ�����XML: {Xml}", requestXml);
+ _logger.LogDebug("统一下单请求XML: {Xml}", requestXml);
var responseXml = await PostXmlAsync(UNIFIED_ORDER_URL, requestXml);
- _logger.LogDebug("ͳһ�µ���ӦXML: {Xml}", responseXml);
+ _logger.LogDebug("统一下单响应XML: {Xml}", responseXml);
- // 9. ������Ӧ
+ // 12. 解析响应
var responseData = XmlToDictionary(responseXml);
if (responseData == null)
{
- _logger.LogError("��������Ӧʧ��");
+ _logger.LogError("解析微信响应失败");
return new WechatPayResult
{
Status = 0,
- Msg = "������ϣ����Ժ�����(������Ӧʧ��)"
+ Msg = "网络故障,请稍后重试(解析响应失败)"
};
}
- // 10. ��鷵�ؽ��
+ // 13. 检查返回结果
if (!responseData.TryGetValue("return_code", out var returnCode) || returnCode != "SUCCESS")
{
- var returnMsg = responseData.GetValueOrDefault("return_msg", "δ֪����");
- _logger.LogWarning("ͳһ�µ�ʧ��: return_code={ReturnCode}, return_msg={ReturnMsg}", returnCode, returnMsg);
+ var returnMsg = responseData.GetValueOrDefault("return_msg", "未知错误");
+ _logger.LogWarning("统一下单失败: return_code={ReturnCode}, return_msg={ReturnMsg}", returnCode, returnMsg);
return new WechatPayResult
{
Status = 0,
- Msg = $"������ϣ����Ժ�����({returnMsg})"
+ Msg = $"网络故障,请稍后重试({returnMsg})"
};
}
if (!responseData.TryGetValue("result_code", out var resultCode) || resultCode != "SUCCESS")
{
var errCode = responseData.GetValueOrDefault("err_code", "");
- var errCodeDes = responseData.GetValueOrDefault("err_code_des", "δ֪����");
- _logger.LogWarning("ͳһ�µ�ҵ��ʧ��: err_code={ErrCode}, err_code_des={ErrCodeDes}", errCode, errCodeDes);
+ var errCodeDes = responseData.GetValueOrDefault("err_code_des", "未知错误");
+ _logger.LogWarning("统一下单业务失败: err_code={ErrCode}, err_code_des={ErrCodeDes}", errCode, errCodeDes);
return new WechatPayResult
{
Status = 0,
- Msg = $"֧��ʧ��({GetErrorMessage(errCode, errCodeDes)})"
+ Msg = $"支付失败({GetErrorMessage(errCode, errCodeDes)})"
};
}
- // 11. ��ȡprepay_id
+ // 14. 获取prepay_id
if (!responseData.TryGetValue("prepay_id", out var prepayId) || string.IsNullOrEmpty(prepayId))
{
- _logger.LogError("ͳһ�µ��ɹ���prepay_idΪ��");
+ _logger.LogError("统一下单成功但prepay_id为空");
return new WechatPayResult
{
Status = 0,
- Msg = "������ϣ����Ժ�����(prepay_idΪ��)"
+ Msg = "网络故障,请稍后重试(prepay_id为空)"
};
}
- // 12. �������ظ�ǰ�˵�֧������
+ // 15. 构建返回给前端的支付参数
var timeStamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var payNonceStr = GenerateNonceStr();
@@ -241,10 +241,10 @@ public class WechatPayService : IWechatPayService
{ "signType", "MD5" }
};
- // 13. ����֧��ǩ��
+ // 16. 生成支付签名
var paySign = MakeSign(payParams, merchantKey);
- _logger.LogInformation("��֧�����������ɹ�: OrderNo={OrderNo}, PrepayId={PrepayId}", request.OrderNo, prepayId);
+ _logger.LogInformation("微信支付订单创建成功: OrderNo={OrderNo}, PrepayId={PrepayId}", request.OrderNo, prepayId);
return new WechatPayResult
{
@@ -264,17 +264,17 @@ public class WechatPayService : IWechatPayService
}
catch (Exception ex)
{
- _logger.LogError(ex, "������֧�������쳣: OrderNo={OrderNo}", request.OrderNo);
+ _logger.LogError(ex, "创建微信支付订单异常: OrderNo={OrderNo}", request.OrderNo);
return new WechatPayResult
{
Status = 0,
- Msg = "ϵͳ�������Ժ�����"
+ Msg = "系统错误,请稍后重试"
};
}
}
///
- /// ���ɻص�֪ͨURL
+ /// 生成回调通知URL
///
private string GenerateNotifyUrl(string attach, long userId, string orderNo, string nonceStr)
{
@@ -288,7 +288,7 @@ public class WechatPayService : IWechatPayService
}
///
- /// ���涩��֪ͨ��¼
+ /// 保存订单通知记录
///
private async Task SaveOrderNotifyAsync(string orderNo, string notifyUrl, string nonceStr, decimal amount, string attach, string openId)
{
@@ -310,20 +310,19 @@ public class WechatPayService : IWechatPayService
_dbContext.OrderNotifies.Add(orderNotify);
await _dbContext.SaveChangesAsync();
- _logger.LogDebug("���涩��֪ͨ��¼: OrderNo={OrderNo}, NotifyUrl={NotifyUrl}", orderNo, notifyUrl);
+ _logger.LogDebug("保存订单通知记录: OrderNo={OrderNo}, NotifyUrl={NotifyUrl}", orderNo, notifyUrl);
}
///
- /// �ض���Ʒ��������������ȣ�
+ /// 截断商品描述(微信有最大长度限制)
///
private static string TruncateBody(string body, int maxLength)
{
if (string.IsNullOrEmpty(body))
{
- return "��Ʒ����";
+ return "商品购买";
}
- // ʹ���ַ����������ֽ���
if (body.Length <= maxLength)
{
return body;
@@ -333,7 +332,7 @@ public class WechatPayService : IWechatPayService
}
///
- /// ����32λ����ַ���
+ /// 生成32位随机字符串
///
private static string GenerateNonceStr(int length = 32)
{
@@ -350,17 +349,17 @@ public class WechatPayService : IWechatPayService
}
///
- /// ��ȡ�ͻ���IP
+ /// 获取客户端IP
///
private static string GetClientIp()
{
- // ��ʵ�ʻ�����Ӧ�ô�HttpContext��ȡ
- // ���ﷵ��Ĭ��ֵ��ʵ��ʹ��ʱ��Ҫͨ������ע���ȡIHttpContextAccessor
+ // 在实际环境中应该从HttpContext获取
+ // 这里返回默认值,实际使用时需要通过依赖注入获取IHttpContextAccessor
return "127.0.0.1";
}
///
- /// ���ֵ�ת��ΪXML
+ /// 将字典转换为XML
///
private static string DictionaryToXml(Dictionary parameters)
{
@@ -374,7 +373,7 @@ public class WechatPayService : IWechatPayService
continue;
}
- // �������Ͳ���ҪCDATA
+ // 数字类型不需要CDATA
if (int.TryParse(kvp.Value, out _) || decimal.TryParse(kvp.Value, out _))
{
sb.Append($"<{kvp.Key}>{kvp.Value}{kvp.Key}>");
@@ -390,7 +389,7 @@ public class WechatPayService : IWechatPayService
}
///
- /// ��XMLת��Ϊ�ֵ�
+ /// 将XML转换为字典
///
private static Dictionary? XmlToDictionary(string xml)
{
@@ -428,7 +427,7 @@ public class WechatPayService : IWechatPayService
}
///
- /// ����XML POST����
+ /// 发送XML POST请求
///
private async Task PostXmlAsync(string url, string xml, int timeout = 30)
{
@@ -444,34 +443,34 @@ public class WechatPayService : IWechatPayService
}
catch (Exception ex)
{
- _logger.LogError(ex, "����XML����ʧ��: Url={Url}", url);
+ _logger.LogError(ex, "发送XML请求失败: Url={Url}", url);
throw;
}
}
///
- /// ��ȡ������Ϣ
+ /// 获取错误信息
///
private static string GetErrorMessage(string errCode, string errCodeDes)
{
var errorMessages = new Dictionary
{
- { "NOAUTH", "�̻�δ��ͨ�˽ӿ�Ȩ��" },
- { "NOTENOUGH", "�û��ʺ�����" },
- { "ORDERNOTEXIST", "����������" },
- { "ORDERPAID", "�̻�������֧���������ظ�����" },
- { "ORDERCLOSED", "��ǰ�����ѹرգ���֧��" },
- { "SYSTEMERROR", "ϵͳ����!ϵͳ��ʱ" },
- { "APPID_NOT_EXIST", "������ȱ��APPID" },
- { "MCHID_NOT_EXIST", "������ȱ��MCHID" },
- { "APPID_MCHID_NOT_MATCH", "appid��mch_id��ƥ��" },
- { "LACK_PARAMS", "ȱ�ٱ�Ҫ���������" },
- { "OUT_TRADE_NO_USED", "ͬһ�ʽ��ײ��ܶ���ύ" },
- { "SIGNERROR", "����ǩ���������ȷ" },
- { "XML_FORMAT_ERROR", "XML��ʽ����" },
- { "REQUIRE_POST_METHOD", "δʹ��post���ݲ���" },
- { "POST_DATA_EMPTY", "post���ݲ���Ϊ��" },
- { "NOT_UTF8", "δʹ��ָ�������ʽ" }
+ { "NOAUTH", "商户未开通此接口权限" },
+ { "NOTENOUGH", "用户账号余额不足" },
+ { "ORDERNOTEXIST", "订单号不存在" },
+ { "ORDERPAID", "商户订单已支付,请勿重复提交" },
+ { "ORDERCLOSED", "当前订单已关闭,无法支付" },
+ { "SYSTEMERROR", "系统错误!系统超时" },
+ { "APPID_NOT_EXIST", "参数中缺少APPID" },
+ { "MCHID_NOT_EXIST", "参数中缺少MCHID" },
+ { "APPID_MCHID_NOT_MATCH", "appid和mch_id不匹配" },
+ { "LACK_PARAMS", "缺少必要的请求参数" },
+ { "OUT_TRADE_NO_USED", "同一笔交易不能多次提交" },
+ { "SIGNERROR", "参数签名结果不正确" },
+ { "XML_FORMAT_ERROR", "XML格式错误" },
+ { "REQUIRE_POST_METHOD", "未使用post传递参数" },
+ { "POST_DATA_EMPTY", "post数据不能为空" },
+ { "NOT_UTF8", "未使用指定编码格式" }
};
if (!string.IsNullOrEmpty(errCode) && errorMessages.TryGetValue(errCode, out var message))
@@ -487,7 +486,7 @@ public class WechatPayService : IWechatPayService
{
if (string.IsNullOrEmpty(sign))
{
- _logger.LogWarning("ǩ����֤ʧ�ܣ�ǩ��Ϊ��");
+ _logger.LogWarning("签名验证失败:签名为空");
return false;
}
@@ -496,7 +495,7 @@ public class WechatPayService : IWechatPayService
if (!isValid)
{
- _logger.LogWarning("ǩ����֤ʧ�ܣ�����ǩ��={CalculatedSign}������ǩ��={Sign}", calculatedSign, sign);
+ _logger.LogWarning("签名验证失败:计算签名={CalculatedSign},传入签名={Sign}", calculatedSign, sign);
}
return isValid;
@@ -505,35 +504,35 @@ public class WechatPayService : IWechatPayService
///
public string MakeSign(Dictionary parameters, string? merchantKey = null)
{
- // ��ȡ�̻���Կ
+ // 获取商户密钥
var key = merchantKey ?? _settings.DefaultMerchant.Key;
- // ǩ������һ�����ֵ����������������ASCII���С����
- // ���˵���ֵ��sign�ֶ�
+ // 签名步骤一:将字典按参数名ASCII码从小到大排序
+ // 过滤掉空值和sign字段
var sortedParams = parameters
.Where(p => !string.IsNullOrEmpty(p.Value) && !string.Equals(p.Key, "sign", StringComparison.OrdinalIgnoreCase))
.OrderBy(p => p.Key, StringComparer.Ordinal)
.ToList();
- // ǩ���������������ƴ��ΪURL��ʽ key=value&key=value
+ // 签名步骤二:将参数拼接为URL格式 key=value&key=value
var urlParams = ToUrlParams(sortedParams);
- // ǩ������������string�����KEY
+ // 签名步骤三:在string后面加上KEY
var signString = $"{urlParams}&key={key}";
- // ǩ�������ģ�MD5����
+ // 签名步骤四:MD5加密
using var md5 = MD5.Create();
var hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(signString));
- // ǩ�������壺�����ַ�תΪ��д
+ // 签名步骤五:所有字符转为大写
return BitConverter.ToString(hashBytes).Replace("-", "").ToUpper();
}
///
- /// ������ƴ��ΪURL��ʽ: key=value&key=value
+ /// 将参数拼接为URL格式: key=value&key=value
///
- /// ������IJ����б�
- /// URL��ʽ�ַ���
+ /// 排序后的参数列表
+ /// URL格式字符串
private static string ToUrlParams(IEnumerable> parameters)
{
var parts = parameters.Select(p => $"{p.Key}={p.Value}");
@@ -541,10 +540,10 @@ public class WechatPayService : IWechatPayService
}
///
- /// ���ݶ����Ż�ȡ�̻���Կ
+ /// 根据订单号获取商户密钥
///
- /// ������
- /// �̻���Կ
+ /// 订单号
+ /// 商户密钥
public string GetMerchantKeyByOrderNo(string orderNo)
{
var merchant = GetMerchantByOrderNo(orderNo);
@@ -552,13 +551,13 @@ public class WechatPayService : IWechatPayService
}
///
- /// ��֤�Żص�ǩ��
+ /// 验证微信回调签名
///
- /// �ص�����
- /// �Ƿ���֤ͨ��
+ /// 回调数据
+ /// 是否验证通过
public bool VerifyNotifySign(WechatNotifyData notifyData)
{
- // �ӻص����ݹ��������ֵ�
+ // 从回调数据构建参数字典
var parameters = new Dictionary
{
{ "return_code", notifyData.ReturnCode },
@@ -579,7 +578,7 @@ public class WechatPayService : IWechatPayService
{ "time_end", notifyData.TimeEnd }
};
- // ���ӿ�ѡ�ֶ�
+ // 添加可选字段
if (!string.IsNullOrEmpty(notifyData.ErrCode))
parameters["err_code"] = notifyData.ErrCode;
if (!string.IsNullOrEmpty(notifyData.ErrCodeDes))
@@ -587,34 +586,34 @@ public class WechatPayService : IWechatPayService
if (!string.IsNullOrEmpty(notifyData.SignType))
parameters["sign_type"] = notifyData.SignType;
- // �����̻��Ż�ȡ��Ӧ����Կ
+ // 根据商户号获取对应的密钥
var merchantKey = GetMerchantKeyByMchId(notifyData.MchId);
return VerifySign(parameters, notifyData.Sign, merchantKey);
}
///
- /// �����̻��Ż�ȡ�̻���Կ
+ /// 根据商户号获取商户密钥
///
- /// �̻���
- /// �̻���Կ
+ /// 商户号
+ /// 商户密钥
private string GetMerchantKeyByMchId(string mchId)
{
- // �ȴ����õ��̻��б��в���
+ // 先从配置的商户列表中查找
var merchant = _settings.Merchants.FirstOrDefault(m => m.MchId == mchId);
if (merchant != null)
{
return merchant.Key;
}
- // ���û�ҵ������Ĭ���̻�
+ // 如果没找到,检查默认商户
if (_settings.DefaultMerchant.MchId == mchId)
{
return _settings.DefaultMerchant.Key;
}
- // ����Ĭ���̻���Կ
- _logger.LogWarning("δ�ҵ��̻��� {MchId} �����ã�ʹ��Ĭ���̻���Կ", mchId);
+ // 返回默认商户密钥
+ _logger.LogWarning("未找到商户号 {MchId} 的配置,使用默认商户密钥", mchId);
return _settings.DefaultMerchant.Key;
}
@@ -623,49 +622,49 @@ public class WechatPayService : IWechatPayService
{
try
{
- _logger.LogInformation("��ʼ���Ͷ�������֪ͨ: OrderNo={OrderNo}, OpenId={OpenId}",
+ _logger.LogInformation("开始发送订单发货通知: OrderNo={OrderNo}, OpenId={OpenId}",
request.OrderNo, request.OpenId);
- // 1. ���ݶ����Ż�ȡ�̻�����
+ // 1. 根据订单号获取商户配置
var merchantConfig = _configService.GetMerchantByOrderNo(request.OrderNo);
var mchId = merchantConfig.MchId;
var appId = merchantConfig.AppId;
- _logger.LogDebug("ʹ���̻�����: MchId={MchId}, AppId={AppId}", mchId, appId);
+ _logger.LogDebug("使用商户配置: MchId={MchId}, AppId={AppId}", mchId, appId);
- // 2. ��ȡaccess_token
+ // 2. 获取access_token
var accessToken = await _wechatService.GetAccessTokenAsync(appId);
if (string.IsNullOrEmpty(accessToken))
{
- _logger.LogError("��ȡaccess_tokenʧ��: AppId={AppId}", appId);
+ _logger.LogError("获取access_token失败: AppId={AppId}", appId);
- // �������Զ���
- await SaveFailedShippingOrderAsync(request, merchantConfig, -1, "��ȡaccess_tokenʧ��");
+ // 保存到自动重试队列
+ await SaveFailedShippingOrderAsync(request, merchantConfig, -1, "获取access_token失败");
return new OrderShippingNotifyResult
{
Success = false,
ErrCode = -1,
- ErrMsg = "��ȡaccess_tokenʧ��",
+ ErrMsg = "获取access_token失败",
QueuedForRetry = true
};
}
- // 3. ��������֪ͨ��Ϣ
+ // 3. 构建发货通知消息
var itemDesc = GetShippingItemDesc(request);
- // 4. �����������
+ // 4. 构建请求参数
var uploadTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00";
var requestBody = new
{
order_key = new
{
- order_number_type = 1, // ʹ���̻�������
+ order_number_type = 1, // 使用商户订单号
mchid = mchId,
out_trade_no = request.OrderNo
},
- logistics_type = request.LogisticsType, // �������ͣ�4=������Ʒ
- delivery_mode = request.DeliveryMode, // ����ģʽ��1=ͳһ����
+ logistics_type = request.LogisticsType, // 物流类型,4=虚拟商品
+ delivery_mode = request.DeliveryMode, // 发货模式,1=统一发货
shipping_list = new[]
{
new
@@ -682,32 +681,32 @@ public class WechatPayService : IWechatPayService
var requestJson = JsonSerializer.Serialize(requestBody);
- // 5. ��¼������־
- _logger.LogDebug("����֪ͨ����: OrderNo={OrderNo}, MchId={MchId}, Request={Request}",
+ // 5. 记录请求日志
+ _logger.LogDebug("发货通知请求: OrderNo={OrderNo}, MchId={MchId}, Request={Request}",
request.OrderNo, mchId, requestJson);
- // 6. ������API
+ // 6. 调用微信API
var requestUrl = $"{SHIPPING_NOTIFY_URL}?access_token={accessToken}";
var content = new StringContent(requestJson, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(requestUrl, content);
var responseContent = await response.Content.ReadAsStringAsync();
- // 7. ��¼��Ӧ��־
- _logger.LogDebug("����֪ͨ��Ӧ: OrderNo={OrderNo}, Response={Response}",
+ // 7. 记录响应日志
+ _logger.LogDebug("发货通知响应: OrderNo={OrderNo}, Response={Response}",
request.OrderNo, responseContent);
- // 8. ������Ӧ
+ // 8. 解析响应
using var jsonDoc = JsonDocument.Parse(responseContent);
var root = jsonDoc.RootElement;
var errCode = root.TryGetProperty("errcode", out var errCodeProp) ? errCodeProp.GetInt32() : -1;
var errMsg = root.TryGetProperty("errmsg", out var errMsgProp) ? errMsgProp.GetString() ?? "unknown" : "unknown";
- // 9. �жϽ��
+ // 9. 判断结果
if (errCode == 0 && errMsg == "ok")
{
- _logger.LogInformation("����֪ͨ�ɹ�: OrderNo={OrderNo}", request.OrderNo);
+ _logger.LogInformation("发货通知成功: OrderNo={OrderNo}", request.OrderNo);
return new OrderShippingNotifyResult
{
Success = true,
@@ -717,10 +716,10 @@ public class WechatPayService : IWechatPayService
}
else
{
- _logger.LogWarning("����֪ͨʧ��: OrderNo={OrderNo}, ErrCode={ErrCode}, ErrMsg={ErrMsg}",
+ _logger.LogWarning("发货通知失败: OrderNo={OrderNo}, ErrCode={ErrCode}, ErrMsg={ErrMsg}",
request.OrderNo, errCode, errMsg);
- // �������Զ���
+ // 保存到自动重试队列
await SaveFailedShippingOrderAsync(request, merchantConfig, errCode, errMsg);
return new OrderShippingNotifyResult
@@ -734,9 +733,9 @@ public class WechatPayService : IWechatPayService
}
catch (Exception ex)
{
- _logger.LogError(ex, "����֪ͨ�쳣: OrderNo={OrderNo}", request.OrderNo);
+ _logger.LogError(ex, "发货通知异常: OrderNo={OrderNo}", request.OrderNo);
- // ���Դ������Զ���
+ // 尝试保存到自动重试队列
try
{
var merchantConfig = _configService.GetMerchantByOrderNo(request.OrderNo);
@@ -744,7 +743,7 @@ public class WechatPayService : IWechatPayService
}
catch (Exception saveEx)
{
- _logger.LogError(saveEx, "����ʧ�ܶ��������Զ���ʱ��������: OrderNo={OrderNo}", request.OrderNo);
+ _logger.LogError(saveEx, "保存失败订单到自动重试队列时发生错误: OrderNo={OrderNo}", request.OrderNo);
}
return new OrderShippingNotifyResult
@@ -758,41 +757,45 @@ public class WechatPayService : IWechatPayService
}
///
- /// ��ȡ������Ʒ����
+ /// 获取发货商品描述
///
private static string GetShippingItemDesc(OrderShippingNotifyRequest request)
{
- // ���������ָ����������ֱ��ʹ��
+ // 如果请求中指定了描述,直接使用
if (!string.IsNullOrEmpty(request.ItemDesc))
{
return request.ItemDesc;
}
- // ���ݶ���ǰ�ж���Ϣ����
+ // 根据订单前缀判断消息内容
if (request.OrderNo.StartsWith("FH_"))
{
- // ��������
- return "�����������Ʒ���ڴ��������ϵ�ͷ���ȡ������Ϣ";
+ // 发货订单
+ return "您购买的实物商品正在处理中,请联系客服获取物流信息";
}
else
{
- // ������Ʒ����������ȣ�
- return "��������IJ��������ѿ�ͨ������С�����в鿴";
+ // 虚拟商品(测评服务等)
+ return "您购买的测评服务已开通,请在小程序中查看";
}
}
///
- /// ���淢��ʧ�ܵĶ�����Redis���Զ���
+ /// 保存发货失败订单到Redis自动重试队列
///
+ /// 发货通知请求
+ /// 商户配置
+ /// 错误码
+ /// 错误信息
private async Task SaveFailedShippingOrderAsync(
- OrderShippingNotifyRequest request,
+ OrderShippingNotifyRequest request,
WechatPayMerchantConfig merchantConfig,
- int errorCode,
- string errorMsg)
+ int errCode,
+ string errMsg)
{
try
{
- var key = $"{FAILED_SHIPPING_KEY_PREFIX}{request.OrderNo}";
+ var itemDesc = GetShippingItemDesc(request);
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var failedOrder = new FailedShippingOrderData
@@ -801,38 +804,44 @@ public class WechatPayService : IWechatPayService
AppId = merchantConfig.AppId,
OrderNo = request.OrderNo,
MchId = merchantConfig.MchId,
- ItemDesc = GetShippingItemDesc(request),
- ErrorCode = errorCode,
- ErrorMsg = errorMsg,
+ ItemDesc = itemDesc,
+ ErrorCode = errCode,
+ ErrorMsg = errMsg,
RetryCount = 0,
LastRetryTime = now,
CreateTime = now
};
+ var redisKey = $"{FAILED_SHIPPING_KEY_PREFIX}{request.OrderNo}";
var json = JsonSerializer.Serialize(failedOrder);
- await _redisService.SetStringAsync(key, json, FAILED_SHIPPING_EXPIRY);
- _logger.LogInformation("�ѽ�����ʧ�ܶ����������Զ���: OrderNo={OrderNo}, Key={Key}",
- request.OrderNo, key);
+ await _redisService.SetAsync(redisKey, json, FAILED_SHIPPING_EXPIRY);
+
+ _logger.LogInformation("已保存发货失败订单到重试队列: OrderNo={OrderNo}, ErrCode={ErrCode}, ErrMsg={ErrMsg}",
+ request.OrderNo, errCode, errMsg);
}
catch (Exception ex)
{
- _logger.LogError(ex, "���淢��ʧ�ܶ�����Redisʱ��������: OrderNo={OrderNo}", request.OrderNo);
- throw;
+ _logger.LogError(ex, "保存发货失败订单到Redis异常: OrderNo={OrderNo}", request.OrderNo);
}
}
///
public WechatPayMerchantConfig GetMerchantByOrderNo(string orderNo)
{
- // ʹ�����÷����ȡ�̻�����
return _configService.GetMerchantByOrderNo(orderNo);
}
///
public WechatNotifyData ParseNotifyXml(string xmlData)
{
- var result = new WechatNotifyData();
+ var notifyData = new WechatNotifyData();
+
+ if (string.IsNullOrEmpty(xmlData))
+ {
+ _logger.LogWarning("回调XML数据为空");
+ return notifyData;
+ }
try
{
@@ -840,43 +849,53 @@ public class WechatPayService : IWechatPayService
doc.LoadXml(xmlData);
var root = doc.DocumentElement;
- if (root == null) return result;
-
- result.ReturnCode = GetXmlNodeValue(root, "return_code");
- result.ReturnMsg = GetXmlNodeValue(root, "return_msg");
- result.ResultCode = GetXmlNodeValue(root, "result_code");
- result.ErrCode = GetXmlNodeValue(root, "err_code");
- result.ErrCodeDes = GetXmlNodeValue(root, "err_code_des");
- result.AppId = GetXmlNodeValue(root, "appid");
- result.MchId = GetXmlNodeValue(root, "mch_id");
- result.NonceStr = GetXmlNodeValue(root, "nonce_str");
- result.Sign = GetXmlNodeValue(root, "sign");
- result.SignType = GetXmlNodeValue(root, "sign_type");
- result.OpenId = GetXmlNodeValue(root, "openid");
- result.TradeType = GetXmlNodeValue(root, "trade_type");
- result.BankType = GetXmlNodeValue(root, "bank_type");
- result.FeeType = GetXmlNodeValue(root, "fee_type");
- result.TransactionId = GetXmlNodeValue(root, "transaction_id");
- result.OutTradeNo = GetXmlNodeValue(root, "out_trade_no");
- result.Attach = GetXmlNodeValue(root, "attach");
- result.TimeEnd = GetXmlNodeValue(root, "time_end");
-
- // ��������λ���֣�
- if (int.TryParse(GetXmlNodeValue(root, "total_fee"), out var totalFee))
+ if (root == null)
{
- result.TotalFee = totalFee;
+ _logger.LogWarning("回调XML根节点为空");
+ return notifyData;
}
- if (int.TryParse(GetXmlNodeValue(root, "cash_fee"), out var cashFee))
+
+ notifyData.ReturnCode = GetXmlNodeValue(root, "return_code");
+ notifyData.ReturnMsg = GetXmlNodeValue(root, "return_msg");
+ notifyData.ResultCode = GetXmlNodeValue(root, "result_code");
+ notifyData.ErrCode = GetXmlNodeValue(root, "err_code");
+ notifyData.ErrCodeDes = GetXmlNodeValue(root, "err_code_des");
+ notifyData.AppId = GetXmlNodeValue(root, "appid");
+ notifyData.MchId = GetXmlNodeValue(root, "mch_id");
+ notifyData.NonceStr = GetXmlNodeValue(root, "nonce_str");
+ notifyData.Sign = GetXmlNodeValue(root, "sign");
+ notifyData.SignType = GetXmlNodeValue(root, "sign_type");
+ notifyData.OpenId = GetXmlNodeValue(root, "openid");
+ notifyData.TradeType = GetXmlNodeValue(root, "trade_type");
+ notifyData.BankType = GetXmlNodeValue(root, "bank_type");
+ notifyData.FeeType = GetXmlNodeValue(root, "fee_type");
+ notifyData.TransactionId = GetXmlNodeValue(root, "transaction_id");
+ notifyData.OutTradeNo = GetXmlNodeValue(root, "out_trade_no");
+ notifyData.Attach = GetXmlNodeValue(root, "attach");
+ notifyData.TimeEnd = GetXmlNodeValue(root, "time_end");
+
+ // 解析金额字段
+ var totalFeeStr = GetXmlNodeValue(root, "total_fee");
+ if (int.TryParse(totalFeeStr, out var totalFee))
{
- result.CashFee = cashFee;
+ notifyData.TotalFee = totalFee;
}
+
+ var cashFeeStr = GetXmlNodeValue(root, "cash_fee");
+ if (int.TryParse(cashFeeStr, out var cashFee))
+ {
+ notifyData.CashFee = cashFee;
+ }
+
+ _logger.LogDebug("解析回调XML成功: OutTradeNo={OutTradeNo}, TransactionId={TransactionId}",
+ notifyData.OutTradeNo, notifyData.TransactionId);
}
catch (Exception ex)
{
- _logger.LogError(ex, "�����Żص�XMLʧ��: {XmlData}", xmlData);
+ _logger.LogError(ex, "解析回调XML异常");
}
- return result;
+ return notifyData;
}
///
@@ -885,6 +904,12 @@ public class WechatPayService : IWechatPayService
return $"";
}
+ ///
+ /// 获取XML节点值
+ ///
+ /// XML根节点
+ /// 节点名称
+ /// 节点值,不存在则返回空字符串
private static string GetXmlNodeValue(XmlElement root, string nodeName)
{
var node = root.SelectSingleNode(nodeName);