我们失败了,银行成功了
这是支付里面,最最最常见的一种错误,如果是没有接触过支付的人,100%会踩的一个坑。
这种问题基本都出在网络交互的原理理解上面:
最常规的http交易来说,如果http报了错误,大部分码农是直接认为这笔交易失败。这是非常致命的一种想法,一次Http交互,大致可以分为以下4步:
- 建立连接
- 发送数据
- 接收数据
- 断开连接
这里,每一步都有可能存在错误。但是并不代表每个地方出错的处理方式都是一样的。
- 建立连接时失败了,这笔交易是失败的,这是毫无疑问
- 发送数据失败,我们知道http是建立在TCP基础上面的,TCP相对于UDP来说,能知道发送是否成功,本质是因为多了服务器的ack回复,假设ack回复时,超时了,这时服务器实际上是接收到了支付请求,那就会直接产生交易。所以,这种情况,其实对于我们来说,应该是认为是一种未知的状态,因为我们无法确定银行服务器是否真的收到了请求
- 接收数据失败,那就更加明显了,银行是已经收到了数据,但是到底有没有处理完,或者到底处理的结果是成功还是失败,我们完全无法确定。
- 断开连接失败,那这个和建立连接失败是一种概念,也可以肯定是交易结果
- 在网络交互的过程中,产生了一些不可抗拒力,导致程序停止,比如说机器宕机了,断电了,应用程序别的地方出现异常导致crash了
这里有一些机智的小伙伴就会说到,只要api解决幂等问题,那重复提交就OK了。
很遗憾的告诉你:
- 很多银行系统还是几十年前的系统,他们根本没有幂等的概念
- 你何时可以提交成功?这是一个未知数,在商业环境下面,你不可能让客人等待一个完全不知道何时可以提交成功的交易
正确的交易流程:
这种交易,不管任何情况结束后,只会产生4种交易状态:
- 交易正常成功
- 交易失败
- 交易未完成:由于不可抗力,导致应用程序中间停止运行
- 交易未知
1和2的交易都非常好理解,已经是最终状态了。主要问题在于3和4。这2种状态其实本质都是一样,未知结果。
处理方式:
直接退钱
在发起交易的时候,银行API一般会要求发送一个类似Transaction ID的参数,这个参数由客户端产生,并且唯一。银行一般会返回3种状态:交易不存在,交易已经取消,退款成功。应该都当成是退款成功
查询报警
一些电子现金交易,是无法退钱的,查询出来结果后,如果交易确实是失败了,那就当做失败。如果是成功了,那就需要设置成功,并且标注这笔交易需要人工处理,把金额人工退还给持卡人,不能结算给商户。
切记,在做整个交易过程中的数据库操作,不要做成事务!
处理未知交易的时间,最好定在2个小时后(时间可以根据实际情况自由发挥),2个小时内的交易,不要处理。因为你无法知道未完成的交易是真的未完成还是有问题的未完成。