[不靠谱程序员]订单付款成功后,不是先改状态,而是...

发布时间 2023-10-18 20:42:42作者: buguge

做过税地系统或三方支付或对接过银行支付通道的朋友应该清楚,我们的支付系统在调用银行通道获取到付款单的终态后,涉及到记账、结算、通知下游商户等业务逻辑。这其中,有一项默认的操作是,更新付款单的状态。

并且,应该先变更状态,变更状态成功后,然后再去执行其他业务逻辑。

我们在参与一次代码评审时,就发现了不靠谱的事情。开发人员先发起异步记账,然后才是更新付款单状态。

    /**
     * 订单完成的业务逻辑封装
     * @param paymentOrder 付款单对象
     * @param payResult 付款结果
     */
    void handlePaymentCompletion(PaymentOrder paymentOrder, PaymentResult payResult){
        if (PaymentOrderStatusEnum.isFinalState(payResult.getStatus())){
            return;
        }

        // 订单完成,异步记账
        accounting(paymentOrder);

        // 持久化更新订单付款状态
        updateOrderPayResult(paymentOrder, payResult);

        // 异步通知下游商户系统
        notifyMerchant(paymentOrder);
    }

 

那么,这会出现什么后果呢?

假如持久化记账完成了,商户的账户余额也变更了,但是,变更付款单状态时,由于字段超长等某些原因导致异常,程序中断,付款单状态未能持久化变更,也就是说,此时数据库里付款单的状态依然是“付款中”。那么,定时查单任务(或查单延迟消息队列)在下一次调度时还会读取到这一笔“付款中”的交易,然后继续查询银行通道,通道返回终态后,又开始执行记账逻辑,这为重复记账埋下了种子。

重复记账会产生什么后果呢?

①如果付款单是付款失败,商户账户可用余额会增加。重复记账,就会出现余额异常翻倍增加,意味着商户可以用增加的这笔“意外之财”继续付款。这是资金风险的原罪。

②如果付款单是付款成功,商户账户可用余额减少。重复记账,就会导致商户余额异常成倍减少,意味着商户无法继续付款。我(商户)明明给你充钱了,你却告诉我余额不足,不让我用,我投诉你!

因此,这个顺序颠倒的代码逻辑是不是很可怕?支付系统最怕这种资金风险。上面说的定时任务只是一方面,回调或并发场景下也同样会导致这个资金隐患。

 

借助这个案例,来强调一下,业务处理流程的先后顺序是很重要的,一定要摸索清楚。日常开发中养成严谨的好习惯,关键时刻才能彰显靠谱。

 

ref:§ 业务校验,注意各个校验的先后顺序。

 

【EOF】欢迎大家关注我的微信公众号「靠谱的程序员」,让我们一起做靠谱的程序员。