支付网关的设计
在支付系统中,支付网关和支付渠道的对接是最核心的功能。其中支付网关是对外提供服务的接口,所有需要渠道支持的资金操作都需要通过网关分发到对应的渠道模块上。一旦定型,后续就很少,也很难调整。而支付渠道模块是接收网关的请求,调用渠道接口执行真正的资金操作。每个渠道的接口,传输方式都不尽相同,所以在这里,支付网关相对于支付渠道模块的作用,类似设计模式中的wrapper,封装各个渠道的差异,对网关呈现统一的接口。而网关的功能是为业务提供通用接口,一些和渠道交互的公共操作,也会放置到网关中。
功能概述
支付系统对其他系统,特别是交易系统,提供的支付服务包括签约,支付,退款,充值,转帐,解约等。有些地方还会额外提供签约并支付的接口,用于支持在支付过程中绑卡。 每个服务实现的流程也是基本类似,包括下单,取消订单,退单,查单等操作。每个操作实现,都包括参数校验,支付路由,生成订单,风险评估,调用渠道服务,更新订单和发送消息这7步,对于一些比较复杂的渠道服务,还会涉及到异步同通知处理的步骤。这里详细介绍这些步骤的实现要点。
1. 执行参数校验
所有的支付操作,都需要对输入执行参数校验,避免接口受到攻击。
- 验证输入参数中各字段的有效性验证,比如用户ID,商户ID,价格,返回地址等参数。
- 验证账户状态。交易主体、交易对手等账户的状态是处于可交易的状态。
- 验证订单:如果涉及到预单,还需要验证订单号的有效性,订单状态是未支付。为了避免用户缓存某个URL地址,还需要校验下单时间和支付时间是否超过预定的间隔。
- 验证签名。签名也是为了防止支付接口被伪造。 一般签名是使用分发给商户的key来对输入参数拼接成的字符串做MD5 Hash或者RSA加密,然后作为一个参数随其他参数一起提交到服务器端。
2. 根据支付路由寻找合适的支付服务
根据用户选择的支付方式确定用来完成该操作的合适的支付渠道。用户指定的支付方式不一定是最终的执行支付的渠道。比如用户选择通过工行信用卡来执行支付,但是我们没有实现和工行的对接,而是可以通过第三方支付,比如支付宝、微信支付、易宝支付,或者银联来完成。那如何选择合适的支付渠道,就通过支付路由来实现。支付路由会综合考虑收费、渠道的可用性等因素来选择最优方案。
3. 评估交易风险
检查本次交易是否有风险。风控接口返回三种结果:阻断交易、增强验证和放行交易。
- 阻断交易,说明该交易是高风险的,需要终止,不执行第5个步骤;
- 增强验证,说明该交易有一定的风险,需要确认下是不是用户本人在操作。这可以通过发送短信验证码或者其他可以验证用户身份的方式来做校验,验证通过后,可以继续执行该交易。
- 放行交易,即本次交易是安全的,可以继续往下走。
4.生成交易订单
将订单信息持久化到数据库中。当访问压力大的时候,数据库写入会成为一个瓶颈。
5. 调用支付渠道提供的服务
所有的支付服务都需要第三方通道来完成执行。一般银行渠道的调用比较简单,可以直接返回结果。一些第三方支付,支付宝,微信支付等,会通过异步接口来告知支付结果。
6. 更新订单
对于同步返回的结果,需要在主线程中更新订单的状态,标记是支付成功还是失败。对于异步返回的渠道,需要在异步程序中处理。
7. 发送消息
通过消息来通知相关系统关于订单的变更。风控,信用BI等,都需要依赖这数据做准实时计算。
8. 异步通知
如上述流程,其中涉及到调用远程接口,其延迟不可控。如果调用方一直阻塞等待,很容易超时。引入异步通知机制,可以让调用方在主线程中尽快返回,通过异步线程来得到支付结果。对于通过异步来获取支付结果的渠道接口,也需要对应的在异步通知中将结果返回给调用方。 异步通知需要调用方提供一个回调地址,一般以http或者https的方式。这就有技术风险,如果调用失败,还需要重试。而重试不能过于频繁,需要逐步拉大每一次重试的时间间隔。 在异步处理程序中,订单根据处理结果变更状态后,也要发消息通知相关系统。
整体架构
支付网关前置
支付网关前置是对接业务系统,为其提供支付服务的模块。它是所有支付服务接口的集成前置,将不同支付渠道提供的接口通过统一的方式呈现给业务方。这样接入方就只需要对接支付网关,增加和调整支付渠道对业务方是透明的。 支付网关前置的设计对整个支付系统的稳定性、功能、性能以及其他非功能性需求有着直接的影响。
在支付网关中需要完成大量的操作,为了保证性能,这些操作都尽量异步化来处理。
支付网关前置应保持稳定,尽量减少系统重启等操作对业务方的影响。支付网关也避免不了升级和重启。这可通过基于Nginx的LBS(Load Balance System)网关来解决。LBS在这里有两个作用: 一个是实现负载均衡,一个是隔离支付网关重启对调用的影响。 支付网关也采用多台机器分布式部署,重启时,每个服务器逐个启动。某台服务器重启时,首先从LBS系统中取消注册,重启完成后,再重新注册到LBS上。这个过程对调用方是无感知的。
为了避免接口受攻击,在安全上,还得要求业务方通过HTTPS来访问接口,并提供防篡改机制。防篡改则通过接口参数签名来处理。现在主流的签名是对接口参数按照参数名称排序后,做加密和散列,参考微信的签名规范。
交易流水和记账
每一笔交易都需要记录流水,并登记到个人和机构的分户账户上,统计和分析也需要根据交易流水来更新相关数据。 而个人和机构账户总额更新、交易流水记录以及库存的处理,更是需要事务处理机制的支持。 从性能角度, 可以弱化了事务处理的要求,采用消息机制来异步化和交易相关的数据处理。
- 在支付网关前置的主流程中,仅记录交易流水,即将当前的请求保存到数据库中。
- 完成数据记录后,发送MQ出来,记账、统计、分析,都是接收MQ来完成数据处理。
- 涉及到本地资金支付,比如钱包支付,会需要分布式事务处理,扣减账号余额,记账,扣减库存等,每个操作失败,都要回滚。阿里有很不错的分享,这里不详细描述。
- 当交易量上来后,需要考虑交易表的分表分库的事情。分表分库有两个策略,按照流水号或者交易主体id来走。后者可以支持按用户来获取交易记录。我们用的是前者。后者可以走elastic,确保数据库专用。风控,信用和统计所需要的数据,通过MQ同步到Hbase里面。作为支付系统最有价值的数据,在存储上做到专库专用,无可厚非,毕竟存储成本还是廉价的。
风控模块
风控对支付的重要性怎么强调都不过分。有些系统在风控出问题时可以旁路风控,但是在支付系统中,风控出问题必须停止交易。 整体上,风控可以分为数据采集,数据分析,实时计算,规则配置,实时拦截等模块。风控本身是个大话题,以后专门讨论。又欠一个债。 但风控和交易的接口比较简单。对每一次交易,风控一般返回三个结果:拦截,增强验证,通过。通过指交易没有问题,可以直接放行。拦截则是阻止本次交易。增强验证则是交易存疑,需要用户进一步核实身份才能继续,比如输入手机号或者身份证号,一般用于身份被盗用的场景。而人工核实则是对交易有疑问,一般用于个人恶意消费满场景。
支付路由
支付路由是一个复杂的话题。对支付系统来说,能支持的支付方式越多越好,不能由于支付方式的不支持断了财路。现实中的支付方式多得难以置信。用户随时甩出一张你听都没听说过的卡。如果一个银行卡只有几个用户在用,那针对这个卡开发个对接有点得不尝失。现在第三方支付的爆发,确实给开发支付系统省了不少事。但是公司不可能只对接一个第三方支付,如果这个渠道出问题了,或者闹矛盾了,把链接给掐了,老板还不欲哭无泪。总之,得对接多个渠道。对于交易量大的银行,还得考虑直联。
支付路由的作用是定义对用户选用的银行卡或者其他支付方式,使用什么渠道来完成支付。
一般来说,银行会提供两种支付途径:无跳转的快捷支付接口和带跳转的网银接口。前者在绑卡,支付的时候,不需要跳到银行页面上去处理,后者则需要在银行的网银页面上完成。显然前者对用户来说体验要好多了,用户流程不会被打断。快捷支付要求支付系统在本地保存用户的支付信息,如卡号,登记手机。系统要确保这些信息不被泄漏。风险非常好,所以大部分银行要求接入方必须经过ADSS检验才能够接入快捷支付。
这种固定方式的接入有单点故障的问题,一旦某个渠道出问题,绑定的支付方式就无法使用。改进策略是为每个支付方式定义多个渠道,第一个渠道出问题即选择第二个,以此类推。
当然,更进一步,可以为候选渠道定义权重,按照权重来选择支付方式。当渠道出问题,自动调整权重。
路由实现上还会更复杂,对同一张银行卡,运营上会要求在不同的系统上,比如android,iOS,windows上,或者不同地区,如大陆,台湾,香港,北美等,甚至不同业务上,采用不同渠道来支付。
支付渠道
如果采用微服务来实现,整体设计上,可以考虑将支付渠道分离、支付网关前置分离。支付渠道的微服务实现有两种策略,一种是按照服务来拆分,一种是按照渠道来拆分。
- 按渠道拆分,指每个渠道单独部署在一个容器中,对支付网关提供相同的服务。
- 按服务拆分,是按接口来拆分,分为支付,对账,退款等子系统,每个服务单独部署,所有容器的服务都实现在一起。
渠道拆分
按照服务来拆分的一个典型案例是大众点评网的早期实现。 大众点评支付渠道网关系统的实践之路。 每个支付服务接口实现为一个独立的子系统,独立部署,通过支付网关前置来对外提供服务。 这篇文章里面也提到这种方式存在的问题,
- 银行的加密客户端会有各种奇葩的需求,有些可以支持linux,有些要windows系统,如何在一个容器中满足所有需求?
- 这样拆分后,每个渠道接口独立部署。某个渠道出问题也不会影响其他渠道。至于渠道访问量小导致资源浪费问题,可以通过虚机或者docker的资源调度来解决,谁也不会在物理机上玩微服务。
- 对接渠道难点在于对输入输出做加密和解密,以及组装和解析报文。同一个渠道对不同的服务的加密解密方式是一样的,报文格式也是一样的。按渠道来构建服务可以共用这样方法,减少开发投入。
- 从安全的角度,按渠道划分也有优势。一般渠道都要求只对接到特定ip的机器,这样每个渠道对接系统所在的机器仅开放对渠道和支付网关前置机的访问白名单即可,尽可能的缩减被暴露的风险。
接入渠道
对于支付渠道,首先考虑的是接入哪些渠道。要对接的渠道按优先级有:
- 第三方支付,对大部分应用来说,支付宝和微信支付都是必须的,一般来说,这两者可以占到90%以上的交易量。用户不需要绑卡,授权后直接支付就行。各种平台都支持,性能和稳定性都不错。对于一些特殊业务,比如游戏,企业支付,可以查看一些专用的第三方支付平台。
- 银联,这货的存在,极大方便了和银行的对接。和第三方支付主要不同在两个地方一是需要绑卡,也就是用户先把卡号,手机,身份证号提供出来。这一步会折损不少用户。绑卡后,以后的支付操作就简单了,用户只需要输入密码就行。手机客户端不需要像第三方支付那样安装SDK,都在服务器端完成。当然,这是针对快捷支付。网银支付还是挺麻烦的。银联接入也需要ADSS认证。
- 银行,建议先看这一篇文章,了解下对接银行的难度。那最终需要选择哪些银行?先看个统计数据。 截至 2015 年底,我国银行业金融机构包括 5 家大型商业银行、12 家股份制商业银行、133 家城市商业银行、5 家民营银行、859 家农村商业银行、71 家农村合作银行、1373家农村信用社、1 家邮政储蓄银行、3 家政策性银行、 311 家村镇银行、48 家农村资金互助社。优先选择5家商业银行,他们占40%的交易量。其次是股份制银行和邮储。这就18家银行了。老板要是不满意,城商行和农商行加起来有1000多家呢。一般对接一个银行预计有3周左右的工作量,大部分银行需要专线接入,费用和带宽有关,一年也得几万费用。不同银行对接入环境有不同要求,这也是成本。另外,还有一个重大风险,就是央行在搞得网联系统,毕竟还没有出来,相关资料参考知乎上关于网联的一篇讨论。
- 手机支付,现在不少厂商都内置了各种支付,比如苹果的In-App支付, 三星支付、华为支付等, 这些支付仅针对特定的手机型号, 支持NFC等,根据业务需要也可以接入。 就是目前用户群不大,收益不明显。
- 话费支付, 这一块容易被人忽略,但考虑到国内不少职场人士,话费是公司报销的,每个月多的用不完,所以这块支付还是相当有市场的。 问题是,联通和移动两大运营商,不仅接口不能互通,内部各个地域也是各自为政,所以对接起来还是有点麻烦。不过话费支付领域也有类似支付宝微信的第三方支付公司,比如虹软、联动优势等公司。
这篇文章对支付系统整体设计做一个概要描述,后续会逐步补充内容并完善。其实每个模块都是一个大坑,有很多的技术细节。 欢迎大家提提意见,一起探讨。
修订记录
- 2016年11月20日 完成初稿
感谢您对本文的关注,如需要及时收到凤凰牌老熊的最新作品,或者有相关问题探讨,请扫码关注“凤凰牌老熊”的微信公众号,在公众号里留言或者回复,可以尽快处理,谢谢。
原文转自:http://blog.lixf.cn/essay/2016/11/02/account-7-gateway/