PAYATHON 2026

支付宝生活号支付成功后如何跳转到第三方 APP?

支付老李

结论

支付成功后,可以尝试唤起第三方 APP,但这并不是支付宝支付接口自带的”跳转外部 APP”功能。

支付接口中的同步返回地址通常只能打开 Web 页面,而且页面可能仍在支付宝内置浏览器中。若要唤起第三方 APP,需要在商户自己的支付结果页中调用 Universal Link、App Link 或 URL Scheme,并准备唤起失败时的降级页面。

能否成功唤起,还取决于支付宝客户端版本、系统限制、用户的操作方式和支付宝的安全策略,因此无法保证所有设备都能自动跳出支付宝。

推荐实现流程

可以按以下流程实现:

  1. 服务端创建支付订单,并将同步返回地址设为商户自己的支付结果页。
  2. 支付完成后,服务端通过异步通知确认订单状态。
  3. 支付结果页向商户服务端查询订单状态。
  4. 确认支付成功后,显示”打开 APP”按钮,由用户点击。
  5. 页面优先使用 Universal Link 或 App Link 唤起 APP。
  6. 如果 APP 未安装或唤起失败,跳转到下载页,或者继续显示 Web 页面。

不要根据前端支付回调或 URL 参数直接认定支付成功。前端回调只能改善交互体验,最终支付结果应以服务端查询结果或支付宝异步通知为准。

第三方 APP 需要完成的配置

iOS

建议优先使用 Universal Links,例如:

https://link.example.com/payment/result?order_no=202609090001

目标 APP 需要配置 Associated Domains:

applinks:link.example.com

对应域名还要部署符合 Apple 要求的 apple-app-site-association 文件。

如果使用自定义 URL Scheme,也需要在 APP 中注册,例如:

exampleapp://payment/result?order_no=202609090001

URL Scheme 实现起来较简单,但兼容性和安全性通常不如 Universal Links,也更容易被内置浏览器拦截。

Android

建议优先配置 Android App Links,并为跳转域名设置 intent-filter:

<activity
    android:name=".PaymentResultActivity"
    android:exported="true">

    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:scheme="https"
            android:host="link.example.com"
            android:pathPrefix="/payment/result" />
    </intent-filter>
</activity>

域名端还需要部署 Android Digital Asset Links 文件:

https://link.example.com/.well-known/assetlinks.json

也可以注册自定义 Scheme:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data
        android:scheme="exampleapp"
        android:host="payment" />
</intent-filter>

支付结果页示例

下面的示例会等待用户主动点击按钮。移动系统和内置浏览器通常会限制没有用户手势的外部 APP 唤起,因此这种方式比页面加载后自动跳转更容易成功。

<button id="open-app" type="button">打开 APP 查看订单</button>
<p id="message"></p>

<script>
  const orderNo = "202609090001";

  async function confirmPayment() {
    const response = await fetch(
      `/api/payment/status?order_no=${encodeURIComponent(orderNo)}`,
      {
        credentials: "include",
        cache: "no-store"
      }
    );

    if (!response.ok) {
      throw new Error("Failed to query payment status");
    }

    const result = await response.json();

    if (result.status !== "SUCCESS") {
      throw new Error("Payment has not been confirmed");
    }
  }

  document.getElementById("open-app").addEventListener("click", async () => {
    const message = document.getElementById("message");

    try {
      await confirmPayment();

      // 优先使用 Universal Link 或 Android App Link。
      const appLink =
        `https://link.example.com/payment/result?order_no=${encodeURIComponent(orderNo)}`;

      window.location.href = appLink;
    } catch (error) {
      message.textContent = "暂未确认支付成功,请稍后重试。";
    }
  });
</script>

如果只能使用 URL Scheme,可以加入唤起失败后的降级处理:

<script>
  function openApp(orderNo) {
    const scheme =
      `exampleapp://payment/result?order_no=${encodeURIComponent(orderNo)}`;
    const fallback =
      `https://www.example.com/app-download?order_no=${encodeURIComponent(orderNo)}`;

    const startedAt = Date.now();

    window.location.href = scheme;

    window.setTimeout(() => {
      if (document.visibilityState === "visible" &&
          Date.now() - startedAt < 2500) {
        window.location.href = fallback;
      }
    }, 1500);
  }
</script>

这种定时器方案只能用于兼容性降级,无法准确判断 APP 是否已经安装。部分系统弹出确认框、切换页面或冻结 WebView 后,定时器的行为也会发生变化。

服务端校验示例

支付结果页不能相信客户端传入的 paid=true、trade_status=SUCCESS 等参数,而应根据商户订单号查询服务端保存的支付状态。

app.get("/api/payment/status", async (req, res) => {
  const orderNo = req.query.order_no;

  if (!orderNo) {
    return res.status(400).json({
      status: "INVALID_REQUEST"
    });
  }

  const order = await orderRepository.findByOrderNo(orderNo);

  if (!order || order.userId !== req.user.id) {
    return res.status(404).json({
      status: "NOT_FOUND"
    });
  }

  return res.json({
    status: order.paymentStatus
  });
});

处理异步通知时,还要校验签名、商户身份、订单金额、订单号和应用信息,并保证重复通知不会造成重复发货。

需要注意的问题

同步跳转不等于支付成功

用户可能主动关闭支付页面,也可能遇到网络中断。同步返回也可能早于异步通知到达,因此业务状态需要由服务端异步通知和主动查询共同确认。

不建议自动唤起

iOS、Android 和支付宝内置浏览器都可能拦截没有用户手势的外部跳转。更稳妥的方式是在支付成功页显示清晰的按钮,让用户主动点击。

不要直接使用不明来源的 Scheme

第三方 APP 的 Scheme、Universal Link 或 App Link 必须由该 APP 的开发方提供。不要根据包名自行拼接,也不要依赖未公开的支付宝内部协议。

防止任意跳转漏洞

跳转地址应由服务端生成,或者从固定白名单中选择。不要把请求参数直接赋给 window.location.href。订单号也应使用不可猜测的标识,同时由服务端校验当前用户是否有权查看该订单。

保留降级路径

如果外部 APP 未安装、系统禁止唤起,或者支付宝拦截了相关协议,应提供订单结果页、复制订单号、应用商店下载页等替代入口。

上线前需要真机验证

测试至少要覆盖 iOS、Android、不同支付宝客户端版本,以及目标 APP 已安装和未安装等场景。如果支付宝开放平台后台当前提供”支付后跳转”或相关白名单配置,应以对应产品页面展示的能力和审核要求为准。不同支付产品、签约能力和客户端版本的配置入口可能不同,不能仅凭通用支付参数判断已经支持外部 APP 跳转。

备注:内容仅供参考。