当前位置: 首页 > 科技观察

详细的支付平台高可用架构设计实践

时间:2023-03-21 12:11:53 科技观察

我在之前公司的第一个任务就是开发一个统一的支付平台。由于公司业务需要,需要接入多个第三方支付。图片来自宝途网。以前公司的付款分散在各个项目中,对付款的管理极为不利。于是,聚合三方支付,统一支付平台的任务就落在了我身上。可以说设计完全是从0开始的,经过一些实战总结,得出了一些架构设计的想法。之前一直想把自己的架构设计思路写出来,但是一直没有实现。前几天,技术群里有人问了相关问题。我觉得有必要写出来帮助更多需要开发支付平台的人。开发商。组件模式由于公司业务分布在多个地区,需要提供多种支付方式以满足业务发展,因此设计的支付平台需要接入多种第三方支付渠道,如:微信支付、支付宝payment,PayPal,IPayLinks等。我们都知道每个第三方支付都有自己的一套对外的API,官方SDK有一套API来实现这些API。我们应该如何组织这些API?由于第三方支付渠道会随着业务的发展而发生变化,因此这些SDK的组织需要在不影响支付平台整体架构的情况下灵活、可插拔。这里我使用组件的思想,将支付API拆分成支付组件、退款组件、订单组件、账单组件等各个组件。这样在引入第三方支付SDK时,可以灵活的在组件中添加需要的API。架构设计如下:通过Builder模式,根据请求参数构建相应的组件对象,组件与外部隔离,隐藏组件构建的实现。.组件模式+Builder模式,让支付平台具有高度的可扩展性。当多账户系统接入各种第三方支付平台时,又遇到了账户问题。原因是当时公司的小程序和app使用的是不同的微信账号,所以微信支付会对应多个账号。账户问题。我在设计支付平台的时候,没有考虑这个问题。当时第三方支付只对应一个账户,不同第三方支付的账户是独立的,不统一。所以我引入了多账户系统。多账户体系最重要的核心概念之一就是以账户为粒度接入多个第三方支付,统一账户参数,构建统一的支付账户体系。支付平台不需要关心不同支付之间的账户差异,第三方支付有多少个账户。这时候我在支付平台架构图中加入了账户层:前端只需要传AccountId,支付平台就可以根据AccountId查询到对应的支付账户。然后通过Builder模式构建支付账户对应的组件对象,完全屏蔽了不同支付之间的差异。在多账户系统中,可支持多个支付账户,充分满足公司业务发展需求。统一回调异步分发处理做过支付开发的同学都知道,现在的第三方支付有一个特点,就是支付/退款成功后,会有一个支付/退款回调函数。目的是让商家平台自己验证订单是否合法。例如:防止客户端在支付时恶意篡改金额等参数,那么此时支付成功后,订单会处于支付状态,需要等待第三方支付的回调.如果此时收到回调,发现订单金额与验证时支付的金额不一致,则将订单更改为支付失败,防止资金流失。回调的思路是为了保证最终的一致性,所以我们在调用支付的时候,此时不需要校验参数的正确性,只需要在回调的时候校验即可。说完回调的目的,我们如何设计支付平台的回调呢?由于支付平台接入了多个第三方支付,如果此时每个第三方支付都设置了一个回调地址,那么就会有多个回调地址。由于必须暴露回调API才能接受第三方回调请求,所以会存在安全问题。我们必须在API的外层设置安全过滤,否则很容易出现一些非法访问和暴力破解,所以我们需要统一回调API,统一进行安全验证,再进行一层分发.对于分发机制,我建议使用RocketMQ来处理。可能有人会问,如果使用RocketMQ进行分发处理,此时如何将校验结果实时返回给第三方支付呢?这个问题也是我当时头疼的问题。以下是我对回调设计的思考:①公司系统基于SpringCloud微服务架构,微服务之间通过HTTP进行通信。当时我的支付平台接入了很多微服务。如果使用HTTP进行分发,可以保证消息返回的实时性。但是也会有一个问题。由于网络不稳定,会出现请求失败或超时的问题,无法保证接口的稳定性。②第三方支付如果收到False响应,会在接下来的一段时间内再次发起回调请求。这样做的目的是保证回调的成功率。对于第三方支付来说,这不是问题,但对于商户支付平台来说,可能是一种比较坑爹的设计。想一想,假设一个订单在支付时恶意篡改金额,回调验证失败,返回False给第三方支付。此时第三方支付会重复发送回调。无论发送多少回调,验证都会失败。这增加了不必要的交互。当然这里也可以使用幂等处理。下面是微信支付回调的应用场景说明:基于以上两点考虑,我觉得没有必要给第三方支付返回False。为了系统的健壮性,我使用消息队列进行异步分发,支付平台收到回调请求后直接返回True。这个时候,你可能会问一个问题。如果此时验证失败,但是此时返回true,会不会有问题?首先,如果校验不通过,订单必须处于支付失败的状态,此时返回True目的是减少与第三方支付不必要的远程交互。因为RocketMQ的消息是持久化到磁盘的,所以使用消息队列进行异步分发的好处是可以通过查看消息队列中的消息来排查问题,消息队列可以在业务高峰期进行流量调峰。下面是统一回调分发处理的架构设计图:聚合支付支付平台聚合了多种第三方支付,因此需要在请求层做大量的适配工作来满足各种支付的需求。也许你会想,适配加几行ifelse就可以了。这样做没问题,也可以满足各种支付需求,但是你有没有想过这个时候加入第三方呢?你会做什么来支付?只能在原来的方法上加上多个else条件,这样会导致请求层代码随着业务的发展不断变化,代码极其不优雅,难以维护。这时候我们就不得不使用策略模式来消除这些ifelse代码。当我们添加第三方支付时,只需要新建一个Strategy类即可。策略模式的使用方法可以看大话设计模式。因此,我在Builder模式之前额外增加了一个支付策略层:请求处理由于支付平台涉及资金,支付的各种请求和返回,以及异常记录在支付平台中是极其重要的,所以我们需要记录每一次支付请求记录以便后续故障排除。基于这个需求,我在开始请求第三方支付之前设计了一个Handler层。所有的请求都必须经过Handler层处理。Handler的核心方法如下:publicKhandle(Tt){Kk;try{before(t);k=execute(t);after(k);}catch(Exceptione){exception(t,e);}returnk;}protectedabstractvoidbefore(Tt);protectedabstractvoidafter(Kk);protectedabstractvoidexception(Tt,Exceptionexception);原则上,我设计的Handler层采用模板方式,不仅实现了日志记录,还实现了多种处理方式,比如如请求监听、消息推送等,实现Handler层的高扩展性。下面是Handler层的架构设计图:上面写的***是我的支付平台架构设计思路。综上所述,支付平台需要具备可扩展性、稳定性和高可用性。所以我在设计支付平台的时候,用了很多设计模式,引入了消息队列来处理回调分发的问题,让支付平台具备了这些特点。希望能给大家一些启发和帮助,***我把支付平台的整体架构设计贴出来: