出款请求号(批次号)
出款业务方
出款商户id
出款商户编号
出款商户的父商户id
出款商户的父商户编号
使用的出款产品----worktime nonworktime 工作时间出款,非工作时间出款
回调通知地址
出款明细 出款明细号
出款金额
出款备注信息--传递给银行需要
手续费类型--出款人出/收款人出
收款人银行编码
收款人银行卡号
收款人银行省编码
收款人银行市编码
收款人银行支行编码
收款人姓名
收款人手机号
收款人邮箱
出款类型 秒到(几分钟到) 普通(2小时内到) 下一天(隔天到)
对字典进行空格去掉
字段非空验证
字段长度验证
商户id验证
父商户id验证
判断商户是否激活
判断商户账户是否存在,并且余额是否充足,与出款总金额是否充足
判断出款明细一共有多少笔,可以限制笔数
明细的金额,不能小于0
验证出款明细订单号是否有重复的
明细字段非空校验
明细字段参数长度校验
验证出款明细中,是否有相同的收款人账号,收款金额(可以加个限制,超过几笔就认定为出款有问题)
自建风控,对明细拦截出款金额---对于某个商户在一定时间范围超过一定的出款次数,就拦截下来
验证明细中传递的支行编码,总行编码,是否正确。调用公司的统一接口,或者本地有保存数据
判断明细的出款类型,如果是秒到的,则看其传递的银行编码,是否为我们支持的直连银行编码,不是的话,修改为普通出款,或者直接报错;
可以再商户维度,增加是否开通工作时间出款和非工作时间出款,判断当前时间是工作时间还是非工作日时间,如果两者都开通了,则不做校验;如果只开通了其中一者,则去判断当前时间是工作时间还是非工作时间;
对于不同的银行,校验不同的备注信息,每个银行的备注信息,要求的最大长度不同;超过了就截取;其实,没准银行内部就会自动截取。
判断对公直连银行和对私直连银行,如果传递的是非直连银行,则需要去支行,省编码市编码进行校验,必填;
因为客户需要知道每一笔明细的校验结果,所以在给客户返回的过程,需要告知每笔明细的处理情况,成功或者失败; 对于失败的,需要告知明确的错误信息;
判断商户是否开通了限额设置,是否超过了出款的限额; 判断商户是否开通了对私对公出款,可以对其出款做限额;怎么做判断---验证收款人的名字,是否包含公司。研究所、等等之类字段;
校验结束后,需要实际的入库操作了,我们对于校验通过的数据,进行入库保存,后续在异步进行计费,扣账,打款操作;
这样,客户可以再后续的流程中,观察到这笔打款是否成功,状态是如何了,利于客户的查询操作。。如果没入库的记录,则会直接返回错误信息;
我们还可以基于这些数据,做到账时间的可视化展示,类似于微信提现那种;
以上为基础字段的校验,现在来做 订单唯一性的校验;
对于出款请求号+请求方的唯一性验证,类似于订单表的校验;
订单明细表的校验:业务方+订单明细号+请求批次号 做唯一性校验;
有的系统,有数据库插入的限制,所以需要对明细进行拆分插入,一次插入1000笔或者2000笔不等,如果唯一性索引校验住的话,就将该记录返回响应错误码,告知用户。
没有唯一性错误的就直接保存。
对于出款明细来说,有一点值得注意的是,有的银行有出款限额设置,但是客户方面很大可能会超过此限额,我们需要对明细进行拆分,而拆分的依据就是该笔出款明细收款人银行的金额限制;
由于拆分后,订单明细号重复,所以对于整体数据库设计来说,我们可以增加订单拆分明细表,将拆分后的订单保存到数据库中;
还需要对每一笔订单 设置超时时间,如果超过某段时间还是初始化状态,则进行自动撤销操作,可以视为无效订单;
对于商户来说,主流程结束,后续异步操作。。。同步返回商户。
对于有错误信息的明细来说,均是没有保存入库的,没有错误信息的明细,都是已经保存入库的。。可以通过接口啊,页面啊来查询到。
商户出款请求表;用户展示查询
商户出款请求明细表;用户展示查询
商户出款请求明细拆分表;内部逻辑计算
由于后续步骤都是异步来完成,所以异步时候,有可能需要加上redis锁来处理,怕的就是某一天机器挂了,大面的订单延误b,而定时捞取的频率也很快,前一笔的定时还未处理完成,后一批的定时又开始执行了;
这样,可能会出现重复调用扣账,重复打款的情况发生。
异步: 计算每一笔出款明细的手续费,手续费的配置可以是自己系统来设计,也可以交给单独的计费中心来处理。
需要注意的是,每一笔出款因为时间的不同,而手续费计费的费率也不同。
当商户用秒到出款时,我们就会多收手续费,例如:可以收取基本手续费+秒到手续费+额外手续费(工作时间/非工作时间)+垫资手续费(使用到垫资,则有垫资手续费);
商户来决定是 自己出还是收款人来出手续费;
商户在入网后,还有配置一个手续费收取类型-----实时收取、后收、预付实扣 当是商户自己来承担手续费时,则会出现--实时收取、后收、预付实扣三种情况。
实时收取----收款收到的钱=商户出款的钱-手续费的钱;例如:收款人收取100元,那么商户传递105元过来,手续费为5元。
后收----收款收到的钱=商户出款的钱;
预付实扣----收款收到的钱=商户出款的钱;
当由收款人承担时候,则只会有实时收取的情况。收款收到的钱=商户出款的钱-手续费的钱。例如:收款人应该收到100元,手续费自己承担2元,那么商户传递的是100元过来
理财类app提现: 用户(收款人)承担:提现100元,实际得到98元。 商户(出款人)承担:提现100元,实际得到100元。
手续费计算完成后,把手续费具体金额保存到出款明细中,供后续商户查询,以及扣费操作。
会调用风控系统,风控对出款明细进行校验。。具体规则有待了解;
之后,我们会根据工作时间还是非工作时间,来进行判断是否需要进行垫资逻辑。
如果是非工作时间,则需要进行垫资处理,来调用垫资系统,对系统中的垫资方额度进行校验,看垫资方此时的额度是否充足,是否足够;额度足够,才可以进行流转。
扣减可用打款余额,此概念后续再看怎么加进来。
后续,我们就需要调用账务系统,让账务系统扣减商户的账户余额,并且完成实际的扣手续费操作,并调用打款中心,银行通道进行实际的出款。
实际调用账务系统前,可以增加一个小配置,如果是测试商编,则直接标记为调用账务成功,而不会实际的去进行扣账操作。
调用账务系统,需要设置本方出款系统的回调地址,好让账务再处理完成后,回调我们实际出款结果; 一般来说,账务系统都有一系列交易码,交易相关字段,所以出款这边可能需要根据不同的业务方来传递不同的值;
给账务传递出款的金额和手续费:
收款人承担:则实际请求账务出款的金额 = 请求出款金额-手续费金额
出款人承担:则实际请求账务出款的金额 = 请求出款金额,,手续费直接扣减商户在第三方支付公司所在的账户余额;
非秒到情况下: 如果出款类型为下一天,则设置预约打款时间 为下一天的0点;
如果出款类型为普通,则设置预约打款时间当前时间的2小时后;
关于手续费的扣减,可以完全交给下层账务系统来操作,即容易保证事务的一致性,也可以统一进行余额的管理;而上层业务方只需要将扣减的金额,以及谁来出手续费告诉账务就行;
而之前调用计费的逻辑,如果是单独的系统,例如计费中心来完成的,那么在我们调动账务时候,账务如果不信任上层业务方,则可以去计费中心反查一次,比较数据即可保证金额的准备性;
之后实际的调用账务系统,账务系统回进行余额的扣减操作,如果有失败,账务系统建议做成幂等操作,可以重复请求,但只会进行一次出款。
如果,是余额不足的失败,那么需要将此笔订单进行挂起操作,等待商户对余额进行充值。
如果是别的失败,则需要对出款的订单进行撤销,修改订单状态,并通知商户,告诉此笔订单已经撤销;
接下来,需要进行一系列回滚操作:增加可用打款余额,如果是商户承担手续费的话 增加金额 = 出款金额+手续费金额
回滚垫资额度,银行额度等;
调用完成后,更新订单状态,更新打款状态;
等待下层业务方回调操作;
回调中,可以参考的参数有:打款状态 打款时间 系统间唯一流水号 实际使用的打款通道(调用账务时候可以传递我方规定的打款通道)
账务回调后,如果为打款成功:
账务回调后,如果为打款失败(打款撤销):
失败:一般是用户信息在银行端报错 打款中心透传返回失败状态和信息。也有银行系统故障返回失败。 撤销:订单到了打款中心 用户发现出款有误需要撤销。只要打款中心还没有请求银行打款就能撤销。(只有打款中心手动操作,我们无权处理)
与调用账务时,失败的时候类似,当打款失败时候,我们需要进行退款操作,其实退款操作就是对商户当时扣减的账户余额进行调增处理,手续费和出款的金额都要给用户增加回来;
但是,在实际操作中,我们也可以对商户的属性进行设置,例如退款不返回手续费;
如果出款和打款失败发生在同一天,则需要进行银行额度的调整(垫资情况下);
实际上,退款的操作,还可以交给退款中心来完成,服务化更加明确,职责更加单一,其实也是操作账户的逻辑;
后续,在进行通知商户操作,告知商户一笔打款的处理结果。
思考: 在商户维度下,增加一些配置,更加细粒度的控住出款的金额时间,收取手续费的大小;
逻辑补偿: 调用账务处理 通知商户处理 调用退款处理