Skip to content

支付系统 架构模板

代表产品:Stripe、支付宝、微信支付、PayPal、各类「收银台 / 聚合支付」 一句话定位:在不可靠的网络和不可信的世界里,把「钱从 A 到 B」做到一分不差、不重不漏、笔笔可对账可追溯。


1. 一句话定位

支付系统 = 一台「正确性压倒一切」的资金状态机 + 一本永远对得平的账

它和你熟悉的系统最大的不同:别的系统追求快,它首先追求「对」。 宁可慢、宁可拒绝一笔交易,也绝不能算错一分钱、绝不能扣两次款。它的灵魂不是性能,而是幂等、一致性、可对账这三个词。

2. 业务本质:它在解决什么问题

支付系统是资金流转的可信中介:它站在用户、商户、银行 / 卡组织中间,让一笔钱安全、确定地从付款方到达收款方。

它卖的不是技术,是信任——用户敢把卡号交给它,商户敢等它结款。一次算错账、一次重复扣款,信任就崩了。

钱从哪来:每笔交易的手续费、跨境 / 货币转换费、增值服务(分期、风控、对账报表)。

关键事实:钱不能凭空产生,也不能凭空消失。 这条物理般的守恒律,决定了支付系统几乎所有的架构取舍——它不能像普通系统那样「丢了重算一遍就好」。

3. 核心需求与约束

功能性需求:

  • [ ] 发起支付(多渠道:银行卡 / 余额 / 第三方钱包)
  • [ ] 退款 / 部分退款
  • [ ] 账户与余额(谁有多少钱)
  • [ ] 对账(和银行 / 渠道核对每一笔)
  • [ ] 清算与结算(把钱真正划给商户)

非功能性需求 / 质量属性(这里和普通系统天差地别):

质量属性目标为什么对这类系统重要
正确性一分不差这是底线中的底线,高于一切
幂等性重复请求绝不重复扣款网络会重试,没有幂等就会多扣钱
一致性资金强一致余额、账本不能出现「凭空多 / 少」
可审计每一笔可追溯监管要求、纠纷举证、事后查账
可用性99.99%+正确性优先于可用性:拿不准时宁可拒绝

关键约束(不可逾越的边界):

  • 🔴 资金守恒:任何时刻账必须对得平,不允许中间态把钱「变没」。
  • 🔴 外部渠道不可靠且异步:银行可能超时、可能几小时后才回结果,「状态未知」是常态而非异常。
  • 🔴 合规是硬约束:PCI-DSS(卡数据)、反洗钱、监管报送,不是「以后再说」。
  • 🔴 超时 ≠ 失败:一笔请求超时了,它可能成功了、也可能失败了——这是支付最难的地方。

4. 架构全景图

   用户 / 商户
       │ 发起支付

┌──────────────────┐
│  支付网关 / 收银台 │  接入、验签、令牌化(卡号不落地)
└────────┬─────────┘

┌─────────────────────────────────────────────────────────┐
│  支付编排(状态机引擎)—— 业务核心                          │
│  • 创建支付单(幂等键去重)  • 风控检查                      │
│  • 驱动状态:待支付→处理中→成功/失败/未知                    │
│  • 选路由:走哪个渠道                                       │
└───┬──────────────┬───────────────┬──────────────┬─────────┘
    ▼              ▼               ▼              ▼
┌────────┐  ┌────────────┐  ┌──────────┐  ┌─────────────────┐
│ 风控    │  │ 渠道适配器  │  │  账本     │  │ 异步通知 / 回调   │
│ 反欺诈  │  │ (银行/钱包) │  │ (复式记账) │  │ 处理(以查询为准) │
└────────┘  └─────┬──────┘  └────┬─────┘  └─────────────────┘
                  │ 异步、可能超时   │
                  ▼                ▼
            ┌──────────┐    ┌──────────────┐
            │ 外部银行   │    │   对账系统     │ 每日和渠道逐笔核对,
            │ 卡组织     │───▶│ (兜底真相)    │ 差异自动 / 人工处理
            └──────────┘    └──────────────┘

灵魂部件是账本(Ledger)+ 对账系统:前者用「复式记账」保证任意时刻账都平,后者用「和银行逐笔核对」兜住一切异步与未知。这两样东西的存在,就是为了让「钱」永远算得清。

5. 组件职责

  • 支付网关 / 收银台:接入各端、验签、把敏感卡号令牌化(用 token 代替真实卡号,卡号不进入内部系统)。为什么需要:把合规风险和敏感数据挡在最外层。
  • 支付编排 / 状态机:整个支付的大脑。用幂等键对请求去重,驱动支付单的状态流转,选渠道路由。为什么需要:支付的本质是一个有明确状态、不可乱跳的状态机。
  • 渠道适配器:把内部统一指令翻译成各家银行 / 钱包的协议。为什么需要:屏蔽外部差异;外部调用天然异步、可能超时。
  • 账本(Ledger):用复式记账(每笔交易同时记借方和贷方,两边相等)记录资金变动。为什么需要:用「结构」保证资金守恒、可审计、不可篡改(见决策 2)。
  • 风控 / 反欺诈:实时判断这笔交易是否可疑。为什么需要:支付是欺诈重灾区。
  • 对账系统:每天把自己的流水和银行 / 渠道的流水逐笔核对,找出差异。为什么需要:这是应对「状态未知」的终极兜底——以双方都认的事实为准。
  • 异步通知处理:接收渠道回调,但不轻信回调,以己方主动查询为准。

6. 关键数据流

场景一:一次卡支付(核心路径)

1. 用户提交支付 ──▶ 网关:验签、卡号令牌化
2. ──▶ 编排层:用「幂等键」查重
        已存在 ──▶ 直接返回上次结果(不重复发起!)
        不存在 ──▶ 创建支付单(状态=处理中)
3. ──▶ 风控:放行 / 拦截
4. ──▶ 渠道适配器 ──▶ 外部银行(异步,可能超时)
5. 银行返回成功 ──▶ 账本记一对分录(借:用户  贷:商户),状态=成功
6. ──▶ 异步通知商户;周边系统(积分/发货)订阅事件,最终一致

场景二:超时了怎么办?(支付最难的场景)

第 4 步银行迟迟不回 / 超时:
  ✗ 错误做法:直接判失败 ──▶ 用户可能已被扣款,你却记成失败 → 钱消失
  ✓ 正确做法:状态置为「未知」,启动【查询补偿】:
       隔一会儿主动问银行「这笔到底成没成?」
       仍拿不准 ──▶ 留给当天【对账】兜底,以银行最终流水为准

这就是为什么支付里**「未知」必须是一等公民的状态**,而不能粗暴归为「失败」。

7. 数据模型与存储选择

核心实体:支付单(带状态机);账本分录(借/贷成对);渠道流水;对账差异

数据存储类型为什么
支付单 / 账户关系型(强事务)资金操作要 ACID、强一致
账本分录追加型 / 不可变日志只增不改不删,保证可审计、不可篡改
渠道流水、对账关系型 / 列存海量、按日聚合核对
幂等键KV(带唯一约束)高速查重,防重复扣款

教学点:账本永远只追加、绝不更新或删除(余额是「把所有分录加起来」算出来的,而不是存一个会被覆盖的数字)。这让历史不可篡改、随时可重算、可审计。

8. 关键架构决策与权衡 ⭐

决策 1:如何保证幂等?(支付的生命线)⭐

  • 不做幂等:网络重试时,同一笔支付被发起两次 → 重复扣款,灾难。
  • 做幂等:每个请求带唯一「幂等键」,服务端用唯一约束保证「同一个键只处理一次」,重复请求直接返回首次结果。
  • 取向:必做,没有例外。 幂等是一切「会被重试的写操作」的安全带。

决策 2:账户余额怎么记?「改余额字段」还是「复式记账」?⭐

  • 改余额字段(UPDATE 余额 = 余额 - 100):直观,但并发下易丢更新、无法审计、对不了账、出错难追溯。
  • 复式记账(每笔交易记成对的借贷分录,余额由分录累加得出):天然守恒、可审计、不可篡改、能精确还原任意时刻
  • 取向:正经支付一定用复式记账。代价是模型更复杂、写入更多。这是「用数据结构本身保证正确性」的典范。

决策 3:超时 / 失败如何处理?

  • 超时就当失败:简单,但可能用户已扣款 → 钱消失,严重事故。
  • 引入「未知」状态 + 查询补偿 + 对账兜底:复杂,但不丢钱
  • 取向:必须区分「失败」和「未知」,用主动查询和对账把「未知」收敛到确定。

决策 4:核心强一致,周边最终一致。

  • 全链路都强一致:不可能,也没必要(还拖垮性能)。
  • 取向:资金核心(扣款、记账)强一致;周边(发短信、加积分、发货通知)走事件驱动、最终一致。把「不能错的」和「晚一点没关系的」分开,是关键判断。

9. 规模化与瓶颈

  • 第一个瓶颈:热点账户写入争抢(平台大商户的收款账户被无数笔并发记账)。→ 破解:账户维度的串行化 / 排队、批量合并记账、子账户拆分。
  • 第二个瓶颈:对账数据量随交易量爆炸。 → 破解:分库分表 + 离线批处理 + 增量对账。
  • 第三个瓶颈:外部渠道限流 / 抖动。 → 破解:多渠道路由 + 降级,单渠道故障自动切换。
  • 资金核心不能无脑分片:跨片转账会引出分布式事务,所以分片要顺着「账户」这种天然边界切,避免跨片资金操作。

10. 安全与合规要点

  • 🔴 卡数据合规(PCI-DSS):真实卡号绝不落地,用令牌化 / 第三方代收;内部系统只见 token。
  • 防重放与验签:所有外部请求 / 回调都要签名校验,防伪造。
  • 回调不可信:伪造「支付成功」回调是常见攻击 → 一律以己方主动查询银行的结果为准
  • 风控反欺诈:盗卡、洗钱、套现的实时识别与拦截。
  • 审计日志不可删:谁、何时、改了什么,全留痕,满足监管。
  • 最小权限:能动钱的接口权限收到最紧,关键操作多重审批。

11. 常见误区 / 反模式

  • 用「读余额 - 改余额 - 写回」更新账户 → ✅ 复式记账 + 不可变分录,杜绝丢更新、可审计。
  • 不做幂等,重试导致重复扣款 → ✅ 幂等键 + 唯一约束,是底线。
  • 把外部回调当可信来源 → ✅ 验签 + 以己方查询为准。
  • 超时直接判失败 → ✅ 引入「未知」状态,查询补偿 + 对账兜底。
  • 敏感卡号明文存储 / 打日志 → ✅ 令牌化,卡号不落地。
  • 追求性能牺牲正确性 → ✅ 支付里正确性绝对优先,慢一点、拒一笔都好过算错。

12. 演进路线:MVP → 成长期 → 成熟期

阶段规模量级架构长什么样此时该操心什么
MVP起步接一个成熟第三方支付(自己不碰资金、不存卡号),薄薄一层封装合规与安全外包给专业方,先跑通业务
成长期多渠道 / 上规模自建支付编排与状态机、多渠道路由、自建账本与每日对账、风控幂等、对账、未知态处理,把「对」做扎实
成熟期平台级 / 跨境资金清结算、多币种、监管报送、热点账户优化、多活容灾资金安全、合规、容灾、规模化对账

13. 可复用要点

  • 💡 幂等是一切「会被重试的写操作」的安全带。 不止支付,任何分布式写入都该问:「重复执行一次会不会出事?」
  • 💡 用数据结构本身保证正确性,胜过用代码小心翼翼。 复式记账让「账永远平」成为结构属性,而不是靠程序员不犯错。
  • 💡 把「未知」当一等公民。 分布式世界里超时 ≠ 失败,承认并主动收敛不确定性,比假装它不存在安全得多。
  • 💡 分清「不能错的」和「晚一点没关系的」,核心强一致、周边最终一致,是性能与正确性兼得的关键判断。
  • 💡 外部输入永远不可信——回调要验签、要以己方查询为准。

🎯 随堂检验

🤔支付系统防止『网络重试导致重复扣款』的生命线是?
  • A加锁
  • B幂等键:同一请求只处理一次
  • C多打日志

参考原型与延伸阅读

本模板基于以下真实开源项目工程博客整理。

🔧 开源原型(可直接读代码):

  • tigerbeetle/tigerbeetle — 为金融交易设计的数据库,内置 accounts/transfers 复式记账,体现账本一致性与高性能 OLTP。
  • juspay/hyperswitch — 开源、可组合的支付平台(Rust),多渠道路由 / 对账 / 收单连接器,PCI 合规。

📖 工程博客:


📌 一句话记住支付系统:它不是「一个能扣款的接口」,而是「一台在不确定世界里仍能把账算得分毫不差的状态机」——所有设计都在回答『怎么做到不重、不漏、不错、且笔笔可查』。