PAYATHON 2026

支付宝内部H5调用ap.tradePay支付成功后为何不执行回调?

支付阿杰

结论

ap.tradePay 的回调需要同时满足三个条件才会执行:支付宝客户端已经结束支付流程,原 H5 页面重新激活,并且该页面原有的 JavaScript 运行上下文仍然存在。

支付成功后没有收到回调,并不一定是支付失败。常见原因包括:

  • ap.tradePay 的调用方式或参数不正确;
  • JSAPI 尚未就绪;
  • 支付期间页面发生跳转、刷新或重新创建,导致原回调丢失;
  • 回调已经执行,但其中的代码抛出了异常;
  • 支付结果页与发起支付的 WebView 不在同一个上下文中;
  • 业务把前端回调错误地当成了最终支付结果。

支付是否成功,应通过服务端异步通知或主动查询订单来确认,不能只依赖 H5 回调。

先检查调用方式

ap.tradePay 的典型调用方式如下:

ap.tradePay(
  {
    tradeNO: '支付宝交易号'
  },
  function (result) {
    console.log('tradePay callback:', result);

    switch (String(result.resultCode)) {
      case '9000':
        // 客户端认为支付成功,仍建议向业务服务端查询订单状态
        queryOrderStatus();
        break;
      case '6001':
        // 用户取消支付
        break;
      default:
        // 其他结果,记录完整返回值并查询服务端订单状态
        queryOrderStatus();
        break;
    }
  }
);

有些接入场景使用订单字符串,而不是 tradeNO:

ap.tradePay(
  {
    orderStr: '服务端生成的完整支付订单字符串'
  },
  function (result) {
    console.log('tradePay callback:', result);
    queryOrderStatus();
  }
);

应该使用 tradeNO 还是 orderStr,由当前支付宝支付产品和服务端下单方式决定。不要自行拼装参数,也不要同时传入相互冲突的订单参数。

确保 JSAPI 已经就绪

如果页面加载后需要立即发起支付,应先等待支付宝 JSBridge 可用。否则,调用可能没有进入预期流程,在不同客户端版本中的表现也可能不一致。

function ready(callback) {
  if (window.AlipayJSBridge) {
    callback();
    return;
  }

  document.addEventListener(
    'AlipayJSBridgeReady',
    callback,
    false
  );
}

ready(function () {
  ap.tradePay(
    {
      tradeNO: '支付宝交易号'
    },
    function (result) {
      console.log('tradePay callback:', result);
      queryOrderStatus();
    }
  );
});

同时要确认页面运行在支付宝客户端的 H5 容器中。如果页面位于普通浏览器、应用自建 WebView 或不支持该 JSAPI 的容器中,就不能假设 ap.tradePay 可以正常工作。

检查页面是否在支付过程中被销毁

页面被销毁,是支付成功后返回页面却没有任何反应的常见原因。

调用 ap.tradePay 后,不要立即执行以下操作:

location.href = '/result';
location.reload();
history.back();
window.close();

还要检查框架代码是否会因为路由守卫、登录状态更新、组件卸载或其他状态变化而自动刷新页面。页面一旦重新加载,注册在旧 JavaScript 上下文中的回调也会随之消失。

可以添加生命周期日志来定位问题:

console.log('before tradePay');

window.addEventListener('pagehide', function () {
  console.log('pagehide');
});

window.addEventListener('pageshow', function (event) {
  console.log('pageshow:', event.persisted);
});

document.addEventListener('visibilitychange', function () {
  console.log('visibilityState:', document.visibilityState);

  if (document.visibilityState === 'visible') {
    queryOrderStatus();
  }
});

ap.tradePay(
  {
    tradeNO: '支付宝交易号'
  },
  function (result) {
    console.log('tradePay callback entered:', result);
    queryOrderStatus();
  }
);

如果返回页面时再次出现页面初始化日志,页面很可能已经被重新创建。这时不应继续等待旧回调,而要在页面恢复或重新加载后,根据业务订单号向服务端查询订单状态。

排除回调代码自身异常

有时回调已经执行,但第一行代码就报错了,用户看到的仍然是没有反应。

不要只根据页面是否跳转来判断回调有没有执行。排查时应先记录完整返回值,再通过 try...catch 捕获异常:

ap.tradePay(
  {
    tradeNO: '支付宝交易号'
  },
  function (result) {
    console.log('tradePay raw result:', result);

    try {
      const resultCode = String(result && result.resultCode);

      if (resultCode === '9000') {
        queryOrderStatus();
      } else {
        console.warn('Unexpected payment result:', result);
        queryOrderStatus();
      }
    } catch (error) {
      console.error('tradePay callback error:', error);
    }
  }
);

排查时主要检查以下内容:

  • ap 是否已经定义;
  • 回调中调用的函数是否存在;
  • 是否读取了错误的返回字段;
  • 是否混用了数字和字符串形式的 resultCode;
  • 是否存在未捕获的 Promise 异常;
  • 后续逻辑是否因前端路由变化或组件销毁而中断。

以服务端支付状态为准

前端回调只适合用来改善交互,不能作为确认支付成功的唯一依据。用户可能关闭页面,客户端也可能中断回调,网络还可能在支付完成后断开。

建议按以下流程处理:

  1. 服务端创建业务订单,并向支付宝发起下单。
  2. H5 调用 ap.tradePay。
  3. 服务端接收支付宝异步通知,并按接口要求完成验签。
  4. 服务端更新业务订单状态,处理过程必须保证幂等。
  5. H5 收到回调、页面重新显示或重新加载时,根据业务订单号查询服务端状态。
  6. 服务端确认订单已支付后,前端再展示最终的支付成功结果。

示例:

async function queryOrderStatus() {
  try {
    const response = await fetch(
      '/api/orders/payment-status?orderId=' +
        encodeURIComponent(businessOrderId),
      {
        credentials: 'include',
        cache: 'no-store'
      }
    );

    if (!response.ok) {
      throw new Error('HTTP ' + response.status);
    }

    const data = await response.json();

    if (data.status === 'PAID') {
      renderPaymentSuccess();
    } else if (data.status === 'CLOSED') {
      renderPaymentClosed();
    } else {
      renderPaymentPending();
    }
  } catch (error) {
    console.error('queryOrderStatus failed:', error);
  }
}

推荐的排查顺序

  1. 分别在调用前、回调第一行和页面初始化位置记录日志。
  2. 确认 ap.tradePay 在 AlipayJSBridgeReady 之后调用。
  3. 核对 tradeNO 或 orderStr 是否由服务端提供,并且与当前订单一致。
  4. 暂时移除支付调用后的页面跳转、刷新和关闭代码。
  5. 检查支付期间是否发生组件卸载、路由切换或 WebView 重建。
  6. 暂时将回调内容缩减为一条 console.log,排除业务代码异常。
  7. 检查服务端是否收到异步通知,并核对验签、金额、商户号和订单号的校验结果。
  8. 页面恢复时主动查询业务订单状态,不再等待旧页面的回调。

注意事项

resultCode === '9000' 可以作为客户端的交互信号,但不能直接触发发货、开通会员、记账等不可逆的业务操作。最终支付状态必须由可信的服务端结果确认。

不要在前端保存支付密钥或私钥,也不要在前端自行生成签名。下单、签名、异步通知验签和订单状态更新都应由服务端完成。

如果问题只出现在特定支付宝客户端版本或特定入口,还应记录客户端版本、页面 URL、调用参数类型、业务订单号和完整回调结果,用于继续排查容器兼容性或返回链路问题。

备注:内容仅供参考。