1、微信发起Native支付付款
1. 流程图:
2. 在开发微信-发起Native支付前我们还是先开下UML类图的结构:
- NativePayFace: Native支付方式dubbo接口:商户生成二维码,用户扫描支付
- NativePayFaceImpl: Native支付方式dubbo接口实现
- NativePayAdapter:Native支付方式适配:商户生成二维码,用户扫描支付
- NativePayAdapterImpl: Native支付方式适配实现,对交易单加锁,防止支付并发多次生成,同时调用ShowApiService生成二维码图片
- RegisterBeanHandler: IOC容器工具,可以对NativePayHandler获得具体的实现类,例如这里获得实现类:aliNativePayHandler
- NativePayHandler:Native支付方式Handler,此类有多个实现
- WechatNativePayHandler: 微信Native支付方式Handler
- BeforePayHandler:支付前置校验,幂等性处理
- WechatpayConfig:微信核心配置
- WechatPayHttpClient: 微信httpclient
- Config: 微信配置信息
- Factory: 微信工厂方法
3. 开发步骤:
- 收银员发起收银:
- 根据订单表生成交易单(下图为交易单的作用)
- 发起支付请求(携带tradingVo交易单)
- 对订单加锁(对交易订单加锁防止并发,这里加的锁和前面不同,使用watchdog的机制,并且同一时刻只会有一个线程进来处理
==当然此处的并发不止是此接口,免单、挂账等接口也对其有并发情况需要注意==)
(适配器选择支付方式)每种支付取实现RegisterBeanHandler,使用getBean的方式,得到此次是微信支付。(从IOC容器中找到NativePayHandler实现,此处采用构建一个Map,启动时加载,构建map容器中具体实现,如果有新支付方式可以扩展)
getBean
put进俩个支付方式
启动扫描:map集合接受
- 如果交易单参数完整,那么进行交易幂等性判断
- 通过交易单中的订单编号,查询交易单信息
- 如果交易单的状态为已结算、免单:直接抛出重复支付异常
- 如果交易单的状态为付款中,直接抛出重复支付异常
- 如果交易单的状态为取消订单,挂账:创建交易号,对原交易单发起支付(延用之前交易单的id)
- 其他情况,直接交易失败
- 如果是首次支付 生成交易单号(雪花算法) 目的: 保证三方支付平台支付的幂等性
- 返回交易单信息
6.获取微信配置文件,如果为空,抛出异常
7.调用微信API面对面支付,设置交易单号 , 设置实付金额,进行统一下单处理
8.判断受理是否成功 , 如果受理成功 , 修改交易单信息
- 设置统一下单返回编码
- 设置统一下单返回信息
- 设置统一下单返回信息json【用于生产二维码、Android ios唤醒支付等】
- 修改交易单的状态为FKZ [付款中]
时序图:
查询支付的流程图:
实现流程:
- NativePayFace实现调用适配,实现查询三方返回的信息
- 根据支付渠道的名称,从IOC容器中获取具体的实现类bean对象
- adapter适配调用NativePayHandler具体实现,执行底层查询方法
- 交易前置处理 , 校验参数的完整性 , 如果参数不完整 , 那么直接抛出异常
- 如果参数完整,那么通过交易单中的商户id查询微信配置 , 如果为空, 那么抛出异常
- 通过Factory使用配置 , 调用微信API通过订单号查询支付情况
判断是否响应成功 , 受理成功 , 获取交易的状态
必须是发生实际付款后才可发起退款
- 退款金额不能超过实际支付金额
功能拆解:
发起退款
点击退款:
时序图:
退款流程图:
实现流程:
- 调用Native退款
- 调用适配器进行退款操作 , 对交易单进行加锁,判断加锁是否成功
- 加锁成功后 , 根据交易单的支付渠道得到具体的实现,使用NativePayHandler接收,底层实现退款操作
- 我们今天的支付类型为微信,所以进到WeChatpayNativePayHandler实现中
- 根据交易单利用雪花算法生成退款编号,将生成的退款编号设置进交易单对象中
- 退款前置处理,校验参数完整性,如果满足条件,那么再进行校验幂等性
- 校验幂等性:
- 查询交易单是否为已结算交易单
- 如果交易单不存在,或者不为已结算状态,抛出退款失败异常
- 否则设置订单号到交易单vo对象中,设置交易单id,设置交易金额
- 根据交易单中的订单号查询是否有退款中的退款记录
- 如果已经存在退款中的退款记录,那么抛出退款失败的异常
- 校验幂等性:
- 获得微信的配置文件,判断是否合法,如果不合法,抛出异常
- 发起退款请求,判断是否合法,如果合法,接收第三方返回的受理情况,判断是否成功
- 如果成功,指定该交易单为退款交易单,更新交易单信息
- 保存退款单信息
- 将退款单id设置为null
- 设置退款单号
- 设置本次退款金额
- 设置返回编码
- 设置返回信息
- 如果受理成功,将退款单的状态改为请求中,否则改为退款失败状态
- 将退款单保存到数据库中
- 返回最终交易单信息
核心:
- 对交易单进行加锁操作
- 根据交易单的支付渠道得到具体的实现(使用了spring工厂)
- 根据交易单使用雪花算法生成退款单号
- 保存退款单信息时,id设置为null,每次退款时都会生成一个新的退款单,不能重复
- 业务中是可以实现多次退款的
- 如果受理成功,将退款单的状态改为请求中,否则改为退款失败状态
4、微信-查询Native支付退款结果
退款成功受理之后,我们会对受理成功的退款进行退款结果处理,我们有2种方式处理Native支付结果:
1、在支付平台中配置回调地址,直接由三方推送消息
2、使用计划任务【xxl-job】,主动查询三方
退款查询状态流程图:
实现流程:
- native调用适配进行退款查询
- 根据退款的退款渠道在nativePayHandlers集合中查询具体的实现,根据查询到的实现类名,通过getBean( )方法得到具体的实现类对象
- 调用具体实现的查询退款结果的方法
- 退款前置处理 , 校验退款单的参数是否完整
- 不完整直接抛出查询统一下单交易失败的异常
- 满足条件,weChatpayConfig获取微信的配置 , 容器如果为空那么抛出微信配置为空异常
- 使用配置,调用微信queryRefund( )方法,进行退款查询
- 判断是否查询成功
- 成功, 查询退款的状态,如果状态为【微信退款返回状态】REFUND_SUCCESS:成功
- 那么修改退款单的信息
- 修改退款状态为成功 REFUND_SUCCESS
- 修改返回统一编码
- 修改返回统一信息
- 根据退款单id修改退款单数据
5、具体业务是哪里使用
支付:定时查询支付结果、定时查询退款结果
结果通知:有两种(推送,主动轮询)配合使用
2、具体使用
在任务调度中心中配置执行器(名称)、配置任务
路由策略是怎么配置,你是怎么思考?
阻塞处理策略,怎么思考?
定时任务怎么配置
JobHandler和项目代码@XXljob(“”)
失败重试次数=0(设置为0是因为对数据库的压力会非常大)
4.xxl-job
4.1 你们项目中是否使用到定时任务?
我们在项目中使用xxl-job查询支付结果,退款结果
4.2 为什么用?业务背景?
我们在支付服务中,有两种方式获得支付结果:
- 三方推送消息:一种是三方(支付宝,微信)把支付结果推送给我们设置的回调地址和用户,三方会以某种评率发送消息知道我们获取,但可能会因为网络或抖动,我们未能及时获取结果
- 主动轮询:鉴于以上原因,我们在项目中配合使用xxl-job发起请求,查询支付结果,考虑到三方限流,以及数据库的承受压力,我们设置评率为间隔10s.
4.3怎么用xxl-job具体配置?
如上图:
- 执行器:我们先设置一个model-trading-job-listen执行器(app_name与配置文件上的名称保持一致)
- 路由策略:我们采用的主动轮询,可以在指定间隔时间内重复执行查询结果
- 阻塞处理策略:单机穿行
- cron表达式:间隔10s,考虑三方限流,数据库更新压力大
- JobHandler:和项目中使用@XXLjob(“”)注解中的值保持一致
- 失败重试次数:设置为0,避免给数据库造成太大压力,同时也是考虑到三方限流
4.4路由策略为什么是轮询?你是怎么考虑的?
我们项目中为了及时获取支付和退款的结果,我们使用xxljob主动轮询的策略,间隔10s查询一次数据库状态为付款中(FKZ)的订单,可解决代码不能重复执行的问题,能及时更新订单,交易单状态;并且我们设置的间隔时间稍长,在一定程度上减少了访问数据库的压力
4.5如果执行任务失败了,业务考虑是否需要重试?
我们设置失败重试的次数为0,即任务执行失败不考虑重试
原因:
- 三方可能限流
- 支付或退款请求,重试后会给数据库造成太大的压力
- 我们主动轮询是配合三方推送消息的作用,就算我们主动轮询失败了,三方依然以某个频率发送消息,直到我们获取结果
4.6 任务满了怎么办?
当任务满了时,会触发阻塞处理策略,我们项目中采用的是单机串行,即任务会依次排序执行,每一个任务内容是一致的
10 . v2跟v3的区别是?
v3支付官方推荐使用:
1、参数和返回值基于JSON格式传输
2、公钥/私钥非对称加密方式
3、http协议 rest风格请求