Hello,大家好,我是楼下小黑哥~

好久没写支付相关的文章了,今天继续从事老本行~

上次在文章钱被扣走了,但是订单却未成功!支付掉单异常最全解决方案提到,支付过程会出现掉单、卡单的情况,这种情况对于用户来讲,体验非常差,明明自己付了钱,扣了款,但是订单却未成功。

上篇文章我们简单说了下解决方案,这次小黑哥就结合生产实际碰到的情况,给出两种详细设计的方案:

定时轮询补偿方案

延迟消息补偿方案

大家可以根据自己系统的实际情况,选择性参考。

当然了,以下设计方案可能并不完美,如果各位读者还有其他解决方案,欢迎留言指出,一起讨论,一起成长~

定时轮询补偿方案

整体流程

这个方案主要采用定时任务,批量查询掉单记录,从而驱动查询具体支付支付结果,然后更新内部订单。

整体方案流程图如下:

前三步流程没什么好说的,正常的支付流程,咱们针对后面几步具体详细说下。

第三步调用支付通道之后,如果支付通道端返回支付受理成功或者支付处理中,我们就需要调用第四步,将这类订单插入掉单表。

如果支付直接成功了,那就正常流程返回即可。

第五步,补单应用将会定时查询数据库,批量查询掉单记录。

第六步,补单应用使用线程池,多线程异步的方式发起掉单查询。

第七步,调用支付通道支付查询接口。

重点来了,如果第七步支付结果查询为以下状态:

支付结果为扣款成功

支付结果为明确失败

掉单记录查询达到最大次数

第八步就会删除掉单记录。

最后,如果掉单查询依旧还是处理中,那么经过一定的延时之后,重复第五步,再次重新掉单补偿,直到成功或者查询到达最大次数。

相关问题

为什么需要新建一张掉单表?不能直接使用支付订单表,查询未成功的订单吗?

这个问题,实际上确实可以直接使用的支付订单表,然后批量查询当天未成功的订单,补单程序发起支付查询。

那为什么需要新建一张掉单表?

主要是因为数据库查询效率问题,因为支付订单表每天都会大量记录新增,随着时间,这张表记录将会越来越多,越来越大。

支付记录越多,批量范围查询效率就会变低,查询速度将会变慢。

所以为了查询效率,新建一张掉单表。

这张表里仅记录支付未成功的订单,所以数据量就会很小,那么查询效率就会很高。

另外,掉单表里的记录,不会被永久保存,只是临时性。当支付结果查询成功,或者支付结果明确失败,再或者查询次数到达规定最大次数,就会删除掉单记录。

这就是第八步为什么需要删除掉单表的原因。

如果需要保存每次掉单表查询详情,那么这里建议再新增一张掉单查询记录表,保存每一次的查询记录。

针对这个方案,如果还有其他问题,欢迎留言。

方案优缺点

定时轮询补偿方案,最大的优点可能就是系统架构方案比较简单,比较容易实施。

那么这个方案的缺点主要在于定时任务上。

定时任务轮询方案天然会存在以下不足:

轮询效率稍低

每次查询数据库,已经被执行过记录,仍然会被扫描(补单程序将会根据一定策略决定是否发起支付通道查询),有重复计算的嫌疑

时效性不够好,如果每小时轮询一次,最差的情况下,时间误差会达到1小时

如果为了解决时效性问题,增加定时任务查询效率,那么 1 中查询效率跟 2 的重复计算问题将会更加明显。

延迟消息补偿方案

下面介绍另外一种掉单补偿方案,延迟消息补偿方案,这个方案整体流程与定时任务方案类似,最大区别可能在于,从一种拉模式变成一种推模式。

整体方案流程图如下:

这个方案主要流程跟定时方案类似,主要区别在于第四步,第五步,第八步。

第四步的流程从插入掉单表变更为往延迟队列发送掉单消息。

第五步,补单程序接收掉单消息,然后触发支付掉单查询。

第八步,如果第七步支付结果查询为以下状态:

支付结果为扣款成功

支付结果为明确失败

掉单记录查询达到最大次数

补单程序将会告知延迟队列消费成功,延迟队列将会删除这条掉单消息。

其他状态将会告知消费失效,延迟队列将会在一定延时之后,再次发送掉单消息,然后继续重复第五步。

延迟队列

这里的延迟队列需要自己实现,复杂度还是比较高的,这里给大家推荐几种实现方案:

第一种,基于 Redis SortedSet 实现延迟队列。可以参考一下有赞的实现方案https://tech.youzan.com/queuing_delay/

第二种,基于时间轮算法(TimingWheel)实现延迟队列,具体可以参考 Kafka 延时队列。

第三种,基于 RocketMQ 延迟消息。

前两种方案说起来还需要再开发,所以还是比较复杂的。

这里重点说下第三种方案,该方案是 RocketMQ 已经支持的特性,开箱即用,使用起来还是比较简单的。

RocketMQ 延迟消息支持 18 个等级,分别如下:

1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

消息发送方可以通过以下方式指定延迟等级,对应上方的延迟时间。

Message#setDelayTimeLevel

消息消费方,如果消费失败,默认将会在消息发送方的的延迟等级基础上加 1。如果消息消费方需要指定其他的延迟等级,可以使用如下方式:

ConsumeConcurrentlyContext#setDelayLevelWhenNextConsume

RocketMQ 延迟消息,支持的特性还是比较基础、简单,不支持自定义延迟时间。不过对于掉单补偿的这个场景刚好够用,但是如果需要自定义延迟的,那还是得采用其他的方案。

方案优缺点

延迟消息的方案相对于定时轮询方案来讲:

无需再查询全部订单,效率高

时效性较好

不过延迟消息这种方案,需要基于延迟队列,实现起来比较复杂,目前开源实现也比较少。

小结

支付掉单、卡单是支付过程中经常会碰到的事,我们可以采用异步补偿的方案,解决该问题。

异步补偿方案可以采用如下两种:

定时轮询补偿方案

延迟消息补偿方案

定时轮询补偿方案实现起来比较简单,但是时效性稍差。

而延迟消息补偿方案总体来说比较优秀,但是实现起来比较复杂。如果没有自定义的延迟时间的需求,可以直接采用 RocketMQ 延迟消息,简单快捷。

另外延迟队列使用场景还是比较多,不仅仅能用在掉单补偿上,还可以用于支付关单等场景。所以有能力开发的团队,可以开发一个通用的延迟队列、

好了,今天的文章就到这里了。

我是楼下小黑哥,下篇文章再见,886~

历史支付文章推荐

钱被扣走了,但是订单却未成功!支付掉单异常最全解决方案

一笔订单,但是误付了两笔钱!这种重复付款异常到底该如何解决?

收款神器!解读聚合收款码背后的原理|原创

手机没网了,却还能支付,这是什么原理?|原创

轻轻一扫,立刻扣款,付款码背后的原理你不想知道吗?|原创

支付渠道路由系统进化史

从零开始设计对账系统

微信支付宝接入大全

多支付通道路由网关设计

银行卡支付,背后到底发生了什么?

java掉单_【Java】抄答案就是了,两套详细的设计方案,解决头疼的支付掉单问题...相关推荐

  1. java取负数_[Java] 告别“CV 工程师”码出高效!(基础篇)

    作为一名资深的 CV 工程师,某天,当我再一次日常看见满屏的报错信息与键盘上已经磨的泛白的 Ctrl.C.V 这三个按键时,我顿悟了. 百度谷歌复制粘贴虽然很香,但是总是依靠前人种树,终会有一天失去乘 ...

  2. java控制语句练习题_[Java初探实例篇02]__流程控制语句知识相关的实例练习

    本例就流程控制语句的应用方面,通过三个练习题来深入学习和巩固下学习的流程控制语句方面的知识,设计到,if条件判断语句,switch多分支语句,for循环语句及其嵌套多层使用,while循环语句. 练习 ...

  3. java 文本压缩_[Java基础]Java使用GZIP进行文本压缩

    import java.io.IOException; import java.util.zip.GZIPOutputStream; import org.apache.commons.io.outp ...

  4. java 多线程输出_[Java多线程]ABC三个线程顺序输出的问题

    大概的问题是这样的: 有A,B,C三个线程, A线程输出A, B线程输出B, C线程输出C 要求, 同时启动三个线程, 按顺序输出ABC, 循环10次 这是一个多线程协同的问题, 本身多线程是没有执行 ...

  5. java private 对象_[Java笔记]类的所有构造器都是private权限,就一定没有办法实例化它的对象了么?...

    笔者以前学过C++语言.众所周知,C++也是一门面向对象程序设计语言.还记得当时在大学的时候,老师讲过这样的话:类的构造函数不应该设置成private权限,这样的话还怎么去实例化类的对象?当时也信以为 ...

  6. java resources 目录_[Java] 在 jar 文件中读取 resources 目录下的文件

    注意两点: 1. 将资源目录添加到 build path,确保该目录下的文件被拷贝到 jar 文件中. 2. jar 内部的东西,可以当作 stream 来读取,但不应该当作 file 来读取. 例子 ...

  7. java斗地主发牌_[Java源码]扑克牌——斗地主发牌实现

    --------------------------------------- --------------------------------------- ----------一个扑克牌核心和简单 ...

  8. java小朋友猜拳_[Java教程]Java猜拳小游戏(剪刀、石头、布)

    [Java教程]Java猜拳小游戏(剪刀.石头.布) 0 2015-09-29 08:00:04 import java.util.Random;import java.util.Scanner;pu ...

  9. java星空屏幕_[Java教程]窗口设置_星空网

    窗口设置 2016-04-13 0 /** * 这个是GUI的事例程序: * */ package w160412.wang.main;import java.awt.Color; import ja ...

最新文章

  1. 心得丨吴恩达Deeplearning.ai 全部课程学习心得分享
  2. JavaScript —— 如何判断一个非数字输入
  3. python处理csv数据-python 数据处理 对csv文件进行数据处理
  4. JDK提供的检测工具
  5. php里面sql是什么意思,MySQL和SQL是什么?MySQL和SQL之间的区别有哪些
  6. 如何查询高考成绩2021年的成绩排位,2020年高考成绩排名怎么查,高考成绩排名是怎么排的...
  7. oracle em界面监控出来的奇怪图像_海康poe主机断电重启后不出图像,显示用户被锁定...
  8. vue 指令 v-bind
  9. FlashCache初探(一)
  10. matlab与焓湿图,湿空气性质与焓湿图应用
  11. magicbook屏幕_荣誉Magicbook 15降压笔记本电脑的最佳爆炸现在
  12. win10休眠_最牛的win10优化设置方法大全
  13. 猿如意|初识CSDN的开发者工具合集
  14. 徐直军、何小鹏等大咖加盟,2021互联网岳麓峰会即将重磅开幕
  15. 惯性导航的定位原理是什么?
  16. Homekit智能家居之智能吸顶灯
  17. Respond 的响应式代码阅读
  18. 哈工大近世代数定义、定理、推论汇总
  19. 【echarts柱状图保存为图片并下载】
  20. vi创建html文件

热门文章

  1. 文件上传常见绕过分析
  2. JS-循环清空对象 判断数据类型的5种常用方法
  3. WEB前端学习二 JS作用域和作用域链
  4. JavaScript中的回调地狱及解决方法
  5. 报错空指针异常_让你为之颤抖的Java常见的异常exception
  6. sqlyog表添加列_如何用数据透视表求差,而不是求和?
  7. vxe-table安装和使用
  8. 表单验证JavaScript实现正则匹配、随机验证码、密码强度、加拖拽加蒙板
  9. CNN结构基元:纹理结构和纹理基元方程化GLOH、Gabor...(Code)
  10. ANN:神经网络堆叠/进化故事( 从感知机到DRBN )