您现在的位置是:首页 > 正文

支付中的业务逻辑|支付幂等性 最新发布

2024-02-01 01:41:05阅读 2

支付中的业务逻辑|支付幂等性

前言:何为幂等性?

幂等性就是指“一个操作任意多次执行所产生的影响均与一次执行的影响相同。

幂等性其实是一个数学上的概念,在计算机领域,幂等是指一个方法被多次重复执行的时候所期望的结果要和第一次执行期望的结果保持一致。

支付场景的幂等性问题

很多操作天然具有幂等性,比如查询数据库,获取缓存数据,查询接口等,这些操作不管执行多少次都不会对系统产生影响;而新增、修改和删除可能会导致系统状态发生改变,因此在设计接口时要注意保证其幂等性。

接口幂等性

接口幂等性是指一个接口在多次请求同样的操作时,对系统状态不产生任何影响。具体到支付场景,对于同一笔交易的多次提交,只会产生一次支付结果,并且对账单和余额不会重复扣减,也就是说,无论是因为网络故障、超时等原因,还是由于用户意外或者恶意操作导致多次条件,都不会对支付系统的状态产生任何影响。

保证支付接口的幂等性非常重要,因为一旦出现支付失效、重复扣款等问题,不仅会导致用户体验变差,还可能引发纠纷和信任危机,进而影响到品牌形象和商业利益。

重复支付是怎么产生的?

想要保证支付的幂等,就要了解一下为什么支付是非幂等的,又是哪些场景造成了支付接口的非幂等,这样我们才能”要症下药“,找到解决支付幂等性的合理解决方案。

先来看一套支付流程中的操作:

 1.用户选择支付方式 -> 2.跳转到收银台 -> 3.用户支付成功 -> 4.第三方支付平台回调 -> 5.修改订单状态

导致重复支付的操作是在第三步与第四步。

  1. 首先针对第三步,用户在支付的过程中时可以选择多种付款方式的,如果用户准备微信支付,发现余额不足,或者因为网络原因付款后没有显示成功,又立即切换到了支付宝再次支付,可能会导致同一笔订单产生两笔扣款;再或者用户短时间点击了两次 立即支付 ,同时跳转了两个页面,在一个支付页面上完成了扣款又去另一个界面进行付款。。。

  2. 再来看第四步,用户发起支付后产生待支付订单,但是支付状态是不可控的,支付状态需要由后端从第三方平台【微信、支付宝】的异步回调信息中获取,同样可能因为网络原因后端无法获取到正确的支付信息,导致待支付订单再次产生。

支付幂等方案

解决方案一:前端防抖

经过一些人在实际支付项目中的经验,微信支付平台的扫码支付是不允许同一个订单号短时间内二次创建支付的,但是对于其他平台可能还是需要我们自己手动做出限制.

前端防抖是防止重复支付的第一道防线,整个支付流程就是从用户点击【支付按钮】开始的。对于用户可能短时间连续点击多次支付,所以我们可以在前端进行控制,限制用户在间隔n毫秒内,最多只会执行一次。

仅仅在前端作出限制还不能完全解决重复支付的问题,对于网络原因以及第三方支付平台的异步回调都有可能导致重复支付的发生,因此还要再对后端进行幂等处理。

解决方案二:token令牌

简单理解就是用户初次访问服务器,后端服务器会生成一串加密字符,在其中保存一些信息,返还给客户端,后面客户端再次访问服务器时都会携带这串加密字符,后端解密以后可以获取到之前保存的信息,这串加密字符就是token。

Token可以看成Session的升级版,都能保存信息,但Token的信息是经过加密的,因此更加安全,常用来检验用户的登陆状态。

说回支付幂等性,我们可以在用户提交订单的时候,签发一个Token,并把token保存在redis缓存数据库中,每个订单的token都是不同的,同样一个订单的token只能使用一次,用完即删,那么同一笔订单再次调用支付接口时,发现redis中的token不存在,代表这笔订单已经被支付,就会拒绝请求。

redis + token 的方案在多线程并发的情况的条件下仍然不能避免重复支付,因为上述的操作不是原子性的,我们还需要借助Lua脚本和分布式锁,保证redis获取令牌,生成单号,删除令牌这一系列操作是原子性的。

Lua脚本的示例”

-- 获取传入参数
 local token = ARGV[1]
 local key = ARGV[2]
 local value = ARGV[3]
 ​
 -- 检查 token 是否存在于 Redis 中
 if redis.call("GET", "token:" .. token) then
   -- 如果 token 已经存在,说明该请求已经处理过,直接返回结果
   return redis.call("GET", key)
 else
   -- 如果 token 不存在,则将 token 存储到 Redis 中,并且设置过期时间
   redis.call("SET", "token:" .. token, 1)
   redis.call("EXPIRE", "token:" .. token, 60)
 ​
   -- 执行真正的支付操作,并将结果存储到 Redis 中
   local result = do_payment(value)
   redis.call("SET", key, result)
   redis.call("EXPIRE", key, 3600)
 ​
   -- 返回支付结果
   return result
 end

这个脚本假设 do_payment 是自定义函数,用于执行真正的支付操作。在代码中,我们首先获取传入的 token、key 和 value 参数,然后检查 token 是否存在于 Redis 中,如果存在则直接返回之前处理过的结果,否则将 token 存储到 Redis 中并执行真正的支付操作。最后,将支付结果存储到 Redis 中,并设置过期时间。

通过使用 Redis 和 Token 解决支付幂等性,我们可以保证同一个请求只会被处理一次,从而避免重复支付的问题。

结局方案三:引入“支付中” 的状态

我们可以在支付订单中引入一个“支付中”的中间状态,用户发起支付以后后端要把支付订单的状态设为“支付中”,这样即使是支付没有成功,也可以防止重复支付的产生。

但是这种方式存在『卡单』的问题,因为支付结果需要第三方平台(支付宝,微信)异步回调给后端系统,这其中可能由于各种异常导致第三方平台没有回调支付结果后者后端没有接收到回调信息,从而导致支付订单一直处于 “支付中”,为了解决『卡单』,后端需要加入主动查询的逻辑,去第三方支付平台查询支付结果。

该创作来自于之前查到的某篇文章,如需删除和我联系

网站文章

  • 覆盖率工具gcov,lcov实践

    覆盖率工具gcov,lcov使用记录 背景 本文记录的是gcov和lcov在统计一个分布式数据库的代码覆盖率过程中遇到的问题。 介绍 *gcov是一个测试代码覆盖率的工具,随着gcc一起发布,不需要进...

    2024-02-01 01:40:25
  • Java 进程 CPU 100% 问题排查

    Java 进程 CPU 100% 问题排查

    如果在时间片内,线程一直占有,则认为是 CPU 100% 。CPU 运行速度很快,即主频非常高,除非密集型耗费 CPU 的运算,其它类型任务一般都会在小于时间片的时间内结束。

    2024-02-01 01:40:19
  • spring源码-bean生命周期

    spring源码-bean生命周期

    创建非懒加载单例bean。

    2024-02-01 01:40:13
  • 调整elasticsearch的jvm heap值

    集群里有两台elasticsearch老是服务死机,报错大致如下:Mar 26 08:19:47 es104 systemd[1]: Started Elasticsearch.Mar 27 09:5...

    2024-02-01 01:40:06
  • 【转载】C# 日常小问题:DataGridview转换为DataTable

    【转载】C# 日常小问题:DataGridview转换为DataTable

    C# dgv与datatable转换

    2024-02-01 01:39:36
  • 芯片设计之CDC异步电路(二)

    芯片设计之CDC异步电路(二)

    上文芯片设计之CDC异步电路(一)对比讲述了异步电路在前仿真中的行为与实际芯片的行为区别,本文继续讲几种常用的异步电路处理。(一)单bit信号同步器最经典的2DFF 1-bit同步器如下,下图结构通常...

    2024-02-01 01:39:26
  • Java-集合(LinkedHashSet类)

    Java-集合(LinkedHashSet类)

    1)LinkedHashSet是HashSet的子类;2)LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表;3)LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的;4)LinkedHashSet不允许添加重复元素。...

    2024-02-01 01:39:19
  • configure参数的理解

    configure脚本有大量的命令行选项.对不同的软件包来说,这些选项可能会有变化,但是许多基本的选项是不会改变的.带上--help选项执行configure脚本可以看到可用的所有选项.尽管许多选项是很少用到的,但是当你为了特殊的需求而configure一个包时,知道他们的存在是很有益处的.下面对每一个选项进行简略的介绍:--cache-file=FILEconfigure

    2024-02-01 01:38:49
  • 方向标Directional Sign

    方向标Directional Sign

    方向标题目信息输入输出注测试样例解答题目信息A carpenter has received an order for a wooden directional sign. Each board mu...

    2024-02-01 01:38:43
  • postgresql遇到“Connection refused”和“No route to host”大概的解决方法

    一般我们遇到这个问题的时候都是连接远程主机的时候,会出现这个报错 psql: could not connect to server: connection refused Is the server...

    2024-02-01 01:38:37