PAYATHON 2026

如何通过 JavaScript 和 Payment Request API 安全处理付款?

支付阿杰

结论

Payment Request API 不是支付处理商,也不会自动扣款。它的作用是调用浏览器原生支付界面,再把用户所选支付方式的结果交给网站。授权、扣款、3-D Secure 验证、退款和风控仍需由服务端通过支付服务商完成。

用 JavaScript 获取 PAN、有效期和 CVC,再发送到自己的服务器,并非绝对无法满足 PCI DSS,但这样通常会让网站和服务器进入更严格的合规范围,带来较高的成本和风险。使用 Payment Request API 并不会让这条数据链路自动合规。

实际项目中,更合适的做法是使用支付服务商提供的 JavaScript SDK 或数字钱包,让浏览器返回一次性 token 或 PaymentMethod 标识,避免业务系统接触原始卡号。例如,Stripe 曾通过 Stripe.js 的 Payment Request Button 集成 Payment Request API,并把支付结果转换为 PaymentIntents 可用的 PaymentMethod。服务商可能调整具体产品入口,因此实施前需要核对最新文档和浏览器支持范围。

Payment Request API 在流程中的位置

一笔完整的支付大致经过以下流程:

浏览器展示支付界面
        ↓
Payment Request API 返回支付凭据或钱包令牌
        ↓
前端把令牌发送给业务服务器
        ↓
业务服务器调用支付处理商 API
        ↓
支付处理商完成授权、3-D Secure 和扣款
        ↓
服务器根据 Webhook 确认最终支付状态

Payment Request API 的 response.complete() 只会通知浏览器关闭支付界面,并显示成功或失败状态,并不表示银行卡已经扣款成功。调用它之前,至少要取得支付处理商的初步确认。订单的最终状态应以服务端查询结果或通过签名验证的 Webhook 为准。

为什么会涉及 PCI DSS

是否涉及 PCI DSS,取决于原始银行卡数据经过哪些系统,而不是页面由客户端还是服务端生成。

如果页面获取卡号、有效期和安全码,并把这些信息发送到自己的服务器,那么:

  • 服务器、日志、监控系统和相关网络环境都可能被纳入 PCI DSS 评估范围;
  • 卡号和 CVC 不能写入日志、分析系统、错误报告或数据库;
  • 完成授权后不得保存 CVC;
  • 系统需要满足访问控制、漏洞管理、密钥管理和安全测试等要求;
  • 数据来自浏览器原生界面,并不意味着商户只需填写最简化的 SAQ。

使用支付服务商托管的字段、官方 SDK 或钱包令牌化方案时,原始卡数据通常会直接进入支付服务商的受控环境,商户系统只接触 token。这通常可以缩小 PCI DSS 的适用范围,但不等于完全无需合规。具体适用哪种 SAQ、是否需要其他验证,应由收单机构、支付服务商或合格的 PCI 顾问根据实际数据流确定。

推荐的实现方式

1. 不要依赖 basic-card

早期示例经常使用:

{
  supportedMethods: "basic-card"
}

这种方式要求商户直接处理银行卡明细,而且浏览器支持已经不可靠。Payment Request API 框架仍可用于钱包或特定支付处理程序,但不适合作为新的跨浏览器信用卡方案。

2. 通过支付服务商 SDK 创建令牌

下面的示例展示了 Stripe.js Payment Request 集成的典型结构,主要用于说明正确的数据流。Stripe 的推荐组件、可用支付方式和接口细节可能发生变化,上线时应以当前官方文档为准。

const stripe = Stripe("pk_test_your_publishable_key");

const paymentRequest = stripe.paymentRequest({
  country: "US",
  currency: "usd",
  total: {
    label: "Order total",
    amount: 2999
  },
  requestPayerName: true,
  requestPayerEmail: true
});

paymentRequest.on("paymentmethod", async (event) => {
  try {
    const response = await fetch("/api/payments/confirm", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        paymentMethodId: event.paymentMethod.id,
        orderId: "order_123"
      })
    });

    const result = await response.json();

    if (!response.ok) {
      event.complete("fail");
      return;
    }

    event.complete("success");

    if (result.requiresAction) {
      const confirmation = await stripe.confirmCardPayment(
        result.clientSecret
      );

      if (confirmation.error) {
        // 显示验证失败信息,但不要记录卡片敏感数据
      }
    }
  } catch (error) {
    event.complete("fail");
  }
});

前端传给服务器的是 paymentMethodId,不是卡号和 CVC。最终金额也不能由前端决定,服务器必须根据可信的订单数据重新计算。

服务端可以创建并确认 PaymentIntent:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

app.post("/api/payments/confirm", async (req, res) => {
  const order = await loadOrder(req.body.orderId);

  // 应在这里验证登录用户、订单状态、库存、币种和真实金额
  const paymentIntent = await stripe.paymentIntents.create({
    amount: order.amount,
    currency: order.currency,
    payment_method: req.body.paymentMethodId,
    confirm: true,
    automatic_payment_methods: {
      enabled: true
    },
    return_url: "https://example.com/payment/return",
    metadata: {
      orderId: order.id
    }
  });

  res.json({
    paymentIntentId: paymentIntent.id,
    clientSecret: paymentIntent.client_secret,
    requiresAction:
      paymentIntent.status === "requires_action"
  });
});

生产环境还需要处理幂等键、重复提交、异常恢复和支付服务商 Webhook。不能只根据前端返回的“成功”状态发货。

可以选择哪些支付处理商

Stripe 是一个明确的选择。Stripe.js 可以对支付凭据进行令牌化,服务端再通过 PaymentIntents 完成支付,从而避免商户应用直接传输原始卡号。

Adyen、Braintree 等服务商也提供钱包、托管组件或浏览器支付集成,但它们未必允许商户把 Payment Request API 返回的任意原始卡数据直接提交给普通商户接口。多数服务商要求使用自己的 SDK、组件或约定的支付方法标识,以保证令牌化、3-D Secure 和合规数据流正常工作。

选择支付服务商时,需要确认:

  • 是否明确支持目标浏览器和目标钱包;
  • 银行卡数据是否由服务商直接接收并令牌化;
  • 是否支持当地要求的 3-D Secure 或强客户认证;
  • 商户系统适用的 PCI DSS 范围和 SAQ 类型;
  • 是否提供服务端确认、幂等处理和签名 Webhook;
  • 当用户的浏览器不支持 Payment Request API 时,是否有可靠的回退结账页面。

合适的做法不是寻找能够接收任意原始 PaymentResponse 的处理商,而是选择提供 Payment Request、数字钱包或同类快速结账组件的支付服务商,并按照其要求使用令牌化流程。这样既能完成实际扣款,也能尽量把银行卡敏感数据隔离在支付服务商的 PCI DSS 环境中。

备注:内容仅供参考。