00:00
我们上节课呢,在支付通知当中修改了订单状态,记录了支付日志,但是呢,到目前为止,我们的支付通知的接口呢,还是不完善的,它还存在着一些问题,那么我们来看一下API字典支付通知,支付通知API这个文档当中的一个非常关键的描述,就是注意这块哈,同样的通知可能会多次发送给商户系统。商户系统必须能够正确的处理重复通知,那么什么叫做同样的通知可能会多次发送给商户系统呢?我来给大家描述一下这个场景,首先呢,我们这边是商户系统。这面呢是微信的支付系统,那正常情况下呢,我们的商务系统向微信支付系统发送请求之后,也就是支付请求,等待用户扫码支付,用户扫码支付之后呢,如果成功了,那么微信的支付系统呢,会给我们的商户系统发送一个通知。
01:00
好,那接下来呢,我们会对这个通知呢进行处理,整个的这个通知的接收和处理的过程呢,就是前面几节课我们做的内容。那我们说到这个通知呢,如果我们处理成功了,我们会给微信呢一个应答,而这个应答呢,必须是一个成功的应答,也就是说是200。在这个应答的过程当中呢,我们有一种情况,微信会给我们重复的发通知,是什么情况呢?就是超时情况。好,那我们说如果我们的应答在这个网络传输的过程当中,由于啊,这个网络的不稳定超过五秒了,那么微信呢,就会重复的给我们发通知,对不对。那么这面我们是不是要重复的接收通知,好,我们这面重复的接收到通知之后,我们是不是要重复的对订单进行处理,重复的记录支付日志呀。
02:01
那么我们重复的处理订单还好,因为我们每一次呢,修改的订单状态呢,都是将订单状态修改为支付成功,但是记录支付日志就会对我们的数据库产生影响了,因为用户明明只支付了一笔订单,但是呢,支付日志当中会在这接连不断的。支付通知当中去重复的处理,重复的记录。同样。如果我们在处理订单的过程当中,比如说用户如果。买了一个商品,那么他的积分会增加,那么你会发现用户的积分就会不断的在接收通知的过程当中增加,好,那这个时候我们的系统的安全性是不是就出现问题了,那么现在呢,我们就要解决这个重复通知的这样的一个问题啊,那么我们先来模拟一下这个重复通知的发生,然后我们再来看一下怎么样具体的啊去解决这个问题。
03:05
所以模拟重复通知的发生呢,很简单,我们呢,就在这个位置啊,模拟一个应答超市。那么我们呢,已经在。微信成功的接收到我们的应答,之前呢,把订单呢都成功的处理过了,并且呢支付日志呢也记录了,所以呢,我们在这个地方。设置应答超时的主要目的呢,就是模拟接收微信端的重复通知。好,那么接下来呢,我们重新启动一下我们的服务器。那为了一会儿我们。核对数据方便,也不要忘了先把数据库当中的数据记录啊,现在已经是两个表了,Fo paymentf都给它除掉。
04:03
然后呢,我们来重新的刷新我们的页面,我们来做一笔支付。首先呢,我点击确认支付,弹出二维码,那我呢来进行扫描。好,我输入密码啊。好,现在呢,我已经成功支付了,那么大家看我的后台呢,一定是接收到了我们的支付通知的,但是呢,这次我们的支付通知呢,就接收到了好多次,因为呢我们的响应超时了,而微信那一端呢,针对我们的响应超时呢,它有一个重复发通知的策略,所以呢,他会接连不断的给我们发通知,那这样的话对我们的数据库造成的影响就是订单数据是没有问题的,因为每一次重复的通知呢,他都啊修改了同样的这个字段,把同样的字段修改成了同样的值,但是支付日志有问题,每一次发通知这个支付日志呢,就都会被记录一笔,所以这就是重复的通知对我们系统产生的影响,那接下来呢,我们来看一看如何解决这个问题。
05:15
因为微信那边的策略呢,是咱们这面一旦超时了,他就会在啊一段时间内不断的给咱们发通知,所以呢,为了避免下一次测试的时候,微信还在发通知呢,我这边呢。换一个内网穿透地址啊。这样的话可以保证我下一次重启服务器的时候,不会接收到之前遗留的通知,所以在这面呢,我们CTRLC两次,然后呢,重新启动我们的内网穿透。这样即使微信那面发送了啊,重复的遗留的通知,因为我们的内网穿透地址已经变了,所以呢,微信已经找不到我们了哈,我发不了了。
06:03
啊,我把我需要把这个改一下啊。好,这样再重新启动呢,微信就不会再给我们发送通知了。所以它这个地址总变化也是有好处的哈,在我们的测试阶段,那接下来呢,我们来解决一下咱们前面这个问题啊,看一看啊,如何去避免应答超时的时候的这个重复通知,我们来看文档吧。他这面有一个解决方案,推荐的做法呢,是当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理,也就是说我们要先检查我们的订单状态,如果订单状态当中你发现诶已经变成什么呀,支付成功了,就说明我们曾经接收到过微信的通知对不对,并且我们已经成功的处理了我们的数据,那么我们就可以干嘛呀,如果已处理则直接返回,结果成功就不用再重复的处理了,如果未处理则再进行处理好,那现在呢,我们来啊,针对这一部分的内容呢,进行一个实现,就是我们要判断一个我们的订单状态。
07:29
那我们呢,在我们的。处理订单的这个方法当中。在更新订单状态和记录支付日志之前,我们来处理重复的通知。那么我们用order info。Service get order这个方法当然我还没有哈。啊,我要根据我的订单号,根据订单号呢,拿到一个订单状态好,那接下来呢,我会判断这个订单状态。
08:17
F首先我们从枚举当中。Status第二,No pay未支付啊,判断一下,如果我们的商品的订单的状态呢,是未支付的话。那么我们就继续处理,如果不是未支付的话。啊。那么我们就直接return,也就是说只有在未支付的情况下呢,我们才接收这个支付成功的结果通知,然后呢,将我们的订单状态呢改成已支付啊,如果不是未支付了,那我们就直接return,说明我们之前已经处理过了。接下来实现这个status。
09:06
Out加把具体的方法呢给它创建出来。我们来查询订单query rapper。Order in for。创建一个query wrapper,那么我们利用这个query wrapper呢去构造一个查询条件,就是order number,根据order number呢,我们查询订单,所以base map,第2SELECT one啊,一定只有一个订单,因为我们的呢是唯一的。我们查询出这个订单之后呢,先做一个健壮性的校验啊,判断一下order in等于那。如果order in等于none的话,也就意味着比如说哈,我们因为这个方法是在回调当中接收的,而回调的时候很有可能这个订单我都删掉了,那此时此刻,如果我控台上接收到一个针对于这个订单的回调的话,那么把这个订单号取出来一查,它一定是个空对象,所以这个地方呢就会为空啊,那这地方为空的话,如果我直接从这个order info里面去获取这个订单状态的话,那么就会出现空指针异常。
10:28
所以呢,我们要处理一下这个控制针异常啊,做一个间状性的校验,所以这个地方呢,我们如果凹音for为空,我们直接就返回空就好了,那么它的订单状态的这个数据呢,也会是空,然后呢,在刚才咱们调用的这个地方,它就会返回一个空值。好,那到这为止呢,我们处理重复通知的这个工作呢,就做完了,我们。再重新启动一下我们的服务器,对我们的程序做一个测试,看一看这次。
11:00
我们的数据库当中的数据呢,会不会受到重复通知的影响啊,那依然是为了测试方便啊,我先把之前的这个数据呢。给它清掉。这样的话呢,就没有数据干扰了。那咱们再来进行一个支付。Power扫码。好,扫码成功,我们来看后台。后台呢,我们依然是查询一下我们的支付通知ID啊,这样的一个日志看一看。重复通知呢,没有改变对不对,依然会有重复通知啊,但是我们来看一下数据库,首先看一下order in啊支付成功了,也就是说已经处理过了,再来看一下。Payment info是不是只记录了一条支付日志啊?那这样的话呢,我们就很好的解决了重复通知的问题。
12:01
那实际上这个里面还涉及到一个我们平时开发当中的一个原则啊,叫做接口调用的密等性。我们来写一下好,什么叫接口调用的幂等性呢?就是无论接口被调用多少次。产生的结果是一致的,好,那么我们有一些远程接口呢,必须要确保接口调用的密等性,否则的话这个接口就是不安全的,比如说我们现在的这个支付结果通知,那么如果因为网络的原因,我们的接口被重试的请求调用了多次的话,对我们的业务数据产生了影响,那么我们就说它没有确保接口调用的密等性原则。
我来说两句