文章目录
- 前言
- 一、背景
- 1. 应用架构
- 2. 分层支撑机制
 
- 二、银行卡快捷支付
- 1. 用户操作流程
- 2. 系统执行流程--正常
- 2.1 发送短信
- 2.2 短信确认
 
- 3. 系统执行流程--异常
- 3.1 异常环节
- 3.1.1 路由失败
- 3.1.2 调用支付渠道失败
 
- 3.2 异常处理
- 3.2.1 路由失败
- 3.2.2 调用支付渠道失败
 
 
- 4. 流程解析
- 4.1 核心代码实现
- 4.1.1 Service实现
- 4.1.2 类图
- 4.1.3 快捷支付发送领域模型:QuickPayDomain
- 4.1.4 主动支付领域模型:AbstractActivePayDomain
- 4.1.5 聚合基类:AggregateBase
- 4.1.6 标记领域模型:Domain
- 4.1.7 快捷支付预支付命令链:CardQuickPaySendCommandChain
- 4.1.8 快捷支付预支付指令:XxxCommand
 
- 4.2 涉及表结构
 
 
- 总结
前言
本篇将讲解银行卡快捷支付的实现,正常银行卡快捷支付流程过于简单,没什么好讲的,本篇将讲解如何在银行卡快捷失败后,可以顺滑的不中断内部进行支付方式转换(快捷–>代扣),以提高用户体验和支付成功率,如果觉得失败就失败,没必要想办法提高用户体验和支付成功率,那本篇请忽略,因为在你眼里这些设计都是没必要的。
同时本篇设计实现也可以当做将代扣包装为快捷对外提供。
本篇先讲解银行卡快捷发送短信环节流程实现。
一种实现,仅做参考。
一、背景
1. 应用架构

2. 分层支撑机制

 本篇主要是讲解在基于paygw系统(见《渠道网关设计》)提供原子化的支付接口的基础上paycore系统进行产品的封装,如对银行卡快捷支付进行包装,在银行卡快捷支付环节失败后不中断平滑的转换为代扣。
二、银行卡快捷支付
1. 用户操作流程

用户在商城选购商品后,点击支付,进入到收银台页面,展示支付方式,选择银行卡支付,进入已绑定银行卡列表页面,选择已绑定银行卡,点击立即支付,弹出密码框/短信验证码框,本篇将讲解我们点击获取验证码--->输入验证码完成支付这个流程设计,主要是讲解内部支付方式的转换设计,如果有更好的设计实现方案,欢迎交流。
2. 系统执行流程–正常
2.1 发送短信

2.2 短信确认

 如上两步为正常银行卡快捷支付流程部分系统的交互流程图。
3. 系统执行流程–异常
首先我们看下在发短信流程中的异常环节:
3.1 异常环节

3.1.1 路由失败
首先是路由失败,失败原因比较多,在路由的时候需要判断限额之类的,如果支付额度超限制等,或者压根就没有接入支持此银行卡快捷支付对应的支付渠道。那么支付路由返回无可用的支付渠道,即没有能够支持此快捷支付的支付渠道。
3.1.2 调用支付渠道失败
网络异常导致的调用支付渠道发送短信失败,或者支付渠道内部处理失败,由于是无资金变动类请求,可以当做失败处理。
3.2 异常处理
3.2.1 路由失败
路由返回无可用通道,则将Detail单状态置为失败,当做渠道发短信失败。
3.2.2 调用支付渠道失败
渠道发短信失败,将Detail单状态置为失败,生成交易类型为“代扣支付”的Detail,将Order交易类型也置为“代扣支付”,并指定短信通道为“短信中心”再次请求路由系统 ,获取到通道信息。如果获取成功,请求支付网关通过短信中心完成短信发送。
4. 流程解析
总体执行流程如下:
 
 部分代码实现见下:
4.1 核心代码实现
4.1.1 Service实现
/**
 * 银行卡快捷-预支付(发送短信)
 */
@Override
public PayCoreResult<BaseResultVo> prePaySend(final QuickPaySendRequest request) {
	return serviceTemplate.execute(new ServiceCallback<AggregateBase, PayCoreResult>() {
		@Override
		public AbstractActivePayDomain beforeProcess() throws PayCoreException {
			//参数校验
			request.check();
			//创建领域模型
			return quickPayDomainFactory.createDomain(request);
		}
		@Override
		public void execute(AggregateBase domain) throws PayCoreException {
			//业务流程处理
			QuickPayDomain quickPayDomain = (QuickPayDomain) domain;
			quickPaySendCommandChain.execute(quickPayDomain);
		}
		@Override
		public PayCoreResult resultProcess(AggregateBase domain) {
			//返回结果处理
			ActivePayOrder activePayOrder = ((AbstractActivePayDomain) domain).getActivePayOrder();
			String bizOrderNo = StringUtils.isNotBlank(activePayOrder.getBizOrderNo()) ? activePayOrder.getBizOrderNo() : activePayOrder.getOutBizNo();
			return new PayCoreResult(new BaseResultVo(
					bizOrderNo,
					activePayOrder.getPayOrderNo(),
					activePayOrder.getOutBizNo(),
					activePayOrder.getBizStatus(),
					activePayOrder.getRespCode(),
					activePayOrder.getRespMsg()));
		}
	});
}
4.1.2 类图

4.1.3 快捷支付发送领域模型:QuickPayDomain
/**
 * @author kkk
 * @description 快捷支付发送领域模型
 */
public class QuickPayDomain extends AbstractActivePayDomain implements RouteDomain<ActivePayOrder, CardPayDetail, QuickPaySendRequestBody> {
    /** 状态*/
    private String verifyStatus;
    /**  银行卡子单*/
    protected CardPayOrder cardPayOrder;
    /**  银行卡支付明细*/
    protected CardPayDetail cardPayDetail;
    
    //... ...
}
4.1.4 主动支付领域模型:AbstractActivePayDomain
/**
 * @author kkk
 * @description 主动支付领域模型
 */
public abstract class AbstractActivePayDomain extends AggregateBase{
    /**  主单*/
    protected ActivePayOrder activePayOrder;
    /**  账务明细*/
    protected CcsDetail cssDetail;
    /**  优惠券子单*/
    protected CouponPayOrder couponPayOrder;
    /**  优惠券明细*/
    protected CouponPayDetail couponPayDetail;
    
    //... ...
 }
4.1.5 聚合基类:AggregateBase
/**
 * @author kkk
 * @description 聚合基类
 */
public abstract class AggregateBase implements Domain {
    /** 交易类型 */
    protected String transType;
    /** 是否移除幂等性校验key */
    private boolean removeUnique = false;
    /** 是否外部生成渠道请求流水标识 */
    private boolean outInstReq = false;
    /**
     * 聚合自我检查
     */
    public abstract void selfCheck();
    /**
     * 获取唯一性判断标识
     */
    public abstract String getUniqueKey();
    /**
     * 获取业务订单号
     */
    public abstract String getBizOrderNo();
    /**
     * 是否需要异常幂等性校验缓存key
     */
    public boolean isRemoveUniqueK(){
        return removeUnique;
    }
    /**
     * 是否需要异步MQ通知
     */
    public abstract boolean isNeedNotify();
    
    //... ...
}  
4.1.6 标记领域模型:Domain
/**
 * @author kkk
 * @description 语法堂,标记领域模型
 */
public interface Domain {
}
4.1.7 快捷支付预支付命令链:CardQuickPaySendCommandChain
/**
 * @author kkk
 * @description 快捷支付预支付命令链
 */
@Component
public class CardQuickPaySendCommandChain extends CommandChain {
    /**
     * 构造函数将具体命令加入命令顺序列表
     */
    @Autowired
    public CardQuickPaySendCommandChain(UniqueCheckCommand uniqueCheckCommand,
                                        RepeatPayCheckCommand repeatPayCheckCommand,
                                        CardQuickPaySaveCommand cardQuickPaySaveCommand,
                                        CardQuickPaySendCommand cardQuickPaySendCommand,
                                        QuickPaySmsCommand quickPaySmsCommand){
        // 1.添加幂等性验证指令
        addCommand(uniqueCheckCommand);
        // 2.订单重复支付校验
        addCommand(repeatPayCheckCommand);
        // 3.添加保存数据库命令(收单)
        addCommand(cardQuickPaySaveCommand);
        // 4.添加快捷支付发送指令
        addCommand(cardQuickPaySendCommand);
        // 渠道发短信失败,指定sms发短信
        addCommand(quickPaySmsCommand);
    }
}
4.1.8 快捷支付预支付指令:XxxCommand
略
4.2 涉及表结构
涉及到的表结构核心字段如下:

发送短信环节只涉及到active_pay_order、card_pay_order、card_pay_detail、coupon_pay_order(使用优惠券)
 、ccs_detail(失败情况下)
总结
本篇主要简略分析了银行卡快捷支付的整体流程,以及在发送短信环节出现问题,如何不中断的将支付方式进行转换,以提高用户支付体验以及成功率。下篇再继续分析,短信确认环节执行流程。


















