钱被扣走了,但是订单却未成功!支付掉单异常最全解决方案

掉单异常

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


我们以携程为例,在其上面发起一笔订单支付,将会经过三个系统:

  1. 携程创建订单,向第三方支付公司发起支付请求
    2.第三方支付公司创建订单,并向工行发起支付请求
    3.工行完成扣款操作,返回第三方支付公司
    4.第三方支付完成订单更新并返回携程
    5.携程变更订单状态

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


在这个过程就可能会碰到,用户工行卡已经扣款,但是携程订单却还是待支付,我们通常将这种情况称为掉单。

上述掉单的场景,多数是因为③、⑤环节信息丢失导致,这种掉单我们将其称为外部掉单。

还有一种极少数的情况,收到 ③、⑤环节返回信息,但是在④、⑥环节内部系统更新订单状态失败,从而导致丢失支付成功的信息,这类掉单由于是内部问题,我们通常将其称之为内部掉单。

外部掉单

外部掉单是因为没有收到对端返回信息,这种情况极有可能是网络问题,也有可能对端处理逻辑太慢,导致我方请求超时,直接断开了网络请求。
增加超时时间

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

不过这里需要注意了,在我们增加网络超时时间之后,我们可能还需要调整整个链路的超时时间,不然有可能导致整个链路内部差事从而引起内部掉单。

画外音:对接外部渠道,一定要设置网络连接超时时间与读取超时时间。

接收异步通知

第二个办法,接收渠道异步回执通知信息。

一般来说,现在支付渠道接口我们都可以上送一个异步回调地址,当渠道端处理成功,将会把成功信息通知到这个回调地址上。

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


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

对于异步请求信息,一定需要对通知内容进行签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。
异步通知将会发送多次,所以异步通知处理需要幂等。

掉单查询

有的渠道可能没有提供异步通知的功能,只提供了订单查询的接口,这种情况下,我们只能使用第三种解决办法,定时掉单查询。

我们可以将这类超时未知的订单的单独保存到掉单表,然后定时向渠道端查询订单的状态。

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

若查询依旧未知,这时我们需要等待下次查询的结果。


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

对账

最后,极少数的情况下,订单查询与异步通知都无法获取的支付结果,这就还剩下最后一种兜底的解决办法,对账。

如果第二天渠道端给的对账文件有这一笔支付结果,那么我们可以根据这个记录更新直接更新我们内部支付记录。

那如果第二天也没有这笔记录的结果,这种情况下,我们可以认为这笔是失败的。如果用户被扣款,渠道端内部将会发起退款,将支付金额返回给用户。所以这种情况可以无需处理。

内部掉单异常

支付公司内部订单关系

接下来我们讲下内部掉单异常,首先我们来看下为什么会发生内部掉单的异常,这其实跟我们系统架构有关。

如上图随所示,第三方支付公司内部表通常为支付订单与渠道订单这样一种 1 比 N 的关系。

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

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

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


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

这时如果第三方支付公司也拿相同的内部订单去请求外部渠道系统,有可能外部渠道系统并不支持同一订单号再次请求。

那其实我们也有其他办法,生成一个新的内部单号,更新原有支付订单上内部记录,然后去请求外部渠道系统。但是这样的话就会丢失上次支付失败记录,这就不利于我们做一些事后统计了。

那其实第三方支付公司也可以不支持相同的订单号再次发起请求,但是这样的话,就需要外部商户重新生成的新的订单号。

这样的话,第三方支付公司是系统是简单了,全部复杂度都交给了外部商户。

但是现实的情况,很多外部商户并不是那么容易更换生成新的订单号,所以一般第三方支付公司都需要支持同一外部商户订单号在未成功的情况下,支持重复支付。

在这种情况下,就需要我们上面的 1:N 的订单关系图了。

内部掉单异常的原因

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

由于支付订单是表保存着外部商户订单与内部订单关系,支付订单未成功,所以外部商户也无法查询得到成功的支付结果。

此时渠道订单表已经成功,所以上面外部掉单的方法并不适用内部掉单。
内部掉单异常解决办法

第一种解决办法,分布式事务。

内部掉单异常,说白就是因为支付订单表与渠道订单表无法使用数据库事务保证两者同时更新成功或失败。

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

不过我们没有采用这种分布式事务,一是因为之前开发的时候市面上并没有开源成熟分布式事务框架,第二自己自己开发难度又很大。

所以对于分布式事务这一块,并没有什么使用经验。如果有使用分布式事务解决这类的问题同学,留言去可以评论一下。

第二种解决办法,异步补偿更新。

当发生内部掉单的情况,即更新支付订单失败等情况,可以将这里支付订单保存到一张内部掉单表。

但是这里可能会有一个问题,我们无法保证保存到内部掉单表这一步骤也一定成功。

所以说,我们还需要定时查询,查询一段时间内支付订单未成功,而渠道订单表已成功的支付订单记录,然后也将其插入到内部掉单表。

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

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

总结

今天主要介绍了支付系统中的掉单异常,这类异常往往会导致用户实际已经被扣钱,但是商户订单还是等待支付的情况。

这个异常如果没有很好处理,将会导致客户用户体验很不好,还有可能收到客户的投诉。

掉单的异常,通常可以外部系统与内部系统。而大部分的掉单都是因为外部系统导致,我们可以增加超时时间,掉单查询,以及接受异步通知解决 99% 的问题,剩下 1% 的掉单只能通过次日的对账来兜底。

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

看完点赞 ~养成好习惯,以上内容希望对你有帮助,如果对软件测试、接口测试、自动化测试、面试经验交流感兴趣可以加入我们。642830685,免费领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354