支付中心-重复支付问题解决方案_51xplan的博客-程序员宝宝_重复支付怎么解决

技术标签: 方法论  

支付中心-重复支付问题解决方案

一笔订单,可以做多笔支付,怎么解决?

重复支付的异常背景

  1. 一笔订单,在支付中心可以选择多种支付方式。如支付宝扫码,支付宝app,微信扫码,微信小程序,银联…
  2. 用户选择支付方式后,系统需要和第三方进行交互,获取到结果后,可能跳转到第三方收银台,也可能在当前页面展示收款二维码;
  3. 用户打开一种支付方式,没有完成付款的情况下,又选择打开了其他支付方式。如准备用微信支付,打开收款二维码后,发现微信余额不足,遂即打开支付宝支付,此时两个支付方式都可以做支付操作。
  4. 由于产生待支付单后,支付状态是不可控的,支付状态由第三方回调信息中获取。所以很难控制支付单的再次产生。

概念解释:

  1. 支付单:具体的支付方式,如 支付宝的扫码支付,微信小程序支付
  2. 一个订单对应多个支付单,但一个订单下 只允许出现 一笔成功的支付单

支付流程

支付流程是一套异步的操作。

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

不可控的因素是在第三和第四步。

image

image

解决方案

第一步

用户的一个订单,选择支付方式触发第三方支付平台之前,先扫描该订单下的待支付订单,如果存在,调用其订单关闭接口,使之前的待支付单立即失效。然后再创建新的支付单做支付。

理想状态下,创建的支付单可以随时关闭,这就可以完美解决重复支付问题。

不完美的情况:支付宝的扫码支付可以随时关闭支付订单,但是支付宝的电脑网站支付,必须在用户扫码后才能关闭支付订单。(这一点被坑了很久),应对此问题,解决方案还需要往下走。

第二步

用户支付成功后,收到第三方的回调通知,修改订单状态后,再次扫描待支付的订单,对其进行关闭订单的操作。

这个步骤,不仅是对第一步的加强,也是可以适应不同的需求场景。如:一个业务方常用的是微信和支付宝两种支付方式,为了减少和支付中心的交互次数,就在创建订单的时候,替用户创建了这两种支付的二维码,让用户选择使用,这种情况下就不能对待支付单做立即关闭了,否则只有一个二维码是生效的。

所以创建订单时加了一个选择策略来控制是否立即关闭历史支付订单。然后用第二步来兜底。

到此还是不能完全解决重复支付,原因如下:

  1. 支付宝的不能及时关闭,到此不一定能完全解决。
  2. 部分银行的接口功能中,没有提供及时关闭订单的功能。
第三步

针对以上策略下的漏网之鱼,还需采取补偿策略:

定时任务:定时对账,通过定时任务,扫描那些支付成功,且实际支付金额大于应收金额的支付单,或者说一个订单号,存在多笔成功的支付订单的数据。按时间顺序,将后面支付的支付单做主动退款。

这一步还是不能完全解决问题。原因:部分支付平台不支持退款,这种情况下,无法做到主动退款,只能依赖人工操作。

第四步

最后的无奈之举:发现重复支付的支付单数据,且没有退款接口可用时。发起一条工单,通知到运营端,让运营去人工处理。

CLC

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xplan5/article/details/109639371

智能推荐

JVM 运行时数据区_铁锚的博客-程序员宝宝_jvm 运行时数据区

翻译人员: 铁锚翻译时间: 2013年11月11日原文链接: JVM Run-Time Data Areas参考地址: JVM运行时是什么样子?这是阅读JVM规范时的笔记, 我画了一幅图来帮助自己加深理解:图1 JVM运行时数据区1. 单线程数据区(非共享)从上图可以看出,每个线程都有自己独立的数据区,包括 PC(程序计数器),JVM(方法)栈,以及本地

OLTP VS OLAP_Aaron_Kitty的博客-程序员宝宝_oltp vs olap

数据处理场景大致分为两大类:联机事务处理 OLTP 联机分析处理OLAPOLTP 是事件驱动 面向应用的 基本特征: 前台接收的用户数据可以理解传送到计算中心进行处理,并在很短的时间内给出处理结果,是对用户操作的快速响应 例如银行交易系统OLAP 是面向数据分析的,面向信息分析的处理过程 特征:应对海量数据,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。例如数据仓库,大数据应用。...

MQTT----基于mosquitto库C语言实现发布和订阅_Frank-Hu的博客-程序员宝宝_mqtt c语言

一.MQTT是什么MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。MQTT是一个基于客户端-服务器的消息发布/订阅传输协议

android隐式广播和显式广播是什么,在Android8.0上突破隐式广播的限制_海边的骑士的博客-程序员宝宝

上面所说即:若App的TargetSDK达到了26, 我们正常静态注册的广播就没有用了。能用的仅有以下豁免的Broadcast, 包括我们自己正常发广播,如果不指定包名, 静态注册的也是收不到的。PS:动态注册是没有影响的在我们收不到广播的时候,系统会有如下打印,即这个后台的广播接收器不会被执行04-21 04:12:27.513 2431 4821 W BroadcastQueue: Backg...

Android端本地音乐播放器(二)---应用主界面的实现_RSMung的博客-程序员宝宝_edu whut ruansong.

前言介绍见Android音乐播放器(一)DisplayActivity设计见图1,实际效果见图2。图1的左侧是DisplayActivity布局文件的层次结构。 图1 图2 一.主界面的toolbar的实现流程1.设置主题style.xml想要使用toolbar首先需要将actionbar置空,在styles.xml中设置AppTheme的parent为xxxx.NoActionBar。AppTheme默认在manifest文件中被使用为app

随便推点

One PUNCH Man——线性回归算法_No_Game_No_Life_的博客-程序员宝宝

文章目录回归分析的目的线性回归步骤一元线性回归操作和解释回归分析的目的回归分析的目的大致可分为两种:第一,预测。预测目标变量,求解目标变量y和说明变量(x1,x2,…)的方程。y=a0+b1x1+b2x2+…+bkxk+误差y=a_{0}+b_1x_1+b_2x_2+…+b_kx_k+误差y=a0​+b1​x1​+b2​x2​+…+bk​xk​+误差把上述方程叫做(多元)回归方程或者(多...

SpringBoot自动配置流程图_洺润的博客-程序员宝宝

在学习大佬的文章时(这样讲 SpringBoot 自动配置原理,你应该能明白了吧),为了使执行过程更加直观,也为了方便以后的复习,随手将讲解原理的过程绘制成了一张图,如下:...

node版本升级:与node-sass、sass-loader版本不兼容问题以及npm install时报错问题解决方法_更新node-sass_Zaralike的博客-程序员宝宝

node 版本与node-sass、sass-loader版本不兼容问题npm install 时报错 run `npm audit fix` to fix them, or `npm audit` for details

Linux下安装ElasticSearch 和 Kibana可视化界面服务_踟蹰千年的博客-程序员宝宝

1确保linux环境下已经安装有jdk环境 虚拟机至少保证有1G内存2下载ElasticSearch安装包,并上传到服务器3解压tar -zxvf4设置配置文件 elasticsearch.yml 网络配置5启动es[[email protected] bin]# ./elasticsearch这时候会出现报错原因是不能以root用户启动ES解决方案:...

Hystrix仪表盘--Unable to connect to Command Metric Stream_攻城狮Luke(刘健彬)的博客-程序员宝宝

Unable to connect to Command Metric Stream  这个是错误是链接不上,错误原因可能是缺少jar包或者没有Enable相关服务。针对ribbon实现断路由监控1.则pom中需要加入以下jar包[java] view plain copy<dependency>              <groupId>org.springframew...

java实现计算2000-3000年中的闰年_King丶段的博客-程序员宝宝

java实现计算2000-3000年中的闰年初学java记录贴题目:输出2000年到3000年中的闰年。//普通年能被4整除且不能被100整除的为闰年//世纪年能被400整除的是闰年public class RunNian { public static void main(String[] args) { System.out.println("以下为2000年~3000年中的闰年"); for(int i = 2000; i <= 3000;i++) {

推荐文章

热门文章

相关标签