ASP.NET 中是否有可用的 Payment Gateway?
明确结论
ASP.NET 本身没有 Payment Gateway。它是 Web 开发框架,不处理银行卡收单、资金清算或 Credit Card Transaction。
项目需要接入第三方支付服务商或银行提供的支付接口,比如 Stripe、Adyen、PayPal、Braintree,以及业务所在地的银行或持牌支付机构。选择哪家服务商,要看商户和用户所在地区、结算币种、费率及当地监管要求。
一般建议使用支付服务商提供的托管收银台或前端安全组件,不要让信用卡号进入自己的 ASP.NET 服务器。
推荐的接入流程
常见的支付流程是:
- 用户在 ASP.NET 网站创建订单。
- 服务器生成内部订单号,将订单状态保存为
Pending。 - 服务器调用支付服务商 API,创建 Payment、Payment Intent 或 Checkout Session。
- 用户在服务商托管的页面或安全组件中填写信用卡信息。
- 支付服务商完成验证、授权和必要的 3-D Secure 流程。
- 支付服务商通过 Webhook 将支付结果通知 ASP.NET 后端。
- 后端验证 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,可以降低实现成本和安全风险。
备注:内容仅供参考。
