460 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
using System;
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>
/// 提交退款申请,当前为模拟实现,需根据实际微信支付SDK完善
/// </remarks>
public async Task<WeChatRefundResult> CreateRefundAsync(WeChatPayConfig config, CreateRefundRequest request)
{
try
{
_logger.LogInformation($"微信退款申请: 商户单号={request.OutTradeNo}, 退款单号={request.OutRefundNo}, 退款金额={request.RefundAmount}");
// 模拟退款成功(实际项目中需要调用微信支付SDK)
return await Task.FromResult(new WeChatRefundResult
{
Success = true,
RefundId = Guid.NewGuid().ToString("N"),
OutRefundNo = request.OutRefundNo,
OutTradeNo = request.OutTradeNo,
RefundStatus = "SUCCESS",
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}");
// 模拟退款查询(实际项目中需要调用微信支付SDK)
return await Task.FromResult(new WeChatRefundQueryResult
{
Success = true,
RefundId = outRefundNo,
OutRefundNo = outRefundNo,
RefundStatus = "SUCCESS",
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;
}
}
}