PAYATHON 2026

tradePay支付成功后iOS回调页面自动关闭怎么办?

支付老李

结论

iOS 端无法保证支付宝支付流程创建的临时页面一直保留。不要试图阻止支付宝”自动关闭”,而应从原有业务页面发起 tradePay。支付结束后回到这个稳定页面,再跳转到支付结果页或直接展示结果。

如果回调页是支付过程中创建的临时 WebView,iOS 收起收银台时可能会一并销毁它。即使回调中执行了 location.href 或 setTimeout,页面也可能马上关闭。

原因

调用 tradePay 后,收到支付结果回调只能说明客户端支付流程已经结束,不能保证当前 WebView 仍然存在。

安卓和 iOS 对支付宝容器、页面栈及收银台关闭时机的处理可能不同:

  • 安卓端可能保留当前 WebView,所以跳转后的页面可以继续显示。
  • iOS 端可能在支付结束后清理临时 WebView,刚打开的结果页也会随之关闭。
  • 如果结果页是由中间页、弹窗页或支付页打开的,这个问题通常更明显。
  • 即使在回调中先发送请求,等待数秒后再跳转,也可能因为页面提前销毁而无法完成。

因此,支付成功后的页面展示不能完全依赖 tradePay 回调所在的窗口。

推荐处理方式

1. 从稳定的业务页面发起支付

不要新开临时页面调用 tradePay。应直接在订单详情页、收银台业务页等原有页面中调用支付接口。

支付完成后,由同一个页面处理后续操作:

  1. 保存需要查询的订单号。
  2. 跳转到支付结果页,或直接更新当前页面的状态。
  3. 通过结果页向业务服务器查询订单的真实支付状态。
function startPay(tradeNo, outTradeNo) {
  AlipayJSBridge.call(
    'tradePay',
    {
      tradeNO: tradeNo
    },
    function (result) {
      const resultCode = String(
        result.resultCode ?? result.resultStatus ?? ''
      );

      if (resultCode === '9000') {
        sessionStorage.setItem('pendingPayOrder', outTradeNo);

        location.replace(
          `/pay/result?outTradeNo=${encodeURIComponent(outTradeNo)}`
        );
        return;
      }

      if (resultCode === '6001') {
        showMessage('支付已取消');
        return;
      }

      showMessage('支付尚未完成,请稍后查询订单状态');
    }
  );
}

resultCode 的具体字段和值应以当前接入方式返回的数据为准。如果项目所用 SDK 封装了返回值,应按照该 SDK 的定义进行判断。

2. 避免在临时窗口中跳转

如果当前流程是这样:

window.open('/pay.html');

然后由 /pay.html 调用 tradePay,支付完成后再打开结果页,iOS 可能会关闭整个临时窗口。

更稳妥的做法是在当前业务页中直接发起支付:

document.addEventListener(
  'AlipayJSBridgeReady',
  function () {
    document
      .getElementById('payButton')
      .addEventListener('click', function () {
        startPay(window.tradeNo, window.outTradeNo);
      });
  },
  false
);

如果页面加载时 AlipayJSBridge 已经存在,也可以直接初始化:

function initPayPage() {
  document
    .getElementById('payButton')
    .addEventListener('click', function () {
      startPay(window.tradeNo, window.outTradeNo);
    });
}

if (window.AlipayJSBridge) {
  initPayPage();
} else {
  document.addEventListener('AlipayJSBridgeReady', initPayPage, false);
}

3. 业务页恢复显示时补查订单状态

iOS 关闭收银台后,预期的页面跳转未必能稳定执行。因此,业务页重新变为可见状态时,还要主动查询订单状态。

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState !== 'visible') {
    return;
  }

  const outTradeNo = sessionStorage.getItem('pendingPayOrder');

  if (outTradeNo) {
    checkOrderStatus(outTradeNo);
  }
});

async function checkOrderStatus(outTradeNo) {
  try {
    const response = await fetch(
      `/api/orders/${encodeURIComponent(outTradeNo)}/payment-status`,
      {
        credentials: 'include',
        cache: 'no-store'
      }
    );

    const data = await response.json();

    if (data.status === 'PAID') {
      sessionStorage.removeItem('pendingPayOrder');

      location.replace(
        `/pay/result?outTradeNo=${encodeURIComponent(outTradeNo)}`
      );
    }
  } catch (error) {
    console.error('查询支付状态失败', error);
  }
}

还可以监听 pageshow,用于处理页面从 iOS 页面缓存中恢复的情况:

window.addEventListener('pageshow', function () {
  const outTradeNo = sessionStorage.getItem('pendingPayOrder');

  if (outTradeNo) {
    checkOrderStatus(outTradeNo);
  }
});

支付结果页的处理

结果页不能直接信任 URL 参数或客户端回调,应根据订单号查询服务端状态:

async function loadPaymentResult() {
  const params = new URLSearchParams(location.search);
  const outTradeNo = params.get('outTradeNo');

  if (!outTradeNo) {
    renderError('缺少订单号');
    return;
  }

  const response = await fetch(
    `/api/orders/${encodeURIComponent(outTradeNo)}/payment-status`,
    {
      credentials: 'include',
      cache: 'no-store'
    }
  );

  const data = await response.json();

  if (data.status === 'PAID') {
    renderSuccess();
  } else if (data.status === 'CLOSED') {
    renderClosed();
  } else {
    renderPending();
  }
}

loadPaymentResult();

服务端应以支付宝异步通知或服务端查询结果为准。客户端返回的 9000 只适合用来改善交互体验,不能作为发货、充值或将订单更新为已支付状态的唯一依据。

如果使用的是手机网站支付

如果接入的并非支付宝客户端内的 JSAPI,而是浏览器中的手机网站支付,应检查下单接口配置的同步返回地址,例如 return_url。该地址必须是外部可以访问的完整 HTTPS 地址。

return_url 只处理支付后的页面返回,订单状态仍要以服务端异步通知或主动查询结果为准。不要混用”浏览器返回地址”和 tradePay 的页面回调逻辑,否则可能出现重复跳转或页面栈异常。

注意事项

  • 检查成功回调中是否调用了 closeWindow、popWindow、history.back() 或宿主提供的页面关闭方法。
  • 不要使用 window.open()、弹窗或多层中间页承载支付流程。
  • 跳转到结果页时可以优先使用 location.replace(),以免用户返回已经完成支付的中间页。
  • 不要靠增加 setTimeout 强行解决。延时只能改变执行时机,无法阻止 iOS 销毁临时 WebView。
  • 支付按钮应由用户主动点击触发,不要在页面加载后自动拉起支付。
  • 如果页面运行在小程序、生活号或其他支付宝容器中,应优先使用容器提供的页面导航能力,并把结果页放在自身页面栈内。
  • 如果稳定的业务页也会被整体关闭,需要继续确认页面由哪个 API 打开,以及是否配置了支付完成后关闭容器。不同接入模式的返回参数和页面生命周期并不完全相同。

备注:内容仅供参考。