准备工作
介绍
支付宝服务商模式,可以替商户完成收款功能,商户无需自己研发即可完成收款功能。基于saas企业开发可以采用此种模式替商户完成支付流程
引入如下Nuget包依赖
<PackageReference Include="AlipaySDKNet.Standard" Version="4.6.0" />
<PackageReference Include="Essensoft.AspNetCore.Payment.Alipay" Version="2.4.6" />
支付宝子商户配置
"AlipayPartnerPayConfig": { // 支付宝服务商配置
"appId": "20**************", //服务商AppId
"alipayPublicKey": "********************",//支付宝公钥
"appPrivateKey": "***************************",//应用私钥
"payExpireTime": "35" //支付过期时间(单位:分)
},
代码实现
支付宝支付入口
#region
_logger.LogInformation($"开始发起支付宝支付");
//支付宝支付
var alipayPayRequest = new AlipayH5PayCreateRequest {
MemberCode = memberCode,
OutTradeNo = orderInfo.OutOrderId, // 第三方订单号
Subject = orderInfo.ProductName,
TotalAmount = orderInfo.OrderAmount?.ToString("#0.00"),
NotifyUrl = $"{_apiUrlConfigOption.NotifyUrl}/api/Cashier/alipayPayCallback",
ReturnUrl = request.ReturnUrl ?? orderInfo.ReturnUrl, // request.ReturnUrl,
QuitUrl = request.QuitUrl
};
_logger.LogInformation($"支付宝支付,调服务层基础服务接口,入参:{alipayPayRequest.ToJson()}");
var rsAlipay = await _partnerPaymentService.AlipayH5PayCreate(alipayPayRequest);
_logger.LogInformation($"支付宝支付,调服务层基础服务接口,结果:{rsAlipay?.ToJson()}");
if (rsAlipay == null || rsAlipay.Code != "0") {
return (false, rsAlipay?.Message ?? "支付宝支付失败");
}
return (true, rsAlipay.Data);
#endregion
PartnerPaymentService.cs服务商支付核心代码
/// <summary>
/// 服务商模式支付
/// </summary>
public class PartnerPaymentService : IPartnerPaymentService
{
/// <summary>
/// 实体映射
/// </summary>
protected IMapper _mapper;
private readonly ILogger _logger;
private readonly ITracer _tracer;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAliPaymentService _aliPaymentService;
private readonly IOptions<AlipayOptions> _optionsAccessor;
private readonly IOptions<WechatPayPartnerPayConfigOption> _wechatPayPartnerPayConfig;
private readonly IOptions<AlipayPartnerPayConfigOption> _AlipayPartnerPayConfig;
private readonly IProxyHttpClient _proxyHttpClient;
private readonly AppSettings _appSettings;
private readonly ApiUrlConfigOption _apiUrlConfigOption;
/// <summary>
/// 初始化一个<see cref="PartnerPaymentService"/>类型的实例
/// </summary>
/// <param name="mapper">实体映射</param>
/// <param name="logger"></param>
/// <param name="tracer"></param>
/// <param name="aliPaymentService"></param>
/// <param name="optionsAccessor"></param>
/// <param name="apiEndpoints"></param>
/// <param name="httpContextAccessor"></param>
public PartnerPaymentService(IMapper mapper, ILogger<PartnerPaymentService> logger,
ITracer tracer,
IHttpContextAccessor httpContextAccessor,
IOptions<WechatPayPartnerPayConfigOption> wechatPayPartnerPayConfig,
IOptions<AlipayPartnerPayConfigOption> alipayPartnerPayConfig,
IAliPaymentService aliPaymentService,
IOptions<AlipayOptions> optionsAccessor,
IOptions<ApiUrlConfigOption> apiUrlConfigOption,
IProxyHttpClient proxyHttpClient,
AppSettings appSettings) {
_mapper = mapper;
_logger = logger;
_tracer = tracer;
_httpContextAccessor = httpContextAccessor;
_wechatPayPartnerPayConfig = wechatPayPartnerPayConfig;
_AlipayPartnerPayConfig = alipayPartnerPayConfig;
_aliPaymentService = aliPaymentService;
_optionsAccessor = optionsAccessor;
_proxyHttpClient = proxyHttpClient;
_appSettings = appSettings;
_apiUrlConfigOption = apiUrlConfigOption.Value;
}
#region 支付宝
/// <summary>
/// 支付宝支付
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResponse<string>> AlipayH5PayCreate(AlipayH5PayCreateRequest request) {
_logger.LogInformation($"支付宝支付,入参:{request.ToJson()}");
//获得支付配置
var (options, alipayConfig) = await GetAlipayPayOptions(request.MemberCode);
//切换动态配置
_aliPaymentService.OptionsAccessor = new OptionsWrapper<AlipayOptions>(options);
var param = new AlipayWapPayModel {
OutTradeNo = request.OutTradeNo,
Subject = request.Subject,
SellerId = alipayConfig.PId,
TotalAmount = request.TotalAmount,
NotifyUrl = request.NotifyUrl,
ReturnUrl = request.ReturnUrl,
AppAuthToken = alipayConfig.AppAuthToken,
PayExpireTime = alipayConfig.PayExpireTime,
QuitUrl = request.QuitUrl
};
var (result, response) = await _aliPaymentService.WapPay(param);
_logger.LogInformation($"支付宝支付,结果:{response?.ToJson()}_{result}");
if (string.IsNullOrWhiteSpace(result) || response == null || response.IsError) {
return ResponseResult.Execute(string.Empty, "-1", $"{response?.Code}{response?.SubMsg ?? "支付宝错误"}");
}
return ResponseResult.Execute(result);
}
/// <summary>
/// 支付宝支付查询
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResponse<PayQueryResponse>> AlipayPayQuery(AlipayPayQueryRequest request) {
_logger.LogInformation($"支付宝支付查询,入参:{request.ToJson()}");
//获得支付配置
var (options, aplipayConfig) = await GetAlipayPayOptions(request.MemberCode);
//切换动态配置
_aliPaymentService.OptionsAccessor = new OptionsWrapper<AlipayOptions>(options);
var param = new AlipayQueryModel {
OutTradeNo = request.OutTradeNo,
AppAuthToken = aplipayConfig.AppAuthToken
};
var response = await _aliPaymentService.Query(param);
_logger.LogInformation($"支付宝支付查询,结果:{response?.ToJson()}");
if (response == null || response.IsError) {
return ResponseResult.Execute(new PayQueryResponse(), "-1", $"{response?.Code}{response?.SubMsg ?? "支付宝错误"}");
}
return ResponseResult.Execute(new PayQueryResponse {
TradeState = response.TradeStatus,
PayNumber = response.TradeNo
});
}
/// <summary>
/// 支付宝支付退款
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<DataResponse<bool>> AlipayPayRefund(AlipayPayRefundRequest request) {
_logger.LogInformation($"支付宝支付退款,入参:{request.ToJson()}");
//获得支付配置
var (options, aplipayConfig) = await GetAlipayPayOptions(request.MemberCode);
//切换动态配置
_aliPaymentService.OptionsAccessor = new OptionsWrapper<AlipayOptions>(options);
var param = new AlipayRefundModel {
OutTradeNo = request.OutTradeNo,
RefundAmount = request.RefundAmount,
AppAuthToken = aplipayConfig.AppAuthToken,
OutRequestNo = request.OutRequestNo
};
var (result, response) = await _aliPaymentService.Refund(param);
_logger.LogInformation($"支付宝支付退款,结果:{response?.ToJson()}_{result}");
if (response == null || response.IsError) {
return ResponseResult.Execute(false, "-1", $"{response?.Code}{response?.SubMsg ?? "支付宝错误"}");
}
return ResponseResult.Execute(result);
}
#endregion
/// <summary>
/// 支付宝服务商支付options
/// </summary>
/// <param name="tenantId"></param>
/// <returns></returns>
private async Task<(AlipayOptions, AlipayPayPartnerPayConfigModel)> GetAlipayPayOptions(string tenantId = null) {
var payConfig = await GetAlipayPayConfig("AlipayPartnerPay", tenantId);
var options = new AlipayOptions {
AppId = payConfig.AppId,
AlipayPublicKey = payConfig.AlipayPublicKey,
AppPrivateKey = payConfig.AppPrivateKey
};
return (options, payConfig);
}
/// <summary>
/// 获取支付宝支付配置
/// </summary>
/// <param name="parameterCode"></param>
/// <param name="memberCode"></param>
/// <returns></returns>
public async Task<AlipayPayPartnerPayConfigModel> GetAlipayPayConfig(string parameterCode, string memberCode = null) {
var payConfig = new AlipayPayPartnerPayConfigModel {
AppId = _AlipayPartnerPayConfig.Value.AppId,
AlipayPublicKey = _AlipayPartnerPayConfig.Value.AlipayPublicKey,
AppPrivateKey = _AlipayPartnerPayConfig.Value.AppPrivateKey,
PayExpireTime = _AlipayPartnerPayConfig.Value.PayExpireTime,
};
if (!string.IsNullOrWhiteSpace(memberCode)) {
payConfig.PId = "******";
payConfig.AppAuthToken = "******";
}
return payConfig;
}
}
AliPaymentService.cs 支付宝底层支付核心方法
using System;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Aop.Api;
using Essensoft.AspNetCore.Payment.Alipay;
using ICH.Util.Extension;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Payment.Alipay.Model;
namespace Payment.Alipay
{
public class AliPaymentService : IAliPaymentService
{
private readonly IAlipayClient _client;
public IOptions<AlipayOptions> OptionsAccessor { get; set; }
private readonly ILogger _logger;
public AliPaymentService(IOptions<AlipayOptions> optionsAccessor, IAlipayClient client, ILogger<AliPaymentService> logger)
{
OptionsAccessor = optionsAccessor;
_client = client;
_logger = logger;
}
public async Task<(string, Aop.Api.Response.AlipayTradeWapPayResponse)> WapPay(AlipayWapPayModel model)
{
_logger.LogInformation($"支付宝wap支付入参:{model.ToJson()}");
var payModel = new Aop.Api.Domain.AlipayTradeWapPayModel
{
Body = model.Body,
Subject = model.Subject,
TotalAmount = model.TotalAmount,
OutTradeNo = model.OutTradeNo,
SellerId = model.SellerId,
PassbackParams = HttpUtility.UrlEncode(model.PassbackParams),
ProductCode = model.ProductCode,
TimeExpire = DateTime.Now.AddMinutes(model.PayExpireTime).ToString("yyyy-MM-dd HH:mm"),
QuitUrl = model.QuitUrl
};
var request = new Aop.Api.Request.AlipayTradeWapPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
//此次只是参数展示,未进行字符串转义,实际情况下请转义
request.SetBizModel(payModel);
request.SetNotifyUrl(model.NotifyUrl);
request.SetReturnUrl(model.ReturnUrl);
_logger.LogInformation($"支付宝支付:{model.ToJson()}");
DefaultAopClient client = new Aop.Api.DefaultAopClient("https://openapi.alipay.com/gateway.do",
OptionsAccessor.Value.AppId, OptionsAccessor.Value.AppPrivateKey, "json", "1.0", "RSA2",
OptionsAccessor.Value.AlipayPublicKey, "utf-8", false);
var response = client.pageExecute(request, null, model.AppAuthToken,"POST");
//调用成功,则处理业务逻辑
if(!response.IsError)
{
var body = response?.Body;
return (GetWapPayUrlToHtml(body), response);
}
return (string.Empty, new Aop.Api.Response.AlipayTradeWapPayResponse());
}
public async Task<(bool, Aop.Api.Response.AlipayTradeRefundResponse)> Refund(AlipayRefundModel model)
{
_logger.LogInformation($"支付宝退款入参:{model.ToJson()}");
var refundModel = new Aop.Api.Domain.AlipayTradeRefundModel()
{
OutTradeNo = model.OutTradeNo,
TradeNo = model.TradeNo,
RefundAmount = model.RefundAmount,
OutRequestNo = model.OutRequestNo,
RefundReason = model.RefundReason
};
var request = new Aop.Api.Request.AlipayTradeRefundRequest();
request.SetBizModel(refundModel);
_logger.LogInformation($"支付宝退款:{refundModel.ToJson()}");
DefaultAopClient client = new Aop.Api.DefaultAopClient("https://openapi.alipay.com/gateway.do",
OptionsAccessor.Value.AppId, OptionsAccessor.Value.AppPrivateKey, "json", "1.0", "RSA2",
OptionsAccessor.Value.AlipayPublicKey, "utf-8", false);
var response = client.Execute(request, null, model.AppAuthToken,"POST");
_logger.LogInformation($"aliRefundResponse:{response?.ToJson()}");
return (response?.IsError == false && response.Code ==
"10000" && response.Msg == "Success" && response.FundChange == "Y", response);
}
/// <summary>
/// 查询
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<Aop.Api.Response.AlipayTradeQueryResponse> Query(AlipayQueryModel model)
{
var queryModel = new Aop.Api.Domain.AlipayTradeQueryModel
{
OutTradeNo = model.OutTradeNo,
TradeNo = model.TradeNo
};
var request = new Aop.Api.Request.AlipayTradeQueryRequest();
request.SetBizModel(queryModel);
_logger.LogInformation($"支付宝查询:{queryModel.ToJson()}");
DefaultAopClient client = new Aop.Api.DefaultAopClient("https://openapi.alipay.com/gateway.do",
OptionsAccessor.Value.AppId, OptionsAccessor.Value.AppPrivateKey, "json", "1.0", "RSA2",
OptionsAccessor.Value.AlipayPublicKey, "utf-8", false);
var response = client.Execute(request, null, model.AppAuthToken,"POST");
_logger.LogInformation($"wechatAlipayResponse:{response?.ToJson()}");
return response;
}
/// <summary>
/// 通过form获取url
/// </summary>
/// <param name="payForm"></param>
/// <returns></returns>
private static string GetWapPayUrlToHtml(string payForm)
{
HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
document.LoadHtml(payForm);
try
{
var rootnode = document.DocumentNode;
var form = rootnode.SelectSingleNode("form");
var urlSb = new StringBuilder();
var url = form.Attributes["action"].Value;
url = url.Split('?')[0];
urlSb.Append(url);
var appid = form.SelectSingleNode("input[@name=\"app_id\"]").Attributes["value"].Value;
urlSb.Append("?app_id=" + appid);
var bizContent = form.SelectSingleNode("input[@name=\"biz_content\"]").Attributes["value"].Value;
urlSb.Append("&biz_content=" + System.Web.HttpUtility.UrlEncode(bizContent));
var charset = form.SelectSingleNode("input[@name=\"charset\"]").Attributes["value"].Value;
urlSb.Append("&charset=" + charset);
var format = form.SelectSingleNode("input[@name=\"format\"]").Attributes["value"].Value;
urlSb.Append("&format=" + format);
var method = form.SelectSingleNode("input[@name=\"method\"]").Attributes["value"].Value;
urlSb.Append("&method=" + System.Web.HttpUtility.UrlEncode(method));
var notifyUrl = form.SelectSingleNode("input[@name=\"notify_url\"]").Attributes["value"].Value;
urlSb.Append("¬ify_url=" + System.Web.HttpUtility.UrlEncode(notifyUrl));
var returnUrl = form.SelectSingleNode("input[@name=\"return_url\"]").Attributes["value"].Value;
urlSb.Append("&return_url=" + System.Web.HttpUtility.UrlEncode(returnUrl));
var signType = form.SelectSingleNode("input[@name=\"sign_type\"]").Attributes["value"].Value;
urlSb.Append("&sign_type=" + signType);
var timestamp = form.SelectSingleNode("input[@name=\"timestamp\"]").Attributes["value"].Value;
urlSb.Append("×tamp=" + System.Web.HttpUtility.UrlEncode(timestamp));
var version = form.SelectSingleNode("input[@name=\"version\"]").Attributes["value"].Value;
urlSb.Append("&version=" + version);
var sign = form.SelectSingleNode("input[@name=\"sign\"]").Attributes["value"].Value;
urlSb.Append("&sign=" + System.Web.HttpUtility.UrlEncode(sign));
var alipaySdk = form.SelectSingleNode("input[@name=\"alipay_sdk\"]")?.Attributes["value"]?.Value;
if (!string.IsNullOrWhiteSpace(alipaySdk))
{
urlSb.Append("&alipay_sdk=" + System.Web.HttpUtility.UrlEncode(alipaySdk));
}
var appAuthToken = form.SelectSingleNode("input[@name=\"app_auth_token\"]")?.Attributes["value"]?.Value;
if (!string.IsNullOrWhiteSpace(appAuthToken))
{
urlSb.Append("&app_auth_token=" + System.Web.HttpUtility.UrlEncode(appAuthToken));
}
return urlSb.ToString();
}
catch
{
return "";
}
}
}
}
总结
- 子商户需要签约服务商模式特约商户才可以支付
- 签约为子商户后会得到{“pid”:““, “appAuthToken”:””}