从下单/计算开始:
- 下单/结算:这一步虽然不是直接的支付起点,但是支付相关的金额等等信息都来自结算,此时订单的状态是未支付
- 申请支付:用户选择申请支付,客户端调用支付服务,此时在系统内产生一笔支付流水,这笔流水的状态是未支付
- 发起支付:支付服务调用三方支付,通常这种钱包类的支付,在发起支付这一步,会响应一些支付的链接,客户端会对链接进行对应的处理。
- 钱包支付:用户进行支付,通常是通过对应的钱包进行的,大家可以回忆一下自己在购物中,支付的过程,不同的端,对钱包支付的处理是不太一样的:
- PC端:PC端,通常是打开收银台,展示一个二维码,通过钱包扫码支付,下面是京东的微信支付扫码页
- WAP端:手机的网页站,WAP端的支付一般是直接拉起对应的钱包,如果拉起钱包失败,就跳转界面
- APP端: 在国内,购物大部分都是在APP端,产品经理会想法设法把用户带到APP,为什么我的示例图都用京东,不用淘宝呢?因为我拿UC打开淘宝,会直接跳转APP。
- APP端的钱包支付,我们应该都非常熟悉,一般是拉起钱包,支付。
- 支付回调:用户完成支付后,三方支付平台,会回调商户,通知支付结果。
- 同步订单状态:支付服务在确认支付完成后,会向订单服务同步支付的结果,订单服务变更订单的状态,由未支付-》待发货,客户端通过轮询、长连接,或者服务端主动推送的方式,在界面上变更订单状态。
我们再从支付流水的角度看一下支付状态的变化:
- 从未支付,到有支付结果的终态,中间还有一个中间状态支付中
- 用户通过打开钱包--》完成支付--》支付回调,这段时间的支付流水就处于支付中
为什么要花这么多篇幅来讲支付的业务流程、交互过程呢?因为我认为,防止订单的重复支付,不止是技术上的问题,也是业务和产品上的问题。
为什么订单会重复支付未防重导致的重复支付我们可以看到PC端支付,是扫描二维码,这些二维码,就是对应相应的支付流水,假如用户重复点击支付,如果不做防重的的话,会生成两笔支付流水,也就是两个不同的二维码,要是用户分别扫了两个不同的支付码,那么毫无疑问,就会产生重复支付。
掉单导致的重复支付“我明明付款了,为什么我的订单还没支付呢?”
这就是所谓的“掉单”:
- 外部掉单:三方支付的支付状态没有同步或者没有及时同步到商城,这叫外部掉单
- 内部掉单:支付服务的状态没有同步到订单,或者客户端没有及时获取到订单状态,这叫内部掉单。
用户一看,自己付了款,结果商城里订单还未付款,但是又特别想要,可能就会再下一单,这样就重复支付了。
多渠道导致的重复支付我们国内支付的体验还是非常快捷的,大家可能没有感觉,如果了解过海外支付的可能了解,很多支付的渠道,消耗的时间非常长。
比如用户保罗选择了一种支付方式Boleto,结果支付的网点离保罗他们村太远了,保罗又选择了Paypal支付,保罗去赶集的时候,又顺手去网点把Boleto的这一笔支付了,结果就重复支付了。
这种情况大家可能很少遇到,我们可以用美团下一个单,先打开微信支付,不要支付啊,接着回到美团,打开支付宝,用支付宝支付完成后,用微信接着支付,大家猜猜,两笔支付是不是都能成功?答案是可以。
如何防止订单重复支付加锁不管是3.申请支付、还是5.支付回调,都应该以订单维度加锁,防止并发下的重复操作。
加锁,毫无疑问,也是分布式锁,通常我们会选择Redis分布式锁。