PAYATHON 2026

ASP.NET 2.0 电商网站如何选择支付网关和 HTTPS?

支付老李

明确结论

电商网站应全站启用 HTTPS,不能为了节省主机费用而继续使用纯 HTTP。即使支付页面由第三方支付网关托管,商品页、登录页、购物车、结账入口和支付结果通知地址也都应使用 HTTPS。

支付方式应优先选择支付服务商提供的 Hosted Checkout(托管收银台)或 Tokenization(令牌化)方案,让银行卡号、CVV 等敏感信息直接提交给支付服务商。ASP.NET 2.0 应用不应自行接收、保存或记录完整的银行卡数据。

ASP.NET 2.0 已经非常陈旧,不适合用来新建支付系统。正式上线前,应优先升级到仍受支持的 .NET 平台。如果遗留系统暂时无法升级,至少要确认操作系统、IIS 和运行时支持支付服务商要求的 TLS 版本和加密套件。

为什么必须使用 HTTPS

HTTPS 不只是让浏览器显示一个安全标志,它还能解决以下实际问题:

  • 加密浏览器与服务器之间传输的登录信息、地址、订单和支付状态。
  • 防止页面在传输过程中被篡改,例如结账按钮被替换为攻击者的支付链接。
  • 保护 Cookie 和 Session,降低会话被窃取的风险。
  • 满足现代浏览器、支付接口和第三方 SDK 的基本安全要求。
  • 避免用户结账时看到“不安全”提示,进而影响信任和转化率。

如果银行卡信息会直接提交到自己的网站,没有 HTTPS 就不应上线。即使银行卡表单由第三方托管,HTTP 页面本身仍可能遭到篡改,因此不能把第三方收银台当成不启用 HTTPS 的理由。

HTTPS 只能保护数据传输,无法独自保证整个支付流程的安全。应用仍要妥善处理身份验证、订单金额校验、回调验签、权限控制、日志脱敏和服务器安全更新。

如何选择支付网关

没有适合所有项目的“最佳网关”。选择时要考虑客户所在国家、结算币种、交易量和团队的开发能力,重点比较以下内容:

  1. 是否支持目标地区使用的 credit card、debit card 和所需卡组织。
  2. 是否提供 Hosted Checkout、Tokenization,或符合 PCI DSS 要求的前端组件。
  3. 是否提供 ASP.NET 可以调用的标准 REST API,或者兼容当前运行环境的 SDK。
  4. 交易费、退款费、拒付费、跨境费、月租费和最低消费。
  5. 结算周期、支持的币种,以及资金是否直接结算到客户的商户账户。
  6. 是否支持退款、部分退款、重复付款保护和 3-D Secure。
  7. Webhook 是否带有签名,是否提供测试环境和完整文档。
  8. 服务商是否接受客户所属行业,开户审核需要多长时间。

对于 ASP.NET 2.0 项目,可以按以下顺序选择:

  • 首选:跳转到支付服务商的托管收银台。
  • 次选:使用支付服务商托管的银行卡输入组件,服务器只接收 token。
  • 不建议:将银行卡号直接提交到自己的 ASP.NET 应用。
  • 应避免:自行保存银行卡号或 CVV。

托管收银台通常开发成本最低,也能大幅缩小 PCI DSS 的合规范围,但商户仍需承担自身的安全和合规责任。具体要求应向支付服务商、收单机构或合规顾问确认。

推荐的支付流程

较为稳妥的支付流程如下:

  1. 用户在网站创建订单。
  2. 服务器根据数据库中的商品价格重新计算金额,不接受浏览器提交的总价作为依据。
  3. 服务器调用支付网关创建支付会话。
  4. 用户跳转到支付服务商的 HTTPS 收银台。
  5. 支付完成后,用户返回网站的结果页面。
  6. 支付服务商另行向网站的 Webhook 地址发送通知。
  7. 网站验证通知签名,再通过支付网关 API 核实交易状态、金额、币种和订单号。
  8. 验证通过后,以幂等方式将订单更新为已支付。

不能仅凭用户返回页面中的 success=true 确认订单。返回地址由用户的浏览器访问,其中的参数可能被伪造。最终支付状态应以通过验签和服务器核验的支付通知为准。

ASP.NET 示例结构

不同支付网关使用的字段、签名算法和接口地址各不相同。下面的代码只演示安全的处理结构,不能代替具体服务商的官方 SDK 或文档。

protected void PayButton_Click(object sender, EventArgs e)
{
    int orderId = GetValidatedOrderId();

    Order order = OrderRepository.GetPendingOrder(orderId);

    // 金额必须从服务器端订单数据计算,不能使用页面提交的金额。
    decimal amount = OrderCalculator.CalculateTotal(order);

    PaymentSessionRequest request = new PaymentSessionRequest();
    request.OrderReference = order.Id.ToString();
    request.Amount = amount;
    request.Currency = "GBP";
    request.ReturnUrl = "https://shop.example.com/payment/return.aspx";
    request.WebhookUrl = "https://shop.example.com/payment/webhook.aspx";

    PaymentSession session = PaymentGateway.CreateSession(request);

    // 只允许跳转到配置中认可的支付服务商域名。
    ValidatePaymentUrl(session.CheckoutUrl);

    Response.Redirect(session.CheckoutUrl, false);
    Context.ApplicationInstance.CompleteRequest();
}

处理 Webhook 时必须验证签名,同时防止重复通知导致重复发货:

protected void Page_Load(object sender, EventArgs e)
{
    string payload;

    using (StreamReader reader = new StreamReader(Request.InputStream))
    {
        payload = reader.ReadToEnd();
    }

    string signature = Request.Headers["X-Payment-Signature"];

    if (!PaymentGateway.VerifyWebhookSignature(payload, signature))
    {
        Response.StatusCode = 400;
        return;
    }

    PaymentNotification notification =
        PaymentGateway.ParseNotification(payload);

    Order order = OrderRepository.GetByReference(
        notification.OrderReference);

    bool valid =
        notification.Status == "paid" &&
        notification.Amount == order.ExpectedAmount &&
        notification.Currency == order.Currency;

    if (!valid)
    {
        Response.StatusCode = 400;
        return;
    }

    // 数据库更新应具有幂等性,同一个支付通知只能生效一次。
    OrderRepository.MarkPaidOnce(
        order.Id,
        notification.PaymentId);

    Response.StatusCode = 200;
}

X-Payment-Signature、状态值和验签方法都只是占位示例,必须替换为所选支付网关规定的名称和算法。密钥应存放在受保护的服务器配置中,不能写入源码、JavaScript 或版本库。

HTTPS 配置要点

证书应由受信任的证书颁发机构签发,并覆盖网站实际使用的域名。配置完成后,要把 HTTP 请求永久重定向到 HTTPS,并将认证 Cookie 和 Session Cookie 设置为仅通过安全连接发送。

ASP.NET 可以在 web.config 中启用安全 Cookie:

<configuration>
  <system.web>
    <httpCookies requireSSL="true" httpOnlyCookies="true" />
    <forms requireSSL="true" />
  </system.web>
</configuration>

HTTP 到 HTTPS 的重定向最好由 IIS、反向代理或负载均衡器处理。如果只能在应用层完成,可以使用类似下面的逻辑:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    if (!Request.IsSecureConnection)
    {
        string secureUrl =
            "https://" + Request.Url.Host + Request.RawUrl;

        Response.Redirect(secureUrl, true);
    }
}

如果网站部署在反向代理、CDN 或负载均衡器之后,Request.IsSecureConnection 可能无法反映用户实际使用的协议。此时应由可信代理传递协议状态,并按照代理厂商提供的配置方式进行判断。不要无条件信任公网请求携带的任意转发头。

此外还要完成以下配置:

  • 禁用过时的 SSL/TLS 协议和弱加密套件。
  • 确认应用能够使用支付网关要求的 TLS 版本建立连接。
  • 等 HTTPS 稳定运行后启用 HSTS。
  • 避免 HTTPS 页面加载 HTTP 图片、脚本或样式,以免产生 mixed content。
  • 不要在日志中记录密码、完整银行卡号、CVV、API secret,或原始支付报文中的敏感字段。
  • 为登录、结账和支付接口配置 CSRF 防护与合理的频率限制。

关于主机费用

启用 HTTPS 不等于必须购买价格翻倍的服务器。额外成本可能来自证书、独立 IP、托管套餐限制或服务商的代管费用。部分较老的 Windows 主机不支持 SNI,因此要求每张证书使用独立 IP;现代托管环境通常没有这个限制。

在购买 £39.99 或 £89.99 的方案前,应先向主机商确认:

  • 较低价套餐是否允许安装自有 TLS 证书。
  • 差价来自证书、独立 IP,还是服务器规格。
  • 是否支持证书自动续期。
  • 支持哪些 TLS 版本。
  • 能否通过反向代理或 CDN 终止 TLS。
  • ASP.NET 2.0 应用调用支付 API 时,是否兼容对方要求的 TLS 版本。

如果低价套餐无法可靠提供 HTTPS,就不适合作为电商网站的正式生产环境。此时应更换套餐或主机商,而不是取消 HTTPS。

最后需要注意

支付系统的主要风险通常不在于选择了哪个支付控件,而在于应用会直接接触多少银行卡数据。由成熟的支付服务商托管银行卡输入和验证流程,通常比在 ASP.NET 2.0 中自行开发支付表单更安全,也更容易维护。

合理的方案是升级技术平台、全站启用 HTTPS、采用 Hosted Checkout 或 Tokenization,通过服务器端 Webhook 验签确认支付,并将主机安全、证书维护和支付合规纳入上线所需的必要成本。

备注:内容仅供参考。