如何防止订单重复支付

news2025/1/16 3:45:44

想必大家对在线支付都不陌生,今天和大家聊聊如何防止订单重复支付。

看看订单支付流程

我们来看看,电商订单支付的简要流程:

订单钱包支付流程

从下单/计算开始:

  1. 下单/结算:这一步虽然不是直接的支付起点,但是支付相关的金额等等信息都来自结算,此时订单的状态是未支付;

  2. 申请支付:用户选择申请支付,客户端调用支付服务,此时在系统内产生一笔支付流水,这笔流水的状态是未支付;

  3. 发起支付:支付服务调用三方支付,通常这种钱包类的支付,在发起支付这一步,会响应一些支付的链接,客户端会对链接进行对应的处理;

  4. 钱包支付:用户进行支付,通常是通过对应的钱包进行的,大家可以回忆一下自己在购物中,支付的过程。不同的端,对钱包支付的处理是不太一样的:

京东PC端支付页

APP 端

在国内,购物大部分都是在 APP 端,产品经理会想法设法把用户带到 APP,为什么我的示例图都用京东,不用淘宝呢?因为我拿 UC 打开淘宝,会直接跳转 APP。

APP 端的钱包支付,我们应该都非常熟悉,一般是拉起钱包,支付。

APP支付

WAP 端

手机的网页站,WAP 端的支付一般是直接拉起对应的钱包,如果拉起钱包失败,就跳转界面。

京东支付 WAP端

PC 端

PC 端,通常是打开收银台,展示一个二维码,通过钱包扫码支付,下面是京东的微信支付扫码页。

  1. 支付回调:用户完成支付后,三方支付平台,会回调商户,通知支付结果。

  2. 同步订单状态:支付服务在确认支付完成后,会向订单服务同步支付的结果,订单服务变更订单的状态,由未支付 -> 待发货,客户端通过轮询、长连接,或者服务端主动推送的方式,在界面上变更订单状态。

我们再从支付流水的角度看一下支付状态的变化:

支付状态变化

  • 未支付,到有支付结果的终态,中间还有一个中间状态支付中

  • 用户通过打开钱包 -> 完成支付 -> 支付回调,这段时间的支付流水就处于支付中。

为什么要花这么多篇幅来讲支付的业务流程、交互过程呢?因为我认为,防止订单的重复支付,不止是技术上的问题,也是业务和产品上的问题

为什么订单会重复支付

未防重导致的重复支付

我们可以看到 PC 端支付,是扫描二维码,这些二维码,就是对应相应的支付流水,假如用户重复点击支付,如果不做防重的的话,会生成两笔支付流水,也就是两个不同的二维码,要是用户分别扫了两个不同的支付码,那么毫无疑问,就会产生重复支付。

掉单导致的重复支付

“我明明付款了,为什么我的订单还没支付呢?”

黑我钱是吧

这就是所谓的“掉单”:

  • 外部掉单:三方支付的支付状态没有同步或者没有及时同步到商城,这叫外部掉单;

  • 内部掉单:支付服务的状态没有同步到订单,或者客户端没有及时获取到订单状态,这叫内部掉单。

用户一看,自己付了款,结果商城里订单还未付款,但是又特别想要,可能就会再下一单,这样就重复支付了。

多渠道导致的重复支付

我们国内支付的体验还是非常快捷的,大家可能没有感觉,如果了解过海外支付的可能了解,很多支付的渠道,消耗的时间非常长。

比如用户保罗选择了一种支付方式 Boleto,结果支付的网点离保罗他们村太远了,保罗又选择了 Paypal 支付。保罗去赶集的时候,又顺手去网点把 Boleto 的这一笔支付了,结果就重复支付了。

这种情况大家可能很少遇到,我们可以用美团下一个单,先打开微信支付。不要支付啊,接着回到美团,打开支付宝,用支付宝支付完成后,用微信接着支付。大家猜猜,两笔支付是不是都能成功?

答案是可以。

美团多渠道支付

如何防止订单重复支付

加锁

不管是 3.申请支付、还是 5.支付回调,都应该以订单维度加锁,防止并发下的重复操作。

加锁,毫无疑问,也是分布式锁,通常我们会选择 Redis 分布式锁。

加锁

缓存结果

申请支付成功,支付回调成功,都应该缓存结果。

再申请支付,收到成功回调的时候,都应该先去检查支付的状态。

 

支付中流水取消

假如说,用户重复支付了,再次申请支付的时候,如果已经申请支付成功了,那么这笔支付肯定是要拒绝的。

但是,要是已经存在的这笔流水还在支付中呢?我们不确定它是成功还是失败,肯定是不能拒绝支付的,因为可能用户支付失败了,但是状态还没同步,这样肯定是不行的。

所以,我们可以取消掉正在支付中的流水,再进行支付。

支付中流水取消

已支付流水退款

现在又有新的问题了,假如发起支付的时候,有流水正在支付中,如果第三方支付平台不支持取消支付,或者用户新的支付是通过不同的渠道,我们希望尽可能提高用户的支付成功率,怎么办呢?

我们可以在发起支付的时候,订单还在支付中的情况下,允许用户发起多笔支付,在支付回调的时候,检查用户是否已经有成功流水,对后来的流水进行退款处理。

支付回调

当然,退款是个很危险的操作,毕竟钱退了,可就很难追回来,一定要做好风险的控制。

主动轮询与重试防止掉单

如果因为故障没有收到回调,或者没有及时收到回调,就可能会发生所谓的外部掉单。

防止外部掉单的关键,就在于,不能傻傻地只等三方的回调通知,而要主动去查询,用户发起支付的 3s 之后,就可以发起轮询了,直到拿到支付流水的最终状态,主动轮询,一般可以这么实现:

轮询

1) 定时任务轮询

使用定时任务,扫描表中支付中的流水,主动查询支付的状态,定时任务的实现方式有很多,线程池、调度框架、分布式调度框架等等。

定时任务轮询的缺点有两个:

  • 对数据库有一些压力,观察监控,会发现定时任务扫表的时候,有时候会造成数据库的一些“峰刺”;

  • 不便调整频率,实际上,用户发起一笔支付之后,一般都会在 10s-1min 中完成支付,越往后,用户完成支付,所以轮询梯度进行,会更合理一些,轮询的间隔可以设置成类似这种:3s,10s,30s,3min 等等

2) 延时消息轮询

另外一种方式就是使用延时消息。用户发起支付之后,发送一个延时消息,消费到延时消息之后,查询流水支付状态,没有拿到最终状态,就再发一个延时消息。延时消息的好处是对数据库的压力没有那么大,轮询的梯度也可以进行控制,缺点是实现起来复杂一些,而且要维护消息队列。

同步+异步防止内部掉单

支付服务在收到异步通知回调、或者主动轮询到流水的最终状态后,要通知订单服务支付流水的变化,订单服务同步更新订单的状态,这个过程要尽可能保证通知成功,可以采用同步+异步的方式。

  • 同步调用:支付服务调用订单服务的通知接口,有可能会因为网络等等的原因失败,也可以重试,但是根据经验,如果网络出现一些波动,重试很可能也会失败。

  • 异步通知:支付服务还应该发送一个支付成功的消息,订单服务可以利用消息队列的重试机制,来尽可能保证支付状态的同步。

这里还有一个问题,客户端如何同步这个状态?

因为可能服务端更新了订单状态,但是客户端的界面上还是未支付,得用户主动刷新一下,才能拿到最新的状态,这样明显是不太合适的。

服务端、客户端的状态同步,无非就拉和推:

  • :很简单,就是客户端在用户跳回订单状态页的时候,轮询一会。如果用户完成支付,通常很短时间就能获取到状态的变更。当然,这种方式对客户端的性能会有一些影响,而且很出现状态同步“漏网之鱼”的情况。

  • :推的实现有些麻烦,Web 通常是用 Websocket,对 APP 端的推送,一般采用第三方的推送平台。

客户端支付尽可能不外跳

不管从产品的角度,还是技术的角度,客户端发起支付这一步,其实应该尽可能地不要外跳,PC 端使用支付服务生成的支付码,而不是跳转;移动端网页、APP 在应用内展示支付页,当然这个是由第三方支付平台决定的。

在UC内内嵌支付宝

不知道大家留意到了没有,现在的支付宝,已经做到了不用拉起钱包,在应用内就可以完成支付,这个对于商家的意义还是比较大的,对用户体验、支付成功率,都有正面的作用,相信以国内的内卷程度,其它支付供应商,一定会“跟进”的。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

opencv场景识别

opencv场景识别 文章目录 一、需求1、现状2、设想 二、模型使用1、opencv dnn支持的功能2、ANN_MLP相关知识3、MLP图像分类模型训练学习 三、更换方向1、目标检测模型2、darknet网络介绍 四、opencv调用darknet模型1、**darknet模型获取**2、python调用darknet模型3、遇到的一些…

NewBing最新更新使用体验(无需等待人人可用)

NewBing最新更新使用体验 微软Bing爆炸级更新!无需等待人人可用! 今天,微软突然官宣全面开放BingChat: 无需任何等待。只需注册一个账户,首页即可体验。 NewBing最新更新新特性官方文档 https://www.microsoft.com/en-…

ETL 组件Spark资源设置指导

一、概念介绍 1.RDD RDD(Resilient Distributed Dataset):弹性分布式数据集,是Spark中最基础的数据抽象。它本质就是一个类,屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便数据转换和求值的方法。 简单来说…

研报精选230505

目录 【行业230505国信证券】风电or电网产业链周评(4月第5周):海风开发资源集中释放,黑色类原材料价格持续下行 【行业230505天风证券】通信AI系列之:人工智能之火点燃算力需求,AI服务器迎投资机遇 【行业2…

数字孪生遇上VR:未来的新生态

数字孪生和虚拟现实(VR)是当今技术领域备受关注的两个概念。 数字孪生作为物理世界的数字映像,已经在许多行业得到了广泛应用。而VR则是一种基于计算机生成的三维交互式虚拟环境,被广泛应用于娱乐、教育和游戏等领域。 数字孪生…

十一、MyBatis的逆向工程

文章目录 十一、MyBatis的逆向工程11.1 (清新简洁版)创建逆向工程的步骤11.2 (奢华尊享版)创建逆向工程的步骤11.3 说明尊享版和简洁版的区别 本人其他相关文章链接 十一、MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库…

Spring MVC程序开发

文章目录 一、什么是 Spring MVC(一)MVC的定义(二)MVC 和 Spring MVC 的关系(三)为什么要学习 Spring MVC 二、Spring MVC使用方法和技巧(一)Spring MVC 创建和连接(二&a…

clonezilla(再生龙)克隆系统操作

一、前言 背景:由于存在对嵌入式设备系统备份的需求,主要使用在对一个嵌入式设备安装好ros以及其他插件(安装时间十分冗长),然后对该系统进行备份,快速复制到下一台嵌入式设备中。因此引出了克隆linux系统…

MySQL知识学习08(MySQL自增主键一定是连续的吗)

众所周知,自增主键可以让聚集索引尽量地保持递增顺序插入,避免了随机查询,从而提高了查询效率。 但实际上,MySQL 的自增主键并不能保证一定是连续递增的。 下面举个例子来看下,如下所示创建一张表: 1、自…

C++类与对象(二)——拷贝构造函数

文章目录 拷贝构造函数1.拷贝构造函数的概念2.拷贝构造函数的特性 前言 本章继续学习类的6个默认成员函数——拷贝构造函数 拷贝构造函数 1.拷贝构造函数的概念 拷贝构造函数:使用已经存在的一个对象初始化创建另一个对象。 举例: class Date { publ…

Golang每日一练(leetDay0056) 单个编辑距离、寻找峰值

目录 161. 单个编辑距离 One Edit Distance 🌟🌟 162. 寻找峰值 Find Peak Element 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 …

vmware 安装Kylin-Desktop-V10-SP1-General-Release-2203-X86_64.iso

下载 官网:国产操作系统、银河麒麟、中标麒麟、开放麒麟、星光麒麟——麒麟软件官方网站 (kylinos.cn) 点击桌面操作系统 选择No1 点击申请试用 填写相关信息,点击立即提交,就会获取到下载连接, 点击下载按钮等待下载完成即可 安…

Java中顺序表详解

前言 在Java编程中,顺序表是一种基础且重要的数据结构。它通常用来表示线性结构数据,如数组等。通过使用顺序表,我们可以轻松管理和操作大量的数据,并实现各种算法和功能。本篇博客将详细介绍Java中顺序表相关的原理、方法和实例&…

BRDF

文章目录 0. 写在前面1. 简单理解2. Cook-Torrance BRDF模型2.1 BRDFD 法线分布函数F 菲涅尔G 几何函数1.1.3 L i ( p , ω i ) L_i(p,\omega_i) Li​(p,ωi​)1.1.1 积分框架1.1.2 f r ( p , ω i , ω o ) f_r(p,\omega_i,\omega_o) fr​(p,ωi​,ωo​): 个人疑惑及解答1.…

ESL设计概述

‍‍ ‍‍前言 随着芯片面临着应用场景丰富多变、集成功能模块越来越多、片内通信及模块间接口越来越复杂、设计规模越来越大以及PPA要求越来越高的需求,芯片设计方法面临越来越大的挑战。架构的合理性、完备性和一致性很大程度上决定了芯片设计的成败。基于同样的I…

《基于光电容积图法的两种可穿戴设备在不同身体活动情况下监测心率的一致性:一种新的分析方法》阅读笔记

目录 一、论文摘要 二、论文十问 Q1:论文试图解决什么问题? Q2:这是否是一个新的问题? Q3:这篇文章要验证一个什么科学假设? Q4:有哪些相关研究?如何归类?谁是这一课…

电容笔有必要买最好的吗?推荐的ipad手写笔

随着科技的进步,各种类型的电容笔的生产厂家越来越多。一支好的电容笔,不仅能大大提高我们的工作效率,而且能大大提高我们的学习效果。平替电容笔无论从技术水平,还是从产品品质来看,都具有十分广阔的市场前景。以下是…

redis基本数据类型及常见命令

数据库操作 select <库号>: 切换库 默认共有15个 dbsize: 查看当前库的key数量 flushdb: 清空当前库 flushall: 清空所有库 Key的操作 keys *&#xff1a; 查看当前库的所有key exists <key>: 判断该key是否存在 type <key>: 查看该key的类型 de <…

【原创】使用PowerShell配置新安装的ESXI主机

安装PowerCLI 模块 在线安装 优点&#xff1a;简单 缺点&#xff1a;太慢 启动PowerShell命令行&#xff0c;执行行如下命令 Install-Module -Name VMware.PowerCLI离线安装 先到VMware官网下载离线包&#xff0c;然后分几个步骤安装 https://developer.vmware.com/powercl…

redis5新增数据类型

Bitmaps 概念 &#xff08;1&#xff09; Bitmaps本身不是一种数据类型&#xff0c; 实际上它就是字符串&#xff08;key-value&#xff09; &#xff0c; 但是它可以对字符串的位进行操作。 &#xff08;2&#xff09; 可以把Bitmaps想象成一个以位为单位的数组&#xff0c; 数…