公司有一项储值卡充值业务:客户在微信公众号开通储值卡服务,通过微信支付往卡里面充值,充值成功后客户可收到消息通知,并进行消费。
看起来是一项很简单的业务,最初我们储值卡团队的实现也确实很简单。我们看看最初的实现:
相信聪明的你一眼就能看出问题:
看到这里你可能会大呼开发人员是不是没长脑子?
实际情况是,这个版本的开发是几年前的事情了,那时候公司还是创业早期,第一目标是尽快上线能用,而且客户量没有那么大,虽然中间也出现过一些数据不一致的情况,也都通过人工处理了事了。
随着公司业务的发展,用户量越来越大,而且还要和第三方合作(储值卡作为一种支付方式提供给第三方使用),问题出现得也越来越频繁,不得不将这块提上重构议程。
那么,针对上面提的几点问题,我们大体能想到如下重构项:
初步设计如下:
这里我们重点讨论下对第 14 步(卡充值接口返回结果)的处理:
骚年你等等!
你说什么?重试失败了就去退款?
实践中,远程调用失败的一个很大原因是网络超时(而超时的很大原因又是对方负载过高),而面对超时,我们是不知道对方到底有没有处理成功的,万一这边把钱退掉了,那边又充值成功咋办?(我们是 saas 服务商,这时真正的损失方是我们的商户,而商户无疑会找我们索赔的)
一种方案是:
在多次重试失败后发起微信退款之前,先调卡系统查询接口,如果查询结果是充值成功,则不退款,继续后续流程,否则发起退款;
该方案在实际中也基本行不通,因为如果那段时间网络有问题或者对方服务器负载高,查询也有很大概率失败,或者就算查成功了并返回充值记录不存在,也有可能之前调的充值接口还在跑(比如处于锁等待状态)。
面对该问题,我们决定用定时任务来解决。在微信支付回调中,如果多次调卡充值接口失败,我们不发起退款,也不进行后续流程,而是在数据库中写入一条异常记录,然后结束本次处理。
在定时任务中(比如 10 分钟一次),我们取出那些异常记录,调卡系统相关接口核对最终状态,如果充值成功了,则补充执行充值成功的后续流程,否则发起微信退款,并执行其他充值失败流程(如改订单状态,给用户发通知、回调业务系统等)。
为了防止钱退了后卡又充值成功,定时任务中只处理 1 小时前的数据。
另一个隐藏的问题是,在前面的充值流程中,直到微信支付回调,卡系统都没有关于这次充值行为的任何记录。这可能会导致后续一系列问题,其中一个问题是,在最初下单(步骤 5)到最终充值(步骤 13)这段时间内,一旦任何变量(充值规则)发生改变,这次充值就有可能会失败(或者导致数据差错)。这个时间差短则几十毫秒,长则几分钟十几分钟都有可能。另一个次要问题是,一旦发生充值异常,卡系统自身是不知情的(因为没有任何记录),对卡系统的任何查询也都不会反映这次充值行为。
为了解决该问题,我们引入预充值的概念。在下单后调微信支付前,先同步调卡系统的预充值接口,该接口计算充值合法性并生成一条预充值记录,该记录包含充值账号、充值金额、支付金额、充值单号等关键信息,状态为“充值中”。
在微信支付回调中,将预充值状态改成“充值成功”,并处理一些其他逻辑。
综合,最终方案如图:
如对本文有疑问, 点击进行留言回复!!
14、Ribbon整合断路器监控Hystrix Dashboard
网友评论