我现所在公司是开发统一支付平台,因为公司的业务需求,须要接入多个第三方支付,为了后续对支付平台更深刻的思考,整理出来。安全
因为公司业务在不少地区都有,须要提供多种支付途径,以知足业务的发展,因此设计的支付平台须要接入多种第三方支付渠道,如:微信支付、支付宝支付、卡联支付、迅联 等等,咱们都知道,每一个第三方支付,都有本身一套对外 API,官方都有一套 SDK 来实现这些 API,咱们应该如何组织这些 API 呢?微信
因为第三方支付渠道会随着业务的发展变更,因此组织这些 SDK 就须要在不影响支付平台总体架构的前提下可灵活插拔,这里我使用了组件的思想,将支付 API 拆分红各类组件支付组件、退款组件、订单组件、帐单组件、订单异常处理组件等等,那么这样就能够当引入一个第三方支付 SDK 时,可灵活在组件上面添加须要的 API,架构设计以下:网络
对接第三方支付都有一个特色,就是支付、退款成功后,会有一个支付、退款的回调功能,目的是:一、让商户平台自行校验订单的合法性,好比:防止支付时,客户端恶意篡改金额等参数,那么此时支付成功后订单处于支付中状态,须要等待第三方回调,若是收到回调校验发现支付金额和订单金额不对,咱们能够把订单状态改成失败,防止资金损失。2:经过回调咱们能够处理咱们本身系统的业务逻辑,商品的分发等等。回调的思想只要保证数据的最终一致性,因此咱们发起支付时不须要校验参数的正确性,只需在回调时校验。那咱们应该如何来设置支付平台的回调。架构
因为支付平台须要接入多个第三方支付,若是此时每一个第三方支付设置一个回调地址,那么将会出现多个回调地址,因为回调的API必须是暴露出去才能接受第三的回调,因此就会有安全问题,咱们必须在API外层设置安全过滤,因此咱们须要统一回调API,统一作安全校验,以后再进行一层分发。异步
分发机制咱们采用的是RabbitMq,这里可能会有疑问,若是用mq来作分发处理,此时怎么实时返回校验结果给第三方?如下是对回调的一些思考:微服务
1. 系统是基于Spring boot微服务架构,服务之间经过Http通讯,若是用Http分发,能够保证消息返回的实时性,但因为网络不稳定,会出现请求失败或超时的问题,接口的稳定性得不到保障。 2. 因为第三方支付若是收到false响应,就会在接下来的一段时间内再次发起回调请求,这么作的目的是为了保障回调的成功率,对于第三方支付来讲,这没毛病,但对于商户支付平台来讲,也许就是一个比较坑爹的设计,你想一下,假设有一笔订单在支付时恶意篡改了金额,回调校验失败,返回 false 到第三方支付,此时第三方支付会再重复发送回调,不管发送多少次回调,都会校验失败,这就额外增长了没必要要的交互,固然这里也能够用幂等做处理,如下是微信支付回调的应用场景说明:微信支付
基于以上两点思考,我认为返回 false 到第三方支付是不必的,为了系统的健壮性,我采用了消息队列来作异步分发,支付平台收到回调请求后直接返回 true,这时你可能会提出一个疑问,若是此时校验失败了,但此时返回 true,会不会出现问题?首先,校验失败状况,订单一定是处于支付失败的状态,此时返回 true 目的是为了减小与第三方支付没必要要的远程交互。ui
由于Mq是把消息持久化到磁盘的,因此消息队列来作分发的最大好处,就是复查消息队列里面的消息来排查问题,并且消息队列能够在业务高峰期进行流量削峰。架构设计
如下是统一回调和分发的架构图:设计
支付平台聚合了多种第三方支付,所以在请求层须要作不少的适配工做,以知足多种支付的需求,可能你会想,直接在适配那边加几行if else 不就得了吗,这么作也没问题,也能够知足多种支付的需求,但若是再加一个第三方支付呢,你只能在原有方法上加多个else条件,这么会致使请求层代码不断随着业务发展改变,使得代码极其不优雅,并且也不方便维护,这时咱们能够用策略模式,将这些if else代码消除,当咱们增长一个第三方支付时,咱们只须要新建一个strategy类就能够了,架构以下:
因为支付平台涉及到资金,支付的各类请求和返回,以及异常记录在一个支付平台中异常重要,所以咱们须要记录每一次的支付请求记录,以便后续排查问题。
基于这一点,咱们在开始请求第三方支付以前,设计一层handler层,全部请求都必须通过Handler层处理,Handler核心方法以下:
public K handle(T t) { K k; try { before(t); k = execute(t); after(k); } catch (Exception e) { exception(t, e); } return k; } protected abstract void before(T t); protected abstract void after(K k); protected abstract void exception(T t, Exception exception);
Handler层利用了模板模式,不只能够实现日志的记录,还能够实现多种处理方式,好比请求监控,消息推送等等,实现了handler层的高扩展性