腾讯云支付系统架构介绍

2,375 阅读10分钟
原文链接: mp.weixin.qq.com

近年来,得益于丰富的场景、便捷的服务,移动支付用户总量和支付频度持续快速增加,移动支付已经成为人们的生活习惯。但是便捷之后也暗藏隐忧,调查报告显示(《2017移动支付用户调研报告》),商户不支持和安全隐患成为移动支付用户最担心的问题,其次是付费失败等问题。

腾讯云支付是借力TEG多年沉淀的技术能力,由腾讯云联合微信支付推出的移动收单SaaS服务,旨在为商户提供一个安全、稳定、高效、易用、低成本接入微信支付的解决方案,助力移动支付行业快速健康地发展。

——引言

一、云支付是什么

1.1项目背景

微信支付面临的问题:

ISV的质量参差不齐:为商户开发接入微信支付系统的ISV质量参差不齐,存在系统的稳定性、安全性不高,普通用户使用微信支付进行支付时体验差的问题,降低用户对微信支付的信心。

普通服务商面临的问题:

技术门槛高:大部分服务商没有能力开发对接微信支付的收单系统。

系统成本高:市场上少量高质量的系统,价格昂贵,服务商难以承担。

1.2 项目定位

云支付旨在提供端到端(从用户到微信支付以及其他第三方支付渠道)的安全、稳定、高效、易用、低成本的商业支付解决方案,完善从用户到支付渠道的最后一公里。

对于微信支付:提高支付流程的安全性和稳定性,提升用户信心,减少用户投诉。

对于服务商:提供一个安全、易用、低成本、功能丰富的商业支付解决方案,让服务商可以将精力集中到微信支付的推广。

对于用户:提高用户体验,提高用户使用微信支付的意愿和信心。

1.3 云支付在支付链路中的位置

二、云支付资金安全

对于支付类系统而言,安全性就是指资金安全,资金安全又可以分为数据权限和一致性两个层面。接下来三节分别介绍一下:常见的支付场景和业务模型,数据安全性保障和一致性策略。

2.1 常见的支付场景和业务模型

Ⅰ 刷卡支付

Ⅱ 公众号支付(一码付)

Ⅲ 扫码支付

2.2 数据安全

数据安全的挑战及对应的解决方案可以从三个角度来分析:数据传输、数据存储和数据操作。

数据传输

窃听:加密传输(https)

篡改:签名(RSA2)

中间人攻击:证书

伪服务器:签名(RSA2)、证书

数据存储

拖库:加密存储

篡改:签名

丢失:数据库主从、快照+操作日志、数据冗余

数据操作

非法访问、非法修改:权限控制,数据完整性检查

逻辑错误:请求限制,流量清洗

上述内容大致可以总结为四种挑战类型:数据泄漏、数据篡改、数据丢失、非法操作。

2.3 一致性挑战 

数据一致性

内部异常:导致执行流中断。

支付渠道异常:导致执行流中断,状态未知。

网络异常:支付渠道一般走公网访问,网络异常比较常见。

消息竞争:支付类逻辑链路长,在网络差的情况下,重试逻辑会导致消息竞争。

消息乱序: 支付类逻辑流程多,一般都是将操作流拆分成多步,消息乱序在所难免;使用CMQ进行可靠消息发送,在多个进程同时发消息时,也会导致消息乱序。

支付渠道接口不可重入:导致异常恢复逻辑复杂,易出错,恢复周期长。

数据一致性方案选择:

说到数据一致性,一定绕不开CAP理论:

 云支付系统所处的场景有其特殊性:

1.  云支付系统跟支付渠道的上下游关系,导致天然分区,P一定要满足;

2.  支付类系统对数据一致性要求高,C也得满足;

3. 云支付需要有99.99%的稳定性,因此A也得尽量满足。

强一致性牺牲了可用性,弱一致性牺牲了一致性,所以都没法使用。最终只能选择最终一致性。根据BASE理论,最终一致性是在CAP三个方面进行一定妥协,在保证大部分时间系统可用、数据一致的情况下,允许中间态的存在(短时间、少量数据不一致),只要这个中间态不要被外部系统访问到就好(对外视图一致),同时希望通过快速恢复使数据不一致的状态尽快恢复(最终一致)。

BASE理论在云支付系统的体现:

串行化:使用分布式锁(公众号文章:云计算时代的数据库核弹头-Tencent MySQL

(TXSQL) 锁系统扩展),将外部请求串行化,解决消息乱序的问题。

有序化:支付流程有严格的订单状态有限状态机(FSM),可以通过FSM对消息进行定序,解决消息乱序的问题。

事务化:使用最终一致性方案,使得支付执行流中的每一个完整步骤在系统外部看来就是一个独立完整的事务。

可重入化:不可重入的接口会导致故障发生时,必须记录当前执行流所处的位置以及执行流的上下文,在故障恢复时,必须从故障发生时的位置开始重新执行,会导致逻辑极度复杂。云支付通过将所有的接口都进行可重入化设计和实现,使得故障发生时,无需记录故障发生点,直接从头开始重新执行,即可将执行流推进到正常流程,简化了故障恢复的逻辑设计。

无状态:跟可重入化的作用类似,配合可重入化的设计,将所有的进程都尽量做无状态(或者少状态)的设计,设计故障恢复时,无需记录执行流上下文,简化故障恢复逻辑的设计。

通过这种设计,云支付至今的订单故障率在每百万单1单以下,中间态的恢复时间一般在10秒以内。

逻辑视图一致性

支付渠道繁多:云支付现在已经接入了8个支付渠道,不同渠道之间字段差异大,请求方式不同。

接口逻辑视图不一致:以微信支付为例,微信支付有三个接口都可以返回看似完整的订单信息(刷卡支付下单、查询订单、支付完成回调),但是三个接口返回的字段不尽相同(比如缺少代金券相关信息),同一个接口不同情况下(比如刷卡支付接口),返回的字段不尽相同(比如settlement_total_fee只在有使用免充值代金券的情况下返回,会影响结算金额统计)。

不可重入:以刷卡支付为例,已经支付成功的订单,再次下单时,会报错(订单已经支付),但是在特殊场景下(如果网络延迟,消息丢失时),请求没有及时(或者正常)的返回,就进行重试,有可能看到的现象是,第一次请求没成功,重试的时候又被告知,订单已支付。这种情况会导致异常处理非常的困难。

异常情况繁多:比如,内部异常、银行扣款失败、用户关掉支付键盘、余额不足等,总结起来就是未支付成功,这个订单本质上讲,还可以继续支付,但是由于错误类型繁多,且微信支付对于将某些错误情况(如关掉支付键盘)视为终止支付,使得异常处理逻辑极其复杂。以云支付为例,在用户关掉支付键盘的情况,如果只需还要继续,云支付不得不使用原单数据,换单号,换支付授权码重试。

解决方案:

差异抹平:通过字段补全、查询补偿、字段融合等方式,简化、统一接口语义,解决接口逻辑视图不一致。

接口可重入化:内部接口的可重入化十分简单,需要在系统设计之初就有这个概念,否则当系统成型之后再进行改造,成本高,风险大。外部接口的可重入化,必须对逻辑进行精细的重构,但是也不是一个不能完成任务。接口可重入化之后,逻辑视图一致性在故障恢复阶段遇到的复杂性问题基本解决。

用户视图一致性

用户成功支付-商户未成功收款:比如支付回包/回调丢失,导致商户侧支付流程未完成。

用户未成功支付-商户以为成功收款:比如刷卡支付长时间未收到支付成功的回包,查询订单状态得到的结果也支付中,为了防止用户误支付,商户侧会主动调用撤单(在支付成功的情况下,撤单会导致退款,之前微信支付退款一般情况下有较长时延,有时候甚至是几天)。如果在商户调用撤单的一瞬间,用户支付成功,在商户看来支付未成功,但是在用户看来,已经支付成功,实际撤单导致的退款已经申请成功,可能在用户离开之后的一段时间内,退款成功,导致商户损失。

解决方案:

摒弃有歧义的接口:云支付系统内部,已经完全不在调用撤单接口,这样就不会导致意外退款。微信支付随后也对刷卡支付接口做了优化,1分钟内不完成支付,就会自动撤单,而且不会撤已经支付成功的单,这样就完全排除了意外退款和意外支付(支付几天前的老单)的情况。

故障快速恢复:重试(用于恢复简单故障,如网络闪断)和异常订单恢复(用于恢复严重错误的订单),将异常订单故常恢复时间缩短到10秒左右,即使在异常发生时,系统也会尽快的进行故障恢复,用户只需要稍等一下,就可以得到正确的结果,从而减少纠纷的发生。

三、总结

通过上述一系列举措,基本上可以在保证数据安全性的基础上,为商户服务商提供简单、易用、数据视图一致、逻辑视图一致、用户视图一致的商业支付解决方案,降低商户/服务商的使用微信支付的门槛,降低错误率,提升用户信心,保障用户和商户的资金安全。