PAYATHON 2026

payment gateway 可用性预期与 uptime guarantee

支付小周

结论

要求 payment gateway 明确承诺可用性、故障通知和技术支持,是合理的。支付链路直接影响成交,合同或 SLA 至少应约定可用性指标及其统计口径、故障响应时间和补偿方式。

不过,商户不能完全依赖服务商的监控,还需要建立自己的端到端监控,分别检查创建支付、接收 webhook、订单入账和退款完成等环节。双 gateway 能降低单点故障风险,但切换时可能遇到支付状态不确定、重复扣款、token 不兼容和对账分裂等问题,不能在请求失败后立即换一家重试。

如果候选服务商既没有 SLA、状态页和故障通知,也无法提供稳定的 webhook、幂等机制、查询接口及测试环境,这属于明显的运营风险,不只是少了几项功能。

uptime guarantee 应包含什么

合同只写 “99.9% uptime” 意义有限,还要确认:

  • 可用性按月、季度还是全年计算。
  • “可用”是指接口能够建立连接,还是支付请求能在规定时间内得到正确处理。
  • 指标覆盖哪些组件,例如 API、Hosted Checkout、tokenization、webhook、退款和管理后台。
  • 计划维护、上游银行故障、卡组织故障和网络异常是否排除在外。
  • 故障从什么时间开始计算,以服务商监控还是商户报告为准。
  • 严重故障需要多久确认,多久更新一次进展,恢复目标是什么。
  • SLA 未达标时自动补偿,还是需要在限定时间内申请 service credit。
  • SLA 是否只补偿服务费。多数情况下,它不会赔偿商户因订单损失而少收的收入。

以月度可用性 99.9% 为例,理论上一个月仍可能出现约 43 分钟的不可用时间。这个数字只能用来理解大致量级,实际时长还取决于当月天数和合同规定的统计方式。

SLA 主要用于明确责任和响应流程,无法替代容灾设计。

监控应分为三层

服务商侧监控

成熟的 gateway 通常会提供:

  • 公开或登录后可见的 status page;
  • incident 邮件、短信或 webhook 订阅;
  • 计划维护通知;
  • API 延迟和错误率信息;
  • incident 时间线与事后分析;
  • 可查询的历史可用性记录。

这些信息很有用,但服务商监控的是整个平台,未必能发现特定商户账户、地区、支付方式或配置存在的问题。

商户侧技术监控

商户至少要记录以下指标,并为其设置告警:

  • 请求量、成功率和失败率;
  • HTTP 状态码与 gateway error code;
  • timeout、连接失败和 TLS 错误;
  • p50、p95、p99 响应时间;
  • 支付创建成功后,webhook 长时间未到达的数量;
  • webhook 验签失败、重复投递和处理失败;
  • 支付状态长期停留在 pending 的数量;
  • 退款、撤销和查询接口的异常率。

告警不能只看单次失败。网络抖动和发卡行拒绝本来就会发生,更适合使用滑动窗口,例如:“最近 5 分钟技术失败率超过 10%,且样本数不少于 30”。

业务拒绝也要与技术故障分开统计。insufficient_funds、风控拒绝或 3-D Secure 验证失败,并不代表 gateway 宕机。

端到端业务监控

API 返回成功,不代表用户一定能完成付款。还要观察:

  • checkout 页面能否正常加载;
  • JavaScript SDK 能否正常初始化;
  • 支付方式是否正确显示;
  • 支付完成率是否异常下降;
  • webhook 到达和订单确认是否延迟;
  • 支付成功金额是否与结算及对账文件一致。

真实支付通常不适合频繁探测,因为这可能产生手续费或触发风控。应优先使用服务商提供的 sandbox、health endpoint 或专用测试账户。如果必须在生产环境中探测,需要提前确认服务商的规则,并控制频率和金额。

双 gateway 切换不能只看一次请求失败

双 gateway 可以减少单一供应商故障带来的影响,但要安全切换,至少需要处理以下问题。

区分明确失败和未知结果

出现以下情况时,通常可以考虑改用另一家 gateway:

  • 请求还未发出;
  • DNS、连接或 TLS 建立失败;
  • 服务商明确返回 “未创建支付”;
  • circuit breaker 已确认该 gateway 出现大面积异常。

出现以下情况时,不能直接重试:

  • 请求超时,但请求可能已经到达 gateway;
  • gateway 返回 5xx,但无法确认交易是否已经创建;
  • 客户已经完成验证,但商户还未收到最终状态;
  • webhook 延迟。

遇到这类未知结果,应先通过原 gateway 的支付查询接口和 merchant reference 核实状态,否则可能导致重复扣款。

使用幂等键和稳定的商户订单号

每次支付尝试都要有唯一标识,同时保留不变的业务订单号:

order_id: ORDER-20260901-10001
payment_attempt_id: ATTEMPT-01
gateway: gateway_a
idempotency_key: ORDER-20260901-10001-ATTEMPT-01

切换到第二家 gateway 时,需要创建新的 payment_attempt_id,但仍要关联同一个 order_id。不要假设两家服务商可以共用幂等键。

使用 circuit breaker

下面是一个简化的 TypeScript 示例,用于说明决策边界,不对应任何 gateway 的真实 API:

type GatewayResult =
  | { status: "succeeded"; transactionId: string }
  | { status: "declined"; reason: string }
  | { status: "unknown"; reference: string };

async function createPayment(order: Order): Promise<GatewayResult> {
  if (!circuitBreaker.isOpen("gateway_a")) {
    const result = await tryGatewayA(order);

    if (result.status === "succeeded" || result.status === "declined") {
      return result;
    }

    // 超时等未知结果必须先查询,不能立即换 gateway 重扣。
    const confirmed = await queryGatewayA(result.reference);

    if (confirmed.status !== "not_found") {
      return confirmed;
    }

    circuitBreaker.recordTechnicalFailure("gateway_a");
  }

  return tryGatewayB({
    ...order,
    paymentAttemptId: createNewAttemptId()
  });
}

实际系统还要处理并发请求、浏览器重复提交、查询超时,以及 webhook 与主动查询之间的竞态条件。

注意 token 和支付流程兼容性

双 gateway 经常受以下条件限制:

  • 一家 gateway 生成的 card token 通常不能直接交给另一家使用;
  • 两家的 Hosted Checkout 页面和前端 SDK 不同;
  • 3-D Secure、Apple Pay、Google Pay 等配置可能需要分别申请;
  • 两家的风控规则、支持币种和本地支付方式可能不同;
  • capture、void、refund 等操作的状态模型可能不一致;
  • 由 gateway A 创建的交易,通常仍需通过 gateway A 退款。

因此,商户应在自己的系统中建立统一的支付状态模型和 gateway adapter,避免订单系统直接依赖某家服务商的字段。

一个更安全的路由结构

interface PaymentGateway {
  createPayment(input: CreatePaymentInput): Promise<PaymentResult>;
  getPayment(reference: string): Promise<PaymentStatus>;
  capture(reference: string, amount: number): Promise<PaymentResult>;
  refund(reference: string, amount: number): Promise<RefundResult>;
}

class PaymentRouter {
  constructor(
    private readonly primary: PaymentGateway,
    private readonly secondary: PaymentGateway
  ) {}

  async pay(input: CreatePaymentInput): Promise<PaymentResult> {
    const primaryResult = await this.primary.createPayment(input);

    if (primaryResult.status !== "unknown") {
      return primaryResult;
    }

    const status = await this.primary.getPayment(
      primaryResult.gatewayReference
    );

    if (status !== "not_found") {
      return status;
    }

    return this.secondary.createPayment({
      ...input,
      paymentAttemptId: crypto.randomUUID()
    });
  }
}

在生产系统中,路由决策、每次支付尝试、原始响应摘要和状态变化都应持久化。只把这些信息保存在内存中,服务重启后就可能无法判断交易结果。

webhook 必须支持可靠处理

可靠的 webhook 至少需要具备以下能力:

  • 请求签名和时间戳校验;
  • 明确的事件 ID;
  • 失败后自动重试;
  • 对重试时间表或重试上限作出说明;
  • 支持事件查询和手动重放;
  • 提供 IP 信息或其他辅助验证手段;
  • 事件版本管理;
  • 说明如何处理无序和重复事件。

商户侧必须按事件 ID 进行幂等处理:

app.post("/webhooks/payments", async (req, res) => {
  if (!verifySignature(req)) {
    return res.sendStatus(401);
  }

  const event = parseWebhook(req.body);

  await database.transaction(async (tx) => {
    const inserted = await tx.processedEvents.insertIfAbsent({
      eventId: event.id,
      gateway: event.gateway
    });

    if (!inserted) {
      return;
    }

    await applyPaymentEvent(tx, event);
  });

  res.sendStatus(200);
});

不能假设 webhook 只会发送一次,也不能假设事件会严格按顺序到达。处理状态变化时,应拒绝不合法的状态倒退。例如,一笔已经 refunded 的交易,不能因迟到的事件重新变成 authorized。

还应向 gateway 要求哪些支持能力

API 和开发工具

  • 完整、可搜索并保留版本记录的 API 文档;
  • 独立 sandbox;
  • 覆盖成功、拒绝、超时、3-D Secure 和退款场景的测试数据;
  • 稳定的错误码,以及每种错误是否可以重试的明确说明;
  • idempotency key;
  • 支付状态查询接口;
  • SDK 更新和弃用通知;
  • API versioning 和足够长的迁移窗口;
  • request ID 或 correlation ID,方便双方查找日志。

运维和故障处理

  • status page 和主动故障通知;
  • 明确的支持渠道与升级路径;
  • 严重级别定义及对应的响应目标;
  • 是否提供 7×24 小时紧急支持,以及是否另行收费;
  • incident report 或 root cause analysis;
  • 计划维护需要提前多久通知;
  • webhook 查询、重试或重放工具。

财务与运营

  • 交易、手续费、退款、拒付和结算报表;
  • 可自动下载的 reconciliation 文件或 API;
  • settlement schedule 和延迟处理规则;
  • chargeback 通知、证据提交和截止日期管理;
  • 可搜索交易的管理后台;
  • 子账户、角色和审计日志;
  • 批量退款或批量查询能力。

安全与合规

具体要求取决于业务所在地区、支付方式和数据流,通常需要核查:

  • PCI DSS 合规范围及相关证明;
  • tokenization 和敏感数据的保存方式;
  • webhook 签名与密钥轮换;
  • TLS 要求和 API 凭据权限控制;
  • 管理后台 MFA;
  • 操作审计日志;
  • 数据保存期限和删除机制;
  • 数据处理协议及数据驻留安排;
  • 对 3-D Secure、SCA 等适用法规的支持。

gateway 宣称 “PCI compliant”,不代表商户就没有合规责任。Hosted Checkout、嵌入式组件和直接处理卡号对应不同的 PCI 范围,应由合规人员结合实际集成方式确认。

上线前建议做故障演练

至少应验证以下场景:

  1. gateway 连接超时;
  2. 请求超时,但交易实际上已经成功;
  3. webhook 延迟、重复、乱序或丢失;
  4. 用户连续点击支付按钮;
  5. gateway A 故障后切换到 gateway B;
  6. 切换期间 gateway A 恢复并补发成功事件;
  7. 数据库写入失败,但 gateway 已经扣款;
  8. capture 或 refund 请求结果未知;
  9. 两家 gateway 同时不可用;
  10. 对账时发现订单状态与结算状态不一致。

每个场景都要明确负责人、能否自动恢复、何时需要人工介入,以及如何避免重复扣款。

选择服务商时的判断标准

没有公开 uptime guarantee,不代表服务一定不可靠。小型服务商也可能只在企业合同中提供 SLA。但如果对方无法说明可用性如何统计,也没有事件通知、支付查询、幂等处理、webhook 重试、对账和紧急支持机制,就很难让它安全地承担关键支付流量。

签约前,最好索取 SLA、API 文档、状态历史、支持流程和 sandbox,并完成一次故障演练。如果这些能力仍然缺失,应考虑更换服务商,或者至少只给它分配非关键的低比例流量,不要把整个 ecommerce website 的成交能力都交给这一家 gateway。

备注:内容仅供参考。