前段时间公司业务需要,我对接了银联的网关支付。在对接微信支付的时候,积累的经验是用户会经常下了单很久不支付,或者点了支付却没继续下一步,导致后续的支付失败,订单作废。于是在对接银联的过程中,采取了同样的措施,在每次用户支付的时候生成一个新的流水号,这样就不会出现订单超时的问题,因为对于微信或者银联来说,每次请求都是一个新的订单,超时时间重新计算。

But!!!! 在前两天的一个用户订单中,出现了异常。我们系统只有一个订单,但是银联对账脚本抓取过来的数据却有三条记录,而用户也确实被扣了三次钱!这是个bug啊!涉及到钱的问题,那就非常敏感了,所以查了差日志,数据库记录。分析之后得出结论:第一次订单点击支付,在还没完成支付的时候又点了第二次!导致第二次的流水号把第一次的更新了,所以银联支付成功的回调中的流水号对应不上,更新不了订单。这种操作基本是不会出现的,因为支付在微信公众号中处理的,我们申请的是移动端支付,电脑端的话银联会有提示。所以可能的操作那就是用户在把支付链接给了别人,同时操作了。而这种操作也基本不会出现,打电话问了之后也觉得用户不懂这些。排查nginx日志,发现第二次访问的IP是上海的联通,手机设备型号也不是用户用的那台,emmmm.... 这就有点看不懂了。可能是公司内部哪个哥们拿用户的数据在正式服务器测试了....杀头的。

为了杜绝这种操作会导致的订单异常问题,设置了保护时间,在保护时间内的流水号,不予以更新!并且写了个对比脚本,在银联对账数据与我们的订单出现差异的时候,及时邮件报警。
以下是部分代码

    //判断订单情况
    $check = $this->unionpay_model->checkOrder($order_info['trade_no'] , $area_id);
    // 第一次进来支付
    if(empty($check) &&  empty($order_info['order_trade_no'])){
        //TODO
    }//后续进来的
    elseif ((!empty($check)) && (!empty($order_info['order_trade_no']))){
          //TODO
        //订单未超时 15分钟
        if((!empty($check['update_time']) && time() - strtotime($check['update_time']) < 900) || (time() - strtotime($check['create_time']) < 900)){
            //TODO
        }
        //订单已超时
        if(((empty($check['update_time']) && time() - strtotime($check['create_time']) >= 900)) || (!empty($check['update_time']) && time() - strtotime($check['update_time']) >= 900)){
            //TODO 
        }

    }//异常
    else{
        $this->ajaxReturn(5,'交易异常,请联系管理员处理!');  // 处理失败
    }