钱被拿走了,订单却没有成功!最全面解决支付异常订单取消问题

2024-01-04
来源:网络整理

今天我就分享一些支付系统异常处理的方法。

事实上,这些处理方式不仅限于支付系统,还可以应用于其他系统。 您可以向他们学习并将其应用到您自己的系统中,以提高您自己系统的健壮性。

异常是系统运行过程中不可避免会出现的问题。 如果一切正常的话,我们的系统设计就会相当简单。

但遗憾的是,没有人能做到这一点,所以为了处理异常可能带来的问题,我们不得不添加很多额外的设计来处理这些异常。

可以说,在系统设计中,异常处理需要我们集中思考,会占据我们大部分的精力。

我们先来看看支付系统中最常见的异常:“掉单”。

1、异常掉单

最常见的支付平台架构关系之一如下:

上图是从第三方支付公司支付的角度来看的。 如果是自己公司内部的支付系统,那么外部商户其实就是公司的一些内部系统,比如订单系统,而外部的支付渠道其实就是第三方支付公司。

我们以携程为例。 如果您在携程发起订单支付,会经过三个系统:

携程创建订单并向第三方支付公司发起支付请求。 第三方支付公司创建订单并向工商银行发起支付请求。 工行完成扣款操作并返回给第三方支付公司。 第三方支付完成订单更新并返回携程。 携程更改订单状态。

上面的流程简单如下图所示:

在此过程中,您可能会遇到用户的工行卡已被扣除,但携程订单仍处于待付款状态。 我们通常称这种情况为“掉单”。

上述掉单场景大多是由于“③、⑤”环节信息丢失造成的。 我们把这种掉单称为“外部掉单”。

还有一种罕见的情况是,收到了“③、⑤”步骤返回的信息,但在“④、⑥”步骤中内部系统未能更新订单状态,导致支付成功信息丢失。 这种类型的订单下降是一个内部问题。 ,我们通常称之为“内部掉单”。

2. 外部下单

由于没有收到对等方的响应信息,外部订单被丢弃。 这种情况很可能是网络问题。 也有可能是peer的处理逻辑太慢,导致我们的请求超时,直接断开网络请求。

1.增加超时时间

对于这种情况,第一个也是最简单的解决办法就是“适当增加超时时间”。

不过这里需要注意的是,我们调大网络超时后,可能还需要调整整个链路的超时,否则可能会导致整个链路内部跑腿,导致内部订单下降。

画外音:连接外部频道时,必须“设置网络连接超时和读取超时”。

2. 接收异步通知

第二种方法是从通道接收异步回执通知信息。

一般来说,我们现在可以向支付通道接口发送异步回调地址。 当通道端处理成功后,会将成功信息通知到该回调地址。

这种情况下,我们只需要接收通知信息,解析它,更新内部订单状态即可。

支付系统异常处理-支付异步通知

这种情况下,我们需要注意以下几点:

对于异步请求信息,需要对通知内容进行签名验证,验证返回的订单金额与商户侧的订单金额是否一致,防止数据泄露造成“误通知”和财务损失。 异步通知会被多次发送,因此异步通知处理需要幂等。 3. 下单查询

有些渠道可能不提供异步通知的功能,而只提供订单查询的接口。 这种情况,我们只能采用第三种解决方案,定时下单查询。

我们可以将此类未知超时的订单单独保存到订单下降表中,然后定期向通道侧查询订单的状态。

如果查询成功或者明显失败(例如订单不存在等),可以更新订单状态并删除单表记录。

如果查询仍然未知,那么我们需要等待下一次查询的结果。

支付系统异常处理-定时查询

这里我们需要注意。 某些情况下,可能无法查询到返回订单的状态,所以我们需要设置订单查询的最大数量,防止无限查询浪费性能。

4. 和解

最后,极少数情况下,订单查询和异步通知都无法获取支付结果,这就留下了问题的最后解决方案——对账。

如果第二天渠道提供的对账文件中包含这条支付结果,那么我们就可以根据这条记录更新直接更新我们的内部支付记录。

旁白:为了更加谨慎,您可以先发起查询,然后根据查询结果更新订单记录。

支付单_支付单笔交易限额是什么意思_支付单日限额怎么解除

但在某些极端情况下,查询无法得到结果,可以直接更新内部记录。

如果第二天没有这条记录的结果,在这种情况下,我们就可以认为这次交易失败了。 如果用户被扣款,渠道端内部会发起退款,将支付金额退还给用户。 所以这种情况没有必要处理。

3、内部订单异常掉线 1、支付公司内部订单关系

接下来我们来说说内部掉单异常。 首先我们来看看为什么会出现内部掉单异常。 这其实和我们的系统架构有关。

如上图所示,第三方支付公司的内表通常支付订单和渠道订单是1对N的关系。

支付订单保存了外部商户系统的订单号,代表了第三方支付公司内部订单与外部商户订单的关系。

渠道订单代表了第三方支付公司与外部渠道的关系。 事实上,对于外部渠道系统来说,第三方支付公司就是一个外部商户。

为什么我们需要设计这种关系? 而不是使用下面的一对一关系?

如果我们采用上图中1对1的订单关系,如果第一次支付失败,外部商户可能会使用相同的订单号再次向第三方支付公司发起支付。

此时,如果第三方支付公司也使用相同的内部订单向外部渠道系统请求,则有可能外部渠道系统不支持使用相同的订单号再次请求。

其实我们还有其他的方式,生成新的内部订单号,更新原来支付订单上的内部记录,然后请求外部渠道系统。 但这样的话,最后一次支付失败记录就会丢失,不利于我们做一些事后统计。

事实上,第三方支付公司可能不支持再次使用相同订单号的请求,但在这种情况下,外部商户将需要重新生成新的订单号。

在这种情况下,第三方支付公司的系统很简单,所有的复杂性都交给了外部商户。

但现实中,很多外部商户更换并生成新的订单号并不容易,因此一般第三方支付公司需要支持在订单号不成功的情况下使用同一外部商户订单号重复支付。

这种情况下,我们就需要上面的1:N顺序关系图。

2、内部订单异常掉线的原因

当我们收到外部渠道系统的成功返回信息后,渠道订单表中的记录就被成功更新。 但是,由于渠道订单表和支付订单表可能不在同一个数据库中,或者不在同一个应用程序中,这可能会导致支付订单表的更新失败。

由于支付订单表存储的是外部商户订单与内部订单的关系,如果支付订单不成功,外部商户无法查询并获取成功的支付结果。

支付单笔交易限额是什么意思_支付单日限额怎么解除_支付单

此时渠道下单已成功,因此上述外部下单方法不适用于内部下单。

3、内部订单异常掉单的解决办法

“第一个解决方案是分布式事务。”

内部掉单异常是由于支付订单表和渠道订单表无法使用数据库事务来保证同时更新成功或失败导致的。

这种情况下,我们其实就需要使用分布式事务。

但是我们并没有采用这种分布式事务。 首先,我们开发的时候,市面上还没有开源成熟的分布式事务框架。 其次,我们自己开发它非常困难。

因此,我没有使用分布式事务的经验。 如果有同学使用分布式事务来解决此类问题,欢迎留言评论。

“第二种解决方案是异步补偿更新。”

当发生内部下单,即支付订单更新失败时,可以将这里的支付订单保存到内部下单表中。

但这里可能有一个问题。 我们不能保证保存到内部订单删除表的步骤会成功。

因此,我们还需要定期查询一段时间内是否有支付订单成功,但渠道订单表有成功支付的订单记录,然后插入到内部订单下降表中。

另外一个系统应用只需要定期扫描内部掉单表,支付订单成功,然后删除内部掉单记录即可。

这里需要注意的是,当支付订单表数据量较大时,定时查询可能会很慢。 为了防止影响主库,此类查询可以在备库中进行。

4. 总结

今天我们主要介绍支付系统中的掉单异常。 此类异常往往会导致用户实际上已被扣款,但商户的订单仍在等待付款的情况。

如果这个异常处理不好,就会给客户带来不好的用户体验,也可能会引起客户的投诉。

异常订单通常可能是由外部系统和内部系统引起的。 大部分掉单都是由外部系统造成的。 我们可以增加超时、降单查询、接受异步通知来解决99%的问题。 剩下的1%掉单只能通过次日对账解决。 。

系统内部引起的丢单异常是分布式环境中典型的数据一致性问题。 对于这类问题,我们不需要追求强一致性,只要保证最终一致性即可。 我们可以使用分布式事务来解决这类问题,也可以定期扫描状态不一致的订单,然后进行批量更新。

最后,这次我们只介绍一种支付系统掉单异常。 下一篇文章我将为大家介绍支付系统的其他异常情况,敬请关注!

参考:

知乎@天顺谈异常(一)

作者:楼下小黑哥; 微信公众号@ ,支付行业,后端技术

分享