支付网关集成图:授权、3-D Secure、请款与 Webhook

从商户工程师的视角看一笔银行卡支付:结账时发生了什么,之后通过 Webhook 收到什么,资金和订单状态究竟在哪里变化。

· 创始人兼开发者

发布于

支付网关集成图:授权、3-D Secure、请款与 Webhook

在大多数架构图上,一笔支付就是从商店指向“Stripe”或“Adyen”的一支箭头。在生产环境中,它却是一串参与方和两条时间线:顾客在结账页面等待时发生的事,以及几分钟甚至几天后通过 Webhook 到来的事。大多数支付 bug 都藏在这两者之间的缝隙里。

本文从商户工程师的角度画出一笔银行卡支付:参与方、正常路径、为什么浏览器里的结果不是事实来源、授权之后会发生什么,以及一张展示资金和订单状态在哪里变化的图。本文不绑定任何厂商;Stripe 和 Adyen 只是作为支付服务提供商如何记录细节的例子出现。

参与方

参与方 在银行卡支付中的角色
顾客 持卡人,在浏览器或应用中付款
结账前端 你的结账页面,嵌有支付服务提供商(PSP)提供的卡信息输入框
支付后端 你的服务,负责创建支付并掌管订单状态
PSP 你对接的网关:API、卡信息输入框、3-D Secure、Webhook、报表
收单机构 代表商户受理银行卡支付的银行,通常经由 PSP 签约
卡组织 在收单机构和发卡行之间传递授权和清算
发卡行 持卡人的银行:验证持卡人身份,批准或拒绝,并冻结资金

正常路径

  1. 顾客点击“支付”。 前端请求你的后端为某个订单发起结账。
  2. 后端在 PSP 创建一笔支付,带上金额、币种、订单 ID 和幂等键,并把 PSP 的客户端令牌交给前端。
  3. 前端在 PSP 的输入框中收集卡信息,通常是 iframe 或 SDK,因此卡号从浏览器直接发往 PSP,从不经过你的服务器。
  4. 发卡行决定如何进行 3-D Secure。 EMVCo 描述了两种流程:在无感流程中,发卡行根据风险评估直接批准,不向顾客询问任何信息;在挑战流程中,顾客需要确认,例如输入一次性验证码或使用银行应用。在一些地区,这就是满足强客户认证要求的方式。
  5. PSP 发送授权请求,经收单机构和卡组织到达发卡行,发卡行批准并冻结资金。
  6. 前端显示结果。 显示“谢谢”没有问题,但这还不是发货的时候。
  7. PSP 发送 Webhook,告知支付已获授权。你的后端验证它并更新订单。
  8. 后端在订单发货时请款,数字商品则立即请款。
  9. PSP 打款结算,批量进行并扣除手续费;对账任务把结算款与订单逐一匹配。

卡信息输入框关系到合规。如果电商网站只是嵌入 PSP 的支付表单,自身不存储、不处理也不传输任何卡数据,它可能适用 PCI DSS 的 SAQ A,而不是长得多的 SAQ A-EP。正如 PCI 安全标准委员会所解释的,在 PCI DSS v4.0.1 下,这还要求保护页面免受恶意脚本攻击。请向你的收单机构确认适用的问卷。

为什么浏览器不是事实来源

顾客可能在 3-D Secure 过程中关掉标签页,在发卡行批准后断网,或者在错误的时刻刷新页面。如果你的订单状态依赖浏览器回报,一些已付款的订单会一直显示未付款,一些失败的订单反而会显示已付款。事实来源是 PSP 的 Webhook 或它的 API。像对待任何其他消息流一样对待它们:

  • 验证签名。 PSP 会对 Webhook 请求签名,例如 Stripe 使用 Stripe-Signature 请求头,Adyen 使用 HMAC 签名。拒绝所有无法验证的请求。
  • 先快速响应,再处理。 Stripe 的 Webhook 指南要求在执行任何复杂逻辑之前先返回 2xx。把验证后的事件放进队列,然后返回。
  • 预料到重复。 同一个事件可能被端点接收多次。记录已处理的事件 ID,跳过重复的事件。
  • 不要依赖顺序。 Stripe 明确表示,不保证按照事件生成的顺序投递。当收到与当前状态不符的事件时,从 PSP 的 API 获取这笔支付。
  • 预料到重试。 在正式模式下,Stripe 会以指数退避的方式对投递失败的事件重试,最长持续三天,所以响应慢的端点之后还会再次收到同一个事件。

反方向也需要同样的纪律。在每个创建或修改支付的请求中都附上幂等键,超时后重试时复用同一个键,这样重试就不会重复扣款。幂等键不会永久有效:Stripe 可能在 24 小时后删除它们,Adyen 会保留 7 到 14 天。

授权之后:请款、退款、拒付与结算

  • 授权冻结资金,请款才真正转移资金。 不请款,授权就会过期。在线银行卡支付的期限通常在一周左右,Stripe 按卡品牌以及支付由谁发起分别列出了期限。在到期前请款,否则冻结会被释放。
  • 请款也可能失败。 Adyen 记录了一些罕见情况:PSP 已接受请款,卡组织却将其拒绝,所以你的订单状态也需要为此留出路径。
  • 退款是针对已请款支付的新操作。 它们异步完成,同样以 Webhook 的形式到达。
  • 拒付来自发卡行,有时在数周之后,发生在持卡人对一笔支付提出争议时。有些卡组织会先发起调单;请及时回应,否则可能演变成一笔很难胜诉的拒付。
  • 结算需要对账。 使用 PSP 的结算报表,把每一笔结算款与它所结算的支付、退款、手续费和拒付逐一对应。

图

顾客在结账 Web 应用中付款,该应用在支付 API 中发起结账,并把卡信息发送给支付服务提供商。PSP 与卡组织和发卡行完成 3-D Secure 和授权,然后发送一个 Webhook,经由队列到达支付 API。

11 个组件 · 11 个连接

在 Archboard 中打开
图表内容
  • Customer
  • Acquirer, card network, issuer
  • Payment service provider
  • Merchant systems · no raw card data
  • Checkout web app
  • Payments API
  • Webhook handler
  • Orders + payments
  • Payment events
  • Reconciliation job
  • 1 Pay
  • 2 Start checkout
  • 3 Create payment, later capture
  • 4 Card details (hosted fields)
  • 5 3-D Secure, authorization
  • 6 Webhook: authorized
  • 7 Verified event
  • 8 Update order
  • orders, payments
  • match payouts
  • payout reports, daily
一笔经由支付服务提供商的银行卡支付:托管字段让卡数据远离商户系统,3-D Secure 和授权在 PSP 处完成,只有经过验证的 Webhook 事件到达支付 API 时,订单状态才会改变。

这张图的读法如下:

  • 实线连线是结账本身:付款(1)、发起结账(2)、创建支付(3)、从 PSP 的输入框把卡信息发给 PSP(4),以及 PSP 与卡组织之间的 3-D Secure 和授权(5)。
  • 虚线连线发生在之后:Webhook(6)、放入队列的已验证事件(7),以及更新订单的支付 API(8)。每晚运行的对账任务读取 PSP 的结算报表,并与订单进行匹配。
  • 框架标出商户的系统。原始卡数据从不进入其中;卡号从 PSP 的输入框直接发往 PSP。

如何画支付流程

  • 给步骤编号。 支付是一个序列;有了编号,不需要单独画时序图也能看清顺序。
  • 区分同步和异步。 结账请求用实线,Webhook、队列和批处理任务用虚线。
  • 标出卡数据边界。 说明哪些组件能看到卡号。使用托管字段时,你自己的组件一个都不应该看到。
  • 标出订单状态在哪里变化。 应由一个组件掌管订单状态,并由经过验证的事件驱动,而不是由浏览器驱动。
  • 为资金操作命名。 授权、请款、退款和结算是时机各不相同的不同事件;给它们加上标签。
  • 画出重要的失败情况:创建时超时(用同一个幂等键重试)、重复的 Webhook(去重)、被放弃的 3-D Secure 挑战(让订单过期)、过期的授权(重新授权或取消)。

想看同一流程的时序视图,请参阅支付流程图用例。事件方面请参阅事件驱动架构图指南,安全评审方面请参阅威胁建模中的数据流图。

在 Archboard 中构建

  1. 使用 Google 或 GitHub 登录,并新建一张图。
  2. 为顾客添加一个 C4 模型节点包中的 人员,再添加两个 C4 模型节点包中的 外部系统 节点:一个表示支付服务提供商,另一个表示收单机构、卡组织和发卡行。
  3. 为商户系统添加一个基础节点包中的 框架,在其中放入四个 C4 模型节点包中的 容器 节点:结账 Web 应用、支付 API、Webhook 处理程序和对账任务。
  4. 在框架内添加一个数据节点包中的 Postgres 存放订单和支付,再添加一个数据节点包中的 队列 承载支付事件。
  5. 按顺序给连线编号,把 Webhook、队列和对账的连线设为虚线,并添加一个基础节点包中的 Markdown 块说明线型的含义。

这个示例共用 11 个图表节点,在 Free 套餐的限制之内。

常见问题

支付网关和支付处理商有什么区别? 网关是你的系统与之通信的接口:API、卡信息输入框、Webhook。处理商负责让交易在卡组织网络中流转。许多 PSP 两者都提供,同时还提供收单服务。

浏览器显示支付成功时,可以把订单标记为已付款吗? 可以向顾客显示确认信息,但只有在收到经过验证的 Webhook,或向 PSP 的 API 核实之后,才能更改订单状态。

为什么先授权、后请款? 为了只对实际发出的商品收费。授权冻结资金作为保证;履约时再通过请款转移资金。注意授权的有效期。

需要处理乱序到达的 Webhook 吗? 需要。让处理程序具备幂等性,把订单作为拒绝不可能状态转换的状态机来管理,拿不准时就从 PSP 获取当前的支付信息。

使用托管字段就能满足 PCI 合规吗? 它能缩小合规范围,但不能消除它。你仍然需要填写自评问卷并保护结账页面。请向收单机构确认适用哪一份问卷。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。