diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs index 3361c79..f240e2a 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs @@ -131,6 +131,12 @@ public class WeixinPayMerchant /// [JsonPropertyName("wechat_public_key_content")] public string? WechatPublicKeyContent { get; set; } + + /// + /// 支付回调通知URL + /// + [JsonPropertyName("notify_url")] + public string? NotifyUrl { get; set; } } diff --git a/server/MiAssessment/src/MiAssessment.Admin/Models/Config/WeixinPaySetting.cs b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/WeixinPaySetting.cs index 59c2c8e..bcc9d55 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/Models/Config/WeixinPaySetting.cs +++ b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/WeixinPaySetting.cs @@ -102,4 +102,10 @@ public class WeixinPayMerchant /// [JsonPropertyName("is_enabled")] public string? IsEnabled { get; set; } = "1"; + + /// + /// 支付回调通知URL + /// + [JsonPropertyName("notify_url")] + public string? NotifyUrl { get; set; } } diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts index 2f0cbcd..ea6bf21 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts @@ -139,6 +139,8 @@ export interface WeixinPayMerchant { cert_path?: string /** 是否启用 */ is_enabled?: string + /** 支付回调通知URL */ + notify_url?: string } /** diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/payment.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/payment.vue index f13d3d1..9040756 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/payment.vue +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/payment.vue @@ -76,6 +76,14 @@ + + + + + 微信支付结果回调通知地址,必须为外网可访问的HTTPS地址 + + + @@ -235,7 +243,8 @@ function addMerchant() { wechat_public_key_content: '', api_key: '', cert_path: '', - is_enabled: '1' + is_enabled: '1', + notify_url: '' }) } @@ -269,6 +278,10 @@ async function handleSave() { ElMessage.warning('请填写所有商户的商户号') return } + if (!merchant.notify_url?.trim()) { + ElMessage.warning('请填写所有商户的支付回调URL') + return + } } state.saving = true diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs index a4a9808..8625b6a 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs @@ -75,7 +75,7 @@ public class WechatPayConfigService : IWechatPayConfigService WechatPublicKeyId = m.WechatPublicKeyId, WechatPublicKeyPath = m.WechatPublicKeyPath, WechatPublicKeyContent = m.WechatPublicKeyContent, - NotifyUrl = !string.IsNullOrEmpty(m.NotifyUrl) ? m.NotifyUrl : "https://api.zfunbox.cn/api/notify" + NotifyUrl = m.NotifyUrl ?? "" }); } } @@ -99,7 +99,7 @@ public class WechatPayConfigService : IWechatPayConfigService Key = config.Keys ?? "", OrderPrefix = "MYH", PayVersion = "V2", - NotifyUrl = "https://api.zfunbox.cn/api/notify" + NotifyUrl = "" }); } } diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayService.cs index 85458a0..2aa15dd 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,71 +80,78 @@ 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. ַ + // 3. ��������ַ��� var nonceStr = GenerateNonceStr(); var callbackNonceStr = GenerateNonceStr(); - // 4. ɻص֪ͨURL + // 4. ���ɻص�֪ͨURL var notifyUrl = GenerateNotifyUrl(request.Attach, request.UserId, request.OrderNo, callbackNonceStr); - // 5. ֪ͨ¼order_notify + // 验证回调通知URL + if (string.IsNullOrEmpty(notifyUrl)) + { + _logger.LogError("支付回调URL未配置(NotifyBaseUrl为空),请在运营管理-支付配置中设置回调URL"); + return new WechatPayResult { Status = 0, Msg = "支付回调URL未配置,请联系管理员在后台配置" }; + } + + // 5. ����֪ͨ��¼��order_notify�� await SaveOrderNotifyAsync(request.OrderNo, notifyUrl, callbackNonceStr, request.Amount, request.Attach, openId); - // 6. ͳһµ + // 6. ����ͳһ�µ����� 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; } @@ -164,64 +171,64 @@ public class WechatPayService : IWechatPayService { "openid", openId } }; - // 7. ǩ + // 7. ����ǩ�� unifiedOrderParams["sign"] = MakeSign(unifiedOrderParams, merchantKey); - // 8. תΪXMLAPI + // 8. ת��Ϊ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. Ӧ + // 9. ������Ӧ var responseData = XmlToDictionary(responseXml); if (responseData == null) { - _logger.LogError("Ӧʧ"); + _logger.LogError("��������Ӧʧ��"); return new WechatPayResult { Status = 0, - Msg = "ϣԺ(Ӧʧ)" + Msg = "������ϣ����Ժ�����(������Ӧʧ��)" }; } - // 10. 鷵ؽ + // 10. ��鷵�ؽ�� 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 + // 11. ��ȡ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. ظǰ˵֧ + // 12. �������ظ�ǰ�˵�֧������ var timeStamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(); var payNonceStr = GenerateNonceStr(); @@ -234,10 +241,10 @@ public class WechatPayService : IWechatPayService { "signType", "MD5" } }; - // 13. ֧ǩ + // 13. ����֧��ǩ�� 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 { @@ -257,34 +264,31 @@ 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) { - // ʹõĻURLûʹĬϸʽ var baseUrl = _settings.NotifyBaseUrl; if (string.IsNullOrEmpty(baseUrl)) { - // ĬϻصURLʽ - return $"/api/notify/order_notify"; + return string.Empty; } - // ɴĻصURLPHPһ£ return $"{baseUrl.TrimEnd('/')}/api/notify/{attach}/{userId}/{orderNo}/{nonceStr}"; } /// - /// 涩֪ͨ¼ + /// ���涩��֪ͨ��¼ /// private async Task SaveOrderNotifyAsync(string orderNo, string notifyUrl, string nonceStr, decimal amount, string attach, string openId) { @@ -306,20 +310,20 @@ 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; @@ -329,7 +333,7 @@ public class WechatPayService : IWechatPayService } /// - /// 32λַ + /// ����32λ����ַ��� /// private static string GenerateNonceStr(int length = 32) { @@ -346,17 +350,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) { @@ -370,7 +374,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}>"); @@ -386,7 +390,7 @@ public class WechatPayService : IWechatPayService } /// - /// XMLתΪֵ + /// ��XMLת��Ϊ�ֵ� /// private static Dictionary? XmlToDictionary(string xml) { @@ -424,7 +428,7 @@ public class WechatPayService : IWechatPayService } /// - /// XML POST + /// ����XML POST���� /// private async Task PostXmlAsync(string url, string xml, int timeout = 30) { @@ -440,34 +444,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", "appidmch_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)) @@ -483,7 +487,7 @@ public class WechatPayService : IWechatPayService { if (string.IsNullOrEmpty(sign)) { - _logger.LogWarning("ǩ֤ʧܣǩΪ"); + _logger.LogWarning("ǩ����֤ʧ�ܣ�ǩ��Ϊ��"); return false; } @@ -492,7 +496,7 @@ public class WechatPayService : IWechatPayService if (!isValid) { - _logger.LogWarning("ǩ֤ʧܣǩ={CalculatedSign}ǩ={Sign}", calculatedSign, sign); + _logger.LogWarning("ǩ����֤ʧ�ܣ�����ǩ��={CalculatedSign}������ǩ��={Sign}", calculatedSign, sign); } return isValid; @@ -501,35 +505,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); - // ǩstringKEY + // ǩ������������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ʽַ + /// ������IJ����б� + /// URL��ʽ�ַ��� private static string ToUrlParams(IEnumerable> parameters) { var parts = parameters.Select(p => $"{p.Key}={p.Value}"); @@ -537,10 +541,10 @@ public class WechatPayService : IWechatPayService } /// - /// ݶŻȡ̻Կ + /// ���ݶ����Ż�ȡ�̻���Կ /// - /// - /// ̻Կ + /// ������ + /// �̻���Կ public string GetMerchantKeyByOrderNo(string orderNo) { var merchant = GetMerchantByOrderNo(orderNo); @@ -548,13 +552,13 @@ public class WechatPayService : IWechatPayService } /// - /// ֤Żصǩ + /// ��֤�Żص�ǩ�� /// - /// ص - /// Ƿ֤ͨ + /// �ص����� + /// �Ƿ���֤ͨ�� public bool VerifyNotifySign(WechatNotifyData notifyData) { - // ӻصݹֵ + // �ӻص����ݹ��������ֵ� var parameters = new Dictionary { { "return_code", notifyData.ReturnCode }, @@ -575,7 +579,7 @@ public class WechatPayService : IWechatPayService { "time_end", notifyData.TimeEnd } }; - // ӿѡֶ + // ���ӿ�ѡ�ֶ� if (!string.IsNullOrEmpty(notifyData.ErrCode)) parameters["err_code"] = notifyData.ErrCode; if (!string.IsNullOrEmpty(notifyData.ErrCodeDes)) @@ -583,34 +587,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; } @@ -619,49 +623,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 @@ -678,32 +682,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, @@ -713,10 +717,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 @@ -730,9 +734,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); @@ -740,7 +744,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 @@ -754,31 +758,31 @@ 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 "��������IJ��������ѿ�ͨ������С�����в鿴"; } } /// - /// 淢ʧܵĶRedisԶ + /// ���淢��ʧ�ܵĶ�����Redis���Զ��� /// private async Task SaveFailedShippingOrderAsync( OrderShippingNotifyRequest request, @@ -808,12 +812,12 @@ public class WechatPayService : IWechatPayService var json = JsonSerializer.Serialize(failedOrder); await _redisService.SetStringAsync(key, json, FAILED_SHIPPING_EXPIRY); - _logger.LogInformation("ѽʧܶԶ: OrderNo={OrderNo}, Key={Key}", + _logger.LogInformation("�ѽ�����ʧ�ܶ����������Զ���: OrderNo={OrderNo}, Key={Key}", request.OrderNo, key); } catch (Exception ex) { - _logger.LogError(ex, "淢ʧܶRedisʱ: OrderNo={OrderNo}", request.OrderNo); + _logger.LogError(ex, "���淢��ʧ�ܶ�����Redisʱ��������: OrderNo={OrderNo}", request.OrderNo); throw; } } @@ -821,7 +825,7 @@ public class WechatPayService : IWechatPayService /// public WechatPayMerchantConfig GetMerchantByOrderNo(string orderNo) { - // ʹ÷ȡ̻ + // ʹ�����÷����ȡ�̻����� return _configService.GetMerchantByOrderNo(orderNo); } @@ -857,7 +861,7 @@ public class WechatPayService : IWechatPayService result.Attach = GetXmlNodeValue(root, "attach"); result.TimeEnd = GetXmlNodeValue(root, "time_end"); - // λ֣ + // ��������λ���֣� if (int.TryParse(GetXmlNodeValue(root, "total_fee"), out var totalFee)) { result.TotalFee = totalFee; @@ -869,7 +873,7 @@ public class WechatPayService : IWechatPayService } catch (Exception ex) { - _logger.LogError(ex, "ŻصXMLʧ: {XmlData}", xmlData); + _logger.LogError(ex, "�����Żص�XMLʧ��: {XmlData}", xmlData); } return result; diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayV3Service.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayV3Service.cs index 7ca56b3..c447eec 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayV3Service.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayV3Service.cs @@ -96,6 +96,13 @@ public class WechatPayV3Service : IWechatPayV3Service return new WechatPayResult { Status = 0, Msg = "V3 支付配置不完整" }; } + // 验证回调通知URL + if (string.IsNullOrEmpty(merchantConfig.NotifyUrl)) + { + _logger.LogError("支付回调URL未配置: MchId={MchId},请在运营管理-支付配置中设置回调URL", merchantConfig.MchId); + return new WechatPayResult { Status = 0, Msg = "支付回调URL未配置,请联系管理员在后台配置" }; + } + _logger.LogDebug("使用 V3 商户配置: MchId={MchId}, AppId={AppId}", merchantConfig.MchId, merchantConfig.AppId); // 3. 读取私钥(优先使用数据库中的PEM内容) diff --git a/uniapp/config/index.js b/uniapp/config/index.js index 71ebc2e..f8eafa0 100644 --- a/uniapp/config/index.js +++ b/uniapp/config/index.js @@ -5,8 +5,8 @@ const ENV = { development: { - API_BASE_URL: 'http://api.nxt.shhmkjgs.cn/api', - STATIC_BASE_URL: 'http://api.nxt.shhmkjgs.cn', + API_BASE_URL: 'https://api.nxt.shhmkjgs.cn/api', + STATIC_BASE_URL: 'https://api.nxt.shhmkjgs.cn', SIGNALR_URL: 'ws://api.nxt.shhmkjgs.cn/hubs/chat' }, production: {