您现在的位置是:娱乐 >>正文

支付系统设计

娱乐8人已围观

简介支付系统的核心不是支付,是记账。用户点下“付款”按钮的那一瞬间,钱并没有真的从银行卡飞到商户账户里。真正发生的事是:系统在数据库里写了两条记录,一条记在用户头上叫“应减”,一条记在商户头上叫“应加”。 ...

支付系统的支付核心不是支付,是系统记账。用户点下“付款”按钮的设计那一瞬间,钱并没有真的支付从银行卡飞到商户账户里。真正发生的系统事是:系统在数据库里写了两条记录,一条记在用户头上叫“应减”,设计一条记在商户头上叫“应加”。支付这两条记录必须同时成功或同时失败,系统否则账就乱了。设计

我见过一个真实的支付事故。某平台做活动,系统用户下单后调用支付网关,设计网关返回超时。支付前端等了三秒没收到响应,系统提示“支付失败”。设计用户又点了一次。第二次成功了。但第一次的请求其实在网关那边也成功了,只是回执丢了。结果用户被扣了两次钱,商户只收到一笔订单。对账的时候才发现,中间差了整整47笔这样的重复扣款。问题出在哪?出在支付系统没有做幂等。

幂等不是可选项

幂等的意思是:同一个请求执行一次和执行一百次,结果一样。实现方式不复杂——给每笔交易生成一个唯一ID,服务端收到请求先查这个ID有没有处理过。处理过就直接返回上次的结果,没处理过才走扣款逻辑。但很多团队早期为了赶进度,把这一步省了。省的时候觉得“哪那么巧会重复”,上线后才发现用户手抖、网络抖动、前端重试,处处都是坑。

另一个容易被忽视的点是异步通知。支付网关扣完用户的钱,得告诉商户“钱到了”。这个通知必须是异步的,不能同步等。同步等的话,商户服务器卡一下,网关线程就被占住了。高峰期几千个线程堵在那里,整个支付链路全挂。异步通知也有讲究:不能只发一次,要按递增间隔重试,比如15秒、30秒、3分钟、10分钟。商户收到通知后必须返回一个约定好的字符串,比如“SUCCESS”,否则网关认为你没收到,继续重试。这里有个细节——商户处理完业务逻辑再返回成功,别先返回成功再去处理。先返回成功的话,万一后面处理失败了,网关不会再通知你,这笔单子就永远悬着。

对账是最后一道防线

再好的实时系统也会出错。每天凌晨,支付系统必须做一件事:拿自己的流水和渠道(银行、微信、支付宝)的流水逐笔比对。比什么?比金额、比状态、比手续费。不一致的挑出来,人工介入。我见过一个团队,对账程序跑了半年没人看结果。直到某天财务发现账上少了十几万,翻记录才发现对账文件里每天都有几条“金额不一致”,但没人处理。原因很简单:渠道那边扣了手续费,他们这边没扣。每笔差几毛钱,攒了半年就成了一大笔。

对账还有个坑:时间窗口。你不能拿今天0点到24点的流水去和渠道对,因为渠道的日切时间可能不是0点。有的银行是23点,有的是凌晨1点。时间窗口对不上,两边永远对不平。正确做法是拿渠道给的对账文件里的时间范围为准,自己的流水按那个范围去查。

说点实在的

支付系统不需要花哨的架构。核心就三件事:记账准确、状态可查、异常可追。记账用数据库事务保证原子性,状态用状态机管理(待支付、支付中、成功、失败、关闭),异常用日志和告警兜底。别一上来就搞分布式事务、TCC、Saga,那些是给日订单千万级以上的系统准备的。日订单十万以下,单库单表加乐观锁足够跑很久。

最后说个观察:很多支付系统的bug不在支付本身,而在业务系统怎么调支付。比如业务系统先创建订单再调支付,但订单创建失败了支付却成功了,钱收了没订单。或者业务系统把支付结果回调当成了可靠消息,回调丢了就认为没支付。支付系统只保证“钱的状态是对的”,不保证“你的业务逻辑是对的”。这个边界划清楚,能少踩一半的坑。

Tags:

相关文章

  • 国家基金网络信息系统登录平台

    娱乐

    打开国家基金网络信息系统登录平台的那天,我盯着输入框看了半分钟。密码忘了,验证码换了三次才看清。这大概是每个科研人的共同记忆——一个让人又爱又恨的入口。 系统登录进去,界面朴素得有点过分。没有花哨的动 ...

    【娱乐】

    阅读更多
  • 腾讯海外服务器访问google

    娱乐

    腾讯云在新加坡的机房拉一条到Google的专线,延迟能压到30毫秒以内。这不是什么秘密,但很多人不知道的是,你从国内直接ping腾讯云的海外节点,和从那个节点再访问Google,完全是两码事。 我去年 ...

    【娱乐】

    阅读更多
  • 真实的监控器监控网站

    娱乐

    打开某个“实时监控”网站,画面里是一个空荡荡的停车场,右下角时间戳在走,但左上角的“LIVE”标志闪了两下就卡住了。刷新页面,同样的机位,同样的角度,连飘过的云都一模一样。这不是监控,这是循环播放的录 ...

    【娱乐】

    阅读更多


友情链接