GaoXinLibrary.PaySDK
1.0.0
See the version list below for details.
dotnet add package GaoXinLibrary.PaySDK --version 1.0.0
NuGet\Install-Package GaoXinLibrary.PaySDK -Version 1.0.0
<PackageReference Include="GaoXinLibrary.PaySDK" Version="1.0.0" />
<PackageVersion Include="GaoXinLibrary.PaySDK" Version="1.0.0" />
<PackageReference Include="GaoXinLibrary.PaySDK" />
paket add GaoXinLibrary.PaySDK --version 1.0.0
#r "nuget: GaoXinLibrary.PaySDK, 1.0.0"
#:package GaoXinLibrary.PaySDK@1.0.0
#addin nuget:?package=GaoXinLibrary.PaySDK&version=1.0.0
#tool nuget:?package=GaoXinLibrary.PaySDK&version=1.0.0
GaoXinLibrary.PaySDK
统一支付 SDK,支持微信支付 v3、支付宝、银联三大渠道,兼容 .NET 8 / 9 / 10,提供 DI 注入支持。
目录
功能矩阵
| 功能 | 微信支付 | 支付宝 | 银联 |
|---|---|---|---|
| JSAPI / 公众号支付 | ✅ | ✅ | — |
| APP 支付 | ✅ | ✅ | — |
| H5 / 手机网站支付 | ✅ | ✅ | ✅(WAP) |
| Native / 扫码支付 | ✅ | ✅(订单码) | ✅(二维码主扫) |
| 小程序支付 | ✅ | — | — |
| 当面付(B 扫 C 条码) | — | ✅ | ✅(二维码被扫) |
| 电脑网站支付 | — | ✅ | — |
| 在线网关支付 | — | — | ✅ |
| WAP 支付 | — | — | ✅ |
| 无跳转支付 | — | — | ✅ |
| 二维码支付(主扫/被扫) | — | — | ✅ |
| 签约支付 | — | — | ✅ |
| 云闪付(无感支付) | — | — | ✅ |
| Apple Pay | — | — | ✅ |
| 订单查询 | ✅ | ✅ | ✅ |
| 关闭/撤销订单 | ✅ | ✅(含 cancel) | ✅* |
| 申请退款 | ✅ | ✅ | ✅ |
| 退款查询 | ✅ | ✅ | ✅ |
| 异常退款 | ✅ | — | — |
| 账单下载 | ✅ | ✅ | ✅ |
| 支付回调解析/验签 | ✅ | ✅ | ✅ |
| 退款回调解析 | ✅ | — | — |
| 敏感字段加密 | ✅ | — | — |
| 敏感字段解密 | ✅ | — | — |
| 平台证书下载/管理 | ✅ | — | — |
| 跨境电商海关申报 | — | — | ✅ |
| 加密公钥更新查询 | — | — | ✅ |
| 实名认证 | — | — | ✅ |
| 文件传输(对账文件下载) | — | — | ✅ |
| 商家分账(关系绑定/订单分账) | — | ✅ | — |
| 商家转账 | — | ✅ | — |
| OpenAPI 独立模块(OAuth2/非对称) | — | — | ✅(新增) |
*银联关闭订单:银联网关支付未支付订单自动超时关闭,SDK 的统一接口返回
Success=true+IsSimulated=true,并且OperationMode=Simulated以保持一致性,调用方可通过该字段识别是否真实调用了平台接口。
快速开始
安装
dotnet add package GaoXinLibrary.PaySDK
DI 注入(推荐)
方式一:统一注册(推荐)
// Program.cs
builder.Services.AddPaySDK(sdk =>
{
// 微信支付
sdk.AddWechatPay(opt =>
{
opt.AppId = "wx_your_appid";
opt.MchId = "1600000000";
opt.ApiV3Key = "your_32char_api_v3_key";
opt.PrivateKey = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----";
opt.CertSerialNo = "your_cert_serial_no";
opt.NotifyUrl = "https://your-site.com/pay/wechat/notify"; // 支付异步回调地址(全局默认)
opt.RefundNotifyUrl = "https://your-site.com/pay/wechat/refund-notify"; // 退款异步回调地址(全局默认,可选)
// 新版公钥模式(推荐)
opt.PlatformPublicKey = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----";
opt.PlatformPublicKeyId = "PUB_KEY_ID_xxxx";
// 或旧版平台证书模式(留空 PlatformPublicKeyId,通过 DownloadCertificatesAsync 自动管理)
});
// 支付宝
sdk.AddAlipay(opt =>
{
opt.AppId = "2021000000000000";
opt.PrivateKey = "your_rsa2_private_key";
opt.AlipayPublicKey = "alipay_rsa2_public_key";
opt.NotifyUrl = "https://your-site.com/pay/alipay/notify"; // 异步回调地址(全局默认)
opt.ReturnUrl = "https://your-site.com/pay/return"; // 同步跳转地址(全局默认)
});
// 银联
sdk.AddUnionPay(opt =>
{
opt.MerId = "your_mer_id";
opt.CertId = "your_cert_id";
opt.PrivateKey = "your_rsa_private_key_pem";
opt.UnionPayPublicKey = "unionpay_verify_public_key_pem";
opt.FrontUrl = "https://your-site.com/pay/unionpay/front";
opt.BackUrl = "https://your-site.com/pay/unionpay/notify";
});
});
// 注入统一接口
public class OrderService(IPayService pay) { ... }
方式二:按渠道单独注册
builder.Services
.AddWechatPay(opt => { /* ... */ })
.AddAlipay(opt => { /* ... */ })
.AddUnionPay(opt => { /* ... */ })
.AddPayService(); // 注册统一路由(可选)
// 按渠道注入
public class MyService(IWechatPayService wechat, IAlipayService alipay) { ... }
不使用 DI(直接创建客户端)
// 微信支付
using var wechatClient = WechatPayClient.Create(new WechatPayOptions
{
AppId = "wx_your_appid",
MchId = "1600000000",
ApiV3Key = "your_api_v3_key",
PrivateKey = "your_private_key_pem",
CertSerialNo = "your_cert_serial_no",
NotifyUrl = "https://your-site.com/pay/wechat/notify"
});
var resp = await wechatClient.Pay.CreateNativeOrderAsync(request);
// 支付宝
using var alipayClient = AlipayClient.Create(new AlipayOptions
{
AppId = "2021000000000000",
PrivateKey = "your_private_key",
AlipayPublicKey = "alipay_public_key",
NotifyUrl = "https://your-site.com/pay/alipay/notify",
ReturnUrl = "https://your-site.com/pay/return"
});
var preResp = await alipayClient.Pay.PrecreateAsync(content);
// 银联
using var unionPayClient = UnionPayClient.Create(new UnionPayOptions
{
MerId = "your_mer_id",
CertId = "your_cert_id",
PrivateKey = "your_private_key_pem",
UnionPayPublicKey = "unionpay_public_key_pem",
FrontUrl = "https://your-site.com/pay/unionpay/front",
BackUrl = "https://your-site.com/pay/unionpay/notify"
});
var formHtml = unionPayClient.Pay.CreateFrontPay(request);
// 银联海关申报(通过 Customs 属性访问)
var declareResp = await unionPayClient.Customs.DeclareAsync(customsRequest);
统一接口用法
通过 IPayService 统一接口,使用 PayChannel 枚举指定支付渠道,所有金额单位均为分。
PayChannel 枚举值
| 微信支付 | 支付宝 | 银联 |
|---|---|---|
WechatJsapi |
AlipayFaceToFace |
UnionPayGateway(在线网关支付) |
WechatApp |
AlipayPrecreate |
UnionPayNoRedirect(无跳转支付) |
WechatH5 |
AlipayJsapi |
UnionPayWap(WAP 手机网页支付) |
WechatNative |
AlipayApp |
UnionPayQrCode(二维码支付) |
WechatMiniProgram |
AlipayWap |
UnionPayContract(签约支付) |
AlipayPage |
UnionPayQuickPass(云闪付/无感支付) |
|
UnionPayApplePay(Apple Pay) |
创建支付订单
public class OrderService(IPayService pay)
{
// 微信 Native 扫码支付
public async Task<string> WechatNativePayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.WechatNative,
OutTradeNo = "order_001",
Subject = "商品描述",
TotalFee = 100, // 1 元 = 100 分
NotifyUrl = "https://your-site.com/pay/wechat/notify"
});
return resp.CodeUrl!; // 生成二维码给用户扫码
}
// 微信 JSAPI 公众号支付(需要 OpenId)
public async Task<WechatJsPayParams> WechatJsapiPayAsync(string openId)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.WechatJsapi,
OutTradeNo = "order_002",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/wechat/notify",
OpenId = openId
});
return resp.JsPayParams!; // 前端 JS-SDK 调起支付参数
}
// 微信 APP 支付
public async Task<string> WechatAppPayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.WechatApp,
OutTradeNo = "order_003",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/wechat/notify"
});
return resp.SdkOrderString!; // APP SDK 调起参数 JSON
}
// 微信 H5 支付(手机浏览器)
public async Task<string> WechatH5PayAsync(string clientIp)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.WechatH5,
OutTradeNo = "order_004",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/wechat/notify",
ClientIp = clientIp,
SceneType = "Wap"
});
return resp.PayUrl!; // 跳转链接
}
// 微信小程序支付(需要 OpenId)
public async Task<WechatJsPayParams> WechatMiniProgramPayAsync(string openId)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.WechatMiniProgram,
OutTradeNo = "order_005",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/wechat/notify",
OpenId = openId
});
return resp.JsPayParams!; // 小程序 wx.requestPayment 参数
}
// 支付宝当面付(商家扫用户付款码 - B扫C)
public async Task<string> AlipayFaceToFacePayAsync(string authCode)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayFaceToFace,
OutTradeNo = "order_006",
Subject = "商品描述",
TotalFee = 100,
AuthCode = authCode // 用户付款码(25-36位数字)
});
return resp.PrepayId!; // 支付宝交易号
}
// 支付宝订单码支付(生成二维码 - C扫B)
public async Task<string> AlipayPrecreateAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayPrecreate,
OutTradeNo = "order_007",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/alipay/notify"
});
return resp.CodeUrl!; // 二维码链接
}
// 支付宝 JSAPI 支付(生活号/小程序内)
public async Task<string> AlipayJsapiPayAsync(string buyerOpenId)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayJsapi,
OutTradeNo = "order_008",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/alipay/notify",
OpenId = buyerOpenId, // buyer_open_id
Extra = new Dictionary<string, string>
{
["OpAppId"] = "your_op_appid" // 可选
}
});
return resp.PrepayId!; // trade_no, 前端 JS-SDK 唤起支付
}
// 支付宝 APP 支付
public async Task<string> AlipayAppPayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayApp,
OutTradeNo = "order_009",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/alipay/notify"
});
return resp.SdkOrderString!; // APP SDK 签名字符串
}
// 支付宝手机网站支付(WAP)
public async Task<string> AlipayWapPayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayWap,
OutTradeNo = "order_010",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/alipay/notify",
ReturnUrl = "https://your-site.com/pay/return"
});
return resp.PayUrl!; // 跳转 URL
}
// 支付宝电脑网站支付(PC)
public async Task<string> AlipayPagePayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.AlipayPage,
OutTradeNo = "order_011",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/alipay/notify",
ReturnUrl = "https://your-site.com/pay/return"
});
return resp.PayUrl!; // 跳转 URL
}
// 银联网关支付(返回自动提交 HTML 表单)
public async Task<string> UnionPayAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.UnionPayGateway,
OutTradeNo = "order_012",
Subject = "商品描述",
TotalFee = 100, // 银联金额单位同为分
NotifyUrl = "https://your-site.com/pay/unionpay/notify",
ReturnUrl = "https://your-site.com/pay/unionpay/front"
});
return resp.PayUrl!; // HTML 自动提交表单
}
// 银联 WAP 手机网页支付
public async Task<string> UnionPayWapAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.UnionPayWap,
OutTradeNo = "order_013",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/unionpay/notify",
ReturnUrl = "https://your-site.com/pay/unionpay/front"
});
return resp.PayUrl!; // HTML 自动提交表单(WAP 页)
}
// 银联二维码支付(主扫 — 生成二维码供用户扫码)
public async Task<string> UnionPayQrCodeAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.UnionPayQrCode,
OutTradeNo = "order_014",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/unionpay/notify"
});
return resp.CodeUrl!; // 二维码链接
}
// 银联无跳转支付(后台消费,需卡号和持卡人信息)
public async Task<string> UnionPayNoRedirectAsync()
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.UnionPayNoRedirect,
OutTradeNo = "order_015",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/unionpay/notify",
Extra = new Dictionary<string, string>
{
["AccNo"] = "6222021234567890", // 卡号
["CustomerInfo"] = "{...}" // 持卡人信息 JSON
}
});
return resp.PrepayId!; // 交易流水号
}
// 银联 Apple Pay(基于 Token 的移动端支付)
public async Task<string> UnionPayApplePayAsync(string payData)
{
var resp = await pay.CreateOrderAsync(new CreateOrderRequest
{
Channel = PayChannel.UnionPayApplePay,
OutTradeNo = "order_016",
Subject = "商品描述",
TotalFee = 100,
NotifyUrl = "https://your-site.com/pay/unionpay/notify",
Extra = new Dictionary<string, string>
{
["PayData"] = payData // Apple Pay Token 数据
}
});
return resp.PrepayId!; // 交易流水号
}
}
查询订单
var result = await pay.QueryOrderAsync(new QueryOrderRequest
{
Channel = PayChannel.WechatNative, // 或任意渠道
OutTradeNo = "order_001" // 商户订单号
// 也可使用: TransactionId = "平台交易号"
});
// result.TradeStatus: SUCCESS / NOTPAY / CLOSED / REFUND ...
// result.TotalFee: 订单金额(分)
// result.SuccessTime: 支付完成时间
申请退款
var result = await pay.RefundAsync(new RefundRequest
{
Channel = PayChannel.WechatNative,
OutTradeNo = "order_001",
OutRefundNo = "refund_001",
RefundFee = 50, // 退款金额(分)
TotalFee = 100, // 原订单总额(分)
Reason = "用户申请退款",
NotifyUrl = "https://your-site.com/pay/wechat/refund-notify" // 可选
});
// result.RefundStatus: SUCCESS / PROCESSING / CLOSED / ABNORMAL
查询退款
var result = await pay.QueryRefundAsync(new QueryRefundRequest
{
Channel = PayChannel.WechatNative,
OutRefundNo = "refund_001"
});
// result.RefundStatus / result.RefundFee
关闭订单
var result = await pay.CloseOrderAsync(new CloseOrderRequest
{
Channel = PayChannel.WechatNative,
OutTradeNo = "order_001"
});
// result.Success: true
// result.IsSimulated: false(微信/支付宝为实际关闭)
// 银联网关支付不提供关闭订单 API,未支付订单会自动超时关闭
// SDK 返回 Success=true + IsSimulated=true 以保持统一接口一致性
var unionResult = await pay.CloseOrderAsync(new CloseOrderRequest
{
Channel = PayChannel.UnionPayGateway,
OutTradeNo = "order_012"
});
// unionResult.Success: true
// unionResult.IsSimulated: true(未实际调用银联 API)
// unionResult.OperationMode: Simulated(可机读识别模拟语义)
IsSimulated/OperationMode标记说明:当IsSimulated == true或OperationMode == Simulated时,表示 SDK 为保持接口一致性而返回的模拟成功,实际并未向支付平台发送关闭请求。调用方可根据该字段决定是否记录警告日志或做额外处理。
下载账单
// 微信交易账单
byte[] csv = await pay.DownloadBillAsync(new DownloadBillRequest
{
Channel = PayChannel.WechatNative,
BillDate = "20250101",
BillType = "ALL" // ALL / SUCCESS / REFUND
});
// 支付宝交易账单
byte[] aliCsv = await pay.DownloadBillAsync(new DownloadBillRequest
{
Channel = PayChannel.AlipayPage,
BillDate = "2025-01-01",
BillType = "trade" // trade / signcustomer
});
// 银联对账文件
byte[] unionCsv = await pay.DownloadBillAsync(new DownloadBillRequest
{
Channel = PayChannel.UnionPayGateway,
BillDate = "0119", // MMdd 格式
BillType = "00" // 00 = 普通对账文件
});
解析回调通知
⚠️ JSON 序列化中文显示提示
使用
JsonSerializer.Serialize()序列化回调对象时,默认会将中文转义为\uXXXX(如\u652F\u4ED8\u6210\u529F)。 推荐使用 SDK 内置的PayJsonSerializer工具类(已配置JavaScriptEncoder.UnsafeRelaxedJsonEscaping+ snake_case):// ❌ 中文会显示为 \uXXXX var json = JsonSerializer.Serialize(order); // ✅ 推荐:使用 PayJsonSerializer(跨渠道通用) using GaoXinLibrary.PaySDK.Core; var json = PayJsonSerializer.Serialize(order); // ✅ 也可使用 WechatPayHttpClient.JsonOptions(仅微信支付场景) using GaoXinLibrary.PaySDK.Wechat.Core; var json = JsonSerializer.Serialize(order, WechatPayHttpClient.JsonOptions);
// ── 微信支付回调(JSON Body + HTTP Header 签名验证) ──
[HttpPost("wechat/notify")]
public async Task<IActionResult> WechatNotify([FromServices] IPayService pay)
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
var headers = new Dictionary<string, string>
{
["Wechatpay-Timestamp"] = Request.Headers["Wechatpay-Timestamp"].ToString(),
["Wechatpay-Nonce"] = Request.Headers["Wechatpay-Nonce"].ToString(),
["Wechatpay-Signature"] = Request.Headers["Wechatpay-Signature"].ToString(),
["Wechatpay-Serial"] = Request.Headers["Wechatpay-Serial"].ToString()
};
var result = await pay.ParseCallbackAsync(PayChannel.WechatJsapi, body, headers);
if (!result.IsValid)
return BadRequest();
// result.OutTradeNo / result.TransactionId / result.TotalFee / result.TradeStatus
// 处理业务逻辑...
return Ok(new { code = "SUCCESS", message = "成功" });
}
// ── 支付宝回调(Form 表单 POST,签名验证) ──
[HttpPost("alipay/notify")]
public async Task<IActionResult> AlipayNotify([FromServices] IPayService pay)
{
var form = Request.Form.ToDictionary(k => k.Key, v => v.Value.ToString());
var formString = string.Join("&",
form.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
var result = await pay.ParseCallbackAsync(PayChannel.AlipayPage, formString);
if (!result.IsValid)
return BadRequest();
// result.OutTradeNo / result.TradeStatus ("TRADE_SUCCESS" / "TRADE_FINISHED")
return Content("success", "text/plain");
}
// ── 银联回调(Form 表单 POST,签名验证) ──
[HttpPost("unionpay/notify")]
public async Task<IActionResult> UnionPayNotify([FromServices] IPayService pay)
{
var form = Request.Form.ToDictionary(k => k.Key, v => v.Value.ToString());
var formString = string.Join("&",
form.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
var result = await pay.ParseCallbackAsync(PayChannel.UnionPayGateway, formString);
if (!result.IsValid)
return BadRequest();
// result.OutTradeNo / result.TransactionId / result.TotalFee
return Content("ok", "text/plain");
}
渠道独立接口
除了统一 IPayService,你也可以直接注入各渠道独立接口获得更精细的控制。
微信支付独立接口
public class WechatController(IWechatPayService wechat)
{
// JSAPI 下单(公众号/小程序内网页支付)
public async Task<WechatJsPayParams> JsapiPayAsync(string openId)
{
var resp = await wechat.CreateJsapiOrderAsync(new WechatJsapiOrderRequest
{
OutTradeNo = "order_001",
Description = "商品描述",
NotifyUrl = "https://your-site.com/pay/wechat/notify",
Amount = new WechatPayAmount { Total = 100 },
Payer = new WechatPayPayer { OpenId = openId }
});
return wechat.BuildJsPayParams(resp.PrepayId);
}
// APP 下单
public async Task<WechatAppPayParams> AppPayAsync()
{
var resp = await wechat.CreateAppOrderAsync(new WechatAppOrderRequest
{
OutTradeNo = "order_002",
Description = "商品描述",
NotifyUrl = "https://your-site.com/pay/wechat/notify",
Amount = new WechatPayAmount { Total = 100 }
});
return wechat.BuildAppPayParams(resp.PrepayId);
}
// H5 下单(SceneInfo 和 H5Info 已预初始化默认值,只需设置 PayerClientIp)
public async Task<string> H5PayAsync(string clientIp)
{
var resp = await wechat.CreateH5OrderAsync(new WechatH5OrderRequest
{
OutTradeNo = "order_003",
Description = "商品描述",
NotifyUrl = "https://your-site.com/pay/wechat/notify",
Amount = new WechatPayAmount { Total = 100 },
SceneInfo = { PayerClientIp = clientIp }
});
return resp.H5Url;
}
// Native 下单(二维码支付)
public async Task<string> NativePayAsync()
{
var resp = await wechat.CreateNativeOrderAsync(new WechatNativeOrderRequest
{
OutTradeNo = "order_004",
Description = "商品描述",
NotifyUrl = "https://your-site.com/pay/wechat/notify",
Amount = new WechatPayAmount { Total = 100 }
});
return resp.CodeUrl;
}
// 查询订单
public async Task<WechatQueryOrderResponse> QueryAsync(string outTradeNo)
=> await wechat.QueryOrderByOutTradeNoAsync(outTradeNo);
// 退款
public async Task<WechatRefundResponse> RefundAsync(string outTradeNo, int refundFee, int totalFee)
{
return await wechat.RefundAsync(new WechatRefundRequest
{
OutTradeNo = outTradeNo,
OutRefundNo = $"refund_{outTradeNo}",
Amount = new WechatRefundAmount
{
Refund = refundFee,
Total = totalFee,
Currency = "CNY"
}
});
}
// 关闭订单
public async Task CloseAsync(string outTradeNo)
=> await wechat.CloseOrderAsync(outTradeNo);
// 下载交易账单
public async Task<byte[]> DownloadBillAsync(string billDate)
=> await wechat.DownloadTradeBillAsync(billDate, "ALL");
// 解析支付回调
public async Task<WechatPayCallbackDecrypted> ParseCallbackAsync(string body, WechatPayCallbackHeaders headers)
=> await wechat.ParsePayCallbackAsync(body, headers);
// 解析退款回调
public async Task<WechatRefundCallbackDecrypted> ParseRefundCallbackAsync(string body, WechatPayCallbackHeaders headers)
=> await wechat.ParseRefundCallbackAsync(body, headers);
// ── 异常退款 ──────────────────────────────────────────────
// 退款状态为异常(ABNORMAL)时,调用此接口发起异常退款处理
// 敏感字段(银行卡号、姓名)由 SDK 自动加密,无需手动处理
public async Task<WechatAbnormalRefundResponse> ApplyAbnormalRefundAsync(string refundId)
{
return await wechat.ApplyAbnormalRefundAsync(new WechatAbnormalRefundRequest
{
RefundId = refundId, // 微信支付退款单号(路径参数)
OutRefundNo = "refund_001", // 商户退款单号
Type = "USER_BANK_CARD", // USER_BANK_CARD 或 MERCHANT_BANK_CARD
BankType = "ICBC_DEBIT", // 开户银行(退款至用户时必填)
BankAccount = "6222021234567890123", // 银行卡号(明文,SDK 自动加密)
RealName = "张三" // 用户姓名(明文,SDK 自动加密)
});
}
// ── 敏感字段加解密 ────────────────────────────────────────
// 手动加密(一般无需使用,SDK 在 ApplyAbnormalRefundAsync 等接口中自动加密)
public string EncryptField(string plainText)
=> wechat.EncryptSensitiveField(plainText);
// 手动解密(用于自定义场景的下行加密敏感字段)
public string DecryptField(string cipherText)
=> wechat.DecryptSensitiveField(cipherText);
// ── 平台证书管理 ─────────────────────────────────────────
// 下载并自动注册平台证书(旧版平台证书模式下,建议每 12 小时刷新一次)
public async Task DownloadAndRegisterCertificatesAsync()
{
var certs = await wechat.DownloadCertificatesAsync();
// certs: [(SerialNo, CertificatePem), ...]
// 已自动注册到验签缓存,无需额外操作
}
// 手动注册平台证书(如从本地文件加载)
public void RegisterCert(string serialNo, string certPem)
=> wechat.RegisterCertificate(serialNo, certPem);
}
自动解密说明:
RefundAsync、QueryRefundAsync、ApplyAbnormalRefundAsync返回的UserReceivedAccount已自动尝试解密。若平台返回明文或掩码,SDK 会保留原值并继续返回,不会抛异常。
支付宝独立接口
public class AlipayController(IAlipayService alipay)
{
// 当面付(B扫C,扫用户付款码)
public async Task<AlipayTradePayResponse> FaceToFacePayAsync(string authCode)
{
return await alipay.FaceToFacePayAsync(new AlipayTradePayBizContent
{
OutTradeNo = "order_001",
Subject = "商品描述",
TotalAmount = "1.00",
AuthCode = authCode // 用户付款码,25-36位数字
});
}
// 订单码支付(C扫B,生成二维码)
public async Task<string> PrecreateAsync()
{
var resp = await alipay.PrecreateAsync(
new AlipayTradePrecreateContent
{
OutTradeNo = "order_002",
Subject = "商品描述",
TotalAmount = "9.90"
});
return resp.QrCode;
}
// JSAPI 支付(生活号/小程序)
public async Task<string> JsapiPayAsync(string buyerOpenId)
{
var resp = await alipay.CreateOrderAsync(
new AlipayTradeCreateContent
{
OutTradeNo = "order_003",
Subject = "商品描述",
TotalAmount = "1.00",
BuyerOpenId = buyerOpenId,
ProductCode = "JSAPI_PAY"
});
return resp.TradeNo;
}
// APP 支付(返回 SDK 签名字符串)
public string AppPayAsync()
{
return alipay.BuildAppPayString(
new AlipayTradeAppPayContent
{
OutTradeNo = "order_004",
Subject = "商品描述",
TotalAmount = "9.90"
});
}
// 手机网站支付(WAP)— notifyUrl / returnUrl 已在 AlipayOptions 中配置
public string WapPay()
{
return alipay.BuildWapPayUrl(
new AlipayTradeWapPayContent
{
OutTradeNo = "order_005",
Subject = "商品描述",
TotalAmount = "9.90",
ProductCode = "QUICK_WAP_WAY"
});
}
// 电脑网站支付(PC)— notifyUrl / returnUrl 已在 AlipayOptions 中配置
public string PagePay()
{
return alipay.BuildPagePayUrl(
new AlipayTradePagePayContent
{
OutTradeNo = "order_006",
Subject = "商品描述",
TotalAmount = "9.90",
ProductCode = "FAST_INSTANT_TRADE_PAY"
});
}
// 撤销订单(当面付场景专用,已支付会自动退款)
public async Task<AlipayTradeCancelResponse> CancelAsync(string outTradeNo)
{
return await alipay.CancelOrderAsync(new AlipayTradeCancelContent
{
OutTradeNo = outTradeNo
});
}
// 关闭订单(未支付状态)
public async Task<AlipayTradeCloseResponse> CloseAsync(string outTradeNo)
{
return await alipay.CloseOrderAsync(
new AlipayTradeCloseContent { OutTradeNo = outTradeNo },
ignoreNotExist: true); // App/H5/PC 用户未跳转支付宝时交易可能不存在
}
// 查询订单
public async Task<AlipayTradeQueryResponse> QueryAsync(string outTradeNo)
{
return await alipay.QueryOrderAsync(new AlipayTradeQueryContent
{
OutTradeNo = outTradeNo
});
}
// 退款
public async Task<AlipayTradeRefundResponse> RefundAsync(string outTradeNo)
{
return await alipay.RefundAsync(new AlipayTradeRefundContent
{
OutTradeNo = outTradeNo,
RefundAmount = "1.00",
RefundReason = "用户申请退款",
OutRequestNo = $"refund_{outTradeNo}"
});
}
// 退款查询
public async Task<AlipayTradeRefundQueryResponse> QueryRefundAsync(string outTradeNo, string outRequestNo)
{
return await alipay.QueryRefundAsync(new AlipayTradeRefundQueryContent
{
OutTradeNo = outTradeNo,
OutRequestNo = outRequestNo
});
}
// 账单下载
public async Task<byte[]> DownloadBillAsync(string billDate)
{
return await alipay.DownloadBillAsync(new AlipayBillDownloadContent
{
BillType = "trade",
BillDate = billDate
});
}
// 回调验签
public AlipayCallbackParams ParseCallback(IDictionary<string, string> formParams)
{
var result = alipay.ParseCallback(formParams);
// result.IsValid / result.TradeStatus / result.OutTradeNo / result.TradeNo
return result;
}
}
支付宝扩展能力(分账 / 转账)
public class AlipayAdvancedController(IAlipayService alipay)
{
// 1) 绑定分账关系(一次绑定,多次分账)
public async Task BindRoyaltyAsync()
{
await alipay.BindRoyaltyRelationAsync(new AlipayTradeRoyaltyRelationBindContent
{
OutRequestNo = "royalty_bind_001",
TransOut = "2088xxxxxx_out",
TransIn = "2088xxxxxx_in",
TransInType = "userId",
Type = "transfer",
Desc = "平台分账关系"
});
}
// 2) 对已支付订单发起分账
public async Task SettleAsync(string outTradeNo)
{
await alipay.SettleOrderAsync(new AlipayTradeOrderSettleContent
{
OutTradeNo = outTradeNo,
OutRequestNo = $"settle_{outTradeNo}",
RoyaltyParameters =
[
new AlipayRoyaltyDetail
{
TransIn = "2088xxxxxx_in",
TransInType = "userId",
Amount = "0.30",
Desc = "平台服务费"
}
]
});
}
// 3) 商家转账
public async Task TransferAsync()
{
await alipay.TransferAsync(new AlipayFundTransUniTransferContent
{
OutBizNo = "transfer_001",
TransAmount = "1.00",
PayeeInfoIdentity = "2088xxxxxx_user",
PayeeInfoIdentityType = "ALIPAY_USER_ID",
BizScene = "DIRECT_TRANSFER",
Remark = "活动补贴"
});
}
}
银联独立接口
public class UnionPayController(IUnionPayService unionPay)
{
// 在线网关支付(PC 前台跳转,返回 HTML 自动提交表单)
public string FrontPay(string orderId, int fee)
{
var resp = unionPay.CreateFrontPay(new UnionPayFrontPayRequest
{
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
OrderDesc = "商品描述"
});
return resp.FormHtml; // 注入页面,浏览器自动 POST 到银联
}
// WAP 手机网页支付(移动端前台跳转)
public string WapPay(string orderId, int fee)
{
var resp = unionPay.CreateWapPay(new UnionPayWapPayRequest
{
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
OrderDesc = "商品描述"
});
return resp.FormHtml; // WAP 页面自动提交表单
}
// 二维码支付 — 主扫(商户生成二维码,用户扫码支付)
public async Task<string> QrCodeApplyAsync(string orderId, int fee)
{
var resp = await unionPay.ApplyQrCodeAsync(new UnionPayQrCodeApplyRequest
{
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
OrderDesc = "商品描述"
});
return resp.QrCode; // 二维码链接,前端生成二维码图片
}
// 二维码支付 — 被扫(商户扫用户付款码,后台扣款)
public async Task<UnionPayBackPayResponse> QrCodeConsumeAsync(string orderId, int fee, string qrNo)
{
return await unionPay.QrCodeConsumeAsync(new UnionPayQrCodeConsumeRequest
{
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
OrderDesc = "商品描述",
QrNo = qrNo // 用户付款码
});
}
// 无跳转支付(后台消费,需卡号和持卡人信息)
public async Task<UnionPayBackPayResponse> NoRedirectPayAsync(string orderId, int fee, string accNo, string customerInfo)
{
return await unionPay.CreateBackPayAsync(new UnionPayBackPayRequest
{
BizType = "000301",
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
AccNo = accNo,
CustomerInfo = customerInfo
});
}
// 签约支付(通过签约协议号免密扣款)
public async Task<UnionPayBackPayResponse> ContractPayAsync(string orderId, int fee, string contractNo)
{
return await unionPay.CreateBackPayAsync(new UnionPayBackPayRequest
{
BizType = "000301",
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
ContractNo = contractNo
});
}
// 云闪付(无感支付,通过 tokenPayData 扣款)
public async Task<UnionPayBackPayResponse> QuickPassPayAsync(string orderId, int fee, string tokenPayData)
{
return await unionPay.CreateBackPayAsync(new UnionPayBackPayRequest
{
BizType = "000902",
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
TokenPayData = tokenPayData
});
}
// Apple Pay(基于 Token 的移动端支付,bizType=000802)
public async Task<UnionPayBackPayResponse> ApplePayAsync(string orderId, int fee, string payData)
{
return await unionPay.CreateBackPayAsync(new UnionPayBackPayRequest
{
BizType = "000802",
OrderId = orderId,
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = fee.ToString(),
PayData = payData // Apple Pay Token
});
}
// 查询订单
public async Task<UnionPayQueryResponse> QueryAsync(string orderId, string txnTime)
{
return await unionPay.QueryOrderAsync(new UnionPayQueryRequest
{
OrderId = orderId,
TxnTime = txnTime
});
}
// 退款
public async Task<UnionPayRefundResponse> RefundAsync(string origQueryId, int refundAmt)
{
return await unionPay.RefundAsync(new UnionPayRefundRequest
{
OrderId = $"refund_{DateTime.Now:yyyyMMddHHmmss}",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = refundAmt.ToString(),
OrigQueryId = origQueryId
});
}
// 对账文件下载
public async Task<byte[]> DownloadBillAsync(string settleDate)
{
return await unionPay.DownloadBillAsync(settleDate, "00");
}
// 回调验签
public UnionPayCallbackParams ParseCallback(IDictionary<string, string> formParams)
{
var result = unionPay.ParseCallback(formParams);
// result.IsValid / result.RespCode / result.OrderId / result.QueryId
return result;
}
}
微信支付高级功能
异常退款
退款状态为异常(ABNORMAL)时,可调用 ApplyAbnormalRefundAsync 发起异常退款处理。
支持退款至用户银行卡(USER_BANK_CARD)或退款至交易商户银行账户(MERCHANT_BANK_CARD)两种方式。
💡 敏感字段自动加密:银行卡号(
BankAccount)和用户姓名(RealName)只需传入明文,SDK 会自动使用微信支付公钥 / 平台证书进行 RSAES-OAEP 加密,并自动携带Wechatpay-Serial请求头。
// 退款至用户银行卡
var resp = await wechat.ApplyAbnormalRefundAsync(new WechatAbnormalRefundRequest
{
RefundId = "50000000382019052709732678859", // 微信支付退款单号(路径参数)
OutRefundNo = "refund_001", // 商户退款单号
Type = "USER_BANK_CARD", // 退款至用户银行卡
BankType = "ICBC_DEBIT", // 开户银行
BankAccount = "6222021234567890123", // 银行卡号(明文,SDK 自动加密)
RealName = "张三" // 用户姓名(明文,SDK 自动加密)
});
// resp.Status: SUCCESS / PROCESSING / ABNORMAL / CLOSED
// 退款至商户银行账户
var resp2 = await wechat.ApplyAbnormalRefundAsync(new WechatAbnormalRefundRequest
{
RefundId = "50000000382019052709732678859",
OutRefundNo = "refund_001",
Type = "MERCHANT_BANK_CARD"
});
敏感信息加解密
SDK 同时支持微信支付公钥模式和平台证书模式两种加密方式,通过配置自动区分:
| 配置 | 模式 | Wechatpay-Serial 请求头 |
|---|---|---|
PlatformPublicKeyId 以 PUB_KEY_ID_ 开头 |
微信支付公钥模式 | PUB_KEY_ID_xxx |
PlatformPublicKeyId 为空或非 PUB_KEY_ID_ 前缀 |
平台证书模式 | 平台证书序列号 |
// ── 上行加密(商户 → 微信支付)──
// 通常无需手动调用,SDK 在 ApplyAbnormalRefundAsync 等接口中自动加密
// 如需手动加密其他场景的敏感字段:
var encrypted = wechat.EncryptSensitiveField("6222021234567890123");
// encrypted: Base64 编码的 RSAES-OAEP 密文
// ── 下行解密(微信支付 → 商户)──
// 微信支付使用商户 API 证书公钥加密下行敏感信息,SDK 使用商户私钥解密:
var decrypted = wechat.DecryptSensitiveField(encryptedBankAccount);
// decrypted: 银行卡号明文
加密算法:RSA/ECB/OAEPWithSHA-1AndMGF1Padding(对应 .NET
RSAEncryptionPadding.OaepSHA1)
参考文档:
平台证书管理
旧版平台证书模式下,需要定期下载平台证书用于回调验签。SDK 提供自动下载并注册的接口:
// 下载平台证书并自动注册到验签缓存(建议每 12 小时刷新一次)
var certs = await wechat.DownloadCertificatesAsync();
foreach (var (serialNo, certPem) in certs)
{
Console.WriteLine($"已注册证书: {serialNo}");
}
// 也可手动注册(如从本地文件加载)
wechat.RegisterCertificate("CERT_SERIAL_NO", certPemContent);
新版公钥模式下(配置了
PUB_KEY_ID_前缀的PlatformPublicKeyId),验签使用配置的微信支付公钥,无需下载平台证书。
银联跨境电商海关申报
银联跨境电商海关申报服务是独立于支付流程的非支付接口,用于将银联支付订单的支付信息向海关申报,实现海关对跨境业务支付流、订单流、物流的三单比对核查。
ℹ️ 海关申报不是支付渠道,因此不通过统一
IPayService路由,而是通过独立的IUnionPayCustomsService接口或UnionPayClient.Customs属性访问。
DI 注入用法
注册银联支付时会自动注册海关申报服务,直接注入即可:
public class CustomsController(IUnionPayCustomsService customs)
{
// 提交海关申报
public async Task<UnionPayCustomsDeclarationResponse> DeclareAsync(string origQueryId)
{
return await customs.DeclareAsync(new UnionPayCustomsDeclarationRequest
{
OrderId = $"customs_{DateTime.Now:yyyyMMddHHmmss}",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = "10000", // 金额(分)
OrigQueryId = origQueryId, // 原始支付交易的 queryId
CustomsCode = "GUANGZHOU", // 海关代码
MerAbbr = "商户备案名称", // 商户在海关备案的名称
MerCatCode = "1234" // 商户在海关备案的编号
});
}
// 查询海关申报结果
public async Task<UnionPayCustomsQueryResponse> QueryAsync(string orderId, string txnTime)
{
return await customs.QueryDeclarationAsync(new UnionPayCustomsQueryRequest
{
OrderId = orderId,
TxnTime = txnTime
});
// resp.OrigRespCode: "00" = 申报成功
}
// 加密公钥更新查询(建议每天调用 1 次,获取最新加密公钥证书)
public async Task<UnionPayEncryptKeyQueryResponse> QueryEncryptKeyAsync()
{
return await customs.QueryEncryptKeyAsync(new UnionPayEncryptKeyQueryRequest
{
OrderId = $"key_{DateTime.Now:yyyyMMddHHmmss}",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
CertType = "01" // 01 = 敏感信息加密公钥
});
// resp.SignPubKeyCert: 最新的加密公钥证书内容,替换本地证书
}
// 实名认证(验证银行卡信息与身份信息一致性)
public async Task<UnionPayRealNameAuthResponse> RealNameAuthAsync(
string accNo, string customerInfo)
{
return await customs.RealNameAuthAsync(new UnionPayRealNameAuthRequest
{
OrderId = $"auth_{DateTime.Now:yyyyMMddHHmmss}",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
AccNo = accNo, // 银行卡号(需加密上送)
CustomerInfo = customerInfo // Base64: certifTp=01&certifId=xxx&customerNm=xxx&phoneNo=xxx
});
// resp.RespCode: "00" = 认证通过
}
// 文件传输(下载对账文件)
public async Task<UnionPayFileTransferResponse> DownloadFileAsync(string settleDate)
{
return await customs.FileTransferAsync(new UnionPayFileTransferRequest
{
OrderId = $"file_{DateTime.Now:yyyyMMddHHmmss}",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
SettleDate = settleDate, // 清算日期,格式 MMdd
FileType = "00" // 00 = 普通对账文件
});
// resp.FileContent: 解压后的对账文件文本内容
// resp.FileData: 原始字节数据
}
}
非 DI 用法
using var client = UnionPayClient.Create(new UnionPayOptions { /* ... */ });
var declareResp = await client.Customs.DeclareAsync(new UnionPayCustomsDeclarationRequest
{
OrderId = "customs_001",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
TxnAmt = "10000",
OrigQueryId = "orig_query_id",
CustomsCode = "HANGZHOU"
});
var queryResp = await client.Customs.QueryDeclarationAsync(new UnionPayCustomsQueryRequest
{
OrderId = "customs_001",
TxnTime = declareResp.TxnTime
});
// 加密公钥更新查询
var keyResp = await client.Customs.QueryEncryptKeyAsync(new UnionPayEncryptKeyQueryRequest
{
OrderId = "key_001",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
CertType = "01"
});
// keyResp.SignPubKeyCert → 替换本地加密公钥证书
// 实名认证
var authResp = await client.Customs.RealNameAuthAsync(new UnionPayRealNameAuthRequest
{
OrderId = "auth_001",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
AccNo = "6222021234567890",
CustomerInfo = "Base64编码的持卡人信息"
});
// 文件传输(对账文件下载)
var fileResp = await client.Customs.FileTransferAsync(new UnionPayFileTransferRequest
{
OrderId = "file_001",
TxnTime = DateTime.Now.ToString("yyyyMMddHHmmss"),
SettleDate = "0119", // MMdd 格式
FileType = "00"
});
// fileResp.FileContent → 对账文件文本
银联 OpenAPI 独立模块(OAuth2 / 非对称)
银联 OpenAPI 与收单交易接口不是同一能力域。SDK 已提供独立的 IUnionPayOpenApiService 与 AddUnionPayOpenApi,避免把 OpenAPI 认证流程(OAuth2/非对称)与支付下单接口耦合在一起。
// Program.cs
builder.Services.AddUnionPayOpenApi(opt =>
{
opt.BaseUrl = "https://openapi.unionpay.com";
opt.AppId = "your_openapi_appid";
// 二选一:
// 1) OAuth2
// opt.AuthMode = UnionPayOpenApiAuthMode.OAuth2;
// opt.OAuthToken = "your_access_token";
// 2) 非对称验签(RSA2)
opt.AuthMode = UnionPayOpenApiAuthMode.Asymmetric;
opt.PrivateKey = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----";
});
// 使用
public class UnionPayOpenApiController(IUnionPayOpenApiService openApi)
{
public async Task<string> QueryDemoAsync()
{
return await openApi.PostAsync("your.biz.method", new { foo = "bar" });
}
}
该模块定位为 OpenAPI 认证与请求骨架,不改变现有收单支付接口行为。你可以在其上按产品文档逐步扩展具体 OpenAPI 能力。
进阶用法
瞬态故障自动重试
SDK 内置了瞬态故障自动重试机制,当遇到网络抖动、连接超时、服务端 5xx 等临时性故障时,会按指数退避策略自动重试。此机制适用于所有三个支付渠道(微信支付、支付宝、银联)。
可重试的故障类型:
- 网络层错误(连接失败、DNS 解析失败等
HttpRequestException) - HTTP 请求超时(
TaskCanceledException,非用户主动取消) - 服务端错误(HTTP 5xx 状态码)
配置参数 — PayRetryOptions:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MaxRetries |
int |
2 |
最大重试次数(不含首次请求),设为 0 则不重试 |
InitialDelay |
TimeSpan |
500ms | 首次重试前的等待时间,后续按指数退避递增 |
MaxDelay |
TimeSpan |
5s | 单次重试等待时间上限 |
// 使用默认重试配置(2 次重试,500ms 起始延迟,指数退避)
builder.Services.AddWechatPay(opt =>
{
opt.AppId = "wx_your_appid";
opt.MchId = "1600000000";
opt.ApiV3Key = "your_api_v3_key";
opt.PrivateKey = "your_private_key";
opt.CertSerialNo = "your_serial_no";
// RetryOptions 默认已启用,无需额外配置
});
// 自定义重试策略
builder.Services.AddAlipay(opt =>
{
opt.AppId = "2021000000000000";
opt.PrivateKey = "your_private_key";
opt.AlipayPublicKey = "alipay_public_key";
opt.RetryOptions = new PayRetryOptions
{
MaxRetries = 3, // 最多重试 3 次
InitialDelay = TimeSpan.FromMilliseconds(200), // 首次重试等待 200ms
MaxDelay = TimeSpan.FromSeconds(10) // 单次最多等待 10s
};
});
// 禁用重试
builder.Services.AddUnionPay(opt =>
{
opt.MerId = "your_mer_id";
opt.CertId = "your_cert_id";
opt.PrivateKey = "your_private_key";
opt.UnionPayPublicKey = "unionpay_public_key";
opt.RetryOptions = new PayRetryOptions { MaxRetries = 0 };
});
幂等重试支持(微信支付 v3)
微信支付 v3 接口支持 Idempotency-Key 请求头,用于防止因网络重试导致的重复扣款。SDK 在所有 POST 方法中暴露了可选的 idempotencyKey 参数:
// 通过独立接口使用幂等键
public class WechatController(IWechatPayService wechat)
{
public async Task<WechatNativeOrderResponse> SafeCreateOrder()
{
// 使用商户订单号作为幂等键,确保同一订单不会重复创建
var orderId = "order_001";
var resp = await wechat.CreateNativeOrderAsync(new WechatNativeOrderRequest
{
OutTradeNo = orderId,
Description = "商品描述",
Amount = new WechatPayAmount { Total = 100 }
});
return resp;
}
}
💡 说明:
Idempotency-Key在WechatPayHttpClient的PostAsync、PostNoContentAsync、PostWithEncryptionAsync方法中均可通过参数传入。瞬态重试机制独立于幂等键工作 — 瞬态重试针对的是网络层故障(在请求未到达微信服务器时安全重试),而幂等键则保护已到达服务器的请求不被重复处理。
统一日志追踪(推荐)
PayService 已补充统一日志字段,建议在业务层补齐 requestId 并在落单/回调时记录以下字段:channel、outTradeNo、requestId、tradeStatus、errorCode。
// Program.cs
app.Use(async (ctx, next) =>
{
var requestId = ctx.Request.Headers.TryGetValue("X-Request-Id", out var v) && !string.IsNullOrWhiteSpace(v)
? v.ToString()
: Guid.NewGuid().ToString("N");
ctx.Items["requestId"] = requestId;
ctx.Response.Headers["X-Request-Id"] = requestId;
await next();
});
建议在 Controller 中将
channel/outTradeNo/tradeStatus/errorCode与requestId一并写入结构化日志,便于对账和问题排查。
JSON 序列化工具
PayJsonSerializer 提供 SDK 预配置的 JSON 序列化选项,适用于所有支付渠道的日志记录、调试输出等场景:
using GaoXinLibrary.PaySDK.Core;
// 序列化(中文不会被转义为 \uXXXX,使用 snake_case 命名)
var json = PayJsonSerializer.Serialize(myObject);
// 反序列化
var obj = PayJsonSerializer.Deserialize<MyModel>(json);
// 直接获取 JsonSerializerOptions 用于自定义场景
var options = PayJsonSerializer.Options;
预配置选项:
- 命名策略:
JsonNamingPolicy.SnakeCaseLower(自动 snake_case) - 空值处理:
JsonIgnoreCondition.WhenWritingNull(忽略 null 字段) - 编码器:
JavaScriptEncoder.UnsafeRelaxedJsonEscaping(中文直接输出,不做 Unicode 转义)
配置验证
SDK 在 DI 注册时会自动校验必填配置项。所有 Options 类的必填属性均标注了 [Required] 特性,注册时通过 Validator.ValidateObject 提前验证,而不是等到第一次 API 调用时才报错:
// ❌ 缺少必填项,注册时立即抛出 ValidationException
builder.Services.AddWechatPay(opt =>
{
opt.AppId = "wx_appid";
// 未设置 MchId、ApiV3Key、PrivateKey、CertSerialNo
});
// 抛出: ValidationException: "微信支付 MchId 不能为空"
// ✅ 所有必填项已配置
builder.Services.AddWechatPay(opt =>
{
opt.AppId = "wx_appid";
opt.MchId = "1600000000";
opt.ApiV3Key = "your_api_v3_key";
opt.PrivateKey = "your_private_key";
opt.CertSerialNo = "your_serial_no";
});
各渠道必填项:
| 渠道 | 必填属性 |
|---|---|
| 微信支付 | AppId、MchId、ApiV3Key、PrivateKey、CertSerialNo |
| 支付宝 | AppId、PrivateKey、AlipayPublicKey |
| 银联 | MerId、PrivateKey、CertId、UnionPayPublicKey |
配置选项参考
WechatPayOptions
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
AppId |
string | ✅ | 应用 ID(公众号 / 小程序 / APP AppID) |
MchId |
string | ✅ | 商户号 |
ApiV3Key |
string | ✅ | API v3 密钥(32 字节,用于回调解密 AEAD_AES_256_GCM) |
PrivateKey |
string | ✅ | 商户私钥(PEM 格式) |
CertSerialNo |
string | ✅ | 商户证书序列号 |
PlatformPublicKey |
string | ⚠️ | 微信支付平台公钥 / 平台证书公钥(验签用,新版公钥模式必填) |
PlatformPublicKeyId |
string | — | 公钥 ID(PUB_KEY_ID_xxxx,新版公钥模式必填) |
NotifyUrl |
string | — | 支付结果异步通知回调地址(notify_url),配置后所有下单请求自动携带,也可在下单时覆盖 |
RefundNotifyUrl |
string | — | 退款结果异步通知回调地址,配置后退款请求自动携带,也可在退款时覆盖 |
BaseUrl |
string | — | API 基础地址,默认 https://api.mch.weixin.qq.com |
HttpTimeout |
TimeSpan | — | HTTP 超时,默认 30 秒 |
RetryOptions |
PayRetryOptions |
— | 瞬态故障重试配置(默认 2 次重试,500ms 起始延迟),详见瞬态故障自动重试 |
AlipayOptions
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
AppId |
string | ✅ | 开放平台应用 ID |
PrivateKey |
string | ✅ | 商户 RSA2 私钥(PEM 或 Base64) |
AlipayPublicKey |
string | ✅ | 支付宝 RSA2 公钥(PEM 或 Base64,用于回调验签) |
NotifyUrl |
string | — | 异步通知回调地址(notify_url),配置后所有支付请求自动携带,也可在下单时覆盖 |
ReturnUrl |
string | — | 同步跳转地址(return_url),配置后手机网站 / 电脑网站支付自动携带,也可在下单时覆盖 |
SignType |
string | — | 签名类型,默认 RSA2 |
GatewayUrl |
string | — | 网关地址,默认 https://openapi.alipay.com/gateway.do |
HttpTimeout |
TimeSpan | — | HTTP 超时,默认 30 秒 |
RetryOptions |
PayRetryOptions |
— | 瞬态故障重试配置(默认 2 次重试,500ms 起始延迟),详见瞬态故障自动重试 |
UnionPayOptions
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
MerId |
string | ✅ | 商户号 |
PrivateKey |
string | ✅ | 商户 RSA 私钥(PEM) |
CertId |
string | ✅ | 商户证书序列号 |
UnionPayPublicKey |
string | ✅ | 银联根证书公钥(PEM,用于回调验签) |
FrontUrl |
string | ✅ | 前台通知 / 同步跳转地址 |
BackUrl |
string | ✅ | 后台通知 / 异步回调地址 |
FrontGatewayUrl |
string | — | 前台网关,默认 https://gateway.95516.com/gateway/api/frontTransReq.do |
AppGatewayUrl |
string | — | WAP 前台网关,默认 https://gateway.95516.com/gateway/api/appTransReq.do |
BackGatewayUrl |
string | — | 后台网关,默认 https://gateway.95516.com/gateway/api/backTransReq.do |
QueryGatewayUrl |
string | — | 查询网关,默认 https://gateway.95516.com/gateway/api/queryTrans.do |
FileGatewayUrl |
string | — | 文件下载网关,默认 https://filedownload.95516.com/ |
Version |
string | — | 版本号,默认 5.1.0 |
SignMethod |
string | — | 签名方式,01=RSA / 11=SM2,默认 01 |
RetryOptions |
PayRetryOptions |
— | 瞬态故障重试配置(默认 2 次重试,500ms 起始延迟),详见瞬态故障自动重试 |
💡 配置验证:所有标注为 ✅ 必填的属性均在 DI 注册时通过
[Required]+Validator.ValidateObject自动校验,缺失时立即抛出ValidationException,而非等到首次 API 调用时才报错。
错误处理
SDK 使用异常机制报告错误,所有支付异常都继承自 PayException:
try
{
var resp = await pay.CreateOrderAsync(request);
}
catch (PayException ex)
{
// ex.ErrorCode - 错误码(如 "PARAM_ERROR")
// ex.ErrorMessage - 错误信息
// ex.Channel - 发生错误的渠道(可选)
Console.WriteLine($"[{ex.ErrorCode}] {ex.ErrorMessage}");
}
各渠道也有独立异常类型,包含更详细的渠道级错误信息:
| 异常类型 | 说明 |
|---|---|
PayException |
SDK 统一基础异常 |
AlipayException |
支付宝 API 业务错误 |
UnionPayException |
银联 API 响应错误 |
能力边界(支持 / 不支持 / 规划中)
| 分类 | 能力 | 状态 | 说明 |
|---|---|---|---|
| 微信支付 | JSAPI/APP/H5/Native/小程序、退款、退款查询、回调验签解密 | ✅ | 已支持 |
| 微信支付 | 异常退款、平台证书下载注册、敏感字段加解密 | ✅ | 已支持 |
| 支付宝 | 收单核心链路(当面付/预下单/JSAPI/App/WAP/Page) | ✅ | 已支持 |
| 支付宝 | 分账关系绑定、订单分账、商家转账 | ✅ | 本版本新增 |
| 支付宝 | 投诉/风控 API(交易投诉、RiskGO) | 🚧 | 规划中,按业务优先级扩展 |
| 银联 | 收单交易、退款、查询、回调、海关申报 | ✅ | 已支持 |
| 银联 | OpenAPI 独立模块(OAuth2/非对称认证骨架) | ✅ | 本版本新增 |
| 银联 | OpenAPI 具体产品 API(按产品逐项封装) | 🚧 | 规划中,建议按业务申请逐步接入 |
项目结构
GaoXinLibrary.PaySDK/
├── Core/ # 统一基础类型
│ ├── IPayService.cs # 统一支付接口
│ ├── PayChannel.cs # 渠道枚举(17 种子渠道,含 Apple Pay)
│ ├── PayChannelExtensions.cs # 渠道枚举扩展方法
│ ├── PayException.cs # 基础异常
│ ├── PayRetryOptions.cs # 瞬态故障重试配置(指数退避)
│ ├── PayJsonSerializer.cs # 统一 JSON 序列化工具
│ ├── CreateOrderRequest.cs # 创建订单请求
│ ├── CreateOrderResponse.cs # 创建订单响应
│ ├── QueryOrderRequest.cs # 查询订单请求
│ ├── QueryOrderResponse.cs # 查询订单响应
│ ├── RefundRequest.cs # 退款请求
│ ├── RefundResponse.cs # 退款响应
│ ├── QueryRefundRequest.cs # 退款查询请求
│ ├── QueryRefundResponse.cs # 退款查询响应
│ ├── CloseOrderRequest.cs # 关闭订单请求
│ ├── CloseOrderResponse.cs # 关闭订单响应(含 IsSimulated 标记)
│ ├── DownloadBillRequest.cs # 账单下载请求
│ ├── PayCallbackResult.cs # 回调解析结果
│ └── WechatJsPayParams.cs # 微信 JS 调起支付参数
├── Wechat/ # 微信支付 v3
│ ├── Core/ # WechatPayOptions / WechatPaySigner / WechatPayHttpClient
│ ├── Models/ # 所有微信支付请求/响应模型
│ │ ├── WechatAbnormalRefundRequest.cs # 异常退款请求
│ │ ├── WechatAbnormalRefundResponse.cs # 异常退款响应
│ │ └── ... # 其他支付/回调模型
│ ├── Services/ # IWechatPayService / WechatPayService
│ └── WechatPayClient.cs # 非 DI 场景的客户端入口
├── Alipay/ # 支付宝
│ ├── Core/ # AlipayOptions / AlipaySigner / AlipayHttpClient
│ ├── Models/ # 所有支付宝请求/响应模型
│ │ ├── AlipayTradeRoyaltyRelationBindContent.cs # 分账关系绑定
│ │ ├── AlipayTradeOrderSettleContent.cs # 订单分账
│ │ ├── AlipayFundTransUniTransferContent.cs # 商家转账
│ ├── Services/ # IAlipayService / AlipayService
│ └── AlipayClient.cs # 非 DI 场景的客户端入口
├── UnionPay/ # 银联
│ ├── Core/ # UnionPayOptions / UnionPaySigner / UnionPayHttpClient
│ ├── Models/ # 所有银联请求/响应模型
│ │ ├── UnionPayCustomsDeclarationRequest.cs # 海关申报请求
│ │ ├── UnionPayCustomsQueryRequest.cs # 海关申报查询请求
│ │ ├── UnionPayEncryptKeyQueryRequest.cs # 加密公钥更新查询请求
│ │ ├── UnionPayEncryptKeyQueryResponse.cs # 加密公钥更新查询响应
│ │ ├── UnionPayRealNameAuthRequest.cs # 实名认证请求
│ │ ├── UnionPayRealNameAuthResponse.cs # 实名认证响应
│ │ ├── UnionPayFileTransferRequest.cs # 文件传输请求
│ │ ├── UnionPayFileTransferResponse.cs # 文件传输响应
│ │ └── ... # 其他支付/回调模型
│ ├── Services/ # IUnionPayService / UnionPayService
│ │ ├── IUnionPayCustomsService.cs # 海关申报接口(非支付)
│ │ └── UnionPayCustomsService.cs # 海关申报实现
│ ├── OpenApi/ # 银联 OpenAPI(OAuth2/非对称)独立模块
│ │ ├── UnionPayOpenApiOptions.cs
│ │ ├── IUnionPayOpenApiService.cs
│ │ └── UnionPayOpenApiService.cs
│ └── UnionPayClient.cs # 非 DI 场景的客户端入口(Pay + Customs)
├── Extensions/ # DI 注入扩展方法
│ ├── PayServiceCollectionExtensions.cs # AddPaySDK / AddPayService
│ ├── WechatPayServiceCollectionExtensions.cs # AddWechatPay
│ ├── AlipayServiceCollectionExtensions.cs # AddAlipay
│ ├── UnionPayServiceCollectionExtensions.cs # AddUnionPay
│ └── UnionPayOpenApiServiceCollectionExtensions.cs # AddUnionPayOpenApi
└── PayService.cs # IPayService 统一路由实现
许可证
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Http (>= 10.0.7)
- Microsoft.Extensions.Options (>= 10.0.7)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Options (>= 8.0.2)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.15)
- Microsoft.Extensions.Http (>= 9.0.15)
- Microsoft.Extensions.Options (>= 9.0.15)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.0-beta1 | 49 | 5/27/2026 |
| 1.0.0 | 102 | 5/8/2026 |
| 1.0.0-beta.4 | 72 | 3/13/2026 |
| 1.0.0-beta.3 | 66 | 3/11/2026 |
| 1.0.0-beta.2 | 64 | 3/11/2026 |
| 1.0.0-beta.1 | 64 | 3/9/2026 |