第15篇:应付亿级用户支付系统完结篇

「提醒」:是付费专栏,但是在知识星球里是免费的。目前星球里已更新了6个专栏:「支撑6000万会员的秒杀系统实战」、「几亿用户,百万并发的C端商品系统实战」、「DDD领域驱动设计三年落地实战」、「应付亿级用户规模的支付系统代码实战」、「应付亿级用户的会员体系代码实战」和「我的项目管理实战手记:10个真实主导项目,还原实战现场」,后续的订单,结算和购物车以及用户和营销专栏等,星球内也都是免费的。

14篇文章写完了,从需求PRD到渠道对账,把这套支付系统完整地讲了一遍。这篇做个串联回顾,把散落在各篇里的设计决策拉通来看。

先说清楚这个专栏的定位:这是一套应付亿级用户、平均每日50万笔订单的支付系统。不是调个微信SDK就跑起来的Demo,是在真实流量下扛过的生产系统。日均50万笔这个体量意味着什么?意味着每天有50万次用户把钱交给平台,每一次都是一笔真实的资金流动,每一笔背后都有履约承诺。系统出故障,不是几笔交易受影响,是几十万笔。微信渠道挂2小时,按交易分布可能影响10万+笔订单。这个量级下,每个设计决策都有实际的代价在推动。

在这个体量下,有一件事非常的重要:每个支付渠道是一个独立的物理单元。后面会展开讲为什么。

还有一点要反复强调:一定要基于自己公司当时的业务实际情况,采取对应的架构设计。这个专栏给的是一套在特定业务场景下验证过的方案,不是放之四海皆准的标准答案。你的业务体量、渠道组合、团队结构都不一样,架构决策自然也不一样。

从需求到架构:一条支付请求走过的完整链路

用一笔150元的组合支付订单把14篇文章的核心设计串起来:用户下单150元,选择余额支付,后端按自由卡30→钱包100→微信20的顺序分摊。

网关层(第6篇)。 请求先到service-pay-gateway,网关做参数校验、签名验证、终端适配。APP和小程序的请求格式不一样,网关把这些差异消化掉,往下游传的是标准化后的请求。网关还要补全渠道信息,比如小程序的openId不能由前端直接传,得从用户服务里查。

编排层(第7篇)。 请求到了payment-orchestration,编排层判定这是一笔CENTER类型的组合支付,调费用分摊引擎算出各渠道的金额:自由卡30、钱包100、微信20。然后按顺序发起各渠道的预下单。自由卡和钱包是内部渠道,同步扣款;微信是第三方渠道,调wechat-base预下单拿到prepayId。三个渠道都成功后,等用户在客户端确认付款,微信回调到达后和内部渠道的结果合并,通过RocketMQ通知订单系统。

渠道层(第8-10篇)。 每个渠道是一个独立的微服务:wechat-base封装微信支付的SDK和签名,alipay-base封装支付宝的SDK和签名,hk-base/jp-base/mo-base封装各国际渠道的HTTP裸调和鉴权。编排层通过Dubbo Client防腐层调这些渠道服务,不直接依赖任何渠道的SDK。

数据层(第4篇)。 支付主单加渠道明细的双层结构,主单记录整体支付状态,明细记录每个渠道的扣款状态和流水号。状态机严格控制状态流转,NEW→PROCESSING→SUCCESS/FAIL,不允许逆向跳转。

MQ通知(第12篇)。 组合支付的所有渠道都回调成功后,编排层发一条MQ到订单系统。消息体有两层结构:外层是支付主单的结果,内层是各渠道明细的结果。订单系统拿到消息后更新订单状态,触发履约。

退款(第11篇)。 用户申请部分退款50元,退款模板方法按渠道优先级处理:先退微信20(第三方异步退款),再退钱包30(内部同步退款)。退款状态机和支付状态机独立流转,退完了也是一笔完整的退款记录。

对账(第13-14篇)。 每天凌晨2点,两套对账任务跑起来。内部对账拿订单系统和支付系统的数据比对状态一致性,渠道对账拿支付系统和第三方渠道的账单文件比对资金一致性。差异发钉钉群,运营逐笔核实。

这条链路走下来,14篇文章的核心设计全串上了。

网关→编排→base:为什么说这个分层是非常好的设计

这套支付系统从上到下分成三层:网关层、编排层、渠道base层。每层各管各的事,上层只依赖接口不依赖实现。

网关层:一个入口管所有终端。 四种终端(APP、微信小程序、支付宝小程序、抖音小程序)的请求格式不一样,openId和unionId的获取方式不一样,签名规则不一样。网关把这些差异全消化在自己内部,往下游传的是标准化后的请求。编排层不需要关心这个请求是从APP来的还是从小程序来的。如果将来新增一种终端,只改网关,编排层和渠道层不动。

编排层:只管协调,不碰渠道细节。 编排层做三件事:费用分摊、事务协调、回调合并。它知道自由卡要先扣、钱包其次、第三方兜底,但它不知道微信支付的签名算法是什么、支付宝的回调格式是什么。编排层调渠道服务用的是统一的PaymentChannelProcessor接口,wechat-base和alipay-base实现同一套接口,编排层通过策略模式路由到对应实现。新增一个渠道,编排层的代码一行不改。

base层:渠道物理隔离。 wechat-base、alipay-base、douyin-base、hk-base、jp-base、mo-base,每个渠道一个独立的微服务。微信SDK升级只改wechat-base,支付宝API变更只改alipay-base,其他渠道零风险。一个渠道宕机不影响其他渠道的交易。

这三层分层的好处是逐层解耦:网关解耦终端差异,编排层解耦渠道差异,base层解耦渠道间的故障传播。上层依赖接口而不是实现,任何一层的变更都不会穿透到其他层。

渠道独立:每个支付渠道是一个独立的物理单元

每天50万笔订单,如果某个渠道出了故障,影响的不只是几笔交易,是几十万笔。微信渠道挂2小时,按交易分布可能影响10万+笔订单,每一笔背后都是用户的钱和平台的履约承诺。

这就是为什么渠道独立在亿级体量下不可妥协:

微信SDK升级、支付宝API变更,只改对应的base服务,其他渠道零风险。 渠道接口变更在生产环境里是高频事件。微信每年都有几次API调整,支付宝也有。如果所有渠道的代码混在一个服务里,改微信的签名逻辑可能意外影响支付宝的回调处理。独立部署后,改谁只部署谁,其他渠道完全不受影响。

一个渠道宕机,其他渠道正常交易,故障不传染。 支付宝渠道因为网络问题连不上,微信和钱包的支付照样走。编排层检测到alipay-base超时,可以自动降级,把原本走支付宝的金额转到其他可用渠道(如果业务允许),或者直接返回该渠道不可用,让用户换一种支付方式。不管哪种处理,故障范围被锁死在单个渠道内。

新增渠道只需实现PaymentChannelProcessor接口,编排层代码不动。 当年接抖音支付的时候,新建了一个douyin-base服务,实现预下单、回调、退款三个接口,编排层通过策略模式自动路由到新渠道。零改动上线。

渠道独立部署独立扩容,哪个渠道流量大就给哪个渠道加资源。 微信支付的流量可能是支付宝的3倍,wechat-base单独扩容,不需要因为微信流量大就把整个支付系统都扩一遍。

一定要基于自己公司当时的业务实际情况

架构决策不是拍脑袋定的,是跟着业务现状走的。这个专栏里的每一个设计选择,背后都有具体的业务条件在约束。

当时的业务体量决定了对账用两个HashSet而不是分布式计算。 日均50万笔,T-1当天的数据量装进内存完全扛得住,两个HashSet求差集O(n)搞定,没必要上Spark或者Flink。但如果你的日订单量是5000万,这个方案就不够用了,得换分布式计算。

连锁门店22点闭店决定了T+1对账数据稳定的前提。 凌晨2点跑对账时,T-1的交易早已全部结束,数据是稳定的终态。如果你的业务是7×24小时的(比如外卖、电商),T+1对账就不一定跑得准,当天零点附近的数据可能还在变化,对账策略要做相应调整。

国内为主+少量国际业务决定了服务拆分的粒度。 国内三个渠道(微信、支付宝、抖音)各一个base服务,国际三个地区各一个base服务。如果你的业务只做国内,hk-base、jp-base、mo-base这些服务根本不需要建。反过来,如果你的国际业务占大头,国际渠道的服务拆分粒度可能要更细。

不是所有公司都需要组合支付,但一旦需要,编排层的设计就是核心。 如果你的业务只有单一渠道支付,编排层可以大幅简化,甚至不需要独立的编排服务,网关直接调渠道就行。但当用户需要把余额和第三方渠道组合在一起支付的时候,费用分摊、多渠道事务协调、回调合并这些问题就全冒出来了,编排层的价值就体现出来了。

不要照搬这个专栏的架构。理解每个设计决策背后的业务条件,然后对照你自己的业务现状,做你自己的取舍。

对账:支付系统的最后一道防线

第13篇和第14篇讲了两套对账,解决的是不同的问题,但缺一不可。

内部对账(第13篇)解决系统间状态同步。 支付系统发了MQ,订单系统有没有收到?订单系统认为已支付,支付系统有没有对应的成功流水?两个系统之间的数据对不上,靠实时链路发现不了,只能靠离线对账捞出来。内部对账只比状态,不比金额,因为状态不一致本身就是问题,不管金额多少。

渠道对账(第14篇)解决资金一致性。 支付系统记了一笔收款100元,微信账单里真的有这笔入账吗?退款50元,支付宝真的退了吗?渠道对账比的是金额,差一分钱都要核实。6个方向的对账覆盖了支付和退款的所有差异场景:渠道有但订单没有、订单有但渠道没有、两边都有但金额不一致。

差异不一定都是Bug。渠道T+1结算延迟、手续费计算口径不同、测试商户交易未关闭,都可能产生差异。但每笔差异都必须核实清楚,不能因为「可能是正常的」就跳过。对账的意义不在于找出多少Bug,在于确保每一笔钱的去向都有据可查。

小结

支付系统的核心不是对接了多少渠道,而是架构上能不能让每个渠道独立运作、互不干扰。网关层隔离终端差异,编排层协调多渠道事务,base层让每个渠道成为独立的物理单元。这三层分层,每一层都在做同一件事:把变化局限在最小范围内,让故障不传染,让变更不穿透。

日均50万笔订单的体量下,渠道故障的代价是极高的。微信渠道挂2小时就是10万+笔交易受影响,每一笔都是用户的钱和平台的履约承诺。渠道独立不是锦上添花,是守住底线的必需品。

这个专栏给的不是标准答案,而是一个在真实业务压力下做过取舍的系统长什么样。每天50万笔、连锁门店22点闭店、国内为主少量国际,这些业务条件决定了架构决策的方向。当你面对自己的业务场景时,该做什么取舍,取决于你自己的业务现状。

做支付系统,别抄架构,抄思路。思路是:让每个渠道独立、让故障不扩散、让对账兜住底。

所有的代码都可以在知识星球里获取。「应付亿级用户规模的支付系统代码实战」这个专栏在星球里是免费的,也可以接受无限次的咨询。后续新写的所有付费专栏,在知识星球里都是免费的。我的星球是:

  • 老码头的技术浮生录

当然你也可以知乎订阅这个专栏,我的知乎账号是:

  • SamDeepThinking
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SamDeepThinking

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值