收藏
回答

微信支付成功后,异步通知同时多次通知如何拦截?

大佬们好

现在开发遇到一个情况,当微信支付完成后,会在同时[时间跨度1秒内]收到2个异步通知,这种情况下应该如何拦截掉多余通知呢?

我的业务逻辑是在收到异步通知后就执行一次,执行前会先去判断支付是否成功,并推送消息给客服,但是由于这个时间过短,所以每一次的判断都会是成功的,都会执行一次业务逻辑,一下会推送两条一样的消息

后端开发框架是larave

有没有什么好的办法屏蔽一次

回答关注问题邀请回答
收藏

4 个回答

  • Memory
    Memory
    2021-05-21

    微信支付为保证回调通知触达有效性,会有保障策略,在第一次通知如果网络链路返回无法连接或者状态不明,微信支付会换一条链路进行通知。此时可能会造成您这边收到两次通知。微信文档已强调同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。需要正确对自己数据进行状态唯一性处理。

    支付结果通知:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8

    应用场景

    支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。

    注意:

    1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。

    2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

    3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【查询订单API】确认订单状态。

    特别提醒:

    1、商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。

    2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

    3、技术人员可登进微信商户后台扫描加入接口报警群,获取接口告警信息。

    参考

    2021-05-21
    有用 1
    回复 2
    • 高权
      高权
      2021-05-21
      我知道会推送多次,现在就是想知道出了查库有没有办法其他屏蔽一次
      2021-05-21
      回复
    • Memory
      Memory
      2021-05-21回复高权
      返回
      2021-05-21
      回复
  • 至上
    至上
    2022-05-18

    可以使用Redis的setnx命令来解决

    @Override
    public void processOrder(Map<String, Object> plainTextMap) {
        String orderNo = (String) plainTextMap.get("out_trade_no");
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Boolean isFlag = redisTemplate.opsForValue().setIfAbsent(orderNo, "已支付");
        log.info("还没修改~~~");
        if (isFlag) {
            //修改订单支付成功
            log.info("修改订单状态");
            QueryWrapper<OrderInfo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("order_no", orderNo);
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderStatus(OrderStatus.SUCCESS.getType());
            baseMapper.update(orderInfo, queryWrapper);
    
            //记录支付日志
            paymentInfoService.createPaymentInfo(plainTextMap);
            String redisKey = (String) plainTextMap.get("attach");
            redisTemplate.delete(redisKey);
        }
    }
    
    2022-05-18
    有用
    回复
  • NK.七夜
    NK.七夜
    2021-06-04
    刚遇到同样问题,不想用redis缓存机制,使用map的key值唯一性,解决方法有待验证
    使用全局变量,一个订单号只能用一次,用完后就不怕了。大佬们,看看这样会有其他问题吗?
    //通知结果高并发情况下拦截.全局变量
    private Map repeatRequstMap = new ConcurrentHashMap<>();
      
    public String xxx(String orderNo
    

    ){

    if (repeatRequstMap.get(orderNo) == null) {
        repeatRequstMap.put(orderNo, orderNo);
    
        returnMsg = receiveBaseService.syncFinancial(financeUnifiedOrder);
        if (returnMsg != null) {
            logger.error("同步财务接口失败-{}-{}", orderNo, returnMsg);
            repeatRequstMap.remove(orderNo);
            return returnMsg;
        } else {
            if (repeatRequstMap.size() >= 60) {
                repeatRequstMap = new ConcurrentHashMap<>();
            }
        }
        //修改同步系统状态
        FinanceUnifiedOrder sync = new FinanceUnifiedOrder();
        sync.setId(financeUnifiedOrder.getId());
        sync.setSyncStatus("1");
        financeUnifiedOrderMapper.updateByPrimaryKeySelective(sync);
    }
    return null;
    

    }

    2021-06-04
    有用
    回复 1
    • NK.七夜
      NK.七夜
      2021-06-04
      改良版 
      //通知结果高并发情况下拦截
          private Map<String, String> repeatRequstMap = new ConcurrentHashMap<>();
          /**
           * @throws
           * @Title: notifyStuchargePay
           * @Description: 交费订单支付结果回调处理
           * @param: @return
           * @return: ResultJSON
           */
          @RequestMapping(value = "/notifyStuchargePay", method = {RequestMethod.GET, RequestMethod.POST})
          @ResponseBody
          public void notifyStuchargePay(HttpServletRequest request, HttpServletResponse response) {
              logger.info("交费订单支付结果回调处理-{}", request.getRequestURI());
              Map<String, Object> result = new HashMap<>();
              try {
                  logger.info("微信支付异步通知结果:{}", sb.toString());
                  Map<String, String> notifyMap = WxPayUtil.doXMLParse(sb.toString());
                  String orderNo = notifyMap.get("out_trade_no");
                  if (repeatRequstMap.get(orderNo) == null) {
                      repeatRequstMap.put(orderNo, orderNo);//占用
                      //todo 支付结果处理,处理完后再重复调用就有数据库拦截了
                      repeatRequstMap.remove(orderNo);//释放
                  }
                  BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                  String resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                          + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                  out.write(resXml.getBytes());
                  out.flush();
                  out.close();
              } catch (Exception e) {
                  logger.info("微信支付异步通知异常-", e);
              }
          }
      2021-06-04
      回复
  • 跨商通
    跨商通
    2021-05-22

    基本没办法屏蔽。

    将业务逻辑代码改成适应这种情况吧。

    2021-05-22
    有用
    回复
登录 后发表内容