本支付系统提供企业支付与个人支付的单通道支付和混合支付方案。

二、模块结构

1. 总体架构设计

模块划分,从上至下,各层通信。

 
技术图片

2. 分层意义

为了项目的可扩展性和可维护性。进一步的追求高可用、高并发的支持。

3. 分层说明

  1. 终端显示层,包括各个终端的收银台页面,审批页面,审批短信等。开放接口层,为直接暴露给外部的 https口,与 service 层直接通信,主要用于与外部系统集成服务。
  2. Web 层,用于处理对显示层的数据渲染,对顶层操作的转发控制。这层的原则是”轻”,避免逻辑处理,可以允许少量的校验,与 service 层保持一对一关系。
  3. Service 层,负责对支付流程的抽象,支付安全的前置校验,支付过程的处理等。
  4. Manager 层,可以对 Service 层通用的逻辑的下沉,多个 Dao 层仓库的组合。
  5. Proxy层,通过代理层引用外部接口,避免外部接口的变化影响主流程。代理层可以直接与 Service 或 Manager层通信。
  6. Dao 层,原则上不进行逻辑处理,与数据库表一一对应。
  7. 在实现一个新功能时,应充分考虑代码在项目中结构位置。

三、表结构设计

1. 支付计划

 
技术图片
  • 一个支付计划由一个或多个业务订单组成
  • 一次成功的支付可以有一个或多个支付行项目
  • 支付计划不支持修改。订单的二次支付,重新生成一个新的支付计划

2. 退款计划

 
技术图片
  • 一个业务系统的退单对应一个退款计划
  • 退单退款时,根据正向订单来校验订单是否有可退的金额
  • 一个退款计划可以有一个或多个退款行项目
  • 退款任务分为 待客服审批 或 待确认定时任务 的退款任务两种

3. 企业账户

技术图片
  • 企业账户的 可用额度为 授信额度 +企业余额
  • 支付计划的支付行项目与企业账户的交易流水一一对应

4. 个人账户与现付

 
技术图片
  • 个人账户的金额来源于现付(支付宝+微信)的金额
  • 个人账户 与 个人账户行项目为 1对多关系
  • 个人账户在途物质为订单使用个人账户的临时金额记录

四、支付

1. 收银台时序图

 
技术图片

 

2. 现付流程图

 
技术图片
  • 现付付款在整个支付系统的流向图
  • 业务系统出票失败或者拒单退款后,客户现付付款的钱,转入个人余额,记录一笔个人账户行项目
  • 客户使用个人账户付款,扣去个人余额,记录在途物资
  • 个人账户退款,增加个人账户余额,更新账户行项目
  • 定时任务,把个人账户行项目对应的金额,原路返回

3. 构建支付对象

  • 订单是否能支付
    • 操作人是否合法
    • 因公与因私订单不能合并支付
    • 二次支付的订单不能与非二次支付的订单一起支付
  • 是否显示个人账户,个人账户金额大于0则显示
  • 是否显示公司账户
    • 管控规则不允许使用企业支付
    • 违规信息不允许使用企业支付
    • 差旅类型不唯一不允许使用企业支付
    • 有出租车订单收银台不显示企业支付
    • 因私订单,不显示企业支付
    • 成本中心不唯一,不显示企业支付
    • 企业账户不可用,不能使用企业账户支付
  • 是否显示 公司账户支付 - 全额支付
    • 选择了差标自付的违规原因,则不能显示公司账户支付-全额支付,其他情况显示
  • 是否显示 公司账户支付 - 差标支付
    • 乘客管控规则中有能使用差标支付的,且有订单金额大于差标金额的订单,则显示。其他情况不显示
  • 企业支付相关校验点
    • 是否有能使用的沿用授权订单
    • 此次支付是否需要审批
    • 此次支付是否需要确认差旅事由
    • 此吃支付是否需要确认输入密码

4. 支付系统关键词

  1. 二次支付

    业务系统订单可以在支付系统发起多笔成功的支付,故订单和支付计划为 1 对 多关系。

  2. 沿用授权

    授权过的订单在出票失败后,支付系统会生成一笔待沿用授权的订单授权记录,待客户重新下单过后,系统校验两笔订单金额差异,日期差异,地点差异。如果符合条件,新订单则可以使用老订单的授权。

  3. 超标自付

    超标自付,既差标内的金额使用公司账户支付,超出差标的金额,使用个人支付。

  4. 支付审批

    公司支付订单,可能需要审批,外部系统(工作流/集成)决定支付下一步状态。

  5. 支付集成

    集成支付审批工作流,推送支付信息给外部系统。

5. 支付底层实现

(1) 操作器uml
 
技术图片
image-20190121174345958
  • Operation 操作器,定义一个操作器的基本 init(), execute(), rollback() 方法。
  • PayOperation 支付操作器,继承自Operation,添加支付操作的相关行为,prePay(), doPay(), postPay()。

  • OperationExecutor 操作执行器工具类,用来运行操作器的相关方法。

  • AbstractPay,抽象支付操作类,实现PayOperation,实现基本的操作器的相关行为,例如:支付初始化,初始化日志锁,异常账户回滚实现等。

  • MixedPayOperation 真正支付操作实现类,继承 AbstractPay,实现 PayOperation的 prePay ,doPay,postPay 方法。

(2) 支付过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 支付操作
*
* @Author: Jie Ming Chen
* @Date: 2018/11/21
* @Version 1.0
*/
public interface PayOperation<T extends OperationParam> extends Operation<T, PaymentResult> {
/**
* 支付前处理
* 1.校验支付信息
* 2.添加支付计划
*
* @param paymentInfo
* @return
*/
void prePay(T paymentInfo);

/**
* 执行真正支付处理
*
* @param paymentInfo
* @return
*/
PayHandlerResult doPay(T paymentInfo);

/**
* 支付后完成
* 1.更新支付计划
* 2.回调订单信息
* 3.推送支付信息给外部系统
*
* @param paymentInfo
* @param doPayResult
* @return
*/
PaymentResult postPay(T paymentInfo, PayHandlerResult doPayResult);
}
(3) 实现操作器示例
  • 实现一个支付操作类 TestPayOperation,继承抽象支付类(AbstractPay),实现 prePay ,doPay,postPay方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestPayOperation extends AbstractPay<PaymentInfo>{

@Override
public void prePay(PaymentInfo paymentInfo) {
// 支付前置
}

@Override
public PayHandlerResult doPay(PaymentInfo paymentInfo) {
// 实现支付
return null;
}

@Override
public PaymentResult postPay(PaymentInfo paymentInfo, PayHandlerResult doPayResult) {
// 支付后处理
return null;
}
}
  • 通过操作执行器(OperationExecutor)调用 TestPayOperation:
1
2
3
4
5
6
7
8
9
10
@Resource(name = "testPayOperation")
private PayOperation<PaymentInfo> payOperation;

public PaymentResult pay(PaymentInfo paymentInfo) {
try {
return new OperationExecutor<PaymentInfo, PaymentResult>(payOperation).execute(paymentInfo);
} catch(Exception e) {
return new PaymentResult("1", msg);
}
}
(4) 数据加密

#####(5) redis分布式锁

#####(6) mysql 乐观锁

五、退款

1. 退款底层实现

#####(1) 退款过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public abstract class AbstractRefund{

/**
* 退款前处理
* 1.校验退款信息
* 2.添加退款计划
* 3.构建退款对象
*
* @param refundInfo
* @return
*/
abstract void preRefund(MixedRefundInfo refundInfo);

/**
* 执行真正退款处理
* 1.各通道执行真正退款操作
*
* @param refundInfo
* @return
*/
abstract RefundResult doRefund(MixedRefundInfo refundInfo);

/**
* 退款后完成
* 1.更新退款计划
* 2.回调订单信息
*
* @param refundInfo
* @param refundResult
* @return
*/
abstract RefundResult postRefund(MixedRefundInfo refundInfo, RefundResult refundResult);
}