1、微信发起Native支付付款

1. 流程图:

image.png

2. 在开发微信-发起Native支付前我们还是先开下UML类图的结构:

image.png

  1. NativePayFace: Native支付方式dubbo接口:商户生成二维码,用户扫描支付
  2. NativePayFaceImpl: Native支付方式dubbo接口实现
  3. NativePayAdapter:Native支付方式适配:商户生成二维码,用户扫描支付
  4. NativePayAdapterImpl: Native支付方式适配实现,对交易单加锁,防止支付并发多次生成,同时调用ShowApiService生成二维码图片
  5. RegisterBeanHandler: IOC容器工具,可以对NativePayHandler获得具体的实现类,例如这里获得实现类:aliNativePayHandler
  6. NativePayHandler:Native支付方式Handler,此类有多个实现
  7. WechatNativePayHandler: 微信Native支付方式Handler
  8. BeforePayHandler:支付前置校验,幂等性处理
  9. WechatpayConfig:微信核心配置
  10. WechatPayHttpClient: 微信httpclient
  11. Config: 微信配置信息
  12. Factory: 微信工厂方法

day11-微信支付 - 图3

3. 开发步骤:

  1. 收银员发起收银:
  2. 根据订单表生成交易单(下图为交易单的作用)

image.pngimage.png

  1. 发起支付请求(携带tradingVo交易单)
  2. 对订单加锁(对交易订单加锁防止并发,这里加的锁和前面不同,使用watchdog的机制,并且同一时刻只会有一个线程进来处理

==当然此处的并发不止是此接口,免单、挂账等接口也对其有并发情况需要注意==)
(适配器选择支付方式)每种支付取实现RegisterBeanHandler,使用getBean的方式,得到此次是微信支付。(从IOC容器中找到NativePayHandler实现,此处采用构建一个Map,启动时加载,构建map容器中具体实现,如果有新支付方式可以扩展)
getBean
image.png
put进俩个支付方式
image.png
启动扫描:map集合接受
image.png

  1. 如果交易单参数完整,那么进行交易幂等性判断
    1. 通过交易单中的订单编号,查询交易单信息
    2. 如果交易单的状态为已结算、免单:直接抛出重复支付异常
    3. 如果交易单的状态为付款中,直接抛出重复支付异常
    4. 如果交易单的状态为取消订单,挂账:创建交易号,对原交易单发起支付(延用之前交易单的id)
    5. 其他情况,直接交易失败
    6. 如果是首次支付 生成交易单号(雪花算法) 目的: 保证三方支付平台支付的幂等性
    7. 返回交易单信息

6.获取微信配置文件,如果为空,抛出异常
7.调用微信API面对面支付,设置交易单号 , 设置实付金额,进行统一下单处理
8.判断受理是否成功 , 如果受理成功 , 修改交易单信息

  1. 设置统一下单返回编码
  2. 设置统一下单返回信息
  3. 设置统一下单返回信息json【用于生产二维码、Android ios唤醒支付等】
  4. 修改交易单的状态为FKZ [付款中]
    1. 将交易单信息保存或更新到数据库中
    2. 返回交易单信息到adapter适配器,判断统一下单返回信息是否为空,为空则抛出异常
    3. 不为空 , 构建二维码 , 调用ShowApiService生成二维码链接,设置到交易单对象中
    4. 返回交易单信息给前端(交易单信息中包含二维码的链接)

      2、微信-查询Native支付付款结果

      支付成功受理后 , 我们会对受理成功的支付进行支付结果处理,我们有2种方式处理native支付结果:
      1、在支付平台中配置回调地址,直接由三方推送消息 - 不可靠
      2、使用计划任务【xxl-job】,主动查询三方

时序图:
day11-微信支付 - 图9
查询支付的流程图:
day11-微信支付 - 图10
实现流程:

  1. NativePayFace实现调用适配,实现查询三方返回的信息
  2. 根据支付渠道的名称,从IOC容器中获取具体的实现类bean对象
  3. adapter适配调用NativePayHandler具体实现,执行底层查询方法
  4. 交易前置处理 , 校验参数的完整性 , 如果参数不完整 , 那么直接抛出异常
  5. 如果参数完整,那么通过交易单中的商户id查询微信配置 , 如果为空, 那么抛出异常
  6. 通过Factory使用配置 , 调用微信API通过订单号查询支付情况
  7. 判断是否响应成功 , 受理成功 , 获取交易的状态

    1. 如果交易的状态为支付取消:TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款),将交易单的状态设置为取消订单
    2. 如果交易的状态为支付成功:TRADE_SUCCESS(交易支付成功)TRADE_FINISHED(交易结束,不可退款),将交易单的状态改为已结算
    3. 非最终状态不做处理当前交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)不处理

      3、微信-发起Native支付退款

      订单如果结账错误,我们是可以发起退款,注意:
  8. 必须是发生实际付款后才可发起退款

  9. 退款金额不能超过实际支付金额

功能拆解:
发起退款
day11-微信支付 - 图11
点击退款:
day11-微信支付 - 图12
时序图:
day11-微信支付 - 图13
退款流程图:
day11-微信支付 - 图14
实现流程:

  1. 调用Native退款
  2. 调用适配器进行退款操作 , 对交易单进行加锁,判断加锁是否成功
  3. 加锁成功后 , 根据交易单的支付渠道得到具体的实现,使用NativePayHandler接收,底层实现退款操作
  4. 我们今天的支付类型为微信,所以进到WeChatpayNativePayHandler实现中
  5. 根据交易单利用雪花算法生成退款编号,将生成的退款编号设置进交易单对象中
  6. 退款前置处理,校验参数完整性,如果满足条件,那么再进行校验幂等性
    1. 校验幂等性:
      1. 查询交易单是否为已结算交易单
      2. 如果交易单不存在,或者不为已结算状态,抛出退款失败异常
      3. 否则设置订单号到交易单vo对象中,设置交易单id,设置交易金额
      4. 根据交易单中的订单号查询是否有退款中的退款记录
      5. 如果已经存在退款中的退款记录,那么抛出退款失败的异常
  7. 获得微信的配置文件,判断是否合法,如果不合法,抛出异常
  8. 发起退款请求,判断是否合法,如果合法,接收第三方返回的受理情况,判断是否成功
  9. 如果成功,指定该交易单为退款交易单,更新交易单信息
  10. 保存退款单信息
    1. 将退款单id设置为null
    2. 设置退款单号
    3. 设置本次退款金额
    4. 设置返回编码
    5. 设置返回信息
  11. 如果受理成功,将退款单的状态改为请求中,否则改为退款失败状态
  12. 将退款单保存到数据库中
  13. 返回最终交易单信息

核心:

  1. 对交易单进行加锁操作
  2. 根据交易单的支付渠道得到具体的实现(使用了spring工厂)
  3. 根据交易单使用雪花算法生成退款单号
  4. 保存退款单信息时,id设置为null,每次退款时都会生成一个新的退款单,不能重复
  5. 业务中是可以实现多次退款的
  6. 如果受理成功,将退款单的状态改为请求中,否则改为退款失败状态

    4、微信-查询Native支付退款结果

    退款成功受理之后,我们会对受理成功的退款进行退款结果处理,我们有2种方式处理Native支付结果:
    1、在支付平台中配置回调地址,直接由三方推送消息
    2、使用计划任务【xxl-job】,主动查询三方

退款查询状态流程图:
day11-微信支付 - 图15
实现流程:

  1. native调用适配进行退款查询
  2. 根据退款的退款渠道在nativePayHandlers集合中查询具体的实现,根据查询到的实现类名,通过getBean( )方法得到具体的实现类对象
  3. 调用具体实现的查询退款结果的方法
  4. 退款前置处理 , 校验退款单的参数是否完整
  5. 不完整直接抛出查询统一下单交易失败的异常
  6. 满足条件,weChatpayConfig获取微信的配置 , 容器如果为空那么抛出微信配置为空异常
  7. 使用配置,调用微信queryRefund( )方法,进行退款查询
  8. 判断是否查询成功
  9. 成功, 查询退款的状态,如果状态为【微信退款返回状态】REFUND_SUCCESS:成功
  10. 那么修改退款单的信息
    1. 修改退款状态为成功 REFUND_SUCCESS
    2. 修改返回统一编码
    3. 修改返回统一信息
  11. 根据退款单id修改退款单数据

5、具体业务是哪里使用

支付:定时查询支付结果、定时查询退款结果
结果通知:有两种(推送,主动轮询)配合使用
2、具体使用
在任务调度中心中配置执行器(名称)、配置任务
路由策略是怎么配置,你是怎么思考?
阻塞处理策略,怎么思考?
定时任务怎么配置
JobHandler和项目代码@XXljob(“”)
失败重试次数=0(设置为0是因为对数据库的压力会非常大)

4.xxl-job

4.1 你们项目中是否使用到定时任务?

我们在项目中使用xxl-job查询支付结果,退款结果

4.2 为什么用?业务背景?

我们在支付服务中,有两种方式获得支付结果:

  1. 三方推送消息:一种是三方(支付宝,微信)把支付结果推送给我们设置的回调地址和用户,三方会以某种评率发送消息知道我们获取,但可能会因为网络或抖动,我们未能及时获取结果
  2. 主动轮询:鉴于以上原因,我们在项目中配合使用xxl-job发起请求,查询支付结果,考虑到三方限流,以及数据库的承受压力,我们设置评率为间隔10s.

4.3怎么用xxl-job具体配置?

day11-微信支付 - 图16
如上图:

  1. 执行器:我们先设置一个model-trading-job-listen执行器(app_name与配置文件上的名称保持一致)
  2. 路由策略:我们采用的主动轮询,可以在指定间隔时间内重复执行查询结果
  3. 阻塞处理策略:单机穿行
  4. cron表达式:间隔10s,考虑三方限流,数据库更新压力大
  5. JobHandler:和项目中使用@XXLjob(“”)注解中的值保持一致
  6. 失败重试次数:设置为0,避免给数据库造成太大压力,同时也是考虑到三方限流

4.4路由策略为什么是轮询?你是怎么考虑的?

我们项目中为了及时获取支付和退款的结果,我们使用xxljob主动轮询的策略,间隔10s查询一次数据库状态为付款中(FKZ)的订单,可解决代码不能重复执行的问题,能及时更新订单,交易单状态;并且我们设置的间隔时间稍长,在一定程度上减少了访问数据库的压力

4.5如果执行任务失败了,业务考虑是否需要重试?

我们设置失败重试的次数为0,即任务执行失败不考虑重试
原因:

  1. 三方可能限流
  2. 支付或退款请求,重试后会给数据库造成太大的压力
  3. 我们主动轮询是配合三方推送消息的作用,就算我们主动轮询失败了,三方依然以某个频率发送消息,直到我们获取结果

4.6 任务满了怎么办?

当任务满了时,会触发阻塞处理策略,我们项目中采用的是单机串行,即任务会依次排序执行,每一个任务内容是一致的

10 . v2跟v3的区别是?

v3支付官方推荐使用:

1、参数和返回值基于JSON格式传输

2、公钥/私钥非对称加密方式

3、http协议 rest风格请求