PAYATHON 2026

支付宝电脑端支付的queryQRStatus刷新如何停止?

支付小周

结论

alipay.trade.close 只能关闭支付宝交易,无法停止页面中的 queryQRStatus 轮询。轮询是否继续,取决于电脑端支付页面里的 JavaScript 定时器或支付组件逻辑。

商户页面需要在支付成功、交易关闭、超时、用户取消或页面销毁时主动停止轮询。如果轮询由支付宝托管的收银台页面发起,商户无法直接清除页面内部的定时器,只能离开、关闭或替换该支付页面。

原因

关闭交易和页面轮询是两个独立的行为:

  • alipay.trade.close 用于修改交易状态。
  • queryQRStatus 刷新是浏览器发起的轮询。
  • 关闭交易不会触发前端的 clearInterval() 或 clearTimeout()。
  • 如果轮询逻辑没有处理“交易已关闭”状态,请求仍可能继续发送。

所以,调用 alipay.trade.close 不能代替停止轮询。前端需要根据查询结果或业务事件单独结束轮询。

解决步骤

1. 保存轮询定时器

启动查询时保存定时器 ID,不要只调用 setInterval() 而忽略它的返回值。

let qrStatusTimer = null;

function startQueryQRStatus() {
  if (qrStatusTimer !== null) {
    return;
  }

  qrStatusTimer = setInterval(() => {
    queryQRStatus();
  }, 2000);
}

2. 提供统一的停止方法

function stopQueryQRStatus() {
  if (qrStatusTimer !== null) {
    clearInterval(qrStatusTimer);
    qrStatusTimer = null;
  }
}

3. 在终态或异常场景停止轮询

具体的状态字段取决于商户自己的查询接口。下面的代码只演示轮询控制方式,并不代表支付宝接口的固定响应结构。

async function queryQRStatus() {
  try {
    const response = await fetch('/api/payment/status?outTradeNo=ORDER_123');
    const result = await response.json();

    switch (result.status) {
      case 'TRADE_SUCCESS':
        stopQueryQRStatus();
        handlePaymentSuccess(result);
        break;

      case 'TRADE_CLOSED':
        stopQueryQRStatus();
        handlePaymentClosed(result);
        break;

      case 'WAIT_BUYER_PAY':
        // 尚未支付,继续轮询
        break;

      default:
        // 是否继续轮询应根据业务定义决定
        break;
    }
  } catch (error) {
    console.error('queryQRStatus failed:', error);
  }
}

关闭交易后,前端也应立即停止定时器,不必等待下一次查询:

async function closePayment() {
  try {
    const response = await fetch('/api/payment/close', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        outTradeNo: 'ORDER_123'
      })
    });

    const result = await response.json();

    if (result.success) {
      stopQueryQRStatus();
      handlePaymentClosed(result);
    }
  } catch (error) {
    console.error('close payment failed:', error);
  }
}

推荐使用 setTimeout 控制轮询

递归调用 setTimeout 比使用 setInterval 更容易避免请求重叠,也就是前一次请求尚未完成,下一次请求已经开始。

let qrStatusTimer = null;
let pollingStopped = false;

function startQueryQRStatus() {
  pollingStopped = false;
  scheduleNextQuery(0);
}

function scheduleNextQuery(delay = 2000) {
  if (pollingStopped) {
    return;
  }

  qrStatusTimer = setTimeout(queryQRStatus, delay);
}

async function queryQRStatus() {
  if (pollingStopped) {
    return;
  }

  try {
    const response = await fetch('/api/payment/status?outTradeNo=ORDER_123');
    const result = await response.json();

    if (
      result.status === 'TRADE_SUCCESS' ||
      result.status === 'TRADE_CLOSED'
    ) {
      stopQueryQRStatus();
      return;
    }

    scheduleNextQuery(2000);
  } catch (error) {
    console.error('queryQRStatus failed:', error);
    scheduleNextQuery(3000);
  }
}

function stopQueryQRStatus() {
  pollingStopped = true;

  if (qrStatusTimer !== null) {
    clearTimeout(qrStatusTimer);
    qrStatusTimer = null;
  }
}

如果还要取消正在进行的 HTTP 请求,可以配合 AbortController:

let controller = null;

async function queryQRStatus() {
  controller = new AbortController();

  try {
    const response = await fetch('/api/payment/status', {
      signal: controller.signal
    });

    const result = await response.json();

    if (
      result.status === 'TRADE_SUCCESS' ||
      result.status === 'TRADE_CLOSED'
    ) {
      stopQueryQRStatus();
    }
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error(error);
    }
  }
}

function stopQueryQRStatus() {
  if (controller !== null) {
    controller.abort();
    controller = null;
  }

  if (qrStatusTimer !== null) {
    clearTimeout(qrStatusTimer);
    qrStatusTimer = null;
  }
}

如果轮询来自支付宝收银台页面

如果 queryQRStatus 是支付宝托管收银台页面内部发起的请求,而不是商户页面自己实现的轮询,商户代码通常无法通过 clearInterval() 将其停止,因为商户页面不能控制该页面及其脚本。

可以根据页面的接入方式进行处理:

  • 支付完成或取消后,跳转到商户结果页。
  • 用户取消支付时,关闭弹出的支付窗口。
  • 如果支付页嵌在受控容器中,移除该容器或将其导航到其他页面。
  • 不要尝试跨域访问支付宝页面内部的 JavaScript 对象或定时器。

即使已经通过 alipay.trade.close 关闭交易,只要收银台页面仍然打开,其内部查询就可能继续一段时间。具体是否停止以及何时停止,取决于该页面自身的实现。

注意事项

  • 不要只依赖前端轮询判断支付是否成功。最终交易结果应通过支付宝异步通知或服务端主动查询确认。
  • 停止轮询前,应确认交易已经进入明确的终态,避免把一次网络异常误判为交易结束。
  • 设置最长轮询时间和最大失败次数,防止页面长期发送无效请求。
  • 页面离开或组件销毁时,也要清理定时器。
window.addEventListener('beforeunload', () => {
  stopQueryQRStatus();
});

使用 Vue、React 等框架时,应在组件卸载阶段执行相同的清理逻辑。关闭交易与停止页面轮询需要分别处理。

备注:内容仅供参考。