GaoXinLibrary.PaySDK 1.0.0

There is a newer prerelease version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GaoXinLibrary.PaySDK" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GaoXinLibrary.PaySDK" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="GaoXinLibrary.PaySDK" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GaoXinLibrary.PaySDK --version 1.0.0
                    
#r "nuget: GaoXinLibrary.PaySDK, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GaoXinLibrary.PaySDK@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GaoXinLibrary.PaySDK&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=GaoXinLibrary.PaySDK&version=1.0.0
                    
Install as a Cake Tool

GaoXinLibrary.PaySDK

统一支付 SDK,支持微信支付 v3支付宝银联三大渠道,兼容 .NET 8 / 9 / 10,提供 DI 注入支持。

NuGet


目录


功能矩阵

功能 微信支付 支付宝 银联
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 == trueOperationMode == 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);
}

自动解密说明RefundAsyncQueryRefundAsyncApplyAbnormalRefundAsync 返回的 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 请求头
PlatformPublicKeyIdPUB_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 已提供独立的 IUnionPayOpenApiServiceAddUnionPayOpenApi,避免把 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-KeyWechatPayHttpClientPostAsyncPostNoContentAsyncPostWithEncryptionAsync 方法中均可通过参数传入。瞬态重试机制独立于幂等键工作 — 瞬态重试针对的是网络层故障(在请求未到达微信服务器时安全重试),而幂等键则保护已到达服务器的请求不被重复处理。

统一日志追踪(推荐)

PayService 已补充统一日志字段,建议在业务层补齐 requestId 并在落单/回调时记录以下字段:channeloutTradeNorequestIdtradeStatuserrorCode

// 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/errorCoderequestId 一并写入结构化日志,便于对账和问题排查。

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";
});

各渠道必填项:

渠道 必填属性
微信支付 AppIdMchIdApiV3KeyPrivateKeyCertSerialNo
支付宝 AppIdPrivateKeyAlipayPublicKey
银联 MerIdPrivateKeyCertIdUnionPayPublicKey

配置选项参考

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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