448 lines
16 KiB
C#

using Microsoft.Extensions.Logging;
using SKIT.FlurlHttpClient.Wechat.TenpayV3;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models;
using YD_Event.Core.Entities;
using YD_Event.Core.Infrastructure.WeChat.Dto;
using Furion.DependencyInjection;
namespace YD_Event.Core.Infrastructure.WeChat;
/// <summary>
/// 微信支付服务
/// </summary>
/// <remarks>
/// 提供微信支付相关的核心功能,包括:
/// 1. JSAPI支付下单
/// 2. Native支付下单
/// 3. 订单状态查询
/// 4. 退款申请
/// 5. 退款查询
/// 6. 支付通知验证和解密
/// 7. 退款通知验证和解密
/// </remarks>
public class WeChatPayService : ITransient
{
private readonly ILogger<WeChatPayService> _logger;
public WeChatPayService(ILogger<WeChatPayService> logger)
{
_logger = logger;
}
/// <summary>
/// 创建微信支付客户端
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <returns>微信支付客户端实例</returns>
private WechatTenpayClient CreateClient(WeChatPayConfig config)
{
var options = new WechatTenpayClientOptions()
{
MerchantId = config.MerchantIdValue,
MerchantV3Secret = config.MerchantV3Secret,
MerchantCertificateSerialNumber = config.MerchantCertificateSerialNumber,
MerchantCertificatePrivateKey = config.MerchantCertificatePrivateKey
};
return new WechatTenpayClient(options);
}
/// <summary>
/// 创建 JSAPI 支付
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="request">JSAPI 支付请求参数</param>
/// <returns>支付结果,包含 prepay_id 和支付参数</returns>
/// <remarks>
/// JSAPI 支付适用于小程序、公众号等场景,需要用户的 OpenId
/// </remarks>
public async Task<WeChatPayResult> CreateJsApiPayAsync(WeChatPayConfig config, CreateJsApiPayRequest request)
{
try
{
var client = CreateClient(config);
var tenpayRequest = new CreatePayTransactionJsapiRequest()
{
OutTradeNumber = request.OutTradeNo,
AppId = request.AppId,
Description = request.Description,
NotifyUrl = request.NotifyUrl,
Amount = new CreatePayTransactionJsapiRequest.Types.Amount()
{
Total = request.TotalAmount,
Currency = request.Currency
},
Payer = new CreatePayTransactionJsapiRequest.Types.Payer()
{
OpenId = request.OpenId
}
};
if (request.ExpireTime.HasValue)
{
tenpayRequest.ExpireTime = request.ExpireTime.Value;
}
var response = await client.ExecuteCreatePayTransactionJsapiAsync(tenpayRequest);
if (response.IsSuccessful())
{
var result = new WeChatPayResult
{
Success = true,
TradeType = "JSAPI",
PrepayId = response.PrepayId
};
var parameters = client.GenerateParametersForJsapiPayRequest(request.AppId, response.PrepayId);
result.JsApiParameters = new JsApiPayParameters
{
AppId = parameters["appId"],
TimeStamp = parameters["timeStamp"],
NonceStr = parameters["nonceStr"],
Package = parameters["package"],
SignType = parameters["signType"],
PaySign = parameters["paySign"]
};
return result;
}
else
{
_logger.LogError($"微信 JSAPI 支付下单失败: {response.ErrorMessage}");
return new WeChatPayResult
{
Success = false,
Message = response.ErrorMessage
};
}
}
catch (Exception ex)
{
_logger.LogError(ex, "微信 JSAPI 支付下单异常");
return new WeChatPayResult
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 创建 Native 支付
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="request">Native 支付请求参数</param>
/// <returns>支付结果,包含二维码链接</returns>
/// <remarks>
/// Native 支付适用于生成二维码供用户扫描支付的场景
/// </remarks>
public async Task<WeChatPayResult> CreateNativePayAsync(WeChatPayConfig config, CreateNativePayRequest request)
{
try
{
var client = CreateClient(config);
var tenpayRequest = new CreatePayTransactionNativeRequest()
{
OutTradeNumber = request.OutTradeNo,
AppId = request.AppId,
Description = request.Description,
NotifyUrl = request.NotifyUrl,
Amount = new CreatePayTransactionNativeRequest.Types.Amount()
{
Total = request.TotalAmount,
Currency = request.Currency
}
};
if (request.ExpireTime.HasValue)
{
tenpayRequest.ExpireTime = request.ExpireTime.Value;
}
var response = await client.ExecuteCreatePayTransactionNativeAsync(tenpayRequest);
if (response.IsSuccessful())
{
return new WeChatPayResult
{
Success = true,
TradeType = "NATIVE",
CodeUrl = response.QrcodeUrl,
QrcodeUrl = response.QrcodeUrl
};
}
else
{
_logger.LogError($"微信 Native 支付下单失败: {response.ErrorMessage}");
return new WeChatPayResult
{
Success = false,
Message = response.ErrorMessage
};
}
}
catch (Exception ex)
{
_logger.LogError(ex, "微信 Native 支付下单异常");
return new WeChatPayResult
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 查询订单状态
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="outTradeNo">商户订单号</param>
/// <returns>订单查询结果</returns>
/// <remarks>
/// 根据商户订单号查询微信支付订单的状态
/// </remarks>
public async Task<WeChatPayQueryResult> QueryOrderAsync(WeChatPayConfig config, string outTradeNo)
{
try
{
var client = CreateClient(config);
var request = new GetPayTransactionByOutTradeNumberRequest()
{
OutTradeNumber = outTradeNo,
MerchantId = config.MerchantIdValue
};
var response = await client.ExecuteGetPayTransactionByOutTradeNumberAsync(request);
if (response.IsSuccessful())
{
return new WeChatPayQueryResult
{
Success = true,
TradeState = response.TradeState,
TradeStateDesc = response.TradeStateDescription,
TransactionId = response.TransactionId,
OutTradeNo = response.OutTradeNumber,
TotalAmount = response.Amount?.Total,
PayerAmount = response.Amount?.PayerTotal,
SuccessTime = response.SuccessTime
};
}
else
{
_logger.LogError($"查询订单失败: {response.ErrorMessage}");
return new WeChatPayQueryResult
{
Success = false,
Message = response.ErrorMessage
};
}
}
catch (Exception ex)
{
_logger.LogError(ex, "查询订单异常");
return new WeChatPayQueryResult
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 创建退款申请
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="request">退款请求参数</param>
/// <returns>退款结果</returns>
/// <remarks>
/// 提交退款申请,待完善实现
/// </remarks>
public async Task<WeChatRefundResult> CreateRefundAsync(WeChatPayConfig config, CreateRefundRequest request)
{
try
{
_logger.LogInformation($"微信退款申请: 商户单号={request.OutTradeNo}, 退款单号={request.OutRefundNo}");
return await Task.FromResult(new WeChatRefundResult
{
Success = false,
Message = "退款功能待完善"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "微信退款申请异常");
return new WeChatRefundResult
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 查询退款状态
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="outRefundNo">商户退款单号</param>
/// <returns>退款查询结果</returns>
/// <remarks>
/// 根据商户退款单号查询退款状态,待完善实现
/// </remarks>
public async Task<WeChatRefundQueryResult> QueryRefundAsync(WeChatPayConfig config, string outRefundNo)
{
try
{
_logger.LogInformation($"查询微信退款: 退款单号={outRefundNo}");
return await Task.FromResult(new WeChatRefundQueryResult
{
Success = false,
Message = "退款查询功能待完善"
});
}
catch (Exception ex)
{
_logger.LogError(ex, "查询退款异常");
return new WeChatRefundQueryResult
{
Success = false,
Message = ex.Message
};
}
}
/// <summary>
/// 验证支付通知签名
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="notifyRequest">支付通知请求参数</param>
/// <returns>签名是否有效</returns>
/// <remarks>
/// 验证微信支付通知的签名是否有效
/// </remarks>
public async Task<bool> VerifyPayNotifyAsync(WeChatPayConfig config, WeChatPayNotifyRequest notifyRequest)
{
try
{
var client = CreateClient(config);
bool valid = client.VerifyEventSignature(
notifyRequest.Timestamp,
notifyRequest.Nonce,
notifyRequest.Body,
notifyRequest.Signature,
notifyRequest.SerialNumber
);
return await Task.FromResult(valid);
}
catch (Exception ex)
{
_logger.LogError(ex, "验证支付通知签名异常");
return false;
}
}
/// <summary>
/// 验证退款通知签名
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="notifyRequest">退款通知请求参数</param>
/// <returns>签名是否有效</returns>
/// <remarks>
/// 验证微信退款通知的签名是否有效
/// </remarks>
public async Task<bool> VerifyRefundNotifyAsync(WeChatPayConfig config, WeChatRefundNotifyRequest notifyRequest)
{
try
{
var client = CreateClient(config);
bool valid = client.VerifyEventSignature(
notifyRequest.Timestamp,
notifyRequest.Nonce,
notifyRequest.Body,
notifyRequest.Signature,
notifyRequest.SerialNumber
);
return await Task.FromResult(valid);
}
catch (Exception ex)
{
_logger.LogError(ex, "验证退款通知签名异常");
return false;
}
}
/// <summary>
/// 解密支付通知数据
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="notifyRequest">支付通知请求参数</param>
/// <returns>解密后的支付通知数据,失败返回 null</returns>
/// <remarks>
/// 解密微信支付通知的加密数据,仅处理 TRANSACTION.SUCCESS 事件
/// </remarks>
public WeChatPayNotifyData? DecryptPayNotifyData(WeChatPayConfig config, WeChatPayNotifyRequest notifyRequest)
{
try
{
var client = CreateClient(config);
var callbackModel = client.DeserializeEvent(notifyRequest.Body);
if (callbackModel.EventType == "TRANSACTION.SUCCESS")
{
var eventData = client.DecryptEventResource<SKIT.FlurlHttpClient.Wechat.TenpayV3.Events.TransactionResource>(callbackModel);
return new WeChatPayNotifyData
{
EventType = callbackModel.EventType,
OutTradeNo = eventData.OutTradeNumber,
TransactionId = eventData.TransactionId,
TradeState = eventData.TradeState,
TotalAmount = eventData.Amount?.Total,
PayerAmount = eventData.Amount?.PayerTotal,
SuccessTime = eventData.SuccessTime
};
}
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "解密支付通知数据异常");
return null;
}
}
/// <summary>
/// 解密退款通知数据
/// </summary>
/// <param name="config">微信支付配置信息</param>
/// <param name="notifyRequest">退款通知请求参数</param>
/// <returns>解密后的退款通知数据,失败返回 null</returns>
/// <remarks>
/// 解密微信退款通知的加密数据,处理 REFUND.SUCCESS、REFUND.ABNORMAL、REFUND.CLOSED 事件
/// </remarks>
public WeChatRefundNotifyData? DecryptRefundNotifyData(WeChatPayConfig config, WeChatRefundNotifyRequest notifyRequest)
{
try
{
var client = CreateClient(config);
var callbackModel = client.DeserializeEvent(notifyRequest.Body);
if (callbackModel.EventType == "REFUND.SUCCESS" || callbackModel.EventType == "REFUND.ABNORMAL" || callbackModel.EventType == "REFUND.CLOSED")
{
var eventData = client.DecryptEventResource<SKIT.FlurlHttpClient.Wechat.TenpayV3.Events.RefundResource>(callbackModel);
return new WeChatRefundNotifyData
{
EventType = callbackModel.EventType,
OutTradeNo = eventData.OutTradeNumber,
OutRefundNo = eventData.OutRefundNumber,
RefundId = eventData.RefundId,
RefundStatus = eventData.RefundStatus,
RefundAmount = eventData.Amount?.Refund,
PayerRefundAmount = eventData.Amount?.PayerRefund,
SuccessTime = eventData.SuccessTime
};
}
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "解密退款通知数据异常");
return null;
}
}
}