PAYATHON 2026

ASP.NET 中是否有可用的 Payment Gateway?

支付老李

明确结论

ASP.NET 本身没有 Payment Gateway。它是 Web 开发框架,不处理银行卡收单、资金清算或 Credit Card Transaction。

项目需要接入第三方支付服务商或银行提供的支付接口,比如 Stripe、Adyen、PayPal、Braintree,以及业务所在地的银行或持牌支付机构。选择哪家服务商,要看商户和用户所在地区、结算币种、费率及当地监管要求。

一般建议使用支付服务商提供的托管收银台或前端安全组件,不要让信用卡号进入自己的 ASP.NET 服务器。

推荐的接入流程

常见的支付流程是:

  1. 用户在 ASP.NET 网站创建订单。
  2. 服务器生成内部订单号,将订单状态保存为 Pending。
  3. 服务器调用支付服务商 API,创建 Payment、Payment Intent 或 Checkout Session。
  4. 用户在服务商托管的页面或安全组件中填写信用卡信息。
  5. 支付服务商完成验证、授权和必要的 3-D Secure 流程。
  6. 支付服务商通过 Webhook 将支付结果通知 ASP.NET 后端。
  7. 后端验证 Webhook 签名,再根据服务商返回的交易状态更新订单。

浏览器跳转到“支付成功”页面,不代表付款一定成功。浏览器请求可以被伪造,用户也可能在支付完成前关闭页面。订单状态应以通过签名验证的 Webhook 或服务端查询结果为准。

ASP.NET Core 中的基本结构

各家支付服务商的 SDK、请求参数和状态名称并不相同。下面用一组与服务商无关的接口说明基本结构。ProviderPaymentClient 和签名验证逻辑需要按照所选服务商的官方文档实现。

public record CreatePaymentRequest(
    string OrderId,
    long Amount,
    string Currency,
    string SuccessUrl,
    string CancelUrl);

public record CreatePaymentResult(
    string ProviderPaymentId,
    string CheckoutUrl);

public interface IPaymentGateway
{
    Task<CreatePaymentResult> CreatePaymentAsync(
        CreatePaymentRequest request,
        CancellationToken cancellationToken);

    Task<PaymentWebhookResult> ParseWebhookAsync(
        string payload,
        string signature,
        CancellationToken cancellationToken);
}

public record PaymentWebhookResult(
    string EventId,
    string ProviderPaymentId,
    string Status);

控制器只接收订单标识。订单金额必须从服务器数据库读取,不能采用浏览器提交的金额:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/payments")]
public sealed class PaymentsController : ControllerBase
{
    private readonly IPaymentGateway _paymentGateway;
    private readonly IOrderRepository _orders;

    public PaymentsController(
        IPaymentGateway paymentGateway,
        IOrderRepository orders)
    {
        _paymentGateway = paymentGateway;
        _orders = orders;
    }

    [HttpPost("{orderId}/checkout")]
    public async Task<IActionResult> CreateCheckout(
        string orderId,
        CancellationToken cancellationToken)
    {
        var order = await _orders.FindAsync(orderId, cancellationToken);

        if (order is null)
            return NotFound();

        if (order.Status != "Pending")
            return Conflict("订单当前状态不允许发起支付。");

        var result = await _paymentGateway.CreatePaymentAsync(
            new CreatePaymentRequest(
                OrderId: order.Id,
                Amount: order.AmountInMinorUnits,
                Currency: order.Currency,
                SuccessUrl: $"https://shop.example.com/orders/{order.Id}/result",
                CancelUrl: $"https://shop.example.com/orders/{order.Id}"),
            cancellationToken);

        await _orders.SaveProviderPaymentIdAsync(
            order.Id,
            result.ProviderPaymentId,
            cancellationToken);

        return Ok(new { checkoutUrl = result.CheckoutUrl });
    }
}

AmountInMinorUnits 表示最小货币单位。对于支持两位小数的币种,10.50 通常会写成整数 1050。并非所有币种都有两位小数,具体格式必须遵守支付服务商对相应币种的规定。

Webhook 处理示例

支付结果通常通过 Webhook 确认:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/payment-webhooks")]
public sealed class PaymentWebhooksController : ControllerBase
{
    private readonly IPaymentGateway _paymentGateway;
    private readonly IOrderRepository _orders;

    public PaymentWebhooksController(
        IPaymentGateway paymentGateway,
        IOrderRepository orders)
    {
        _paymentGateway = paymentGateway;
        _orders = orders;
    }

    [HttpPost]
    public async Task<IActionResult> Handle(
        CancellationToken cancellationToken)
    {
        using var reader = new StreamReader(Request.Body);
        var payload = await reader.ReadToEndAsync(cancellationToken);

        var signature = Request.Headers["Payment-Signature"].ToString();

        PaymentWebhookResult webhook;

        try
        {
            webhook = await _paymentGateway.ParseWebhookAsync(
                payload,
                signature,
                cancellationToken);
        }
        catch
        {
            return BadRequest();
        }

        var alreadyProcessed = await _orders.HasProcessedEventAsync(
            webhook.EventId,
            cancellationToken);

        if (alreadyProcessed)
            return Ok();

        var order = await _orders.FindByProviderPaymentIdAsync(
            webhook.ProviderPaymentId,
            cancellationToken);

        if (order is null)
            return NotFound();

        if (webhook.Status == "Succeeded")
        {
            await _orders.MarkAsPaidAsync(
                order.Id,
                webhook.EventId,
                cancellationToken);
        }

        return Ok();
    }
}

Payment-Signature、Succeeded 等名称仅用于抽象示例,不能直接用于真实项目。实际接入时,必须采用支付服务商规定的请求头、SDK 验签方法和交易状态。

最好在同一个数据库事务中更新订单状态并记录 Webhook 事件,以免通知重复送达后造成重复发货或重复记账。

如何选择 Payment Gateway

接入前需要确认以下事项:

  • 是否支持商户注册所在地和目标客户所在地。
  • 是否支持所需的信用卡品牌、币种和结算账户。
  • 是否提供 ASP.NET 或通用 .NET SDK。
  • SDK 是否兼容项目所用的 .NET Framework 或 .NET 版本。
  • 是否支持退款、部分退款、订阅、争议处理和 3-D Secure。
  • 是否提供 Sandbox/Test Mode、Webhook 签名验证和交易查询接口。
  • 费率、拒付费用、结算周期及资金保留规则是否合适。

如果项目使用经典 ASP.NET(.NET Framework),还要检查服务商 SDK 当前支持的目标框架。有些新版 SDK 只支持较新的 .NET。遇到这种情况,可以选择兼容版本,也可以使用 HttpClient 直接调用官方 REST API。

安全与合规注意事项

不要用普通表单收集完整卡号和 CVV,也不要把这些数据写入数据库、日志、异常信息或分析系统。尤其不能保存 CVV。

还应做好以下安全措施:

  • 全站使用 HTTPS。
  • 将 API Secret、Webhook Secret 等凭据存放在安全配置或密钥管理服务中,不要写进源代码。
  • 使用服务商提供的托管收银台、iframe 或 Tokenization 组件。
  • 在服务器端重新读取并校验订单金额、币种和商品信息。
  • 验证 Webhook 签名,并核对交易编号、金额、币种及订单关联关系。
  • 为创建支付、Webhook 和退款操作实现幂等处理。
  • 不要向前端返回支付服务商的 Secret Key。
  • 分开管理测试环境和生产环境的密钥、Webhook 地址及交易数据。
  • 根据实际业务范围评估 PCI DSS,以及当地的支付、隐私和税务要求。

正确的接入方式不是寻找“ASP.NET 自带的 Payment Gateway”,而是选择适合业务的支付服务商,再通过其 .NET SDK 或 REST API 接入 ASP.NET 应用。对于大多数电商项目,托管收银台配合服务端 Webhook,可以降低实现成本和安全风险。

备注:内容仅供参考。