您现在的位置是:娱乐 >>正文
支付系统设计
娱乐2651人已围观
简介支付系统的核心不是支付,是记账。用户点下“付款”按钮的那一瞬间,钱并没有真的从银行卡飞到商户账户里。真正发生的事是:系统在数据库里写了两条记录,一条记在用户头上叫“应减”,一条记在商户头上叫“应加”。 ...
支付系统的支付核心不是支付,是系统记账。用户点下“付款”按钮的设计那一瞬间,钱并没有真的支付从银行卡飞到商户账户里。真正发生的系统事是:系统在数据库里写了两条记录,一条记在用户头上叫“应减”,设计一条记在商户头上叫“应加”。支付这两条记录必须同时成功或同时失败,系统否则账就乱了。设计
我见过一个真实的支付事故。某平台做活动,系统用户下单后调用支付网关,设计网关返回超时。支付前端等了三秒没收到响应,系统提示“支付失败”。设计用户又点了一次。第二次成功了。但第一次的请求其实在网关那边也成功了,只是回执丢了。结果用户被扣了两次钱,商户只收到一笔订单。对账的时候才发现,中间差了整整47笔这样的重复扣款。问题出在哪?出在支付系统没有做幂等。
幂等不是可选项
幂等的意思是:同一个请求执行一次和执行一百次,结果一样。实现方式不复杂——给每笔交易生成一个唯一ID,服务端收到请求先查这个ID有没有处理过。处理过就直接返回上次的结果,没处理过才走扣款逻辑。但很多团队早期为了赶进度,把这一步省了。省的时候觉得“哪那么巧会重复”,上线后才发现用户手抖、网络抖动、前端重试,处处都是坑。
另一个容易被忽视的点是异步通知。支付网关扣完用户的钱,得告诉商户“钱到了”。这个通知必须是异步的,不能同步等。同步等的话,商户服务器卡一下,网关线程就被占住了。高峰期几千个线程堵在那里,整个支付链路全挂。异步通知也有讲究:不能只发一次,要按递增间隔重试,比如15秒、30秒、3分钟、10分钟。商户收到通知后必须返回一个约定好的字符串,比如“SUCCESS”,否则网关认为你没收到,继续重试。这里有个细节——商户处理完业务逻辑再返回成功,别先返回成功再去处理。先返回成功的话,万一后面处理失败了,网关不会再通知你,这笔单子就永远悬着。
对账是最后一道防线
再好的实时系统也会出错。每天凌晨,支付系统必须做一件事:拿自己的流水和渠道(银行、微信、支付宝)的流水逐笔比对。比什么?比金额、比状态、比手续费。不一致的挑出来,人工介入。我见过一个团队,对账程序跑了半年没人看结果。直到某天财务发现账上少了十几万,翻记录才发现对账文件里每天都有几条“金额不一致”,但没人处理。原因很简单:渠道那边扣了手续费,他们这边没扣。每笔差几毛钱,攒了半年就成了一大笔。
对账还有个坑:时间窗口。你不能拿今天0点到24点的流水去和渠道对,因为渠道的日切时间可能不是0点。有的银行是23点,有的是凌晨1点。时间窗口对不上,两边永远对不平。正确做法是拿渠道给的对账文件里的时间范围为准,自己的流水按那个范围去查。
说点实在的
支付系统不需要花哨的架构。核心就三件事:记账准确、状态可查、异常可追。记账用数据库事务保证原子性,状态用状态机管理(待支付、支付中、成功、失败、关闭),异常用日志和告警兜底。别一上来就搞分布式事务、TCC、Saga,那些是给日订单千万级以上的系统准备的。日订单十万以下,单库单表加乐观锁足够跑很久。
最后说个观察:很多支付系统的bug不在支付本身,而在业务系统怎么调支付。比如业务系统先创建订单再调支付,但订单创建失败了支付却成功了,钱收了没订单。或者业务系统把支付结果回调当成了可靠消息,回调丢了就认为没支付。支付系统只保证“钱的状态是对的”,不保证“你的业务逻辑是对的”。这个边界划清楚,能少踩一半的坑。
Tags:
上一篇:免费图书馆网站
相关文章
网站源码百度网盘
娱乐我去年帮一个做跨境电商的朋友恢复老网站数据,对方报价两千块,说要从备份磁带里捞。我问他原始源码在哪儿,他愣了三秒,回了一句“百度网盘啊,我传上去了”。结果那网盘链接早就失效了,因为账号太久没登录被清了 ...
【娱乐】
阅读更多git 添加凭证
娱乐昨天在终端里敲了第无数次 git push,又弹出那个熟悉的提示:fatal: Authentication failed for 'https://github.com/...'。我盯着屏幕愣了两秒 ...
【娱乐】
阅读更多windows7旗舰版 service p ack1
娱乐```html windows7旗舰版 service pack 1 Windows 7 旗舰版 Service Pack 1:那个让无数人钉在旧硬盘里的补丁 2009年10月22日,Wi ...
【娱乐】
阅读更多