课程安排
- 支付微服务的需求
- 阅读渠道管理相关的代码
- 理解分布式锁的应用
- 阅读支付宝扫码支付的代码
- 参考微信支付文档完成微信的扫码支付(实战)
- xxl-job的入门学习
- 读懂同步支付状态的两种方式
背景
完成了运费微服务的开发。开发三组所负责的支付微服务,目前支付微服务完成了支付宝和微信的对接,主要实现的功能有支付渠道的维护、扫码支付(微信称Native支付,支付宝称当面付)、退款等功能。目前,此功能已经完成了支付宝的对接,只是微信的对接还没有完成。
需求分析
整体流程

产品需求
【付款方式】判断寄付/到付交互
- 寄付→点击【取件】进入取件成功页面,点击左上方返回按钮返回待取件任务列表;点击【去收款】按钮进入扫码支付页面,此时用户有双向选择:
- 在用户端【待支付】页面进行支付
- 在快递员端【扫码支付】页面进行支付,可选择微信或支付宝进行支付,分别生成不同的收款码,用户进行扫码支付;
- 点击页面左上方返回按钮页面返回至上一页;
- 两种方式支付成功,均显示支付成功页面,点击【知道了】,返回任务列表首页
- 到付→点击【取件】按钮,进入取件成功页面,点击返回主页按钮进入任务列表主页
流程分析


以下为异步通知的方式

1 付款阶段给第三方支付平台提供notify_url
2 交易成功后更新交易单,各个微服务订单查询MQ以更新自己订单状态,从而保持支付微服务的独立。
3 快递员app轮询查询支付微服务已得到支付状态
代码结构

git拉取代码

IDEA 自动切换JDK编译版本

将依赖的父工程中的java编译配置改为11

数据库表结构

tradeing交易表:核心表
payChannel交易方式表:各种交易方式的相关配置
refundRecord退款记录表:一笔交易单可对应多笔退款,通过tradingOrderNo与交易表关联
阅读代码
参考视频,参考文档(day3支付微服务下)
支付渠道管理
数据库表
在数据库中可以扩展其他的支付方式,通过channelLabel
otherConfig属性:除去通用的属性,各个支付方式的定制化属性
enterpriseId:区分不同企业(查询数据库获得不同的支付方式配置)
Entity类
与数据库属性相同
mapper类
Service类
通过mybatis-plus方式定义
通过findByEnterpriseId(enterpriseId,channelLabel)获取对应企业支付方式的相关配置。使用redis缓存进行改进,减少查询数据库的时间
Controller类
扫码支付
数据库表
productOrderNo:订单号
tradingOrderNo:交易单号
tradingChannel:支付提交的企业,支付渠道
tradingState:交易单状态(待付款,付款中,等等)
tradingAmount:交易金额,decimal(22, 2),共有22个数字(包括小数点前后),保留两位小数
enterpriseId:企业id
qrCode:二维码数据
流程

幂等性:当前的请求因为网络抖动或其他原因发起了多个,为了避免重复操作,使用幂等性校验
工厂模式+反射+IOC容器:将Alipay,WXpay存储到容器当中,通过对应参数使用工厂类生成对应容器
NativePayServiceImpl(当面支付)
流程
0 controller返回DTO对象,再调用service中的下单接口(封装为trading对象再填充信息)
public NativePayResponseDTO createDownLineTrading(@RequestBody NativePayDTO nativePayDTO) {
TradingEntity tradingEntity = BeanUtil.toBean(nativePayDTO, TradingEntity.class);
TradingEntity trading = this.nativePayService.createDownLineTrading(tradingEntity);
return BeanUtil.toBean(trading, NativePayResponseDTO.class);
}
1 先对参数进行校验证(参数不为空,金额>0)
2 设置交易类型tradingType,是否有效enableFlag
3 使用redission对订单加分布式锁
3.1hash结构(大key锁的名称,小key客户端id,value加锁的次数)。
同一种业务用同一个大key,后加订单号以区分订单。小key记录谁加的锁。redis加锁可嵌套,加几次解几次。

一个交易加锁后,另外的交易想操作订单要获得锁(即等第一个交易释放锁后)
3.2加锁类型:公平锁(有先后顺序),非公平锁
3.3 使用tryLock方式获取锁(即有时间期限,超过时间未获得锁抛异常返回fasle)
3.4.unlock()解锁。固定在finally语句块中解锁,否则可能锁未解开。(finally 语句块保证无论在 try 块或 catch 块中发生了什么异常,都会执行其中的代码。也就是说,无论是否发生异常,锁都将被正确释放)
//对交易订单加锁
Long productOrderNo = tradingEntity.getProductOrderNo();
String key = TradingCacheConstant.CREATE_PAY + productOrderNo;
RLock lock = redissonClient.getFairLock(key);
4 幂等性处理(对是否重复交易判断和处理)
4.1 根据订单id查询数据库中是否有对应交易单
4.2 未查到交易单(第一次为null),使用雪花算法设置交易单id,在交易单中设置订单id,return。
4.3 查到关联交易单,根据交易单支付状态进行操作
4.3.1 交易单支付状态是已经【支付成功】或是【免单 - 不需要支付】,直接抛出异常。
4.3.2 支付状态是【付款中】,此时有两种情况
-
如果支付渠道相同(此前使用支付宝付款,本次也是使用支付宝付款),这种情况抛出异常
-
根据支付方式生成二维码后又更换支付方式,此时需要重新生成新交易单号,此时交易单号与id将不同。
如图状态后又更换支付方式
4.3.3 如果支付状态是【取消订单】或【挂账】,将id设置为原交易单id,交易号重新生成,这样做的目的是既保留了原交易单id,又可以生成新的交易号(不重新生成的话,没有办法在支付平台进行支付申请),与之前不会有影响。
5 调用不同的支付渠道进行处理,动态支付(工厂模式+注解+IOC容器)
原理:
1每一个支付都实现了一个nativepayhandler的接口
2每一个支付的实现都注册到ioc容器当中
3工厂模式实现:根据传入的Class handler,在Spring ioc容器中找到nativepayhandler实现类
4在不同的渠道实现类中,都指定了@PayChannel注解,通过type属性指定具体的平台(支付宝/微信),添加注解标识后,在HandlerFactory中就可以根据指定的参数获取对应的渠道类型。
//调用不同的支付渠道进行处理
PayChannelEnum payChannel = PayChannelEnum.valueOf(tradingEntity.getTradingChannel());
NativePayHandler nativePayHandler = HandlerFactory.get(payChannel, NativePayHandler.class);
nativePayHandler.createDownLineTrading(tradingEntity);
ioc容器存储所有的支付平台,通过注解找到对应的支付平台
NativePayHandler会有不同平台的实现,比如:支付宝、微信。NativePayHandler继承了PayChannelHandler,它有两个实现类(不同平台的实现,比如:支付宝、微信),分别是AliNativePayHandler,WechatNativePayHandler。每个平台的接口参数、返回值都不一样,所以是没有办法共用的,只要是每个平台都去编写一个实现类。
那问题来了,该如何选择呢? 在这里我们采用了工厂模式进行获取对应的NativePayHandler实例,在不同的渠

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



