简易版出款系统架构

2018-05-11 15:52:53 浏览数 (1)

参数:

出款请求号(批次号)

出款业务方

出款商户id

出款商户编号

出款商户的父商户id

出款商户的父商户编号

使用的出款产品----worktime nonworktime 工作时间出款,非工作时间出款

回调通知地址

出款明细 出款明细号

代码语言:javascript复制
出款金额

出款备注信息--传递给银行需要

手续费类型--出款人出/收款人出

收款人银行编码

收款人银行卡号

收款人银行省编码

收款人银行市编码

收款人银行支行编码

收款人姓名

收款人手机号

收款人邮箱

出款类型 秒到(几分钟到) 普通(2小时内到) 下一天(隔天到)

对字典进行空格去掉

字段非空验证

字段长度验证

商户id验证

父商户id验证

判断商户是否激活

判断商户账户是否存在,并且余额是否充足,与出款总金额是否充足

判断出款明细一共有多少笔,可以限制笔数

代码语言:javascript复制
明细的金额,不能小于0

验证出款明细订单号是否有重复的

明细字段非空校验

明细字段参数长度校验

验证出款明细中,是否有相同的收款人账号,收款金额(可以加个限制,超过几笔就认定为出款有问题)

自建风控,对明细拦截出款金额---对于某个商户在一定时间范围超过一定的出款次数,就拦截下来 
验证明细中传递的支行编码,总行编码,是否正确。调用公司的统一接口,或者本地有保存数据
判断明细的出款类型,如果是秒到的,则看其传递的银行编码,是否为我们支持的直连银行编码,不是的话,修改为普通出款,或者直接报错;

可以再商户维度,增加是否开通工作时间出款和非工作时间出款,判断当前时间是工作时间还是非工作日时间,如果两者都开通了,则不做校验;如果只开通了其中一者,则去判断当前时间是工作时间还是非工作时间;

对于不同的银行,校验不同的备注信息,每个银行的备注信息,要求的最大长度不同;超过了就截取;其实,没准银行内部就会自动截取。

判断对公直连银行和对私直连银行,如果传递的是非直连银行,则需要去支行,省编码市编码进行校验,必填;

因为客户需要知道每一笔明细的校验结果,所以在给客户返回的过程,需要告知每笔明细的处理情况,成功或者失败; 对于失败的,需要告知明确的错误信息;

判断商户是否开通了限额设置,是否超过了出款的限额; 判断商户是否开通了对私对公出款,可以对其出款做限额;怎么做判断---验证收款人的名字,是否包含公司。研究所、等等之类字段;

校验结束后,需要实际的入库操作了,我们对于校验通过的数据,进行入库保存,后续在异步进行计费,扣账,打款操作;

这样,客户可以再后续的流程中,观察到这笔打款是否成功,状态是如何了,利于客户的查询操作。。如果没入库的记录,则会直接返回错误信息;

我们还可以基于这些数据,做到账时间的可视化展示,类似于微信提现那种;

以上为基础字段的校验,现在来做 订单唯一性的校验;

对于出款请求号 请求方的唯一性验证,类似于订单表的校验;

订单明细表的校验:业务方 订单明细号 请求批次号 做唯一性校验;

有的系统,有数据库插入的限制,所以需要对明细进行拆分插入,一次插入1000笔或者2000笔不等,如果唯一性索引校验住的话,就将该记录返回响应错误码,告知用户。

没有唯一性错误的就直接保存。

对于出款明细来说,有一点值得注意的是,有的银行有出款限额设置,但是客户方面很大可能会超过此限额,我们需要对明细进行拆分,而拆分的依据就是该笔出款明细收款人银行的金额限制;

由于拆分后,订单明细号重复,所以对于整体数据库设计来说,我们可以增加订单拆分明细表,将拆分后的订单保存到数据库中;

还需要对每一笔订单 设置超时时间,如果超过某段时间还是初始化状态,则进行自动撤销操作,可以视为无效订单;

对于商户来说,主流程结束,后续异步操作。。。同步返回商户。

对于有错误信息的明细来说,均是没有保存入库的,没有错误信息的明细,都是已经保存入库的。。可以通过接口啊,页面啊来查询到。

商户出款请求表;用户展示查询

商户出款请求明细表;用户展示查询

商户出款请求明细拆分表;内部逻辑计算

由于后续步骤都是异步来完成,所以异步时候,有可能需要加上redis锁来处理,怕的就是某一天机器挂了,大面的订单延误b,而定时捞取的频率也很快,前一笔的定时还未处理完成,后一批的定时又开始执行了;

这样,可能会出现重复调用扣账,重复打款的情况发生。

异步: 计算每一笔出款明细的手续费,手续费的配置可以是自己系统来设计,也可以交给单独的计费中心来处理。

需要注意的是,每一笔出款因为时间的不同,而手续费计费的费率也不同。

当商户用秒到出款时,我们就会多收手续费,例如:可以收取基本手续费 秒到手续费 额外手续费(工作时间/非工作时间) 垫资手续费(使用到垫资,则有垫资手续费);

商户来决定是 自己出还是收款人来出手续费;

商户在入网后,还有配置一个手续费收取类型-----实时收取、后收、预付实扣 当是商户自己来承担手续费时,则会出现--实时收取、后收、预付实扣三种情况。

实时收取----收款收到的钱=商户出款的钱-手续费的钱;例如:收款人收取100元,那么商户传递105元过来,手续费为5元。

后收----收款收到的钱=商户出款的钱;

预付实扣----收款收到的钱=商户出款的钱;

当由收款人承担时候,则只会有实时收取的情况。收款收到的钱=商户出款的钱-手续费的钱。例如:收款人应该收到100元,手续费自己承担2元,那么商户传递的是100元过来

理财类app提现: 用户(收款人)承担:提现100元,实际得到98元。 商户(出款人)承担:提现100元,实际得到100元。

手续费计算完成后,把手续费具体金额保存到出款明细中,供后续商户查询,以及扣费操作。

会调用风控系统,风控对出款明细进行校验。。具体规则有待了解;

之后,我们会根据工作时间还是非工作时间,来进行判断是否需要进行垫资逻辑。

如果是非工作时间,则需要进行垫资处理,来调用垫资系统,对系统中的垫资方额度进行校验,看垫资方此时的额度是否充足,是否足够;额度足够,才可以进行流转。

扣减可用打款余额,此概念后续再看怎么加进来。

后续,我们就需要调用账务系统,让账务系统扣减商户的账户余额,并且完成实际的扣手续费操作,并调用打款中心,银行通道进行实际的出款。

实际调用账务系统前,可以增加一个小配置,如果是测试商编,则直接标记为调用账务成功,而不会实际的去进行扣账操作。

调用账务系统,需要设置本方出款系统的回调地址,好让账务再处理完成后,回调我们实际出款结果; 一般来说,账务系统都有一系列交易码,交易相关字段,所以出款这边可能需要根据不同的业务方来传递不同的值;

给账务传递出款的金额和手续费:

收款人承担:则实际请求账务出款的金额 = 请求出款金额-手续费金额

出款人承担:则实际请求账务出款的金额 = 请求出款金额,,手续费直接扣减商户在第三方支付公司所在的账户余额;

非秒到情况下: 如果出款类型为下一天,则设置预约打款时间 为下一天的0点;

如果出款类型为普通,则设置预约打款时间当前时间的2小时后;

关于手续费的扣减,可以完全交给下层账务系统来操作,即容易保证事务的一致性,也可以统一进行余额的管理;而上层业务方只需要将扣减的金额,以及谁来出手续费告诉账务就行;

而之前调用计费的逻辑,如果是单独的系统,例如计费中心来完成的,那么在我们调动账务时候,账务如果不信任上层业务方,则可以去计费中心反查一次,比较数据即可保证金额的准备性;

之后实际的调用账务系统,账务系统回进行余额的扣减操作,如果有失败,账务系统建议做成幂等操作,可以重复请求,但只会进行一次出款。

如果,是余额不足的失败,那么需要将此笔订单进行挂起操作,等待商户对余额进行充值。

如果是别的失败,则需要对出款的订单进行撤销,修改订单状态,并通知商户,告诉此笔订单已经撤销;

接下来,需要进行一系列回滚操作:增加可用打款余额,如果是商户承担手续费的话 增加金额 = 出款金额 手续费金额

回滚垫资额度,银行额度等;

调用完成后,更新订单状态,更新打款状态;

等待下层业务方回调操作;

回调中,可以参考的参数有:打款状态 打款时间 系统间唯一流水号 实际使用的打款通道(调用账务时候可以传递我方规定的打款通道)

账务回调后,如果为打款成功:

账务回调后,如果为打款失败(打款撤销):

失败:一般是用户信息在银行端报错 打款中心透传返回失败状态和信息。也有银行系统故障返回失败。 撤销:订单到了打款中心 用户发现出款有误需要撤销。只要打款中心还没有请求银行打款就能撤销。(只有打款中心手动操作,我们无权处理)

与调用账务时,失败的时候类似,当打款失败时候,我们需要进行退款操作,其实退款操作就是对商户当时扣减的账户余额进行调增处理,手续费和出款的金额都要给用户增加回来;

但是,在实际操作中,我们也可以对商户的属性进行设置,例如退款不返回手续费;

如果出款和打款失败发生在同一天,则需要进行银行额度的调整(垫资情况下);

实际上,退款的操作,还可以交给退款中心来完成,服务化更加明确,职责更加单一,其实也是操作账户的逻辑;

后续,在进行通知商户操作,告知商户一笔打款的处理结果。

思考: 在商户维度下,增加一些配置,更加细粒度的控住出款的金额时间,收取手续费的大小;

逻辑补偿: 调用账务处理 通知商户处理 调用退款处理

0 人点赞