一、前言
最近正在做电商相关的项目,整理一下解决方案并帮助自己巩固知识点,此方案是结合了目前的业务环境,若有更好的解决的方式很高兴与大家一起讨论。
二、支付流程
要想知道什么时候会发生重复支付,首先梳理一下支付的流程。(以APP为例)
- 用户在APP中对订单进行发起支付。
- 服务端根据订单信息构建支付参数,调起三方支付。
- 三方返回支付的参数。
- APP跳转到相应三方APP。
- 展示支付信息。
- 用户进行支付操作。
- 返回支付结果。
- 异步通知服务端,订单支付结果。
以上是支付的一般流程,当支付成功后,三方系统会立即回调系统,当没有应答时,还会间隔一段时间进行回调,直至回答或两天以上还没有回答为止。
看似以上流程上没有漏洞,其实其中有多个漏洞。
三、为什么会重复支付
情景一
触发多次步骤2.调起支付
,把返回的支付二维码进行记录,那么这几个二维码都是可以进行支付的,这样可以造成重复支付。或者说多端的情形下,PC和APP同时调起了支付,并都进行支付操作。
情景二
假设步骤8.异步通知支付结果
没有触发,或者网络突然不同,没有接收到支付结果,导致APP跳转回订单页面时,查看订单还是未支付状态,可能会再次支付。
情景三
以上情景都是建立在同一种支付方式上,若第一次是微信支付,第二次换成了AliPay,在不同支付方式中进行支付,也会导致重复支付的情况下出现。
四、如何防止重复支付
在上述情景中,情景一
和情景三
都是支付多次导致的,这样我们还是以加锁的方式去解决,当然这里是分布式锁。
我们展开说明其中的细节问题:
- 当我们第一次调起支付时,会在
Redis
中获取到此订单的锁,这样其他相对此订单调起支付的线程都会被拒绝。 - 锁在什么时候释放呢,在调起支付后就会释放锁,此时其他线程可以拿到此订单的锁,可以去调用其他支付方式,此时若调起的支付方式与之前的方式不同,需要去三方调用取消订单方法,返回成功后,才可以进行此次请求,即调用第二个支付方式接口。这样能保证就算是用户拿到两个支付二维码,也是只能支付其中一个的。
还有一个细节
在调起支付时,会有一个有效期,例如20:00调起支付,设置失效时间为20:30。一般会把整个消息放入延时队列,等时间到期,消费数据的时候会主动查询三方接口,若已经支付,需要对数据进行支付处理,若未支付,修改订单即可。
这样的话在订单失效后的时间是不会重复支付的。在订单可支付的时间内,保证只有一种支付方式可以支付,且支付方式切换时保证能够关闭之前的订单。