支付失败,页面刷新问题的讨论

如题今天要描述一个问题是:程序在确认订单时拉起第三方支付,支付失败了,引起的问题。

为了能清楚的描述问题,我把场景复现一下,大家肯定都有过APP购物的体会,大家一定知道有一个按钮叫“确认”或者“结算”之类的功能按钮,
点击一下弹出一个框让进行微信支付或支付宝支付或银联支付或其他什么支付的。
那么这个“确认”或者“结算”功能按钮在背后到底做了哪些事情,成功或失败是怎么处理的,需要怎么处理,这些都是值得讨论的问题。
我们在做商城时就遇到了这样的问题,我们的问题是我们第一次点击“确认”按钮时程序报错说支付失败了,第一次失败没问题呀,可能就是支付有问题吧,
那么我们再次支付呗,OK,那我们再点,这个时候又报错了,说购物车为空,我们测试的妹妹就晕了。
明明页面上购物车里有商品啊,谁说的为空,你自己看嘛,你自己看嘛,谁说的为空。

这就是我们的问题。

那么遇到这样问题怎么办,我们把问题细化,细化,在细化,我们一步一步看每一个步骤。
然后我们会看到在订单提交,支付付款时,这里边的过程需要细化的地方就是,
先要创建订单把用户购物车里所有的商品、优惠券、积分等所有东西计算好之后把的结果落库,把购物车清空。然后在向微信、支付宝等发起支付。

这里细化出来了两步:

1、创建订单,清空购物车
2、向第三方支付平台支付。

既然分出来了两步那么每一步就都会有出错的可能,每一步都可能出错。出错并不可怕,可怕的是出错不认,知错不改,明知故犯。
上边的两步必须是顺序的执行,也就是说如果第一步出错,那么第二部不用执行了,而如果第一只执行成功,第二步失败,这个问题就比较麻烦。
这就是我们的测试妹妹看到的问题,
第一次是真的支付失败了,但是创建订单成功了,所以购物车在后台数据库清空了,而页面没有刷新,第二次呢购物车真的是清空了,在也无法支付了,因为后台会重新检测购物车。

我们先来这样讨论问题

首先我们已经把上边说步骤分成了两步,那么我们就按照两步的方式来思考问题,
每一步在一个单独的事务里。为什么要这么看,好像APP的开发人员说支付这一步骤是APP直接调用微信或支付宝不通知服务器端的。
那么只有第一步是服务器完全可以控制并且在自己的一个事务里,那么可以给APP一个约定,成功怎么处理,失败怎么处理。
如果失败还好,直接说你不用走第二步,当然失败不是我们期望的,我们也不希望失败,所以第一步成功了。
那么到第二步怎么办呢,这里按之前所说的这一步是APP直接拉起微信或支付宝的,服务器端根本不知道,
那么这里的问题就来了,如果支付成功,当然是我们期望的一个结果,直接写会数据库,支付成功回写,这没有问题,是我们期望的结果。

(这里没有讨论,在支付成功后回写失败怎么办,这也是一个大坑,虽然概率不大,但是不能假设,特别是量大的时候,一旦出了问题,用户可是真的付钱了,非常特别的麻烦。你拿用户钱,告诉用户没有支付,不给用户东西,你抢钱嘛)

那么支付失败怎么办,我们的焦点问题就是如果支付失败了,APP又不刷新,仍然能够看到购物车里有商品,
而实际上因为第一步的调用成功,购物车已经被清空,购物车里的商品已经落库成为了订单。
这时不能在对购物车进行支付,而实际上是订单。订单的状态是未支付或者支付未成功。

讨论到这里,我们已经非常明确的看到以下内容,首先在显示上混淆了支付的是订单还是购物车,
因为在APP上显示的是操作一次,按了一个按钮发起两个调用,让用户感觉到或者给你迷惑的感觉的是我这次是对购物车进行的支付,
买的是购物车里的商品嘛,但实际上是由于APP在进行了第一次创建订单调用后,紧接着直接拉起支付,没有通知服务器。
这是两个步骤在一起有没有很好的异常处理或回滚机制,看似友好减少用户操作的行为却是给用户造成了极大的麻烦。
首先这里一定要明确的是支付的一定是订单,不是购物车。

当然用户是可以不用感知到支付的是购物车或者是订单的,也可以不用感受到有任何变化的,这个时候如果两次调用全部成功或一起失败,都是没有问题的,
但我们的问题就是用户没有任何感知的情况下,第一个调用成功了,第二个失败了,APP还没有任何变化,或者没有刷新的意愿,
那么第一个数据想回滚,或者有回滚的意愿,或者想有其他的处理,问题就来了。

对于这样的一个分析,上边解决问题是思路相对也比较明确,
第一种,那就分成两步,每一步单独处理,从业务流程上给出一个解决方案,
就是可以一起提交,但是如果是第一步成功,第二步失败了,那么在这个时候,APP上可以给给提示,比如说:你的订单已经提交但是支付失败请尽快完成支付,等。
或者干脆就是直接分成两步,第一步订单提交,并给出提示。第二,订单支付并给出提示。

第二种,我们假设这个支付可以有服务器端发起,把创建订单落库和支付的业务放进一个事务里,同样是一次调用,那么由于事物的支持,可以一起失败,一起成功,这想来是比较棒的解决方案。

第三种可能,是不是可以有APP来发起事务,然后第二调用失败了,让第一次操作取消,那这里就涉及到两种方案,
第一种,使用事务,让第一次的提交在第二支付成功时,才真正落库。
第二种,可以把第一次是数据库状态变成提交前的状态,相当于回滚,但实际上更加类似又重新了一份数据,或者更改原来的为落库前的状态。
或者第三种,让用户先支付成功,然后在写订单,(这种感觉是骚主意吧,还涉及到库存,那一超卖,在那个点上卖完了怎么办)

第四种可能,APP同时把自己拉起支付调用的支付结果在告诉服务器,让服务器来返回APP的这一次行为是最终该怎么办。

其他解决方案,欢迎大家一起吐槽,讨论~

回来散步时又想到一种办法,

第五种办法,在用户点“确认”或“结算”按钮时,在服务器端校验除了校验购物车是不是为空的话,
再校验最近的没有支付或者支付失败的订单的,对比一下点击按钮的时间和订单的时间,在某个可允许的范围内,可以假设就是支付的这个订单。
(想来这个也是一种不算好的办法,但理论上也可以解决这样情况的大部分问题)

本来我想是非常简单的问题,除了第三种然APP来做事务相对困难,其他的都是业务流程稍微变化一下就能很好的解决问题,
但是我反应了几次这样的问题,也是没人理我,感觉世界好悲哀。
有人认定了创建订单有问题,有人说支付有问题,支付不成功嘛,我给人解释,但是感觉...就是那种感觉“他人笑我太疯癫,我笑他人看不穿”。

但是就像我上边说的出错不认,知错不改,产品不改进,用户体验差,拼命的让程序员改一些明明流程问题引起的看似是BUG的问题,可怜那些悲催的程序员吧。

时间: 2024-10-11 00:25:38

支付失败,页面刷新问题的讨论的相关文章

到处都是坑的微信支付V3之 微信支付回调页面

据上次 到处都是坑的微信支付V3 后很多园友在被虐了千百遍后终于跳转到了亲切的微信支付界面,但输入密码支付后却不知道怎么处理了,接下来补上支付后的处理流程. 1. html中根据前台支付后反馈信息成功与否跳转到相关页面 if (res.err_msg == "get_brand_wcpay_request:ok") { // message: "微信支付成功!", window.location.replace("@Url.Content("~/

asp.net 防止页面刷新或后退引起重复提交

 项目中经常遇到刷新后重复的向数据库增加一条相同的记录,造成数据重复,如何规避这些问题呢?下面我们就一起讨论一下在asp.net怎样防止页面刷新或后退引起重复提交数据的问题: 其实asp.net防止刷新是asp.net开发中经常遇到的问题.通常有多种方法来实现:(下面只是个人总结的一些方法)不全,也很希望朋友们能多多补充.谢谢. 1:请求转发(即在数据提交操作后立即转跳到其他页面,防止页面刷新引起回发操作) 2:不保存缓存 Response.Cache.SetNoStore(); (即提交后表单

Django项目:CRM(客户关系管理系统)--49--40PerfectCRM实现全局账号注册+验证码+页面刷新保留信息

1 # gbacc_urls.py 2 # --------38PerfectCRM实现全局账号登录注销-------- 3 from django.conf.urls import url 4 from gbacc import gbacc_views 5 urlpatterns = [ 6 url(r'^gbacc_login/', gbacc_views.gbacc_login, name='gbacc_login'), # 全局登录 7 # LOGIN_URL = '/gbacc/gba

Vuex数据页面刷新丢失问题解决方案

用Vue做项目开发很久了,对于vuex能用.会用,但是因为状态脱离页面和刷新丢失两个原因,一直都有种抵触,特别是一些简单的数据都是通过query或者本地存储就解决了,然而对于一些复杂内容,不可避免的还是要使用Vuex去处理(真香),但是刷新丢失的问题,的确叫人头大.最近闲下来,我们来研究下怎么干掉这个问题- 不大了解Vuex的同学,可以先去官网溜溜 由于Vuex的数据是存储在内存中的,相当于memory cache,当页面刷新的时候内存被清空重载新内容,原来的数据就丢了,为了解决这个我们可以借助

03 页面刷新和表格刷新

表格赋予id var table = layui.table; table.render({ elem: '#test' ,url:'http://localhost:7300/mock/5e06d6ef83b40c266813ee7f/example/user/list' ,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板 ,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标.如无

笔记03 异常支持、防止页面刷新和后退、方法验证

Struts2对异常支持(声明式异常.自动的异常处理), 异常处理(运行期异常事务自动回滚) 1. 自定义异常类,继承RuntimeException或Exception实现构造方法. 2. 配置异常:全局页面跳转,与全局异常处理.其他包如果想使用,需要继承. <global-results> <result name="error">/error.jsp</result> <result name="input">/

ASP.NET—012:避免页面刷新造成的闪烁问题

本文继续说一个常识问题.大家都知道,ASP.NET页面都有一个page_load事件.当页面第一次加载时都会执行这个事件.而细心的童鞋肯定也发现,当给页面上的控件注册事件后,执行这个控件的事件时也会执行这个page_load.也就是说每点击一次事件都会刷新一次页面,造成页面闪烁.这样的效果是不好的.那么如何避免这个问题呢,当然是靠javascript了,以及高级一点的jQuery等.一般ASP.NET页面必然要配合javascript来实现功能了.页面控件的事件最好都用javascript来实现

AngularJS进阶(二十八)解决AngualrJS页面刷新导致异常显示问题

解决AngualrJS页面刷新导致异常显示问题 绪 俗话说,细节决定成败,编程亦是如此.编程过程中我们可能会不自觉的忽视一些细节问题,殊不知,这些细节正是导致页面显示出现问题的地方.今略举一例,与君共勉之. 页面正常加载后,显示如下: 按F5刷新之后,页面如下所示: 很明显,页面显示出现了异常.回过头再看看Chrome的错误提示, 具体代码如下: 正是以上代码导致了错误的发生. 追根溯源 让我们回顾一下,错误到底是如何发生的.正常加载情况下,页面正常显示很容易理解,程序是按照既定的数据流走的.但

ASP.NET页面刷新的实现方法总结

先看看ASP.NET页面刷新的实现方法: 第一: C#代码   private void Button1_Click( object sender, System.EventArgs e ) { Response.Redirect( Request.Url.ToString( ) ); } 第二: C#代码   private void Button2_Click( object sender, System.EventArgs e ) { Response.Write( " < scri