一 引言
出金系统:对接银联、网联。以完成用户资金结算(提现)。
出金系统是公司将资金结算到用户的最后一道流程,一旦出现异常导致重复结算,则会直接造成公司的资金损失,所以我们秉承一个原则:稳
二 名词解释
出金:从备付金提现至用户银行卡
网联:这里特指网联付款业务
银联:这里特指银联客户资金结算
渠道:出金渠道,指银联客户资金结算,网联付款业务。可以利用其能力完成用户提现。
退票:银联渠道先告知出金成功,后因账户或者其它原因资金退回。
三 “稳” 的几个原则
3.1 不更换提现流水号
流水号在请求出金渠道前生成,一旦生成,则后期不再变更。保证单一,降低系统复杂度,充分利用渠道幂等。
之所以这样,我们做了以下分析:
流水号在什么场景下需要更换?
在当天第一次请求渠道后,如果因系统在处理报文过程中有数据异常导致渠道提现失败,需要重新修正数据更换新流水号请求渠道。这种情况出现次数多吗?
报文拼装等异常,在测试阶段已经基本排除,极少会出现拼装异常导致渠道失败,正常的情况下,渠道能达到99.9%以上正常受理,而且就算失败,也多是因为账户本身错误相关,与流水号无关。出现了渠道失败,但账户本身没问题,换流水号能解决的时候怎么办?
我们目前有两个出金渠道,网联与银联。我们优先使用网联,如果出现异常,我们不换流水号,在确保当前渠道确实失败的前提下,切换到银联渠道重试。如果切渠道还是不能成功怎么办?
此次提现置为失败,用户检查账户信息重新发起。
3.2 不更换交易时间
记录第一次请求渠道的交易时间,之后与渠道交互不更换时间。这样以来可以充分利用渠道对于当天同一流水号的幂等的特性。
之所以这样,也源于渠道的幂等性,渠道给出的幂等保证为:同一机构,当天流水号唯一。
3.3 严格控制异常交易处理
控制异常处理,未经确认的异常交易种类,不允许处理。
出金系统只自动处理渠道明确的成功响应。除此之外的响应均进入异常处理流程。
接入的出金渠道返回的异常信息比较繁多,对于每一类异常,出金系统接收后,先经人工判定,只有明确后,更新到运营操作里才可对其进行相关操作,没有确认并处理过的异常交易种类不允许处理。
采取这种方式,在前期会投入一些人工成本,但能保证其准确性。在处理了大部分异常种类后。人工投入也相应减少了。
在此基础之上,我们再考虑进行系统自动处理异常,逐渐减少人工成本。
3.4 捕获所有系统和业务异常
在收到提现的请求,通过幂等判断及参数校验,就可存入数据库。出金就返回给上游受理状态。
剩下的异常都捕获,能让上游感知的,都是明确的、确保出金不能处理的异常。
出金受理提现请求后,异步进行提现渠道交互,不会因任何交互异常而影响上游获取结果的准确性。
3.5 充分利用资金监控手段
接入资损防控系统,按照定义的监控规则,一旦有异常提现,会通过多种告警方式通知,以便能及时处理。
接入渠道对账系统,利用其能力及时发现交易不对等的情况,通过告警感知并及时处理。
3.6 提供多种快速熔断手段
提供多种主动熔断手段,在系统异常时及时中断出金以减少影响。
四 系统建设过程
4.1 接入银联客户资金结算业务
银联处理交易的方式:同步请求受理接口,得到受理结果,异步通过回调通知交易结果。
做为初入支付领域的新人,初次与银联打交道。发现接入文档中异常的罗列并不完整。因没有经验可遵照执行,为保证资金安全我们选择了仅处理明确的交易成功状态。对于其它状态归为异常。初期投入人工成本,对于常见的异常类型进行必要的统计,人工判定后,再进行重试、同步状态、置为失败等操作。以保证提现异常处理的准确性。
之所以做出初期投入人工成本,也是考虑到实际在投产后,异常交易数量占比应该不会太高,因为提现时用户进行信息填写时,相对还是较为谨慎。如果填错信息,一是浪费了提现处理时间,再者严重的话会导致资金损失。在实际上线后,也验证了我们的想法。
在经过梳理需求,参照银联客户资金结算接入文档后,定义出提现处理的主要流程:
以上阶段中,每个阶段都会出现异常情况,所以需要有补偿操作:
在处理所有异常交易前先将交易存在异常处理缓存池中,在每个补偿完成后,再将交易从缓存池中清除,以避免重复补偿。
一阶段:成功落库提现记录,但长时间处在申请状态。
补偿逻辑: 获取超过一定时长的申请提现单,查询缓存中是否有该交易,如无,则重新发起进入二阶段。
二阶段:请求超时或者无响应,这时系统需要进行补偿处理。
补偿逻辑:
a. 以入库的交易时间和交易流水号先发起交易查询,如果仍无响应,则等待下次补偿再次尝试
b. 得到查无交易响应,重新发送报文到银联
c. 得到重复交易响应,进入三阶段
三阶段:长时间未收到异步回调
补偿逻辑: 适时发起状态查询请求及时同步状态。避免异步回调时间较长或者没有收到异步回调导致交易状态不能及时更新。
四阶段:上游未能及时收到结果
补偿逻辑: 提供提现状态查询接口,上游可调用及时获取结果
设计并实现以上补偿逻辑后,从正常提现到异常处理就相对完整了。测试完成后投产上线,运行结果良好,出现的提现失败,做了对应统计,对银联的常见异常及处理方式有了很好的把握。
4.2 出金运营功能
因系统只自动处理成功的提现(自动发送成功消息&通知上游业务系统),所有异常交易,需要人工进行判定并处理。
所以在上线银联渠道的同时,我们也上线了运营功能,提供后台页面以供使用。
出金运营功能:
- 打回:设置提现为失败,并发送失败消息通知上游。
- 同步:主动查询渠道状态,同步出金交易状态。
- 重试:重新按原交易信息封装报文请求渠道。
- 切渠道:此功能是网联上线后才具备的,用于网联渠道提现失败后,切换到银联重新提现。前提是保证不重复出金。
因银联对于提现的异常交易,响应了不同的交易响应码。所以以上操作均是按响应码来判定是否可被操作。
新异常提现操作流程:
出金 --> 得到异常码 --> 人工确认 --> 配置此类异常可用操作 --> 运营操作(填写对应文案给上游及用户展示)--> 系统保存处理文案
同类异常出现后,下次处理时:
出金 --> 得到异常码 --> 运营操作(自动填充之前的处理文案)--> 操作
同类异常处理时,自动填充处理文案,提升了人工处理效率,也降低了不同处理人员在处理异常交易时的判定成本。
4.3 接入网联代付业务
网联的交易处理方式:同步请求,同步响应提现结果。对于同步未能正常处理的,7分钟内做出异步通知。
提现如果仅有单一的一个出金渠道肯定是不满足需求的,所以紧接着我们着手接入网联。
提现流程仍和之前定义的一致,稍有不同的是,因网联将异步交易做成了同步响应,所以在提现正向请求时,少了处理异步回调的逻辑。但对于网联本身处理过程中,产生的一些处理中交易,网联仍会通过异步的形式来告知我们。所以处理异步回调。
由于网联的处理方式是同步响应出金结果,而在此过程中,对于账户不正确这类异常,也是能快速得知,不会产生退票(因为一旦有退票产生,会对上游也有影响。上游需要针对退票情况做出相应的资金调整)。所以网联上线后,我们将网联做为主要的出金渠道。
4.4 设计出金路由
目前出金系统具有两个出金渠道,这就涉及到出金渠道的选择及控制。
我们面临的需求
- 当出现渠道不可用的情况,比如银行维护,专线维护等,我们可以暂停或者关掉某一渠道。
- 对提现单笔超限额,月累计,日累计进行控制。
- 路由初期,无法预料之后出金路由的规则的变化及数量,希望能做到灵活,可扩展。
在路由实现过程中,相对难以控制的是对于累计金额的处理,主要面临以下几个问题:
- 一:数据计算的实时性
- 二:数据计算准确性
- 三:计算性能
- 四:实现复杂度
备选的两种方案
- 缓存加内存计算
- 数据库统计
对比两种方案后,最后选择了数据库统计,并应用了优化的方案。经验证,完全可以满足性能需要。
概要设计
我们抽象了规则及规则详情,存入数据库:
规则:主要包括执行的优先权重,判定条件。
规则详情:用于填充判定条件的具体内容。
在执行规则时,用规则详情去填充规则里的判定条件,通过规则执行引擎来执行并拿到结果。
这样以来,具备了较好的灵活性,能通过配置和添加数据库数据来扩展路由。在之后我们通过这一功能,快速了添加了渠道黑名单控制这一需求。
对于累计金额的处理:利用数据库中sql的sum函数进行统计,只不过统计近1小时的实时数据。1小时之前的数据,以天的粒度,启动一个定时任务每半小时计算一次并存入一张辅助表。所以sum运算时联表相加即可。
实验证明,可满足每1小时50W笔提现量。如果有更大的提现量,可以再缩短实时统计的数据范围,比如缩短至统计近10分钟的实时数据,加上每5分钟计算更新前10分钟的提现金额。
4.5 目前提现流程
五 后续的优化
面临一个相对陌生的领域,只能摸索前行,在没有经验指导的情况下,渠道的接入方式有所不同,加之面临紧张的上线周期安排,所以在有些设计细节回过头来想一下,可能并不很合适,还需投入精力做优化,但是,所有的改动及优化,前提还是只有一个:稳