支付接口逻辑
三方支付
- sm2加密传参、校验参数
- 校验防重令牌,防止重复提交
- 支付校验
- 获取分布式锁
- 校验预支付缓存
- 不存在:订单超时
- 校验支付缓存
- 不存在:首次/超时后调起支付
- 存在:校验此次支付和缓存,不一致需关闭之前三方支付
- 调起支付
- 发送消息到防掉单延迟队列
- 返回调起结果
接口逻辑说明
这里存在两个缓存:
- 预支付缓存,与延时队列取消订单时间一致,30min,缓存存在说明订单还未超时
- 支付缓存,调起三方支付缓存,5min,超时后三方支付通道也会关闭。
支付中存在的问题
以下问题只针对于同一个人来说
支付调起
不同端之间,PC和APP间重复支付
针对于不同端之间的操作,在接口上使用分布式锁,锁住当前用户的操作,防止多端重复类型操作。
不同支付方式间,既调起了微信又调起了支付宝
针对于不同支付方式,可以使用缓存的方式,缓存有效期和支付有效期同步,即缓存存在说明调起的支付还有效,当存在有效支付时,调起了其他类型支付,需要对之前的支付方式做关单处理(三方提供)然后使用新调起的支付作为此用户的支付缓存。
保证当前最多有一种支付方式存在于缓存中。
支付
多端同时支付
在支付调起逻辑中可以保证当前最多有一种有效的支付方式。此时压力来到了三方支付系统,经测试(微信)两部设备同时扫描一个支付码,当其中有一个支付成功后,其他设备会有弹窗提示,保证一个码只能支付一次。
超时支付
三方支付调起时,有可选参数过期时间,当前时间超过此时间后扫码会显示此二维码失效,来防止可能得多次支付,压力存在于三方。
订单后续逻辑
支付回调
在防止了多次调起多次支付后,回调也存在坑,回调这个通知方式,由于是异步操作,就会存在不及时性,可能性很多,所以我们需要从多个方面进行限制。
首先讨论回调和订单超时问题,正常情况下:
- 订单支付成功
- 异步接收三方支付结果
- 订单的后续逻辑
- 延迟队列执行对订单是否超时处理
即支付回调在延时队列操作之后,若由于某些原因回调信息没有收到,导致系统误以为订单尚未支付,把订单修改为了未支付,出现了数据异常。
针对上述问题,
- 延迟队列操作时,需要验证三方是否支付、正在支付,再对订单进行关单,若已经支付,直接走后续支付成功逻辑。
- 当收到支付成功回调时,订单以变为未支付时,支付优先于一切,即便订单已变为取消状态也是需要更改为已支付,做支付成功逻辑。
支付问题解决方案
如何防止多端重复支付
对于同一用户来说,支付时获取分布式锁,当一端发起支付直到调起成功,其他端无法调起。
如何防止重复调起并支付
场景:PC调起微信支付后,扫码不付款,随后APP调起支付宝支付,扫码与微信同时付款。
获取此用户支付分布式锁后,校验缓存中是否已存在支付缓存(记录调起支付的信息),存在且不同支付方式需要对之前的支付方式做关单处理,才可以进行此次操作的调起。若支付方式相同,直接返回之前调起结果即可。
ps:关单后APP即便扫码了,也会提示订单关闭
用户压时间点支付存在的问题
前提:当订单创建时,就会把订单放入延时队列,默认30min后取出把未支付的订单改为取消状态
场景:用户支付逻辑中,验证订单过期时未过期,支付回调时订单过期。(角度刁钻)
分析一下,此时操作订单的逻辑有两个,一是延时队列进行订单超时操作,二是支付成功回调操作。
- 回调在前,延时队列在后。没有影响,延时队列关单时校验三方支付状态。
- 延时队列在前,回调在后。以支付为中心,即便订单取消,也是可以更改为已支付走支付成功逻辑。
上述情况中发生概率还是存在的,对于极端情况做了一些梳理。
如何防止重复支付
这里讨论的场景时,当一个订单支付成功后,由于种种原因没有接收到支付成功信息,导致订单还是未支付状态,用户可以再次发起支付
场景:使用微信支付后,由于网络等原因,支付回调没有调通。用户切换支付方式使用支付宝支付,并付款。
对于系统而言,什么时候支付的是无感知的,只是调用了三方接口生成支付链接/二维码,用户何时支付并不会感知,只能被动接收支付结果信息。想到这里,我们可以在调起支付后,添加异步线程,隔一段时间请求三方支付的结果,化被动为主动。
这里推荐使用延时队列的方式,间隔时间是由小到大的,例如,10s、30s、60s…
当请求三方支付结果时,显示支付成功,即便系统已经把订单修改为了取消状态,也是可以修改为支付成功的。
订单手动取消
取消前需要根据缓存,去对应支付方做关单处理后才可以关闭,若显示已支付,则直接走支付成功后续逻辑。