回调的一些经验谈

微信支付通知规则

用户支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理该消息,并返回应答。

对后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)

两种扣库存模式

1,支付前扣库存(即下单成功锁库存),订单超时支付自动取消,回滚库存

这种模式下,可以避免超卖,但是会出现待支付订单在未取消的时间周期内占用库存,需要把下单和支付的间隙控制在较短的时间周期内,支付后收到回调的时候,只需要修改订单状态即可

2,支付后扣库存(即下单成功不扣库存,支付成功了才扣),订单的取消对库存无影响

这种模式下,可能导致超卖,系统收到支付回调时,需要检测库存是否还在,如果没有,需要设计自动退款或者补货的逻辑。

订单取消与回调的竞争

这个场景是订单即将取消的时间点,用户完成了支付,当系统收到回调的时候,订单已经被系统自动取消。

两种库存模式下,不同的处理:

1,支付前扣库存

订单取消之后,库存已经归还,此时可以做两个选择:1,直接自动退款,2,检查库存 (有库存)-->扣库存-->恢复订单状态,(库存不足)-->自动退款

2,支付后扣库存

扫描二维码关注公众号,回复: 13470800 查看本文章

订单取消之后,支付回调过来,可以直接按照原有逻辑检查库存完成订单即可

因为有些活动是限定了顾客的身份等级或者限购次数之类的规则,所以具体的逻辑需要在收到回调的时候进行对应的处理。

事务处理时间与多次回调的冲突

理想状态下,收到回调之后,我们只需要考虑基本的商品,订单相关, 更新订单状态,支付状态,支付时间, 更新商品销量,款式销量, 记录一些有价值的日志即可。但正常场景下,我们需要做很多插件,活动,统计系统的数据记录,这就不可避免的需要增加事务的长度和处理业务的时间。

在一些存在竞争的场景下,回调中的事务等待时间,超出了我们回调的15s等待时间,第二次甚至第三次回调过来的时候,第一次回调的事务还没有结束的极端场景下,我们需要加锁来保证业务的原子性

<?php
  //判断是否有锁
  if(Cache::hasLock($orderNo)){
    return;
  }
	Db::beginTransaction();
  try{
    //进入事务后加锁
    Cache::lock($orderNo, 60);
    ....
    Db::commit();
  }catch(\Throwable $e){
    Db::rollback();
    //事务失败解锁
    Cache::unlock($orderNo);
  }finally{
    //异常处理结束解锁
    Cache::unlock($orderNo)
  }
复制代码

这里我们设计锁时间是60秒,但正常结束或者失败也会自动解锁,只是当存在超过60s的事务的时候,应该去考虑代码和设计问题了。

超长事务本身的出现也是对于业务组织的一个提醒,我们这里需要考虑对于长事务进行拆分

1,必须保证关键流程不受影响,修改订单状态,扣库存,核销码入库等核心业务,是必须在回调时刻及时进行的,不能受其他次要业务流程异常的影响

2,拆分按模块进行,但要有补偿的机制

订单回调的流程,往往会涉及会员,分销,活动,统计,结算,仓储和外部插件等等业务,这些业务相互之间彼此独立,没有依赖,可以考虑放入队列进行消费。

事务中存在回滚的风险,在事务commit之前不允许有发送队列的逻辑

猜你喜欢

转载自juejin.im/post/7036639486507220999