图解支付系统设计与实现
1. 前言
记得第一次看埃隆·马斯克(Elon Musk)讲第一性原理的视频时,深受震撼,原来还可以这样处理复杂的事务。这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。
虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。
文章内容来自《图解支付系统设计与实现》电子书,只介绍一些支付行业十几年来都没有太大变化的通用知识点。当然不可避免会夹杂一些个人不成熟的见解,请各位以“取其精华,去其糟粕”的精神辩证地看待此文内容。
2. 十年前各大公司支付系统架构图
看一下十年前各大互联网公司支付系统架构图,就会发现最基础的偏底层的那部分到现在也很有参考价值,但是性能、可用性、基础架构等已经发生了天翻地覆的变化。比如根据公开资料,2019 年双11 支付宝的支付峰值为54.4 万笔/秒,真实承载能力应该远高于此。创新的业务也发生了极大的变化,比如京东白条的横空出世,在海外旅游可以直接用支付宝或微信扫码付款等。
3. 基本概念
下面描述的概念大部分做了极致简化,只是用于入门,对于理解概念应该是够用的。真实的实现会复杂非常多。
这些概念如同支付核心系统拼图的一些小碎片,串起这些小碎片,就是一个完整的支付系统大图。
另:后面的描述中,经常混着用“支付系统”、“支付平台”,“支付机构”,“收单机构”,本质是一个东西。在内部来说,就是一个支付系统,但从和外部机构交互来说,就是一个支付平台。对用户来说是支付,对商户来说就是帮商户收单。
3.1. 最简支付流程
说明:
这是一个最简化的支付流程。真实的交互比这个复杂得多,单收银台渲染就可以写一整篇文章。但对于讲清楚支付系统的作用,已经足够。
从图中可以引申出支付系统最核心的作用:帮商户收钱。所以有牌照的也称“收单机构”。如果没有资质,只是做信息转发,也被称为“收单转接”。
有支付当然就有退款、撤销等逆向操作,复杂的跨境支付还会有外汇交易,跨境结算等业务。
3.2. 最简清结算流程
说明:
-
这里画的是信息流。
-
银行和支付平台之间是机构对机构的关系,通常使用清算概念,因为金融机构之间大部分情况下会由独立的清算机构完成清算服务。
-
支付平台和商户之间,通常使用结算概念,由支付平台直接打款给商户。(清算与结算区别是中文环境才会有,本质是一个东西)
-
上面画的是结算到商户开在支付平台的内部账户余额,所以需要商户手动提现,支付平台通常也支持直接结算到卡,这样就不需要商户手动提现。
-
清结算三个字还有另外一层含义:清分 + 结算。前者是把钱算清楚,后者是真实打款。也有些公司叫清分清偿,前者算好钱怎么分,后面完成债权任务关系的完结。本质也是一个东西。
3.3. 最简本对本收单流程
说明:
-
所谓本对本收单,就是指商户的商品标价币种、向支付系统的下单币种、用户支付币种、商户结算币种都是同一个币种。不涉及到外汇交易。
-
一个中国人拿着中国招商银行信用卡在中国境内通过多多买了两斤山东大樱桃,就是标准的本对本收单。
3.4. 最简跨境收单流程
说明:
所谓跨境收单,就是结算给商户的币种和用户支付的币种不一样,需要经过外汇机构换汇。
在扣款EUR成功后,支付平台会调用外部的外汇机构进行锁汇(HA)。
在银行清算后,支付平台再调用外部的外汇机构进行真正的换汇(TA)。
最后支付平台结算给商户USD。
上面也是只是跨境的一个小场景,真实的跨境场景极为丰富和复杂。不信你问问这段时间做俄罗斯生意收卢布的朋友们。
说明:
-
上面之所以有锁汇,是因为外汇时刻在变化,支付平台不想承担汇损风险,直接在支付款里加点手续费。能力强的支付机构也不需要锁汇,更高风险,但可能有更多收益。
-
还有些渠道直接提供空中换汇的能力。比如土耳其用户使用TRY进行支付,在支付成功后,由渠道侧直接换汇成USD,然后由渠道直接结算USD给支付平台。
-
一般来说,很多国家的货币是受管制的,无法自由出境,如果使用空中换汇直接拿到的就是USD,就比较容易出境。
-
涉及跨境场景下,往往需要设计各种各样的资金流,最主要是考虑合规诉求,某次是收益。如果能力强,利用流动性管理,资金量大,收益还是非常可观的,毕竟外面不像某国要求备付金100%缴存央行,还不给利息。
3.5. 稍复杂的跨境支付示例
我们以最典型的电商购物举个例子(只是举例):小明使用PayPal在拼多多电商(海外)通过多多钱包(海外)支付了50美金。
经过简化后的交互图如下:
说明:
-
持牌的第三方支付机构和电商是独立的法律主体,所以多多钱包和多多电商是互相独立的,需要走独立的结算。
-
为突出重点,中间省略了很多中间机构,比如花旗通过清算网络才能转账到汇丰,清算网络先略过。
-
为简化描述,还有几个假设:
- 假设拼多多电商选择结算到银行卡。还有一个场景是电商选择结算到余额,然后自己手动提现。
- 假设单币种场景,跨币种场景还涉及到外汇兑换。
3.6. 最简信息流与资金流
说明:
-
用户在支付平台充值10元,支付平台向银行发起扣款请求,这些指令操作归属于信息交互,属于信息流。
-
真实资金流:银行账户余额的变动。比如:银行在内部把用户的余额减10元,给支付平台备付金账户加10元。
-
虚拟资金流:支付平台内部账户余额的变动。比如:支付平台内部把银行应收账户加10元,给用户余额账户加10元。
-
为什么会有真实资金流和虚拟资金流之分?因为我们真正能拿到钱的地方是银行,在支付系统内看到的只是一个数字,如果想变成真实世界的钱,还得发给银行提现。
3.7. 加入结算的极简信息流与资金流
在支付流程中,就是商户委托收单机构(支付平台)把用户的钱收回来,然后再把钱结算给商家。
下面以典型通过外部渠道的卡支付为例说明。
说明:
-
用户的钱最终会走到商户的收款银行账户。真实情况下用户的支付的钱会分成多份,包括通道收的费用,支付平台收的手续费,税费,营销分润,商户结算款等。通道费用还可以继续细分为发卡行手续费,收单行手续费,清算机构手续费等。
-
跨行一般都需要通过清算机构,这里为简化也没有画出来。
-
支付平台内部的资金流在详细版中给出。
3.8. 极简跨境收单的协议关系
说明:
-
这只是跨境收单的一种协议关系,真实场景存在多种形态。
-
上述的收单机构是持牌的,但是没有跨境结算的能力,所以需要委托有跨境结算牌照的金融机构代为处理跨境结算业务。
-
跨境电商平台只是一个商户平台,没有收单资质,所以需要委托收单机构给它下面的供应商结算打款。
-
剩下的协议关系都是一目了然的,只是我们日常没有注意。比如用户和电商平台之间在注册时就会有会员协议要签署。
-
特殊的情况下,一些实力雄厚的机构,比如蚂蚁、财付通、连连支付、空中云汇等,下面会成立多个实体,然后用不同的实体去申请不同的牌照(收单、银行、外汇、跨境代发等),这样表面上全部是一家公司搞定,但是实际的协议关系仍然是上面这样的,在各实体之间仍然需要签署各种协议。
-
如果是本对本收单场景就简单很多,没有外汇和跨境结算这一层关系,如果跨境电商的货品全部是电商实体自营的,那就更简单,没有供应商委托结算的协议。
-
一般电商平台在没有牌照情况下是不能开设余额账户的,如果电商想开通余额,可以委托第三方有牌照的公司托管(通常也是收单机构,收单机构一般会同时申请PA、PG牌照),这种情况下,电商平台和收单机构还会签署账户委托协议。
3.9. 极简跨境资金方案
说明:
-
这是一个典型的跨境资金流案例。用户支付USD,收单机构收到的是USD,但是需要结算CNY给中国境内的商户。
-
收单机构(也就是支付平台)需要先将USD兑换成CNH(离岸人民币),再由入境代发机构把CNY结算给中国境内商户。这是所谓的“结汇入境”。
-
如果采用“入境结汇”的方式,则收单机构直接结算USD给商户在境外的银行账户中,由商户以USD汇入境内,再兑换成CNY。或者收单机构先把USD汇入境内备付金账户,再兑换成CNY,然后再结算CNY给中国境内商户。
-
以上这些不同的资金处理方案,统称为资金方案。
-
不同的资金方案,一方面要考虑合规的诉求,另一方面就是考虑收益最大化,以及资金周转的时效性。
3.10. 渠道路由
渠道路由核心作用是当有多个渠道同时满足业务诉求时,综合支付成功率、支付成本、用户体验、渠道状态等多种因素挑选出最优的一条渠道。具体如下:
-
提高支付成功率:通过选择最合适的渠道,可以提高支付的成功率,减少支付失败带来的用户流失。原因在于不同的渠道在其内部的风险偏好是不一样的,同一个请求在A渠道会失败,但在B渠道会成功。
-
优化成本:不同渠道的费用可能不同,通过合理的路由,可以降低支付成本。一些渠道还有阶梯收费,需要通过分流不同的渠道,保持整体成本最优。
-
提升用户体验:快速、稳定的支付体验能增强用户的满意度和忠诚度。用户如果经常在A渠道支付,新的请求过来后,仍然发给A渠道支付的成功率往往会更高。
-
负载均衡:将支付请求合理分配到不同的渠道,避免某个渠道过载,提升整体系统的稳定性。
3.11. 简明复式记账
金融机构的记账一定是基于复式记账法。下面以用户通过支付平台使用银行支付500块为例做个简要说明。
假设:支付平台使用CMB做为收单行,在CMB开设有备付金账户。
涉及的支付平台内部账户:
账户类型 | 账户 | 备注 |
---|---|---|
借记账户 | 应收-渠道-CMB | 应收归属借记账户 |
贷记账户 | 应付-过渡-网关过渡户应付-平台托管-商户待结算应付-平台托管-商户余额手续费收入-商户-消费 | 应付归属贷记账户手续费意味着所有者权益增加,归属贷记账户 |
记账步骤:
阶段 | 操作账户 | 金额 |
---|---|---|
第一步资金从渠道到网关过渡户 | 借:应收-渠道-CMB贷:应付-过渡-网关过渡户 | 500 |
第二步扣除手续费 | 借:应付-过渡-网关过渡户贷:手续费收入-商户-消费 | 10 |
第三步网关过渡户到商户待结算账户 | 借:应付-过渡-网关过渡户贷:应付-平台托管-商户待结算 | 490 |
第四步结算给商户 | 借:应付-平台托管-商户待结算贷:应付-平台托管-商户余额 | 490 |
说明:
- 支付系统的记账一定是复式记账法。内部开设了很多账户和科目。
【借记类】账户:资产,应收款等;
【贷记类】账户:负债,所有者权益,应付款等;
- 借贷简要公式(不太严谨,但是够用):
【借记类】账户(如资产,应收款),【增加】为【借】,【减少】为【贷】;
【贷记类】账户(如负债和所有者权益,应付款),【增加】为【贷】,【减少】为【借】;
- 复式记账的专业书籍很多,这里只摘录几个重要的说明:
复式记账法定义:对每项经济业务按相等的金额在两个或两个以上有关账户中同时进行登记的方法。
记账原则:有借必有贷,借贷必相等。
记账依据:会计恒等式:1. 资产 = 负债 + 所有者权益;2. 利润 = 收入 - 费用。
账户:具有一定格式和结构,能够用来连续、系统、全面的记录反映某种经济业务的增减变化及其结果。
科目:同类财务交易的分类,比如资产、负债、所有者权限、收入或费用等都属于科目。一般科目会分为多级。
账户和科目的区别:科目只有名字,账户包括结构和格式,每个账户对应一个特定的科目。
3.12. 账户分类
在账务系统中,通常包含以下几种账户类型:
- 客户账户:对客可见。包括:对私的个人客户账户,对公的商户账户。
- 内部账户:对客不可见。包括:头寸、手续费收入、过渡户(也称中间户)等。
3.13. 记账方向
说明:
- 账户类型与借贷方向,相同为加,相异为减,也就是所谓的:DD+,DC-,CC+,CD-。
- 示例:用户提现100元,记账如下:
DR:用户余额(负债类账户)100
CR:提现过渡户(负债类账户)100
3.14. 实时记账与缓冲记账
一般来说,客户账户的记账需要是实时的,比如用户充值、提现,商家提现,用户退款等。
这些账户如果不做实时记账,一来有损用户体验,二来有资损风险。比如用户充值100块,如果延时不到账,用户可能会投诉。如果提现不实时记账,用户有可能重复提现成功。如果退款不实时记账,有可能在退款场景下被透支。
假设记账需要几十毫秒(数据库性能决定的),一个账户最高也就只支持几十个TPS的记账请求,对于一些高并发的账户(也称为热点账户)一定是性能不足的。这个时候一般使用缓冲记账,以提高性能。开通缓冲记账的,通常是内部账户或允许商户透支的流出场景。
缓冲记账通常就是先记录流水,然后起定时任务去捞取流水,汇总后进行记账。前提是一定要做好资损防控。
除了缓冲记账外,还有拆分账户的方式来解决热点账户问题。
3.15. 会计科目与会计分录
会计科目就是把会计要素进行分类,比如资产、负债等。通常都会有多级分类。
会计科目示例:
说明:
- 一般支付系统使用三级科目就已经足够。部分特别复杂的系统,可能会用到五级科目。
- 为便于理解,上面的示例做了很大的精简,各公司内部对科目的编制差异可能会比较大。
3.16. 记账方案
有了账户和会计科目,发生一笔交易时,如何让系统自动去记账?这个是记账方案做的事。其中一个解决方案就是给不同的交易场景制定不同的交易码,通过交易码来驱动记账。
下面是一个典型的支付系统的记账方案示例。
3.17. 会计日与日切
会计日,也称为会计结算日或账务结算日,是支付平台在会计周期中进行账务处理和结算的特定日期。比如在分布式环境下,各机器可能存在时间差,一笔交易在零点时有可能跨天处理,如何判断一笔交易归属于哪天,就依据会计日来计算。
所谓日切,简单理解就是切换到下一个会计日。主要做的工作:
- 借贷试算平衡:也就是所谓“有借必有贷,借贷必相等”这条会计恒等式的落地。
- 父子科目试算平衡。
- 总账试算平衡。
- 日、月、季度、年汇总。
- 会计日变更。
日切试算平衡核心逻辑:
- 借方发生额 = 贷方发生额
- 借方余额 = 贷方余额
- 期末余额 = 期初发生额 + 发生额
- 父科目累积额 = 子科目累积额
3.18. 对账差异处理
对账一般有几种结果:
-
对平:双方交易类型、单号、状态、币种、金额都是一致的。
-
长款:我方多钱。支付长款:支付90块,渠道清算100块,或我方失败,渠道成功。退款长款:退款100块,渠道清算90块。充值长款、提现长款类比。
-
短款:我方少钱。支付短款:支付00块,渠道清算90块。退款长款:退款90块,渠道清算100块。充值短款、提现短款类比。
因为我方和渠道之间有一定的时间差,所以长短款在T+1对账对不上时,往往先进入存疑清单里面,第T+2对账还是对不上,才会进入差异处理。
3.19. 银行通道三层对账体系
-
第一层是信息流明细对账。我方流水和银行清算文件的流水逐一核对。可能会存在长短款情况。
-
第二层是账单对账。就是把我方流水汇总生成我方账单,然后把银行流水汇总生成银行账单,进行对账。可能会存在银行账单和我方账单不一致的情况,比如共支付100万,渠道分2次打款,一笔98万,一笔2万。
-
第三层是账实对账。就是我方内部记录的银行头寸和银行真实的余额是否一致。可能存在我方记录的头寸是220万,但是银行实际余额只有200万的情况。
3.20. 支付记账
我们通常说的记账,哪怕是一笔简单的支付,也会有多次记账。具体在什么节点记什么账,一般由财务人员决定。
下面是一个典型的使用银行通道进行支付的记账,会涉及网关过渡户,渠道待清算,商户待结算,手续费,银行头寸等多个内部户。
说明:
-
图中只画了正常场景,像明细对账出现差异(长短款)、账单对不平(渠道少打款或多打款)等场景没有画出来。
-
上面只是一个典型的记账方案,真实的场景有些更简单,有些更复杂。
-
开多个中间账户,什么场景下记到哪个账户,一般都是由财务团队决定。
3.21. 商户结算记账
商户结算和用户支付是两个独立流程。
以典型的商户结算到卡记账为例,通常涉及商户待结算户,网关过渡户,渠道应清算,渠道已清算,银行头寸等内部户。
说明:
- 上述是商户结算到卡场景。
- 各公司的内部户编制可能有所不同。
**
4. 简明产品架构图
所谓产品架构图,简单的理解,就是站在产品角度,提供什么样的服务能力。下面是一个典型的支付系统的产品架构图。实际实现时差异会很大,尤其是上面的产品或应用层,有很多机构为特殊的行业提供一些特殊的能力,比如携程的支付就会有航空方面的B2B业务。但基础的能力基本也就这些。
说明:
- 这个图画得比较简单,但是已经涵养一个支付系统最核心的产品能力。
- 上面部分是会员或商户感知的产品能力,包括门户、收银台,收单产品,资金产品等。下面部分是支付系统最核心的服务,用于支撑对外的产品能力。
- 如果多跳几家公司,就会发现基础的部分大家都差不太多。
5. 极简支付系统架构图
说明:
- 这个图很精简,但是基本已经够用,应付本对本交易这种简单的业务是完全没有问题的。
- 一些复杂的支付系统可能还有外汇、额度中心、产品中心、卡中心等,甚至一个子系统可能会拆分为多个应用独立部署,比如收单结算就可以拆成收单和结算两个独立的应用。
6. 完整支付系统架构图及各子系统简介
跳过几个支付公司,这些基础的概念在几家公司都差不太多,区别是底层技术实现。比如RPC框架,数据库,业务流程,部署架构等。
说明:
-
这是一比较完整的系统架构图,属于逻辑划分。在单体应用中,就是一些模块,在分布式应用中,就是一些子域、子应用或子系统。
-
下面针对各子系统/子模板做些简单介绍。
6.1. 核心系统依赖图
说明:
- 简化起见,只画了部分系统,比如额度中心、计收费等就没有画出来。
- 同样简化起见,部分调用关系没有画出来,否则交叉起来太乱,比如收银核心也会调用卡中心查询用户的绑卡信息,且收银核心也会调用渠道网关查询可用渠道信息等。
6.2. 开放网关
主要对接商户,比如下单、支付等接口入口。通常要求有比较高的安全性。部分公司可能会把移动端网关、PC门户网关、商户通知等能力集成在开放网关,也可能会单独拆出部署。
6.3. 收单结算
负责把商户的单收下来,并给商户发起结算。承担的收单产品包括有:线上收单,线下收单,担保交易、即时到账等,每个公司的商业策略不同,开出的收单产品会有差异。
有些公司把结算划到出款中心,对接银企直连的渠道。
6.4. 资金产品
承担无买卖标的的纯资金转移能力。典型的有:充值、转账、提现、代发。和支付的区分在于支付是有买卖标的,而资金产品没有。也就是在系统中没有买卖记录发生,但在线下可能有。
资金产品一般需要独立的牌照。
6.5. 收银核心
渲染可用支付方式。包括查询账户是否有余额,查询营销是否有营销券,查询渠道网关是否有可用的外部渠道,最后组合成可用支付方式,供前端渲染。
收银核心就像一个大内总管,收到请求后,找商户平台核实身份,找合约平台核实权限,找会员平台核实用户身份,找收单看一下这笔单是否可以继续支付,找账务中心获取余额信息,营销看看有没有可用的券,找渠道网关看看没有可用的渠道,找额度中心看看是否超限额了,找风控问一下当前支付是否安全,找会员平台校验支付密码 ... ...
6.6. 支付引擎
负责真正的扣款或转账。有些公司叫支付核心。
如果是余额就调账务扣减余额,如果是红包就调营销做核销,如果是外部银行通道就调渠道网关。
6.7. 渠道网关
负责去外部渠道扣款。通常还会提供渠道路由、渠道咨询等能力,做得细的公司可能会把渠道核心和报文/文件网关单独拆出来。其中渠道核心就提供渠道路由、渠道咨询、渠道开关等服务,报文/文件网关负责报文转换、签名验签等。
6.8. 会员平台
管理会员的生命周期,比如注册、注销、登录等。同时还提供核身服务(比如登录密码,支付密码,短信验证码等)、实名认证服务等。
6.9. 商户平台
管理商户的入驻签约、KYB、交易管理等。
6.10. 产品中心
管理对外提供的产品能力,比如快捷支付,代扣等。一般大的支付系统才会独立成一个子系统。
6.11. 账务中心
负责账户开立,记账等。
6.12. 会计中心
会计科目管理、分录管理、日切管理等。
监管报表有时候也放在这里,有些公司也会独立出去。
很多集团公司往往有一套独立的专业财务系统,这个时候往往需要会计中心做完日切后,要把记账信息合并到集团公司的财务系统中去,简称并账。
6.13. 对账中心
负责明细对账和资金对账。
6.14. 营销平台
提供满减、红包等营销工具。
6.15. 风控平台
针对账户和交易,提供实时、离线风控,控制交易的风险。反洗钱、反欺诈是基本要求。
通常各公司对风控规则看成是机密,研发也可能看不到运营配置的规则。经常看到有网友问:“有xx公司的人在吗?我有xxx场景下的支付总是提示风控不过,是否知道是什么原因,怎么才能通过?”,完全是浪费口舌,谁会对外公布自己的风控规则,让人去钻空子呢?
6.16. 运营平台
订单管理、渠道管理、产品管理等综合运营工具。
6.17. 数据平台
主要用于数据汇总和分析。当前各支付公司基本都是分布式部署N多个应用,数据都在散落在各子系统中,需要汇总到数据平台用于经营分析。
6.18. 卡中心
负责管理用户的绑卡信息。需要经过PCI认证。
6.19. 额度中心
累计用户、商户的额度,通常有日、月、年,单卡等各种分类。
6.20. 外汇平台
负责外汇报价和兑换。
6.21. 流动性与调拨中心
一些跨境支付公司,在多个国家多个银行有头寸,各头寸之间经常需要做流动性管理,提高资金利用率。
毕竟在国外不需要把备付金强制存到央行还不给利息。当资金量大的时候,这笔收益可不少。
6.22. 差错中心
负责差错处理。比如渠道退款失败(银行账号销户,过了银行的退款有效期等),需要通过其它的方式退给用户。
6.23. 拒付中心
处理用户的拒付和举证。在跨境支付场景下,信用卡用户联系发卡行说卡被盗刷或商品没有收到,或商品有问题等,拒绝支付给商户。
国内基本没有看到拒付场景。
7. 一些拓展知识
7.1. 技术风险与资损防控
一般来说,技术风险主要包含稳定性和资损两个方面。其中稳定性风险就是大家经常说的几个9,比如99.999%可用,就是5个9。资损风险就是平台或用户的资金损失。
虽然资损也是技术风险的一种,但是因为对于专业的持牌支付公司来,资损是一种非常严重的事故,容易引发客诉、网络事件、甚至监管介入,所以又较一般的风险更为严重,常常把资损防控单独拿出来说。
技术风险体系过于庞大,这里只谈几点通用知识。
识别风险来自哪里
我们通常先需要知道风险来自哪里,才知道如何去防控。而风险往往来自变化。举几个例子,抛砖引玉:
流量变化:大促场景下,流量会暴增。
代码变化:引入了新的代码。
业务变化:修改了业务流程,或引入了新的业务。
外部变化:外部新的攻击手段。
应对风险
根据变化去应对风险。比如大促引入了流量变化,那就做压测、扩容、限流、降级非核心业务等应对。比如原来只有支付,这些有了用户提现,针对用户提现,内部多个子域可能状态/金额不一致,和银行渠道的状态/金额也可能不一致,那就加入各种对账手段,以及对应的应急预案。
内部系统实时与离线对账
对账是资损防控中最效的手段之一。
前面讲过的三层对账主要是和银行渠道对账,除了这个之外,一般的支付平台还会有内部系统之间的两两核对,这种核对主要是信息流层面的核对,主要核对状态、金额的一致性。
说明:
- 可以拆成离线核对和实时核对。
- 离线核对一般就是把生产数据库的数据定时清洗到离线库(一般还可以分为天表和小时表)。
- 实时核对一般就是监听数据库的binlog,当数据有变动时,延时几秒后请求双方系统的查询接口,查到数据后进行核对。
7.2. 幂等
幂等是针对重复请求的,支付系统一般会面临以下几个重复请求的场景:
- 用户多次点击支付按钮:在网络较差或系统过载情况下,用户由于不确定交易是否完成而重复点击。
- 自动重试机制:系统在超时或失败时重试请求,可能导致同一支付多次尝试。
- 网络数据包重复:数据包在网络传输过程中,复制出了多份,导致支付平台收到多次一模一样的请求。
- 异常恢复:在系统升级或崩溃后,未决事务需要根据已有记录恢复和完成。内部系统重发操作。
所谓业务幂等,就是由各域自己把唯一性的交易ID作为数据库唯一索引,这样可以保证不会重复处理。
在数据库前面可以加一层缓存来提高性能,但是缓存只用于查询,查到数据认为就返回幂等成功,但是但不到,需要尝试插入数据库,插入成功后再刷新数据到缓存。
为什么要使用数据库的唯一索引做为兜底,是因为缓存是可能失效的。
在面临时经常有同学只回答到“使用redis分布式锁来实现幂等”,这是不对的。因为缓存有可能失效,分布式锁只是用于防并发操作的一种手段,无法根本性解决幂等问题,幂等一定是依赖数据库的唯一索引解决。
大部分简单的支付系统只要有业务幂等基本也够用了。
7.3. 分库分表
当数据量大的时间,分库分表是再所难免的。一个经典的面试题是:如果分了100张表,按商户来分表,还是按商户订单号来分表?如果按商户分表怎么解决各表流水数据量平衡问题?如果是按商户订单号来分表,商户想按时间段查询怎么办?
解法有很多种。一种典型的解法,就是线上数据库按商户订单号分表,同时有一个离线库冗余一份按商户号分表的数据,甚至直接使用离线数据平台的能力,把商户的按时间段查询需求从在线库剥离出来。
7.4. 分布式事务
分布式事务是个好东西,但是复杂度也高,还经常出现所谓的事务悬挂问题,且虽然各家都号称简单易用,对业务代码侵入少,但事实并非如此。
所以我个人更倾向于避免使用分布式事务解决方案,而是采用最终一致性来解决。对大部分中小公司来说,最终一致性已经够用。
7.5. 金额处理规范
对于研发经验不足的团队而言,经常会犯以下几种错误:
- 没有定义统一的Money类,各系统间使用
BigDecimal
、double
、long
等数据类型进行金额处理及存储。 - 定义了统一的Money类,但是写代码时不严格遵守,仍然有些代码使用
BigDecimal
、double
、long
等数据类型进行金额处理。 - 手动对金额进行加、减、乘、除运算,单位(元与分)换算。
带来的后果,通常就是资金损失,再细化一下,最常见的情况有下面3种:
-
手动做单位换算导致金额被放大或缩小100倍。
- 比如大家规定传的是元,但是其中有位同学忘记了,以为传的是分,外部渠道要求传元,就手动乘以100。或者反过来。
- 还有一种情况,部分币种比如日元最小单元就是元,假如系统约定传的是分,外部渠道要求传元,就可能在网关处理时手动乘以100。
-
1分钱归属问题。比如结算给商家,或计算手续费时,碰到除不尽时,使用四舍五入,还是向零舍入,还是银行家舍入?这取决于财务策略。
-
精度丢失。在大金额时,double有可能会有精度丢失问题。
最佳实践:
- 制定适用于公司业务的Money类来统一处理金额。
- 在入口网关接收到请求后,就转换为Money类。
- 所有内部应用的金额处理,强制全部使用Money类运算、传输,禁止自己手动加减乘除、单位换算(比如元到分)。
- 数据库使用DECIMAL类型保存,保存单位为元。
- 在出口网关外发时,再根据外部接口文档要求,转换成使用指定的单位。有些是元,有些是分(最小货币单位)
7.6. 业务ID生成规则
数据库一般都会设计一个自增ID作为主键,同时还会设计一个能唯一标识一笔业务的ID,这就是所谓的业务ID(也称业务键)。比如收单域的收单单号。
也有人采用所谓雪花算法,但其实不适用于支付场景。
下面以32位的支付系统业务ID生成为例说明。实际应用时可灵活调整。
第1-8位:日期。通过单号一眼能看出是哪天的交易。
第9位:数据版本。用于单据号的升级。
第10位:系统版本。用于内部系统版本升级,尤其是不兼容升级的时候,老业务使用老的系统处理,新业务使用新系统处理。
第11-13位:系统标识码。支付系统内部每个域分配一段,由各域自行再分配给内部系统。比如010是收单核心,012是结算核心。
第14-15位:业务标识位。由各域内部定,比如00-15代表支付类业务,01支付,02预授权,03请款等。
第16-17位:机房位。用于全球化部署。
第18-19位:用户分库位。支持百库。
第20-21位:用户分表位。支持百表。
第22位:预发生产标识位。比如0代表预发环境,1代表生产环境。
第23-24位:预留。各域根据实际情况扩展使用。
第24-32位:序列号空间。一亿规模,循环使用。一个机房一天一亿笔是很大的规模了。如果不够用,可以扩展到第24位,到十亿规模。
7.7. 状态机设计
状态机,也称为有限状态机(FSM, Finite State Machine),是一种行为模型,由一组定义良好的状态、状态之间的转换规则和一个初始状态组成。它根据当前的状态和输入的事件,从一个状态转移到另一个状态。
下图就是收单子域设计中交易单的状态机设计。
从图中可以看到,一共4个状态,每个状态之间的转换由指定的事件触发。
常见代码实现误区
经常看到工作几年的同事实现状态机时,仍然使用if else或switch case来写。这是不对的,会让实现变得复杂,且容易出现问题。
甚至直接在订单的领域模型里面使用String来定义,而不是把状态模式封装单独的类。
还有就是直接调用领域模型更新状态,而不是通过事件来驱动。
限于篇幅,这里就不给正确的示例了。有兴趣的可以去网上看看,良好的示例有很多。
7.8. 日志规范
只要在公司写过代码,就一定打印过日志,但经常发现一些工作多年的工程师打印的日志也是乱七八糟的。我曾经在一家头部互联网公司接手过一个上线一年多的业务,相关日志一开始就没有设计好,导致很多监控无法实现,出了线上问题也不知道,最后只能安排工程师返工改造相关的日志。
我们要明白日志是用来做什么的。只是先弄明白做事的目的,我们才能更好把事情做对。在我看来,日志有两个核心的作用:1)监控,诊断系统或业务是否存在问题;2)排查问题。
对于监控而言,我们需要知道几个核心的数据:业务/接口的请求量、成功量、成功率、耗时,系统返回码、业务返回码,异常信息等。对于排查问题而言,我们需要有出入参、中间处理数据的上下文、报错的上下文等。
接下来,基于上面的分析,我们就清楚我们应该有几种日志:
-
接口摘要日志。监控接口的请求量、成功量、耗时、返回码等。使用固定格式,需要打印:时间、接口名称、结果(成功/失败)、返回码、耗时等基本信息就足够。
-
业务摘要日志。监控业务的请求量、成功量、核心业务信息、返回码等。使用固定格式,需要打印:时间、业务类型、上一步状态、当前状态、返回码、核心业务信息(不同业务有不同的核心业务信息,比如流入,就有支付金额/退款金额,卡品牌,卡BIN等)。
-
详细日志。用于排查问题,不用于监控。格式不固定。主要包括时间、接口、入参、出参、中间处理数据输入、异常的堆栈信息等。
-
系统异常日志。同时用于监控。格式固定。需要打印:时间、错误码、错误信息、堆栈信息等。
7.9. 支付安全
支付安全核心关注点
支付安全是一个很大的范畴,但我们一般只需要重点关注以下几个核心点就够:
- 敏感信息安全存储。
对个人和商户/渠道的敏感信息进行安全存储。
个人敏感信息包括身份证信息、支付卡明文数据和密码等,而商户/渠道的敏感信息则涉及商户登录/操作密码、渠道证书密钥等。
- 交易信息安全传输。
确保客户端与支付系统服务器之间、商户系统与支付系统之间、支付系统内部服务器与服务器之间、支付系统与银行之间的数据传输安全。这包括采用加密技术等措施来保障数据传输过程中的安全性。
- 交易信息的防篡改与防抵赖。
确保交易信息的完整性和真实性,防止交易信息被篡改或者被抵赖。一笔典型的交易,通常涉及到用户、商户、支付机构、银行四方,确保各方发出的信息没有被篡改也无法被抵赖。
- 欺诈交易防范。
识别并防止欺诈交易,包括套现、洗钱等违规操作,以及通过识别用户信息泄露和可疑交易来保护用户资产的安全。这一方面通常由支付风控系统负责。
- 服务可用性。
防范DDoS攻击,确保支付系统的稳定运行和服务可用性。通过部署防火墙、入侵检测系统等技术手段,及时发现并应对可能的DDoS攻击,保障支付服务的正常进行。
极简支付安全大图
支付安全是一个综合性的系统工程,除了技术手段外,还需要建立健全的安全制度和合规制度,而后两者通常被大部分人所忽略。
下图是一个极简版的支付安全大图,包含了支付安全需要考虑的核心要点。
说明:
- 制度是基础。
哪种场景下需要加密存储,加密需要使用什么算法,密钥长度最少需要多少位,哪些场景下需要做签名验签,这些都是制度就明确了的。制度通常分为行业制度和内部安全制度。行业制度通常是国家层面制定的法律法规,比如《网络安全法》、《支付业务管理办法》等。内部安全制度通常是公司根据自身的业务和能力建立的制度,小公司可能就没有。
- 技术手段主要围绕四个目标:
- 敏感数据安全存储。
- 交易安全传输。
- 交易的完整性和真实性。
- 交易的合法性(无欺诈)。
对应的技术手段有:
-
敏感信息安全存储:采用加密技术对个人和商户/渠道的敏感信息进行加密存储,限制敏感信息的访问权限,防止未授权的访问和泄露。
-
交易信息安全传输:使用安全套接字层(SSL)或传输层安全性协议(TLS)等加密技术,确保数据在传输过程中的机密性和完整性。
-
交易的完整性和真实性:采用数字签名技术和身份认证技术确保交易信息的完整性和真实性,对交易信息进行记录和审计,建立可追溯的交易日志,以应对可能出现的交易篡改或抵赖情况。
-
防范欺诈交易:通过支付风控系统,及时识别和阻止可疑交易行为。
-
服务可用性:部署流量清洗设备和入侵检测系统,及时发现并阻止恶意流量,确保支付系统的稳定运行和服务可用性,抵御DDoS攻击。
8. 结束语
所谓提纲挈领,就是先掌握核心主干,有了这个前提,再去深入了解细节,才不至于“乱花渐欲迷人眼”,解决问题时才能如庖丁解牛,行云流水。伟人邓公提倡的“抓住主要矛盾”,也是这个道理。
本文主要讲了一些支付核心系统相关的基本概念,希望能为大家在学习在线支付系统相关知识时能提供一些有益的参考。