热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

微擎支付返回商户单号_钱被扣走了,但是订单却未成功!支付掉单异常最全解决方案...

前言好了,回归到今天的主题,今天分享一下支付系统中异常一些处理方式。其实这些处理方式并不只是局限于支付系统,也可以适用于其他系统ÿ

前言

好了,回归到今天的主题,今天分享一下支付系统中异常一些处理方式。

其实这些处理方式并不只是局限于支付系统,也可以适用于其他系统,大家可以借鉴,应用到自己系统中,提高自己系统的健壮性。

异常是系统运行不可避免会发生的问题,如果一切都正常,我们的系统设计将会相当简单。

但是可惜没有人能做到这一点,所以为了处理异常可能导致的问题,我们不得不需要加上很多额外的设计,用来应对这些异常。

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

下面我们先来看下支付系统中最常见的异常:「掉单」

❝ 欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn

掉单异常

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

c6e54a5df4b30a2633a3b3d07af95dec.png
❝ 上图我们是站在第三方支付公司支付角度,如果是自己公司的内部支付系统,那么外部商户这一块其实就是公司内部一些系统,比如说订单系统,而外部支付渠道其实就是第三方支付公司

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

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

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

6b7c22f0f1bf27c6ce299b6628c3520b.png

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

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

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

外部掉单

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

增加超时时间

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

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

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

接收异步通知

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

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

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

c835738f335a0929885d0e27c1735cd2.png

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

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

掉单查询

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

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

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

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

4dd82de4cb21c12ea9aefbb047e5528a.png

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

对账

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

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

之前小黑哥写过一篇对账文章,感兴趣的可以再看一下:聊聊对账系统的设计方案

❝ 画外音:稳妥一点,可以先发起查询,然后根据查询结果更新订单记录。
不过有些极端情况,查询无法获取结果,那么直接更新内部记录即可。

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

内部掉单异常

支付公司内部订单关系

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

c6648e739ef1a6c4f33a7bee721a05dd.png

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

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

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

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

9daec997aa7a80e5e4ce1f7ed1cbafb5.png

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

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

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

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

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

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

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

内部掉单异常的原因

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

3cf7be35a425ac6b8b8cc86e0facd125.png

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

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

内部掉单异常解决办法

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

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

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

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

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

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

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

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

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

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

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

总结

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

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

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

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

最后,这次只是介绍支付系统中一类掉单异常,下一篇文章中,再给大家介绍一下支付系统的其他异常,敬请期待!

参考资料

  1. 知乎@天顺 谈谈异常(一)



推荐阅读
  • 通过Web界面管理Linux日志的解决方案
    本指南介绍了一种利用rsyslog、MariaDB和LogAnalyzer搭建集中式日志管理平台的方法,使用户可以通过Web界面查看和分析Linux系统的日志记录。此方案不仅适用于服务器环境,还提供了详细的步骤来确保系统的稳定性和安全性。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
  • 创建项目:Visual Studio Online 入门指南
    本文介绍如何使用微软的 Visual Studio Online(VSO)创建和管理开发项目。作为一款基于云计算的开发平台,VSO 提供了丰富的工具和服务,简化了项目的配置和部署流程。 ... [详细]
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 探讨架构师在项目中应如何平衡对产品的关注和对团队成员的关注,以实现最佳的开发成果。 ... [详细]
  • HBase运维工具全解析
    本文深入探讨了HBase常用的运维工具,详细介绍了每种工具的功能、使用场景及操作示例。对于HBase的开发人员和运维工程师来说,这些工具是日常管理和故障排查的重要手段。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 本文探讨了2012年4月期间,淘宝在技术架构上的关键数据和发展历程。涵盖了从早期PHP到Java的转型,以及在分布式计算、存储和网络流量管理方面的创新。 ... [详细]
  • 科研单位信息系统中的DevOps实践与优化
    本文探讨了某科研单位通过引入云原生平台实现DevOps开发和运维一体化,显著提升了项目交付效率和产品质量。详细介绍了如何在实际项目中应用DevOps理念,解决了传统开发模式下的诸多痛点。 ... [详细]
  • 2018年3月31日,CSDN、火星财经联合中关村区块链产业联盟等机构举办的2018区块链技术及应用峰会(BTA)核心分会场圆满举行。多位业内顶尖专家深入探讨了区块链的核心技术原理及其在实际业务中的应用。 ... [详细]
  • PostgreSQL 10 离线安装指南
    本文详细介绍了如何在无法联网的服务器上进行 PostgreSQL 10 的离线安装,并涵盖了从下载安装包到配置远程访问的完整步骤。 ... [详细]
  • 使用Pandas高效读取SQL脚本中的数据
    本文详细介绍了如何利用Pandas直接读取和解析SQL脚本,提供了一种高效的数据处理方法。该方法适用于各种数据库导出的SQL脚本,并且能够显著提升数据导入的速度和效率。 ... [详细]
  • 本文详细解释了如何使用@IfProfileValue注解来检测Spring框架中的配置文件是否处于活动状态,并探讨其与@Profile和@activeProfiles的区别。 ... [详细]
  • FinOps 与 Serverless 的结合:破解云成本难题
    本文探讨了如何通过 FinOps 实践优化 Serverless 应用的成本管理,提出了首个 Serverless 函数总成本估计模型,并分享了多种有效的成本优化策略。 ... [详细]
author-avatar
孤独秀风_328
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有