@ -14,7 +14,7 @@ using Microsoft.Extensions.Options;
namespace MiAssessment.Core.Services ;
/// <summary>
/// 微信支付服务实现
/// <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʵ<EFBFBD> <EFBFBD>
/// </summary>
public class WechatPayService : IWechatPayService
{
@ -29,29 +29,29 @@ public class WechatPayService : IWechatPayService
private readonly Lazy < IWechatPayV3Service > ? _v3ServiceLazy ;
/// <summary>
/// 微信统一下单API地址
/// <EFBFBD> <EFBFBD> ͳһ <EFBFBD> µ<EFBFBD> API<EFBFBD> <EFBFBD> ַ
/// </summary>
private const string UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder" ;
/// <summary>
/// 微信发货通知API地址
/// <EFBFBD> ŷ<EFBFBD> <EFBFBD> <EFBFBD> ֪ͨAPI<EFBFBD> <EFBFBD> ַ
/// </summary>
private const string SHIPPING_NOTIFY_URL = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info" ;
/// <summary>
/// 发货失败订单Redis键前缀
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʧ<EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> Redis<EFBFBD> <EFBFBD> ǰ
/// </summary>
private const string FAILED_SHIPPING_KEY_PREFIX = "post_order:" ;
/// <summary>
/// 发货失败订单Redis过期时间( 3天)
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʧ<EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> Redis<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʱ<EFBFBD> 䣨3<EFBFBD> 죩
/// </summary>
private static readonly TimeSpan FAILED_SHIPPING_EXPIRY = TimeSpan . FromDays ( 3 ) ;
/// <summary>
/// 测试用户支付金额(分)
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> û<EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֣<EFBFBD>
/// </summary>
private const int TEST_USER_PAY_AMOUNT = 1 ; // 0.01 元 = 1分
private const int TEST_USER_PAY_AMOUNT = 1 ; // 0.01 Ԫ = 1<> <31>
public WechatPayService (
MiAssessmentDbContext dbContext ,
@ -80,71 +80,78 @@ public class WechatPayService : IWechatPayService
{
try
{
_logger . LogInformation ( " 开始创建微信支付订单 : OrderNo={OrderNo}, UserId={UserId}, Amount={Amount}",
_logger . LogInformation ( " <EFBFBD> <EFBFBD> ʼ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : OrderNo={OrderNo}, UserId={UserId}, Amount={Amount}",
request . OrderNo , request . UserId , request . Amount ) ;
// 1. 根据订单号获取商户配置,检查支付版本
// 1. <EFBFBD> <EFBFBD> <EFBFBD> ݶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ż<EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ã<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> 汾
var merchantConfig = _configService . GetMerchantByOrderNo ( request . OrderNo ) ;
// 2. 版本路由:如果配置为 V3 且 V3 服务可用,则使用 V3 服务
// 2. <EFBFBD> 汾·<EFBFBD> ɣ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ϊ V3 <20> <> V3 <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ã<EFBFBD> <C3A3> <EFBFBD> ʹ <EFBFBD> <CAB9> V3 <20> <> <EFBFBD> <EFBFBD>
if ( merchantConfig . PayVersion = = "V3" & & _v3ServiceLazy ! = null )
{
_logger . LogInformation ( " 商户配置为 V3 版本,路由到 V3 服务 : MchId={MchId}", merchantConfig . MchId ) ;
_logger . LogInformation ( " <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ϊ V3 <20> 汾<EFBFBD> <E6B1BE> ·<EFBFBD> ɵ<EFBFBD> V3 <20> <> <EFBFBD> <EFBFBD> : MchId={MchId}", merchantConfig . MchId ) ;
return await _v3ServiceLazy . Value . CreateJsapiOrderAsync ( request ) ;
}
// 3. 使用 V2 流程
_logger . LogDebug ( " 使用 V2 支付流程 : MchId={MchId}, PayVersion={PayVersion}",
// 3. ʹ <EFBFBD> <EFBFBD> V2 <20> <> <EFBFBD> <EFBFBD>
_logger . LogDebug ( " ʹ <EFBFBD> <EFBFBD> V2 ֧<> <D6A7> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : MchId={MchId}, PayVersion={PayVersion}",
merchantConfig . MchId , merchantConfig . PayVersion ) ;
// 4. 获取用户信息和 OpenId
// 4. <EFBFBD> <EFBFBD> ȡ<EFBFBD> û<EFBFBD> <EFBFBD> <EFBFBD> Ϣ<EFBFBD> <EFBFBD> OpenId
var user = await _dbContext . Users . FirstOrDefaultAsync ( u = > u . Id = = request . UserId ) ;
if ( user = = null )
{
_logger . LogWarning ( " 用户不存在 : UserId={UserId}", request . UserId ) ;
_logger . LogWarning ( " <EFBFBD> û<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : UserId={UserId}", request . UserId ) ;
return new WechatPayResult
{
Status = 0 ,
Msg = " 用户不存在 "
Msg = " <EFBFBD> û<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> "
} ;
}
var openId = string . IsNullOrEmpty ( request . OpenId ) ? user . OpenId : request . OpenId ;
if ( string . IsNullOrEmpty ( openId ) )
{
_logger . LogWarning ( " 用户OpenId为空 : UserId={UserId}", request . UserId ) ;
_logger . LogWarning ( " <EFBFBD> û<EFBFBD> OpenIdΪ<EFBFBD> <EFBFBD> : UserId={UserId}", request . UserId ) ;
return new WechatPayResult
{
Status = 0 ,
Msg = " 用户OpenId不存在 "
Msg = " <EFBFBD> û<EFBFBD> OpenId<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> "
} ;
}
// 5. 使用已获取的商户配置
// 5. ʹ <EFBFBD> <EFBFBD> <EFBFBD> ѻ<EFBFBD> ȡ<EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
var appId = merchantConfig . AppId ;
var mchId = merchantConfig . MchId ;
var merchantKey = merchantConfig . Key ;
_logger . LogDebug ( " 使用商户配置 : MchId={MchId}, AppId={AppId}", mchId , appId ) ;
_logger . LogDebug ( " ʹ <EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : MchId={MchId}, AppId={AppId}", mchId , appId ) ;
// 3. 生成随机字符串
// 3. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ַ<EFBFBD> <EFBFBD> <EFBFBD>
var nonceStr = GenerateNonceStr ( ) ;
var callbackNonceStr = GenerateNonceStr ( ) ;
// 4. 生成回调通知 URL
// 4. <EFBFBD> <EFBFBD> <EFBFBD> ɻص<EFBFBD> ֪ͨ 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. <20> <> <EFBFBD> <EFBFBD> ֪ͨ<CDA8> <D6AA> ¼<EFBFBD> <C2BC> order_notify<66> <79>
await SaveOrderNotifyAsync ( request . OrderNo , notifyUrl , callbackNonceStr , request . Amount , request . Attach , openId ) ;
// 6. 构建统一下单参数
// 6. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ͳһ <EFBFBD> µ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
var body = TruncateBody ( request . Body , 30 ) ;
var totalFee = ( int ) Math . Round ( request . Amount * 100 ) ; // 转换为分
var totalFee = ( int ) Math . Round ( request . Amount * 100 ) ; // ת<EFBFBD> <EFBFBD> Ϊ<EFBFBD> <EFBFBD>
// 测试环境下, IsTest=2 的用户支付金额改为 0.01 元
// <EFBFBD> <EFBFBD> <EFBFBD> Ի<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> £<EFBFBD> IsTest=2 <20> <> <EFBFBD> û<EFBFBD> ֧<EFBFBD> <D6A7> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ϊ 0.01 Ԫ
if ( _appSettings . IsTestEnvironment & & user . IsTest = = 2 )
{
_logger . LogInformation ( " 测试用户支付金额调整: UserId={UserId}, 原金额={OriginalAmount}分, 调整为={TestAmount}分 ",
_logger . LogInformation ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> û<EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : UserId={UserId}, ԭ<> <D4AD> <EFBFBD> ={OriginalAmount}<7D> <> , <20> <> <EFBFBD> <EFBFBD> Ϊ={TestAmount}<7D> <> ",
request . UserId , totalFee , TEST_USER_PAY_AMOUNT ) ;
totalFee = TEST_USER_PAY_AMOUNT ;
}
@ -164,64 +171,64 @@ public class WechatPayService : IWechatPayService
{ "openid" , openId }
} ;
// 7. 生成签名
// 7. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ǩ<EFBFBD> <EFBFBD>
unifiedOrderParams [ "sign" ] = MakeSign ( unifiedOrderParams , merchantKey ) ;
// 8. 转换为XML并调用微信 API
// 8. ת<EFBFBD> <EFBFBD> ΪXML<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> API
var requestXml = DictionaryToXml ( unifiedOrderParams ) ;
_logger . LogDebug ( " 统一下单请求 XML: {Xml}", requestXml ) ;
_logger . LogDebug ( " ͳһ <EFBFBD> µ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> XML: {Xml}", requestXml ) ;
var responseXml = await PostXmlAsync ( UNIFIED_ORDER_URL , requestXml ) ;
_logger . LogDebug ( " 统一下单响应 XML: {Xml}", responseXml ) ;
_logger . LogDebug ( " ͳһ <EFBFBD> µ<EFBFBD> <EFBFBD> <EFBFBD> Ӧ XML: {Xml}", responseXml ) ;
// 9. 解析响应
// 9. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ӧ
var responseData = XmlToDictionary ( responseXml ) ;
if ( responseData = = null )
{
_logger . LogError ( " 解析微信响应失败 ") ;
_logger . LogError ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ӧʧ<EFBFBD> <EFBFBD> ") ;
return new WechatPayResult
{
Status = 0 ,
Msg = " 网络故障,请稍后重试(解析响应失败 )"
Msg = " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ϣ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ժ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> (<28> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ӧʧ<D3A6> <CAA7> )"
} ;
}
// 10. 检查返回结果
// 10. <EFBFBD> <EFBFBD> 鷵<EFBFBD> ؽ<EFBFBD> <EFBFBD>
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" , " δ֪<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ") ;
_logger . LogWarning ( " ͳһ <EFBFBD> µ<EFBFBD> ʧ<EFBFBD> <EFBFBD> : return_code={ReturnCode}, return_msg={ReturnMsg}", returnCode , returnMsg ) ;
return new WechatPayResult
{
Status = 0 ,
Msg = $" 网络故障,请稍后重试 ({returnMsg})"
Msg = $" <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ϣ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ժ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ({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" , " δ֪<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ") ;
_logger . LogWarning ( " ͳһ <EFBFBD> µ<EFBFBD> ҵ<EFBFBD> <EFBFBD> ʧ<EFBFBD> <EFBFBD> : err_code={ErrCode}, err_code_des={ErrCodeDes}", errCode , errCodeDes ) ;
return new WechatPayResult
{
Status = 0 ,
Msg = $" 支付失败 ({GetErrorMessage(errCode, errCodeDes)})"
Msg = $" ֧<EFBFBD> <EFBFBD> ʧ<EFBFBD> <EFBFBD> ({GetErrorMessage(errCode, errCodeDes)})"
} ;
}
// 11. 获取 prepay_id
// 11. <EFBFBD> <EFBFBD> ȡ prepay_id
if ( ! responseData . TryGetValue ( "prepay_id" , out var prepayId ) | | string . IsNullOrEmpty ( prepayId ) )
{
_logger . LogError ( " 统一下单成功但prepay_id为空 ") ;
_logger . LogError ( " ͳһ <EFBFBD> µ<EFBFBD> <EFBFBD> ɹ<EFBFBD> <EFBFBD> <EFBFBD> prepay_idΪ<EFBFBD> <EFBFBD> ") ;
return new WechatPayResult
{
Status = 0 ,
Msg = " 网络故障,请稍后重试(prepay_id为空 )"
Msg = " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ϣ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ժ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> (prepay_idΪ<64> <CEAA> )"
} ;
}
// 12. 构建返回给前端的支付参数
// 12. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ظ<EFBFBD> ǰ<EFBFBD> ˵<EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
var timeStamp = DateTimeOffset . UtcNow . ToUnixTimeSeconds ( ) . ToString ( ) ;
var payNonceStr = GenerateNonceStr ( ) ;
@ -234,10 +241,10 @@ public class WechatPayService : IWechatPayService
{ "signType" , "MD5" }
} ;
// 13. 生成支付签名
// 13. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> ǩ<EFBFBD> <EFBFBD>
var paySign = MakeSign ( payParams , merchantKey ) ;
_logger . LogInformation ( " 微信支付订单创建成功 : OrderNo={OrderNo}, PrepayId={PrepayId}", request . OrderNo , prepayId ) ;
_logger . LogInformation ( " <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ɹ<EFBFBD> : 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 , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> 쳣 : OrderNo={OrderNo}", request . OrderNo ) ;
return new WechatPayResult
{
Status = 0 ,
Msg = " 系统错误,请稍后重试 "
Msg = " ϵͳ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ժ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> "
} ;
}
}
/// <summary>
/// 生成回调通知 URL
/// <EFBFBD> <EFBFBD> <EFBFBD> ɻص<EFBFBD> ֪ͨ URL
/// </summary>
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 ;
}
// 生成带参数的回调URL( 与PHP保持一致)
return $"{baseUrl.TrimEnd('/')}/api/notify/{attach}/{userId}/{orderNo}/{nonceStr}" ;
}
/// <summary>
/// 保存订单通知记录
/// <EFBFBD> <EFBFBD> <EFBFBD> 涩<EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> <EFBFBD> ¼
/// </summary>
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 ( " <EFBFBD> <EFBFBD> <EFBFBD> 涩<EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> <EFBFBD> ¼ : OrderNo={OrderNo}, NotifyUrl={NotifyUrl}", orderNo , notifyUrl ) ;
}
/// <summary>
/// 截断商品描述(微信限制最大长度)
/// <EFBFBD> ض<EFBFBD> <EFBFBD> <EFBFBD> Ʒ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȣ <EFBFBD>
/// </summary>
private static string TruncateBody ( string body , int maxLength )
{
if ( string . IsNullOrEmpty ( body ) )
{
return " 商品购买 ";
return " <EFBFBD> <EFBFBD> Ʒ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ";
}
// 使用字符数而不是字节数
// ʹ <EFBFBD> <EFBFBD> <EFBFBD> ַ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֽ<EFBFBD> <EFBFBD> <EFBFBD>
if ( body . Length < = maxLength )
{
return body ;
@ -329,7 +333,7 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 生成32位随机字符串
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> 32λ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ַ<EFBFBD> <EFBFBD> <EFBFBD>
/// </summary>
private static string GenerateNonceStr ( int length = 32 )
{
@ -346,17 +350,17 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 获取客户端 IP
/// <EFBFBD> <EFBFBD> ȡ<EFBFBD> ͻ<EFBFBD> <EFBFBD> <EFBFBD> IP
/// </summary>
private static string GetClientIp ( )
{
// 在实际环境中应该从HttpContext获取
// 这里返回默认值,实际使用时需要通过依赖注入获取 IHttpContextAccessor
// <EFBFBD> <EFBFBD> ʵ<EFBFBD> ʻ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ӧ<EFBFBD> ô<EFBFBD> HttpContext<EFBFBD> <EFBFBD> ȡ
// <EFBFBD> <EFBFBD> <EFBFBD> ﷵ<EFBFBD> <EFBFBD> Ĭ<EFBFBD> <EFBFBD> ֵ<EFBFBD> <EFBFBD> ʵ<EFBFBD> <EFBFBD> ʹ <EFBFBD> <EFBFBD> ʱ<EFBFBD> <EFBFBD> Ҫͨ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ע<EFBFBD> <EFBFBD> <EFBFBD> ȡ IHttpContextAccessor
return "127.0.0.1" ;
}
/// <summary>
/// 将字典转换为 XML
/// <EFBFBD> <EFBFBD> <EFBFBD> ֵ<EFBFBD> ת<EFBFBD> <EFBFBD> Ϊ XML
/// </summary>
private static string DictionaryToXml ( Dictionary < string , string > parameters )
{
@ -370,7 +374,7 @@ public class WechatPayService : IWechatPayService
continue ;
}
// 数字类型不需要 CDATA
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ͳ<EFBFBD> <EFBFBD> <EFBFBD> Ҫ 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
}
/// <summary>
/// 将XML转换为字典
/// <EFBFBD> <EFBFBD> XMLת<EFBFBD> <EFBFBD> Ϊ<EFBFBD> ֵ<EFBFBD>
/// </summary>
private static Dictionary < string , string > ? XmlToDictionary ( string xml )
{
@ -424,7 +428,7 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 发送XML POST请求
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> XML POST<53> <54> <EFBFBD> <EFBFBD>
/// </summary>
private async Task < string > 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 , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> XML<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʧ<EFBFBD> <EFBFBD> : Url={Url}", url ) ;
throw ;
}
}
/// <summary>
/// 获取错误消息
/// <EFBFBD> <EFBFBD> ȡ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ϣ
/// </summary>
private static string GetErrorMessage ( string errCode , string errCodeDes )
{
var errorMessages = new Dictionary < string , string >
{
{ "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" , " <EFBFBD> ̻<EFBFBD> δ<EFBFBD> <EFBFBD> ͨ<EFBFBD> ˽ӿ<EFBFBD> Ȩ<EFBFBD> <EFBFBD> " } ,
{ "NOTENOUGH" , " <EFBFBD> û<EFBFBD> <EFBFBD> ʺ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "ORDERNOTEXIST" , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ų<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "ORDERPAID" , " <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ظ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "ORDERCLOSED" , " <EFBFBD> <EFBFBD> ǰ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ѹرգ <EFBFBD> <EFBFBD> <EFBFBD> ֧<EFBFBD> <EFBFBD> " } ,
{ "SYSTEMERROR" , " ϵͳ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> !ϵͳ<CFB5> <CDB3> ʱ " } ,
{ "APPID_NOT_EXIST" , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȱ<EFBFBD> <EFBFBD> APPID" } ,
{ "MCHID_NOT_EXIST" , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȱ<EFBFBD> <EFBFBD> MCHID" } ,
{ "APPID_MCHID_NOT_MATCH" , "appid <EFBFBD> <EFBFBD> mch_id<EFBFBD> <EFBFBD> ƥ<EFBFBD> <EFBFBD> " } ,
{ "LACK_PARAMS" , " ȱ<EFBFBD> ٱ<EFBFBD> Ҫ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "OUT_TRADE_NO_USED" , " ͬһ <EFBFBD> ʽ <EFBFBD> <EFBFBD> ײ<EFBFBD> <EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> ύ " } ,
{ "SIGNERROR" , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȷ " } ,
{ "XML_FORMAT_ERROR" , "XML <EFBFBD> <EFBFBD> ʽ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "REQUIRE_POST_METHOD" , " δʹ <EFBFBD> <EFBFBD> post<EFBFBD> <EFBFBD> <EFBFBD> ݲ<EFBFBD> <EFBFBD> <EFBFBD> " } ,
{ "POST_DATA_EMPTY" , "post <EFBFBD> <EFBFBD> <EFBFBD> ݲ<EFBFBD> <EFBFBD> <EFBFBD> Ϊ<EFBFBD> <EFBFBD> " } ,
{ "NOT_UTF8" , " δʹ <EFBFBD> <EFBFBD> ָ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʽ " }
} ;
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 ( " ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֤ʧ<EFBFBD> ܣ<EFBFBD> ǩ<EFBFBD> <EFBFBD> Ϊ<EFBFBD> <EFBFBD> ") ;
return false ;
}
@ -492,7 +496,7 @@ public class WechatPayService : IWechatPayService
if ( ! isValid )
{
_logger . LogWarning ( " 签名验证失败:计算签名={CalculatedSign},传入签名 ={Sign}", calculatedSign , sign ) ;
_logger . LogWarning ( " ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֤ʧ<EFBFBD> ܣ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ǩ<EFBFBD> <EFBFBD> ={CalculatedSign}<7D> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ǩ<EFBFBD> <C7A9> ={Sign}", calculatedSign , sign ) ;
}
return isValid ;
@ -501,35 +505,35 @@ public class WechatPayService : IWechatPayService
/// <inheritdoc />
public string MakeSign ( Dictionary < string , string > parameters , string? merchantKey = null )
{
// 获取商户密钥
// <EFBFBD> <EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ
var key = merchantKey ? ? _settings . DefaultMerchant . Key ;
// 签名步骤一: 按字典序排序数组参数( ASCII码从小到大)
// 过滤掉空值和sign字段
// ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> һ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֵ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ASCII<EFBFBD> <EFBFBD> <EFBFBD> С <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
// <EFBFBD> <EFBFBD> <EFBFBD> ˵<EFBFBD> <EFBFBD> <EFBFBD> ֵ<EFBFBD> <EFBFBD> sign<EFBFBD> ֶ<EFBFBD>
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
// ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ƴ<EFBFBD> <EFBFBD> ΪURL<EFBFBD> <EFBFBD> ʽ key=value&key=value
var urlParams = ToUrlParams ( sortedParams ) ;
// 签名步骤三: 在string后加入 KEY
// ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> string<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> KEY
var signString = $"{urlParams}&key={key}" ;
// 签名步骤四: MD5加密
// ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ģ<EFBFBD> MD5<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
using var md5 = MD5 . Create ( ) ;
var hashBytes = md5 . ComputeHash ( Encoding . UTF8 . GetBytes ( signString ) ) ;
// 签名步骤五:所有字符转为大写
// ǩ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> 壺<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ַ<EFBFBD> תΪ<EFBFBD> <EFBFBD> д
return BitConverter . ToString ( hashBytes ) . Replace ( "-" , "" ) . ToUpper ( ) ;
}
/// <summary>
/// 将参数拼接为URL格式 : key=value&key=value
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ƴ<EFBFBD> <EFBFBD> ΪURL<EFBFBD> <EFBFBD> ʽ : key=value&key=value
/// </summary>
/// <param name="parameters"> 已排序的参数列表 </param>
/// <returns>URL 格式字符串 </returns>
/// <param name="parameters"> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> IJ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> б <EFBFBD> </param>
/// <returns>URL <EFBFBD> <EFBFBD> ʽ <EFBFBD> ַ<EFBFBD> <EFBFBD> <EFBFBD> </returns>
private static string ToUrlParams ( IEnumerable < KeyValuePair < string , string > > parameters )
{
var parts = parameters . Select ( p = > $"{p.Key}={p.Value}" ) ;
@ -537,10 +541,10 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 根据订单号获取商户密钥
/// <EFBFBD> <EFBFBD> <EFBFBD> ݶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ż<EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ
/// </summary>
/// <param name="orderNo"> 订单号 </param>
/// <returns> 商户密钥 </returns>
/// <param name="orderNo"> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> </param>
/// <returns> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ </returns>
public string GetMerchantKeyByOrderNo ( string orderNo )
{
var merchant = GetMerchantByOrderNo ( orderNo ) ;
@ -548,13 +552,13 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 验证微信回调签名
/// <EFBFBD> <EFBFBD> ֤<EFBFBD> Żص<EFBFBD> ǩ<EFBFBD> <EFBFBD>
/// </summary>
/// <param name="notifyData"> 回调数据 </param>
/// <returns> 是否验证通过 </returns>
/// <param name="notifyData"> <EFBFBD> ص<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> </param>
/// <returns> <EFBFBD> Ƿ<EFBFBD> <EFBFBD> <EFBFBD> ֤ͨ<EFBFBD> <EFBFBD> </returns>
public bool VerifyNotifySign ( WechatNotifyData notifyData )
{
// 从回调数据构建参数字典
// <EFBFBD> ӻص<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ݹ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֵ<EFBFBD>
var parameters = new Dictionary < string , string >
{
{ "return_code" , notifyData . ReturnCode } ,
@ -575,7 +579,7 @@ public class WechatPayService : IWechatPayService
{ "time_end" , notifyData . TimeEnd }
} ;
// 添加可选字段
// <EFBFBD> <EFBFBD> <EFBFBD> ӿ<EFBFBD> ѡ <EFBFBD> ֶ<EFBFBD>
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 ;
// 根据商户号获取对应的密钥
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> Ż<EFBFBD> ȡ<EFBFBD> <EFBFBD> Ӧ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Կ
var merchantKey = GetMerchantKeyByMchId ( notifyData . MchId ) ;
return VerifySign ( parameters , notifyData . Sign , merchantKey ) ;
}
/// <summary>
/// 根据商户号获取商户密钥
/// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> Ż<EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ
/// </summary>
/// <param name="mchId"> 商户号 </param>
/// <returns> 商户密钥 </returns>
/// <param name="mchId"> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> </param>
/// <returns> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ </returns>
private string GetMerchantKeyByMchId ( string mchId )
{
// 先从配置的商户列表中查找
// <EFBFBD> ȴ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> õ<EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> б <EFBFBD> <EFBFBD> в<EFBFBD> <EFBFBD> <EFBFBD>
var merchant = _settings . Merchants . FirstOrDefault ( m = > m . MchId = = mchId ) ;
if ( merchant ! = null )
{
return merchant . Key ;
}
// 如果没找到,检查默认商户
// <EFBFBD> <EFBFBD> <EFBFBD> û<EFBFBD> ҵ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ĭ<EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD>
if ( _settings . DefaultMerchant . MchId = = mchId )
{
return _settings . DefaultMerchant . Key ;
}
// 返回默认商户密钥
_logger . LogWarning ( " 未找到商户号 {MchId} 的配置,使用默认商户密钥 ", mchId ) ;
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ĭ<EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> Կ
_logger . LogWarning ( " δ<EFBFBD> ҵ<EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> {MchId} <20> <> <EFBFBD> <EFBFBD> <EFBFBD> ã<EFBFBD> ʹ <EFBFBD> <CAB9> Ĭ<EFBFBD> <C4AC> <EFBFBD> ̻<EFBFBD> <CCBB> <EFBFBD> Կ ", mchId ) ;
return _settings . DefaultMerchant . Key ;
}
@ -619,49 +623,49 @@ public class WechatPayService : IWechatPayService
{
try
{
_logger . LogInformation ( " 开始发送订单发货通知 : OrderNo={OrderNo}, OpenId={OpenId}",
_logger . LogInformation ( " <EFBFBD> <EFBFBD> ʼ <EFBFBD> <EFBFBD> <EFBFBD> Ͷ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ : OrderNo={OrderNo}, OpenId={OpenId}",
request . OrderNo , request . OpenId ) ;
// 1. 根据订单号获取商户配置
// 1. <EFBFBD> <EFBFBD> <EFBFBD> ݶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ż<EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
var merchantConfig = _configService . GetMerchantByOrderNo ( request . OrderNo ) ;
var mchId = merchantConfig . MchId ;
var appId = merchantConfig . AppId ;
_logger . LogDebug ( " 使用商户配置 : MchId={MchId}, AppId={AppId}", mchId , appId ) ;
_logger . LogDebug ( " ʹ <EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : MchId={MchId}, AppId={AppId}", mchId , appId ) ;
// 2. 获取 access_token
// 2. <EFBFBD> <EFBFBD> ȡ access_token
var accessToken = await _wechatService . GetAccessTokenAsync ( appId ) ;
if ( string . IsNullOrEmpty ( accessToken ) )
{
_logger . LogError ( " 获取access_token失败 : AppId={AppId}", appId ) ;
_logger . LogError ( " <EFBFBD> <EFBFBD> ȡaccess_tokenʧ<EFBFBD> <EFBFBD> : AppId={AppId}", appId ) ;
// 存入重试队列
await SaveFailedShippingOrderAsync ( request , merchantConfig , - 1 , " 获取access_token失败 ") ;
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD>
await SaveFailedShippingOrderAsync ( request , merchantConfig , - 1 , " <EFBFBD> <EFBFBD> ȡaccess_tokenʧ<EFBFBD> <EFBFBD> ") ;
return new OrderShippingNotifyResult
{
Success = false ,
ErrCode = - 1 ,
ErrMsg = " 获取access_token失败 ",
ErrMsg = " <EFBFBD> <EFBFBD> ȡaccess_tokenʧ<EFBFBD> <EFBFBD> ",
QueuedForRetry = true
} ;
}
// 3. 构建发货通知消息
// 3. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> <EFBFBD> Ϣ
var itemDesc = GetShippingItemDesc ( request ) ;
// 4. 构建请求参数
// 4. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
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 , // ʹ <EFBFBD> <EFBFBD> <EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
mchid = mchId ,
out_trade_no = request . OrderNo
} ,
logistics_type = request . LogisticsType , // 物流类型: 4=虚拟商品
delivery_mode = request . DeliveryMode , // 发货模式: 1=统一发货
logistics_type = request . LogisticsType , // <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ͣ<EFBFBD> 4=<3D> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ʒ
delivery_mode = request . DeliveryMode , // <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ģʽ <EFBFBD> <EFBFBD> 1=ͳһ <CDB3> <D2BB> <EFBFBD> <EFBFBD>
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. <EFBFBD> <EFBFBD> ¼<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ־
_logger . LogDebug ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : OrderNo={OrderNo}, MchId={MchId}, Request={Request}",
request . OrderNo , mchId , requestJson ) ;
// 6. 调用微信 API
// 6. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> 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. <EFBFBD> <EFBFBD> ¼<EFBFBD> <EFBFBD> Ӧ<EFBFBD> <EFBFBD> ־
_logger . LogDebug ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> <EFBFBD> Ӧ : OrderNo={OrderNo}, Response={Response}",
request . OrderNo , responseContent ) ;
// 8. 解析响应
// 8. <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ӧ
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. <EFBFBD> жϽ<EFBFBD> <EFBFBD>
if ( errCode = = 0 & & errMsg = = "ok" )
{
_logger . LogInformation ( " 发货通知成功 : OrderNo={OrderNo}", request . OrderNo ) ;
_logger . LogInformation ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> ɹ<EFBFBD> : 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 ( " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨʧ<EFBFBD> <EFBFBD> : OrderNo={OrderNo}, ErrCode={ErrCode}, ErrMsg={ErrMsg}",
request . OrderNo , errCode , errMsg ) ;
// 存入重试队列
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD>
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 , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֪ͨ<EFBFBD> 쳣 : OrderNo={OrderNo}", request . OrderNo ) ;
// 尝试存入重试队列
// <EFBFBD> <EFBFBD> <EFBFBD> Դ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD>
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 , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʧ<EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD> ʱ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : OrderNo={OrderNo}", request . OrderNo ) ;
}
return new OrderShippingNotifyResult
@ -754,31 +758,31 @@ public class WechatPayService : IWechatPayService
}
/// <summary>
/// 获取发货商品描述
/// <EFBFBD> <EFBFBD> ȡ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ʒ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
/// </summary>
private static string GetShippingItemDesc ( OrderShippingNotifyRequest request )
{
// 如果请求中指定了描述,直接使用
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ָ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ֱ<EFBFBD> <EFBFBD> ʹ <EFBFBD> <EFBFBD>
if ( ! string . IsNullOrEmpty ( request . ItemDesc ) )
{
return request . ItemDesc ;
}
// 根据订单前缀判断消息内容
// <EFBFBD> <EFBFBD> <EFBFBD> ݶ<EFBFBD> <EFBFBD> <EFBFBD> ǰ<EFBFBD> ж<EFBFBD> <EFBFBD> <EFBFBD> Ϣ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
if ( request . OrderNo . StartsWith ( "FH_" ) )
{
// 发货订单
return " 本单购买的商品正在打包,请联系客服获取物流信息 ";
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
return " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ʒ <EFBFBD> <EFBFBD> <EFBFBD> ڴ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ϵ<EFBFBD> ͷ<EFBFBD> <EFBFBD> <EFBFBD> ȡ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ϣ ";
}
else
{
// 虚拟商品(测评服务等)
return " 本单购买的测评服务已开通,请在小程序中查看 ";
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ʒ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȣ <EFBFBD>
return " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> IJ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ѿ<EFBFBD> ͨ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> С <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> в鿴 ";
}
}
/// <summary>
/// 保存发货失败的订单到Redis重试队列
/// <EFBFBD> <EFBFBD> <EFBFBD> 淢<EFBFBD> <EFBFBD> ʧ<EFBFBD> ܵĶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Redis<EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD>
/// </summary>
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 ( " <EFBFBD> ѽ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʧ<EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Զ<EFBFBD> <EFBFBD> <EFBFBD> : OrderNo={OrderNo}, Key={Key}",
request . OrderNo , key ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , " 保存发货失败订单到Redis时发生错误 : OrderNo={OrderNo}", request . OrderNo ) ;
_logger . LogError ( ex , " <EFBFBD> <EFBFBD> <EFBFBD> 淢<EFBFBD> <EFBFBD> ʧ<EFBFBD> ܶ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Redisʱ<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> : OrderNo={OrderNo}", request . OrderNo ) ;
throw ;
}
}
@ -821,7 +825,7 @@ public class WechatPayService : IWechatPayService
/// <inheritdoc />
public WechatPayMerchantConfig GetMerchantByOrderNo ( string orderNo )
{
// 使用配置服务获取商户配置
// ʹ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ÷<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȡ<EFBFBD> ̻<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
return _configService . GetMerchantByOrderNo ( orderNo ) ;
}
@ -857,7 +861,7 @@ public class WechatPayService : IWechatPayService
result . Attach = GetXmlNodeValue ( root , "attach" ) ;
result . TimeEnd = GetXmlNodeValue ( root , "time_end" ) ;
// 解析金额(单位:分)
// <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> λ<EFBFBD> <EFBFBD> <EFBFBD> ֣<EFBFBD>
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 , " <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Żص<EFBFBD> XMLʧ<EFBFBD> <EFBFBD> : {XmlData}", xmlData ) ;
}
return result ;