支付系统设计:轮询扣款设计一

文章目录

  • 前言
  • 一、背景
  • 二、PayCore系统设计
    • 1.应用架构
    • 2.相关表
      • 2.1 Order表
      • 2.2 Detail表
    • 3. 原代扣流程
  • 三、轮询扣款改造
    • 1. 改造后的整体流程概览
    • 2. 改造点梳理
      • 2.1 支付核心系统改造点
      • 2.2 支付路由系统改造点
    • 3. 执行流程分析
      • 3.1 第一种情况
      • 3.2 第二种情况
      • 3.3 第三种情况
  • 总结


前言

支付系统为了能够满足业务方多场景支付需求而对接了大量的支付渠道,特别是做消费金融的面对的更多的业务场景是放款、回款,即对应代发、代扣,为了能顺利放款、回款所以支付系统对接了大量的支付机构。每一笔交易都有一到多个支付渠道能够支持,这也是支付系统对接支付渠道的意义,在能保证顺利完成交易的同时,也要考虑成本,筛选最优支付渠道即是支付路由系统所负责的。

然而很多公司的支付系统是存在不足的,也可以说是缺陷,即业务系统发起一笔交易,支付系统并没有尽力去完成这笔交易。如一笔代扣交易,支付系统在请求支付路由系统返回的最优支付渠道失败后就直接把这笔交易置为失败了,而没有再去请求次最优支付渠道,如果支付系统仅仅作为上游系统的一个支付渠道,上游系统可能在失败后就去请求其他公司的支付渠道了,此场景还好。但是如果作为上游系统唯一的支付渠道,失败后重试还是依旧返回原来的支付渠道,那么可能会由于自己支付系统缺陷导致客户逾期,即支付系统对接的多个支付渠道并没有使用上!

本篇主题将设计实现轮询扣款,主要是针对代扣类交易,即消费金融公司的回款,如果走一个支付渠道扣款失败了,对于一些非特定(余额不足等)失败原因则使用其他支付渠道进行重试,最大程度的完成交易。一方面为公司创造利益,另一方面避免客户逾期。


一、背景

轮询扣款,被很多人认为是非常鸡肋的功能,然而确实如此么?只要你的支付系统做的很完美很健壮那其他辅助性功能确实是很鸡肋的。为什么需要轮询扣款,还不是因为支付系统“误判”了,支付路由系统所认为的最优支付渠道却不能完成此交易。

究其误判原因有哪些?最常见的是系统bug、配置错误、信息不同步等,如支付渠道限额配置错误,配置限额超过实际渠道支持的限额,交易发送到支付渠道返回限额超限;或者系统在计算累计日、月限额时候不准确,实际已交易限额已经超过渠道累计限额限制;或者支付渠道侧对某一银行进行维护,不支持某银行的交易,但是支付系统并没有将渠道维护信息进行同步,所以导致某一银行的交易还是发送到此支付渠道。

可见支付问题原因还是很多,为了使自己的支付系统刚健壮可见轮询还是很重要的,可以在一定程度上避免如上问题。

轮询扣款算是支付系统中比较复杂的一块,不论是系统设计上还是落地到编码上,要求还是很高的,本文将讲解从不支持轮询到支持轮询的一个改造过程
每个公司系统由于技术差异所实现方案也不尽相同,所以仅供参考吧。

背景系统调用图如下:
支付系统设计:轮询扣款设计一_第1张图片
PayOpen作为接入系统,主要进行一些安全校验,支付工具的校验工作;
PayCore支付核心系统;
PayRouter支付路由系统;
PayGw支付网关系统,负责与支付渠道的交互。

所要实现的轮询逻辑在PayCore应用。

二、PayCore系统设计

1.应用架构

首先看下支付核心系统的设计:

支付系统设计:轮询扣款设计一_第2张图片
从上图中可以看到整个系统的设计扩展性很强的,为改造提供了可能性。

/**
 * @author Kkk
 * @Description: 单笔代扣处理命令链
 */
@Component
public class DeductReceiptCommandChain extends CommandChain {
    @Autowired
    public DeductReceiptCommandChain(UniqueCheckCommand uniqueCheckCommand,
                                     CardBinCheckCommand cardBinCheckCommand,
                                     PaySaveCommand deductReceiptSaveCommand,
                                     PaySendGwCommand abstractSendGwCommand,
                                     DeductOrderUpdateCommand deductOrderSaveCommand){
        //添加幂等性验证指令
        addCommand(uniqueCheckCommand);

        //卡bin校验
        addCommand(cardBinCheckCommand);

        //添加保存数据库命令
        addCommand(deductReceiptSaveCommand);

        //发送单笔代扣指令
        addCommand(abstractSendGwCommand);

        //更新order状态,并通知业务系统
        addCommand(deductOrderSaveCommand);
    }
}

2.相关表

涉及到的两张表(订单表、详情表)如下:
改造前对应关系:Order:Detail=1:1

2.1 Order表

支付系统设计:轮询扣款设计一_第3张图片

2.2 Detail表

支付系统设计:轮询扣款设计一_第4张图片

3. 原代扣流程

如下最简单的一个代扣流程:
支付系统设计:轮询扣款设计一_第5张图片
原来系统处理代扣交易流程大概如下:

1.接收代扣请求并进行参数校验,通过校验后构建领域模型(关于什么事领域模型见后期分析DDD设计的讲解);
2.将Order信息落入保存;
3.请求支付路由系统获取最优支付渠道;
4.构建Detail信息并落表保存;
5.请求支付网关系统发送至支付渠道;
6.获取渠道响应结果:
6.1 如果为成功/失败则将Order/Detail表对应信息置为失败并返回业务方成功/失败;
6.2 如果为处理中则将Order/Detail表对应信息置为处理中并返回业务方处理中;
7.处理中交易等待定时任务发起查询或者支付渠道结果通知回调;

如上整个流程是再简单不过的代扣类交易流程了。

三、轮询扣款改造

1. 改造后的整体流程概览

支付系统设计:轮询扣款设计一_第6张图片

2. 改造点梳理

2.1 支付核心系统改造点

(1)新增一个公共可重试组件和业务逻辑隔离,该组件包含功能:

  • 重试次数
  • 每次重试等待时间
  • 最大交易时长(秒)
  • 失败继续
  • 失败退出
  • 超时退出
  • 重试可选渠道

(2)改动逻辑:

  1. 路由渠道并调网关发起交易逻辑独立开来,方便重试组件重复调用该段逻辑;
  2. 数据结构关系,order:detail=1:n,对应代码逻辑要做相应改造;
  3. Order表新增字段round_extend(用于存放轮询信息,JSONString格式存放)、failChannel(已失败渠道)、roundTime(轮询次数,默认0)、isComplete(是否轮询完成,默认0);
  4. 轮询返回的轮询渠道类型channelType、轮询渠道roundChannel在路由渠道时需传给payrouter进行路由决策;
  5. 路由轮询规则时,入参需要有respCode参数,产品在配置轮询规则条件时,可以根据错误码respCode进行配置条件;

(3)代扣逻辑:

  1. 同步代扣、异步代扣、对公代扣,第一次单扣失败时(同步返回paygw异步通知定时查询),调用payrouter查询轮询策略,如果配了轮询策略:
    • 同步代扣时,同步返回处理中,另起线程发起后续的轮询扣款;
    • 异步代扣时,直接主线程发起后续的轮询扣款;
  2. 代扣返回处理中,后续定时任务调网关查询扣款状态返回失败时,如果未查询过轮询规则,则查询轮询规则,如果存在轮询规则,可能情况如下:
    • 超过轮询最大交易时长—扣款失败;
    • 未超过最大轮询交易时长—同步发起轮询扣款;
  3. Paycore接收网关MQ消息告知扣款失败,如果未查询过轮询规则,则查询轮询规则,如果存在轮询规则,可能情况如下:
    • 超过轮询最大交易时长—扣款失败;
    • 未超过最大轮询交易时长—同步发起轮询扣款;

2.2 支付路由系统改造点

  1. 单笔路由时,新增已失败渠道failedChannel、渠道输出类型channelType、轮询渠道roundChannel入参解析,并用于路由规则过滤;
  2. 新增轮询规则路由功能
    • 新增轮询规则表round_rule
  3. 新增轮询规则后台管理功能

3. 执行流程分析

接下来开始分析各种细节问题了,因为结果通知和同步请求不知道是谁先到达paycore支付核心系统,也不知道同步响应的是处理中/成功/失败,所以要进行此块逻辑的分析,因为系统在发送请求之前会先使用行锁进行数据锁定,所以需要等待所释放后才能执行下面流程。

3.1 第一种情况

同步响应失败,回调也是失败。

  1. 如果是同步响应先到达,则将Detail表状态置为失败,并进入重试逻辑,后面回调(失败)再到达后,Detail已经是终态,所以流程终止。
  2. 如果是回调先到达,由于原Detail表此笔交易在发起请求支付渠道时候被行锁锁定,所以即使先到也只能等待状态,等到同步响应回来后释放锁之后才能进入下面流程处理。

支付系统设计:轮询扣款设计一_第7张图片

3.2 第二种情况

支付系统设计:轮询扣款设计一_第8张图片

3.3 第三种情况

支付系统设计:轮询扣款设计一_第9张图片


总结

拙技蒙斧正,不胜雀跃。

你可能感兴趣的:(支付系统设计,大数据,分布式)