支付系统的要求:安全、高效。安全是基本,高效是追求。javascript
要达成两个目标,不免会遇到各类坑,下面挑几个典型的问题来说述,并附上简单的应对方案。php
网络的可靠性要依赖硬件,因此只要是网络调用,必然要考虑超时问题,另外由于支付系统通常内部验证操做多,请求处理时间长,比通常系统超时的几率更大。css
支付系统内的每个请求都应该谨慎处理,而对于没法肯定结果的超时请求更不能轻易肯定终态,绝对不能像一个简单的网页请求同样重试一次。html
通常采起保守策略,将交易状态保持在一个无害的默认状态(处理中或未支付),等待下次触发处理。java
请求超时自己易处理,但它致使的后续问题会不少,下面会提到。python
终态的判断应该是支付系统内最重要也是最容易踩坑的地方,这个处理的复杂程度真的太依赖三方系统的状态码设置了。因为成功和处理中的状态只有一种,而错误则会有各类各样的缘由,有的错误能够重试,有的错误是系统错误。分清交易失败的缘由,关系到系统如何下一步处理交易,因此错误明细码的设计十分重要。nginx
对于一个返回码设计良好的系统,如微信、支付宝,有业务结果码和明细错误码之分,咱们进行终态判断和返回码映射时,能够首先以业务结果码为准,在业务结果为失败时,再去检查明细错误码。git
而一个设计不那么好的系统,将业务结果码和明细错误码混淆在一块儿,判断结果就比较坑,要么将错误码列出对比,要么用很危险的else
。程序员
此问题没法真正避免,只能给出谨慎映射,多向三方系统求证的建议。github
无交易记录应该是最危险的返回码了,恰恰由于偶发的网络波动,交易请求受理超时,这个码还不可避免。 正常逻辑的状况下,无交易记录是没发到三方系统,固然是失败,但是若是在代付交易中,三方系统告诉你,别轻易置失败,万一你参数传错了呢,钱付出去咱们可不赔哟~你还那么有自信么。。。
解决方案中最保守的方式固然是做为处理中来处理,而后人工介入处理,这个只能用在交易量不是太大,网络偏稳定的状况下,目前咱们只在代付交易中使用此策略。
另一种方式是搭配请求时的响应信息来判断,若是三方系统响应信息为成功时,查询为无此交易,那天然是参数或系统逻辑等问题,迅速报警通知处理。若是请求受理时为超时,那么即可以认为是网络问题没有发送成功了,有时候仍是要对本身的代码有一些信心的。
交易及时性不是一个很严重的问题,甚至在支付系统中,太有及时性的交易还会使用户不太放心。但做为一个程序员,追求效率是天性嘛,咱们仍是但愿尽早获取到交易结果,但这也可能致使踩坑。
查询太早致使问题会出如今两种场景:请求超时、三方系统设计问题。
太多频繁的查询是无心义的,交易正在三方系统中处理,查询不会使交易被迅速处理,还会形成网络资源和系统资源的浪费,若是你还记得与三方系统的每一次交互都要重视,那么查询日志也无法看了。
解决此问题,要:
隔日帐问题在对帐过程当中不可避免,因为服务器时间有差别,交易处理也须要时间,在凌晨附近发生的交易可能会遭遇此问题,这会给对帐形成必定的困扰,但合理的处理方式不会有太大的问题:
本身系统与三方系统对帐文件不一致时调用查询接口在缺失交易的系统内查询,先确认交易的存在,再分析交易时间。如在隔日附近,则暂不处理,待第二天对帐文件的对比。
如某一系统内交易不存在,或交易不太可能会发生隔日帐问题,这便须要系统之间人工来处理了,不过这不也是对帐的意义所在么。
并发问题在全部系统内都会存在,只是支付系统内处理很差后果会很严重,处理方式通常是事务、互斥锁。
支付系统单系统内使用这些方式也没有问题,只是锁的粒度会略大,至少须要保证一个模块内交易处理的原子性。 分布式系统内就要考虑分布式锁了,这些业内也都有不少解决方案了。
只是加锁就意味着效率损耗,合理拆分出交易核心模块,并对这些模块添加锁。另外使用合理的“进程-数据”分配方式,也会减小锁冲突。
保持交易中的幂等很重要,它是避免重复支付的基石。 即便系统设计彻底,咱们仍是要追求业务逻辑上的幂等,这也就意味着更多的查询确认,同时意味着效率降低。
效率降低不可避免,咱们可使用缓存来下降效率降低的幅度,在缓存中设置交易状态标识,对交易状态标识异常的交易再去数据库查询。
异步能够抗并发,提升效率,放之四海皆准。支付系统对异步的依赖更强是由于支付系统因为其处理流程冗长更易达到效率瓶颈。
面对异步咱们首先要解决的问题是异步拆分的粒度问题,粗粒度的拆分效率能提高的效率有限,细粒度的拆分调控起来不易,处理异步拆分的粒度,看交易量吧,不作过分设计。
进行异步拆分时,每一步都须要一个触发进程,此进程能够是常进程轮询,也能够是cron进程,事件机制来触发天然是更好的,但它对消息队列的要求很高,设计也较复杂。
除此以外还须要一个确认进程,确保下一步的进程顺利接收处理了,在后续进程受理失败时,可以及时重试处理。
测试是开发中必不可少的步骤,本身测和测试来测,总要彻底走一遍流程才敢放心上线。
支付测试略坑了: 首先测试环境的布置,支付系统牵涉到多个三方系统的交互,靠谱的系统都会提供测试系统,但是不免有些系统不提供测试环境,或者测试限制颇多,限支付行,限金额等,还要提防其测试系统突然就挂了。而后是线上测试,更要当心翼翼,一个不慎就是资金损失。
为了提供良好的测试环境,咱们引入“MOCK”功能,mock 中文意思为“模仿”,即经过模拟三方系统的返回值来测试本系统的稳定性。
但 mock 的代码侵入性略强,完整的 mock 模块必然有if else
语句的存在,因为支付相关的系统较多,要搭建完整的 mock 系统不容易,单点 mock 须要各处埋点。总体 mock 的又不便于测试特定功能。
支付的坑包括但不限于本文介绍的这些,可能还会有其余奇怪的问题,文章没有介绍到。
若想尽可能避免支付系统的坑,那么必定要保持着保守的态度,将状态或交易保持无害。有些须要事务操做,但没法使用典型事务的场景,将次要的一开始执行,即便出了问题,有重试、回滚等操做,也不会形成影响。
支付总结暂时到此为止。