Skip to content

필사 모드: 支付系统入门:流程·PSP·卡组织·结算

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 — 支付按钮背后的世界

用户按下「支付」按钮的那一刻,看起来连一秒都不到。但在这一秒之内,卡号会经过五六家公司、至少两个支付网络,一路往返发卡行。而钱真正从账户转移到账户,并不是在那一刻,而是在几天之后。

比起其他任何系统,支付都是一个「不允许出错」的领域。搜索结果错一条,用户不过是感到不便;支付错一笔,就是有人损失了钱。这也是为什么,一致性(consistency)、幂等性(idempotency)、可审计性(auditability)在支付系统里比其他任何事都优先。

这篇文章是为第一次设计或试图理解支付系统的工程师画的一张地图。我们会看授权、扣款、结算三者有何不同,中间夹着哪些参与方,3-D Secure 和拒付(chargeback)是什么,以及为什么对账、幂等性、PCI-DSS 是支付里的常量。支付离不开安全,如果你想了解认证与令牌的基础,可以参考本站的认证与安全实验室;如果下文频繁出现的响应码看着眼生,也可以对照HTTP 状态码工具

五位参与方

理解卡支付的第一步,是弄清楚「谁在做什么」。一次卡支付里,至少涉及五个参与方。

  • 商户(merchant):出售商品或服务的一方。我们所构建的服务就属于这一方。
  • PSP / 网关(payment service provider / payment gateway):连接商户与复杂金融网络之间的中间方。Stripe、Adyen、Braintree 这样的公司。它们安全地接收卡信息,并代表商户与下面的各方通信。
  • 收单机构(acquirer / acquiring bank):代表商户「收单」卡款的银行。是让钱进入商户结算账户的那一方。
  • 卡组织(card network / scheme):像 Visa、Mastercard 这样,在收单机构与发卡行之间路由交易的网络。同时也是制定规则(rule)与手续费(interchange)的一方。
  • 发卡行(issuer / issuing bank):发行卡片的银行。真正拥有「是否批准这笔交易」最终决定权的一方,负责判断额度、余额、欺诈。

这五者如何连接,用图来看是这样的。

  [商户] --卡信息--> [PSP/网关] --> [收单机构]
                                   [卡组织]
                                  [发卡行] --通过/拒绝--> (应答按原路逆向返回)

核心直觉是这样的:卡组织是「路由器」,发卡行是「决策者」,收单机构是「商户的银行」,PSP 则是把这一切复杂性都藏起来的「适配器」。我们在代码里面对的通常只是一个 PSP 的 API,但在它背后,这整条链每次都要动一遍。

授权 vs 扣款 vs 结算 — 支付的三个阶段

学习支付时最常见的误解,是以为「支付 = 钱发生了转移」。实际上,这里分成三个在时间上错开的阶段。分不清这三者,支付的状态管理必然会乱。

1. 授权(authorization)。 向发卡行询问「这张卡能不能支付这个金额」并得到答复的阶段。发卡行核实额度、余额、欺诈信号后,回复通过或拒绝。一旦通过,对应金额就会在卡的可用额度上被冻结(hold)。但此刻钱并没有真正被扣走,更接近于「预留」。

2. 扣款(capture)。 确认要实际收取已授权金额的阶段。在线商务通常在商品发货时扣款。把授权和扣款分开的好处很直接:如果因为缺货需要取消订单,只需在扣款前撤销(void)授权即可,很干净。也可以扣款金额低于授权金额(部分扣款)。

3. 结算(settlement)。 资金真正从发卡行流向收单机构、再流向商户账户的阶段。这通常以批处理(batch)的方式,按天进行。所以用户支付的那天,和商户实际收到钱的那天之间,会有几天的时差。

把这三者的关系放到时间线上看会很清楚。

  T+0秒       授权(authorization)   -> 冻结额度,钱还没动
  T+数小时~天  扣款(capture)         -> 确认"收取这个金额"
  T+1~3天     结算(settlement)      -> 实际资金移动,入账到商户账户

在即时支付或一键支付产品里,授权和扣款有时看起来像是一次性发生的(sale/purchase 方式)。但内部这些阶段依然存在,结算总是稍后以批处理方式发生——这一点不会变。支付系统的状态机(state machine),应该正是围绕这三个阶段来设计。

跟随一次授权的流程

让我们逐步跟随一次授权是如何流转的,从用户输入卡信息并按下支付的那一刻开始。

  1) 用户输入卡信息 -> 商户前端
  2) 商户(或 PSP SDK)将卡信息发送给 PSP(已令牌化)
  3) PSP -> 向收单机构发起授权请求
  4) 收单机构 -> 路由到卡组织
  5) 卡组织 -> 转发给对应的发卡行
  6) 发卡行判断额度/余额/欺诈 -> 通过或拒绝
  7) 应答沿相同路径逆向返回
  8) 商户收到结果并更新订单状态

这里有几个工程师需要特别留意的地方。

第一,这一来一回要经过多个网络跳(hop),所以又慢又可能失败。 超时、网络中断、PSP 故障随时都可能发生。所以必须始终假设「没有收到应答」这种情况会出现。这正是后面要讲的幂等性之所以必要的原因。

第二,拒绝(decline)有很多种。余额不足、超出额度、卡片被挂失、疑似欺诈、发卡行临时故障等等。有些拒绝重试一下可能就能通过(软拒绝,soft decline),但有些拒绝重试也没用,反而会让卡组织的欺诈评分变差(硬拒绝,hard decline)。区分并分别处理这两者,直接影响支付成功率(conversion)。

第三,应答通常会附带一个授权码(authorization code)。这个码是之后在扣款、撤销、退款、对账中识别这笔交易的钥匙,必须保存下来。

3-D Secure — 转移责任的认证

在线支付(卡不在场,card-not-present)没有实体卡,因此更容易遭受欺诈。为了弥补这一点,卡组织制定了一套认证协议,叫3-D Secure(3DS)。它以 Visa 的「Verified by Visa」、Mastercard 的「Identity Check」等品牌广为人知,目前广泛使用的是 2.x 版本。

3DS 的运作方式概括来说是这样的。支付过程中,用户会被送去由发卡行控制的认证步骤。发卡行可能在这里要求额外认证(比如 App 推送批准、一次性密码、生物识别)。认证结束后,流程回到支付主线,继续走授权。

  支付请求
  发起 3DS 认证 -> 发卡行认证界面(如需要)
     │  (App 批准 / OTP / 生物识别 等)
  把认证结果附在授权请求中发给发卡行
  通过或拒绝

3DS 的核心不是技术,而是责任转移(liability shift)这个商业概念。在经过 3DS 认证的交易上如果发生欺诈,损失的责任会从商户转移到发卡行。也就是说,3DS 与其说是一个百分之百阻止欺诈的工具,不如说更接近于一种改变欺诈损失责任归属的机制。

3DS 2.x 比早期版本更聪明。它会让看起来风险较低的交易不经用户任何确认就直接通过(无摩擦流程,frictionless flow),只在看起来有风险时才要求额外认证(挑战流程,challenge flow)。欧洲的 PSD2/SCA(强客户认证)监管实质上让这类认证成为了强制要求。设计支付系统时,必须把 3DS 步骤中用户中途放弃、认证失败、回调延迟这些情况,全部当作状态来处理。

拒付(chargeback) — 当用户提出异议时

支付成功并不是终点。用户之后仍然可以对这笔支付提出异议。这就是拒付(chargeback)。

拒付始于用户向自己的发卡行提出「这笔交易有问题」的异议。理由多种多样:没收到货、与描述不符、不是本人所为的支付(欺诈)、重复扣款等等。如果发卡行采纳这一异议,就会把已经付给商户的钱撤回(reverse),退给用户。

  用户 -> 向发卡行提出异议
  发卡行通过收单机构发起拒付 -> 从商户处扣回款项
  商户可以提交证据进行申诉(representment)
  由卡组织仲裁给出最终裁定

拒付和退款(refund)不同。退款是商户主动把钱退回去,拒付则是通过卡组织体系被强制扣回。拒付在很多方面都对商户不利。不仅要损失货款,还要额外支付拒付手续费,而且一旦拒付比率升高,还可能受到卡组织的制裁,严重时甚至会失去商户资格。

所以支付系统需要一套处理拒付的流程:接收拒付通知并更新订单状态,收集证据提交申诉(representment),监控拒付比率。为了减少因欺诈导致的拒付,通常会把前面提到的 3DS 责任转移机制和欺诈检测规则结合起来使用。

幂等性 — 防止重复扣款的常量

支付流程要多次往返网络,所以失败很常见,失败之后就会重试。这里就出现了支付特有的一个可怕问题:重试可能造成重复扣款。

场景是这样的。商户向 PSP 发送了一个授权请求。PSP 正常地一路走到发卡行,授权也成功了。但成功的应答在返回商户的途中,网络断了。站在商户的角度,它只知道「没收到应答」,无法判断到底是成功了还是失败了。于是它选择安全起见,重试。但第一次请求其实已经成功了,重试就会造出第二笔支付。用户被扣了两次款。

这个问题的标准解法是幂等键(idempotency key)。客户端为每一次支付请求生成一个唯一的键并随请求一起发送,服务器在第二次收到同样的键时,不会重新处理,而是原样返回第一次的结果。这样无论重试多少次,支付都只会真正发生一次。

  请求 1: POST /charges  Idempotency-Key: abc-123  -> 执行支付,保存结果
  (应答丢失)
  请求 2: POST /charges  Idempotency-Key: abc-123  -> 原样返回已保存的结果
                                                       (不会再次发生支付)

这篇文章只点到这个概念为止,但幂等性是支付系统里核心中的核心,我会另写一篇文章深入讲键的生命周期、去重窗口、唯一约束,以及支付的状态机。现在只需要牢牢记住「支付请求必须始终是幂等的」这条原则就够了。

对账(reconciliation) — 让账目相符的工作

支付系统里有一项不太显眼、却决定性重要的工作,叫对账(reconciliation)。对账,就是核对「我们自己系统记录的交易」「PSP 或卡组织记录的交易」,以及「实际进入银行账户的钱」这三者是否互相一致。

为什么需要这样做?因为支付要经过多个参与方、多个阶段,任何一个环节都可能出现不一致。

  • 我们的系统把一笔交易记为授权成功,但 PSP 的结算明细里却漏掉了这笔交易。
  • 扣款有 100 笔,但结算入账只到账了 98 笔的份(因为手续费、拒付、退款被扣减)。
  • 前面提到的重复扣款、部分扣款、退款交织在一起时,金额会出现细微的偏差。

对账的基本思路是这样的:把我们内部账本里的交易列表,和 PSP 每天提供的结算文件互相比对,分类成「双方都有」「只有我方有」「只有 PSP 方有」。

  内部账本中的交易   vs   PSP 结算文件中的交易
        │                       │
        ▼                       ▼
     比对(按交易ID / 授权码 / 金额匹配)
        ├─ 双方一致            -> OK
        ├─ 只在我方有           -> 结算遗漏? 需要调查
        └─ 只在 PSP 方有        -> 我方漏记的交易? 需要调查

出现不一致时,就把它归类为例外(exception),交给人工调查,或用自动化规则解决。做得好的支付系统会每天自动跑一遍对账,并把不一致的笔数和金额作为指标持续观测。对账干净地对上,是「我们没有丢钱」最直接的证据。而要让对账成为可能,前提是每一笔交易从一开始就要以不可变(immutable)的记录留存下来。走到这一步,支付自然而然就会和会计账本的设计相遇。

PCI-DSS — 处理卡信息的规则

卡号是极度敏感的信息,一旦泄露就会直接造成金钱损失。因此支付卡行业制定了一套叫PCI-DSS(Payment Card Industry Data Security Standard)的安全标准,要求所有存储、处理、传输卡信息的地方都要遵守。

PCI-DSS 的要求非常庞杂,但工程师首先要理解的大原则是这几条。

  • 能不存就不存。 一旦直接存储完整卡号(PAN)、有效期、CVC,PCI 合规的范围(scope)就会急剧扩大。CVC 在授权之后绝对不能存储。
  • 善用令牌化(tokenization)。 不存真实卡号,只在我们的系统里保留一个代替它的、无意义的令牌。真正的卡号只保存在通过 PCI 认证的 PSP 或专门的保险库(vault)里。大多数服务都用这种方式把 PCI 范围降到最小。
  • 把范围降到最小。 卡数据经过的系统越少,审计和管理就越容易。使用 PSP 提供的托管支付页面,或者客户端令牌化(比如把卡信息直接从浏览器发给 PSP,只拿回一个令牌),可以做到卡号根本不经过我们自己的服务器。

概括来说,如今对大多数服务而言,最好的 PCI 策略就是「不直接碰原始卡数据」。积极利用令牌化和 PSP 提供的支付页面,让敏感数据一开始就不进入我们的系统。如果你想进一步了解认证与令牌的基础概念,可以在认证与安全实验室里动手实践相关内容。

再看一次全貌

让我们把到目前为止的这些碎片拼成一张完整的图——从用户按下支付,到商户真正收到钱,再到需要时处理退款或拒付的整个旅程。

阶段发生了什么工程师需要关心的事
支付请求输入卡信息,令牌化最小化 PCI 范围
3DS 认证如需要则进行发卡行认证处理中途放弃/失败/回调延迟
授权发卡行核实额度,冻结额度幂等性,区分软/硬拒绝
扣款确认扣款(如发货时)部分扣款,撤销(void)
结算批处理进行实际资金移动反映手续费/扣减
对账比对内部记录与结算数据检测不一致,处理例外
退款/拒付事后退还款项状态管理、证据、比率监控

这张表就是支付系统设计的摘要版。每一行都是一个状态,也是一个失败点,把它们全部用状态机串起来管理,就是支付后端的本质。

结语

支付系统是这样一个世界:在「按下支付、钱就转移了」这个简单的表象之下,叠着五个参与方、三个阶段、以及众多失败场景。授权是预留,扣款是确认,结算才是真正的转移。在这中间,3DS 用认证守住一道防线,拒付处理事后的异议,幂等性防止重复扣款,对账让账目相符,PCI-DSS 管控敏感数据。

支付工程的第一课,是「牵动资金的系统必须把出错的余地降到最低」。所以幂等性、不可变的记录,以及每天的对账,都不是可选项,而是常量。手握这张地图之后,接下来自然会看到两个值得深入的话题:防止重复扣款的幂等性实现,以及绝不丢钱的复式记账账本设计。

参考资料

현재 단락 (1/116)

用户按下「支付」按钮的那一刻,看起来连一秒都不到。但在这一秒之内,卡号会经过五六家公司、至少两个支付网络,一路往返发卡行。而钱真正从账户转移到账户,并不是在那一刻,而是在几天之后。

작성 글자: 0원문 글자: 6,397작성 단락: 0/116