三方支付系统中的补单机制:解决支付过程中的订单状态问题
2024-08-14
370浏览
三方支付系统中的补单机制:解决支付过程中的订单状态问题中的支付单继续推进后续流程;若不一致则返回错误。本文首先介绍了什么是补单,接着基于三方支付系统的实现完整阐述了补单机制的演进过程,最终演化为一种相对通用的异常处理模式,即基于消息队列、有限状态机与多重任务兜底的业务层最终一致性保障机制,供大家参考指正。

日常生活中,从线下超市购物,到线上外卖订餐、电商购物,支付无时无刻不在发生,无论是通过现金、POS 卡还是微信支付宝等第三方支付。在线支付拥有及时、便捷、流畅完成的极致体验。当然,也有体验不够流畅的少数时候,比如我们早期在 12306 PC 版上购买火车票,支付完成后,订单的支付状态往往更新不及时,会有一段时间的延迟,有时甚至会在未付款状态下等待很长时间。

在支付过程中,由于各种原因(如外部通道处理出现问题、异步回调来不及)导致中途停止,用户看到订单仍未支付时不知所措。这时候就需要一种机制来促使交易的完成。本文以第三方支付系统中的订单补单机制为例,介绍一种比较常见的单据补偿模型。

1.第三方支付体系简介

1.1 什么是第三方支付?

所谓第三方支付,就是与各大银行签约,独立于商户和银行,具有一定实力和信誉保障,为商户和消费者提供支付结算服务的第三方独立机构。是买卖双方之间具有公信力的第三方,扮演担保人和资金保管人的角色。第三方支付也可以称为虚拟账户支付,消费者在第三方支付机构开立虚拟账户,使用虚拟账户中的资金进行支付。业内常见的第三方支付方式有支付宝、微信支付、美团支付、京东支付等。

1.2 第三方支付的交易和支付系统

什么是交易,最直观的描述就是“一方交钱,另一方交货”。一笔交易会形成买卖双方的债权债务关系。交易的存在是支付发生的前提,用户通过使用一定的支付方式完成交易,交易是支付过程的驱动,根据具体场景组合不同的支付指令,完成交易资金的划转。

支付是资金流交易处理的工具,目的是清偿债权债务关系。支持多种支付方式(如银行卡支付、余额支付、券种组合支付、类似花呗的信用支付等),负责与账户、会计、计费系统等资金处理能力对接,接收支付指令,驱动资金交换的完成。将实际支付行为(实际资金)与内部核算(虚拟资金)相结合,确保虚实一致。

三方支付整体业务架构如图1所示。交易核心与支付核心在业务划分上处于“收单支付域”,具备普通交易的收款、支付、退款、充值、转账、提现等通用功能,也包括联付、担保、分账等电商业务支持能力。交易核心与支付核心均有异常校验与赔偿模块,涵盖了所有业务的赔偿流程补单,也是本文的主体部分。

图1.第三方支付业务架构

2. 什么是补充命令?为什么需要补充命令?

一笔交易在支付过程中,因为环节出现各种异常而中断,此时这笔交易处于中间状态,俗称“卡单”,也就是卡在那里,没有往前推进。还有一种情况,支付核心向渠道发起扣款,渠道受理之后,银行卡扣款成功,但是因为各种原因,没有向支付核心发起回调,导致支付没有完成,用户没有享受到相应的权益,但是银行卡里的钱已经扣除了,这种情况就叫“掉单”。

不管是卡单还是掉单,都是处于中间状态的订单。补单就是对中间状态的订单进行补偿,直到达到最终状态(成功或者失败)。补单一般有两个关键点,一个是补偿的有效性,极端情况下可能补偿多次都不成功,不能放弃,需要后备机制。另一个是补偿的时效性,因为挂单时间越长,用户体验越差。

交易核心与支付核心的订单成交功能相辅相成,在设计和实现上具有一定的相似性。我们将以支付核心的订单成交功能为例,介绍异常订单成交机制。

3.如何实现订单补货

本章首先介绍订单履行的业务流程和实现前提条件,然后介绍订单履行机制的演进过程、各个版本中存在的问题以及下一版本中将如何解决这些问题。

3.1 有限状态机和幂等性

识别基金运作的有限状态机

我们首先以用户发起余额提现的例子来说明业务流程,简化后的流程如图2所示。

图 2. 余额提现流程

首先生成支付订单,然后向会计系统请求扣除用户账户中的余额;接下来向外部通道发起支付操作;资金操作完成后统一处理结果并更新单据信息;最后向上下游进行一些异步通知,以消息、RPC回调等形式进行。

我们将每个关键资​​金操作的状态记录都存储在数据库中,如下表所示。其中,提现指钱从哪里来,存入指钱去哪里,冲销指回滚交易。提现场景中,钱从用户的支付账户中提现,并转入用户的银行卡。其他场景,如充值(银行卡 -> 用户支付账户),会有不同的资金流向。表中最后两个加粗的场景还未达到最终状态,我们需要对此进行弥补。

为了便于理解,这里我们省略了取反的相关操作,一个余额提现流程的状态机转换如图3所示。

图 3. 余额提取流程及状态机转换

可重入性和幂等性保证

发起支付会涉及多个系统调用,由于网络原因导致通信超时是常见问题。同时上游系统也可能会重新发起请求,这就需要我们的系统保证操作结果的幂等性。少部分用户还可能同时有多端并发请求,这就需要我们保证接口的可重入性。除了服务本身,我们的下游依赖也需要保证它们的接口具备同样的能力。

我们先来介绍一下什么是可重入性和幂等性。

具体在业务中,幂等性是指一次支付已经到达最终状态,对于第一次没有获取到最终业务结果的请求,再次发起调用的结果可能不一样(处理中 -> 处理成功或者失败)。那么业务流程的可重入性和幂等性如何保证呢?我们来分别分解一下每个步骤:

生成付款单:首先付款单可以使用业务方传递的外部订单号作为唯一索引,这个订单号可以保证唯一,如果插入数据库时​​发生唯一索引冲突,则会查询已有数据进行幂等参数验证,如果和当前请求的参数一模一样,则说明是重复请求,可以使用DB中的付款单继续后续流程;如果不一致,则返回错误。

资金处理流程:账户和渠道系统各自保证接口的幂等性。我们也会维护每个下游操作的状态,并根据状态机决定是否继续进行,尽量不向下游输出重复的流量。例如,如果支付订单已经完成所有资金处理,状态机已经处于最终状态,那么接口可以直接返回相应结果。

更新支付订单信息时,需要先对支付订单加行级排他锁,再进行更新,保证多个并发请求中只有一个能够成功。

异步通知,在支付订单推进到最终状态后进行。

通过可重入性和幂等性保证,我们可以广泛重用前向过程来实现订单填写接口。

3.2 初始版本

一般来说,订单补偿最常见的形式是设置一个定时任务定期扫描表来完成业务补偿。实现比较简单,但是时效性不够,对于收款、转账等交易来说用户体验不好。我们采用了通过消息队列即时补偿的方式,如图4所示。补偿消费者不做补偿工作​​,而是解析消息然后通过RPC调用支付核心暴露的补偿接口。为什么不直接在消费者那里补偿呢?这主要是考虑将逻辑汇聚到一个地方,方便维护。

图4 余额提现异常补单流程

当然订单还是有可能失败的,我们可以再次发送补偿消息。但不能一直这样循环,所以我们需要设置一个最大重试次数,超过之后就停止发送。当补偿多次失败的时候,一般都是下游系统出现了问题,这时候我们就需要放慢补偿的频率,随着重试次数的增加,每次补偿的时间间隔也会逐渐增大,这是通过 RocketMQ 的延时消息来实现的。

我想到以下三个问题:

如果异常消息发送失败,且上游没有重试机制,则订单可能仍无法解决,如图5所示。

当补偿消费者请求支付核心补单失败时,可能是因为超时但实际补偿成功,或者请求根本就没有通,如图6所示。

如果达到了最大重试次数,仍然不成功,这个订单该如何处理?

图5. 补偿消息发送失败

图6. 补偿消息消费失败

3.3 改进版本

针对问题1补单,如果重试后消息还是发送失败,我们引入异常消息表,将发送失败的消息存储在数据库中。表中记录了订单号、当前重试次数、异常分类、记录状态、消息体等字段。如果图6第4步中消息发送失败,则该订单会被放入DB中的异常表中,并设置定时任务进行处理。以RocketMQ目前的可用性,很少出现异常数据。如图7所示。

图 7. 消息生产/消费异常改进版本 - 1

对于第二个问题,如果补偿消费调用支付核心失败,补偿消费HandleMessage会向上层抛出错误。利用RocketMQ的梯度重试机制,当消费重试次数达到一定上限时,就会进入死信队列。如图8所示,这种情况一般是服务或者网络问题导致的。恢复后,可以从死信队列中拉取这些消息,统一处理。

图 8. 消息生产/消费异常改进版本 - 2

当然还有更极端的情况,如果MQ和DB的请求都失败了怎么办?以目前MQ和DB的可用性,同时失败基本可以忽略,可以报警,也可以人工干预。

对于问题3,如果超出最大重试次数,补偿仍然失败,一般是下游依赖出现了问题,这种情况下我们也放到了异常表中。

针对这两类漏单,需要支持补偿单笔/批量支付订单人工干预的运营能力;最好能有备份任务,在业务非高峰期运行,扫描业务单据表,对一段时间内未完成的订单进行补偿。

另外备份任务可能会造成消息暂时堆积,影响在线实时补偿的进度,可以使用独立队列进行隔离。

3.4 最终版本

其实如果只是某个异步通知操作出现异常,没必要每次都把整个业务流程走一遍,补齐缺的就可以了。因此我们将异常分成多种类型,将一些异步操作和业务处理分开,进行精细化处理:

图9以这些异常为例,说明了各个补偿类型的消息参数。

图 9. 分类补偿

我们最终的订单补偿体系如图10所示,既通过即时通讯保证了补偿的及时性,这是主动的措施,又通过延时消息重试、落地失败消息异常表、备份任务等方式保证了补偿的有效性,这是万无一失的盾牌。它不仅可以用于支付单据的补偿,还可以通过保证流程的可重入性,作为通用的解决方案,但并不适用于无状态、不可重入的业务形态。

图10. 异常补偿系统

4. 结论

本文首先介绍了什么是补单,然后结合三方支付系统的实现,完整讲解了补单机制的演化过程,最终演化为一种比较常见的异常处理模式,即基于消息队列、有限状态机和多任务的业务层最终一致性保障机制,供大家参考指正。

以上内容均来自网络搜集,如有侵权联系客服删除

图文阅读
 
QQ在线咨询
客服热线
15370006169
客服微信号
stu006