文章目录
- 前言
- 一、背景
- 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(失败情况下)
总结
本篇主要简略分析了银行卡快捷支付的整体流程,以及在发送短信环节出现问题,如何不中断的将支付方式进行转换,以提高用户支付体验以及成功率。下篇再继续分析,短信确认环节执行流程。