订单如何避免重复支付?

  • 应用场景如下

系统支持 支付宝微信支付
前端是 h5
后端提供的是 接口

这个时候张三下单。
张三 操作如下

客户端A 拉起微信支付
客户端B 拉起支付宝支付

这个时候 张三同时进行输入支付密码支付

考虑 支付宝亦或是微信 回调到服务端有可能失败 会多次回调服务端

或者说 回调处理完了订单 没有返回 微信或者支付 成功 或者说 正在处理中

在这样的一个场景下 应该如何避免这样的问题发生

欢迎各位大佬进行讨论

自由与温暖是遥不可及的梦想
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 65

不是太明白你的意思 :sweat_smile:

2年前 评论
uniquespider 2年前
自由与温暖是遥不可及的梦想 (楼主) 2年前
邪恶的咖啡 (作者) 2年前

这个加锁就能解决,避免同一时间操作同一个订单。

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前
梧桐树下 (作者) 2年前

客户端的请求肯定是有先后顺序的,在支付的时候获取先获取锁,laravel也有相应的分布式锁,可以了解一下缓存系统《Laravel 8 中文文档》

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前

接楼上说的,给前端返回支付参数的时候redis加锁并设置个过期时间,检测到有锁的就提示订单已在支付中,这样能保证他不能同时拉起多个支付,但是比如说你现在拉起了一个支付,一直在输入密码页不动,然后等锁过期后在去拉就会又出现能付多个的问题,拼多多就有这样的问题,解决办法就是回调的时候你要判断下这笔单,如果是重复的直接调退款

2年前 评论
wanzi (作者) 2年前
wanzi (作者) 2年前
自由与温暖是遥不可及的梦想 (楼主) 2年前
liaosp 2年前
自由与温暖是遥不可及的梦想 (楼主) 2年前
自由与温暖是遥不可及的梦想 (楼主) 2年前
justmd5 2年前
C-zx 2年前

考虑这种问题纯属浪费时间,有几个会这么干的,概率低的问题直接忽略即可,重复退款即可,对用户又没损失,关于体验问题,能这么干的都是用户吃饱了没事干,要为这种体验买单?

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前
wanzi 2年前
liaosp 2年前
Kuaile1234 2年前
自由与温暖是遥不可及的梦想 (楼主) 2年前

支付回调发现已经支付了,做退款处理即可,你可以用锁或者其他限制在发起支付限制一下,但是无法完全限制,所以后备方案就是退款处理

2年前 评论

加锁缓存,客户端A和客户端B生成的是同一个订单,重复支付退款处理

2年前 评论

是太闲了吗 如果是加锁 如果不是忽略

2年前 评论

嗯,我也觉得太闲了,真有这样的人工处理退款都行,自动退款都不需要。

2年前 评论
Kuaile1234 2年前

最简单的,加个乐观锁,

订单更新为已支付时,,,加一个条件,,,

Order::query()
    ->where('order_id', 'xx-yy-zz')
    ->where('pay_status', 'unpay')
    ->update(['pay_status' => 'done']);

这样其他修改这个的都会失败,如果用户两边同时支付了,都进入回调阶段,那有一边会回调失败,,,要考虑退款了?

2年前 评论

加锁,拿不到锁就不允许支付。

2年前 评论

这种问题,纯属闲的蛋疼的人会这样做。已经调起支付界面,应用层面是控制不了的。如果要避免,只能加个字段,在调出支付界面后设为1,用户取消支付、支付失败都会有回调,监听这个事件复原为0。但也难防人家直接在支付界面直接关机之类的操作,怎么去恢复原为0?直接关机再开机,就不让支付,也不行。 搞那么复杂,最后你还是会发现,避免不了。简单点,再支付notify的时候,发现重复支付,直接退款。

2年前 评论

开一个数据库事物,然后直接一个悲观锁不就可以了。反正另一个事务会等着的。

2年前 评论
随波逐流

单一登录 可以解决楼主的问题

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前
ichynul 2年前

打开支付页面的时候生成锁,超时不让支付

2年前 评论

个人拙见

前端在拉起支付前,先去请求一个判断订单状态接口,HTTP请求肯定有先后,先到的加锁,并返回 success 给前端告诉他可以拉起支付了。

第二个到达的请求返回 fail,前端接收到参数后,拒绝用户拉起支付

2年前 评论

你这个问题我们之前项目也有讨论过,结果就是人工处理,毕竟这种东西本来就不是正常人能干出来的

2年前 评论

设计数据库的时候 0创建订单 1 待支付 2 已支付 3 支付成功(回调成功) 问题就解决了 完成支付后where('status',0)->update('status',2),第二次支付的时候这条语句就会直接失败 回滚显示订单已支付

2年前 评论

遇到这种用户,款都不给他退。让他自己打客服电话,然后还要求他提供一系列证明,才给他退。

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前

或则只让一个端拉起支付

2年前 评论

是不是只有我没看明白意思?

张三 操作如下
客户端 A 拉起微信支付 \ 客户端 B 拉起支付宝支付
这个时候 张三同时进行输入支付密码支付

我可以理解为 张三 同时 用两种不同的方式付款? 如果是这样,首先假设不成立,这是非正常操作;

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前

直接不让他单账号,多设备登录。拉起支付的时候,肯定要检查一下是否登录。 一个设备登录了,就把另一个设备T下线,或者反过来。

2年前 评论

说到底还是访问共享资源的问题, 就好比多个进程使用一台打印机, 解决此类问题的方法是加互斥锁, 客户端A 发起了微信支付, 此时就应该为该订单加上锁, 直到 客户端A 操作完成(成功支付,关闭支付)后才能释放, 这样就能解决多端同时支付问题, 当然根据你的描述都能走到支付这一步, 说明你们使用的模型是[多进程+多台打印机+只要一份打印结果], 这个时候重心就到如何处理多份打印结果的问题上来了, 这就根据实际开发需求来做处理

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前
liu126 (作者) 2年前

加一个锁

2年前 评论

支付的时候,把支付单加上悲观锁,把其他的支付单取消掉,再把当前支付单更新为已支付。这样可以吗?

2年前 评论
chenvle

Redis 可以解决,A发起微信支付的时候,把Order的ID或者User的ID 保存起来,直到A支付完成就释放,B发起支付的时候,判断缓存里面是否存在正在支付的订单。

2年前 评论
九霄道长

楼主应该说的是wx zfb同时支付 两次回调的问题,那么是不是应该考虑(退款定时任务) 和一端拉起支付时锁定那

2年前 评论

楼主的意思是 ab同时都停留在了微信和支付的输入密码阶段了。。这什么锁也不顶用啊、这个只有日志数据记录客服退款处理的

2年前 评论
自由与温暖是遥不可及的梦想 (楼主) 2年前
CodeUndefined 2年前

我们做社区团购的时候有过这个场景的有业务的考虑。入口可能有不同支付方式的唤起支付或者同一个支付方式重复支付都有可能。我们的处理方式是回调的地方数据库用事务,然后根据订单支付状态校验,如果第一个支付的回调完成之后,后续的所有回调进行退款操作。

2年前 评论
superwen

购物车 -》订单 加一个锁 这个时间很短,比如说10秒没有完成,就解锁,退回购物车。 订单 -》支付 加一个锁,这个时间比较长,比如说30分钟。如果没有支付完成,解锁,同时将订单设为一取消状态。如果用户要重新支付,就要重新下单。

2年前 评论

微信支付和支付宝两方同时开启支付,但是它们选的商品和购物车的商品组成的订单是有序号的,正常逻辑是先创建订单,创建订单的时候,如果是购物车的支付,那么在新建购物车订单要加一个锁避免购物车重复创建订单,单个商品直接支付的就不要管了,这个创建订单的锁ok后,订单就只有一个,另一个会通知他订单已创建,另一端即使到全部订单去点支付,这时候你需要在如果a端或者b端先点击了支付,再加一个订单编号的支付锁,要等到该订单支付结束或者用户主动退出支付后,方可放开锁,这样就好了

2年前 评论

加个支付回调流水表 把所有支付回调都进行记录 多支付的则自动退款不就得了

2年前 评论

这不是测试和技术在抬杠嘛?直接考虑退吧款纯属浪费时间

2年前 评论
  • 假设A、B端同时点击支付按钮支付,服务端拉起支付的之前先给订单加锁,获得锁的可以继续支付,其他的拒绝继续操作,提示失败并结束
  • 这个锁不主动释放,可以由支付方系统主动回调处理后释放,则同一时刻只能有一个端的一个页面调的起支付系统,那么这个锁当然也不能无限期存在下去,你打开一个支付页面,长时间不支付,第三方支付系统也有它的超时付款页面过期机制吧。后台启一个协程(golang)专门处理超时付款的订单锁,由系统处理这些刁民,并关闭订单
  • 最后一道防线就是超极端的退款处理 :yum:多给的钱凭本事耗死你。
2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!