PAYATHON 2026

支付宝网站支付唤起app后放弃支付如何返回自身页面

支付阿杰

结论

quit_url 不能处理“从网站支付唤起支付宝 App 后,用户放弃支付”的返回场景。网站支付接口通常也没有等价参数,能在用户取消支付后强制支付宝 App 自动跳回业务 App 或指定的 H5 页面。

当前页面运行在 iOS App 的内置浏览器中,可以选择以下方案:

  1. 优先改用支付宝 App 支付 SDK。由原生 App 发起支付,并通过 SDK 回调接收取消、成功等结果。
  2. 继续使用网站支付。业务 App 需要保留关闭或返回入口,在 App 恢复前台后查询服务端订单状态,不能依赖支付宝在取消支付后自动回跳。
  3. 为业务 App 配置 Universal Link 或 URL Scheme。这只能解决“如何打开业务 App”,不能保证支付宝会在网站支付取消后主动调用该链接。

为什么 quit_url 无法生效

quit_url 用于支付宝网页收银台。只要用户仍在网页收银台中,就可以通过页面上的返回入口跳转到这个地址。

网页唤起支付宝 App 后,支付流程便进入支付宝客户端。用户取消支付属于客户端内部操作,原来的网站支付页面无法可靠地收到取消事件,quit_url 也不会因此变成 App 之间的回跳地址。

return_url 也不能当作取消回调使用。它通常负责支付完成后的页面跳转,而且跳转结果不能作为支付成功的最终依据。订单是否支付成功,仍要以支付宝异步通知或服务端主动查询结果为准。

此外,iOS 不允许后台 App 强制把自己切回前台。如果用户取消支付后仍停留在支付宝 App,业务 App 无法通过网页脚本主动回到前台。

推荐方案:改用 App 支付 SDK

支付页面本来就嵌套在 iOS App 中,更适合采用下面的流程:

  1. H5 通过 JS Bridge 请求原生 App 发起支付。
  2. 业务服务端创建 App 支付订单,生成签名后的订单字符串。
  3. iOS 原生代码调用支付宝 App 支付 SDK。
  4. 支付结束或用户取消后,SDK 通过业务 App 配置的回跳能力返回结果。
  5. App 把结果通知 H5,并向业务服务端查询订单状态。

原生回调中的取消状态只能用来更新界面,不能直接作为订单的最终状态。支付结果与客户端回调之间可能存在延迟或竞态。

下面是示意代码,具体方法签名应以当前接入的支付宝 iOS SDK 文档为准:

func startAlipay(orderString: String) {
    AlipaySDK.defaultService().payOrder(
        orderString,
        fromScheme: "your-app-scheme"
    ) { result in
        self.handleAlipayResult(result)
    }
}

private func handleAlipayResult(_ result: [AnyHashable: Any]?) {
    let resultStatus = result?["resultStatus"] as? String

    switch resultStatus {
    case "9000":
        // 客户端认为支付成功,仍需向业务服务端查询订单状态
        queryOrderFromServer()
    case "6001":
        // 用户取消支付,可关闭支付页或恢复业务页面
        notifyWebPage(status: "cancelled")
        queryOrderFromServer()
    default:
        // 包括处理中、失败或未知状态,统一通过服务端确认
        notifyWebPage(status: "unknown")
        queryOrderFromServer()
    }
}

把结果通知给 WKWebView:

private func notifyWebPage(status: String) {
    let script = """
    window.onAlipayResult && window.onAlipayResult({
        status: '\(status)'
    });
    """

    webView.evaluateJavaScript(script)
}

H5 页面接收结果:

window.onAlipayResult = function (result) {
  if (result.status === 'cancelled') {
    showPaymentCancelled();
    showCloseButton();
  }

  refreshOrderStatus();
};

继续使用网站支付时的实现方式

如果暂时不能改用 App 支付 SDK,就需要由业务 App 补上返回入口和状态恢复逻辑。

1. 支付前保存业务页面状态

发起支付前,记录当前订单号、返回页面和页面状态:

sessionStorage.setItem(
  'pendingPayment',
  JSON.stringify({
    orderNo: 'ORDER_202609090001',
    returnPath: '/orders/ORDER_202609090001'
  })
);

不要因为客户端显示用户已取消,就直接把订单关闭。用户可能已经完成付款,只是页面没有及时收到结果。

2. App 恢复前台后通知 H5

用户从支付宝切回业务 App 时,iOS App 可以在生命周期回调中通知 WebView:

func sceneDidBecomeActive(_ scene: UIScene) {
    let script = """
    window.onHostAppResume && window.onHostAppResume();
    """

    webView.evaluateJavaScript(script)
}

如果项目没有使用 Scene 生命周期,可以在对应的 UIApplicationDelegate 生命周期方法中执行相同逻辑。

H5 收到恢复事件后,向业务服务端查询订单状态:

window.onHostAppResume = async function () {
  const raw = sessionStorage.getItem('pendingPayment');

  if (!raw) {
    return;
  }

  const pendingPayment = JSON.parse(raw);
  const response = await fetch(
    `/api/payment/status?orderNo=${encodeURIComponent(pendingPayment.orderNo)}`,
    {
      credentials: 'include',
      cache: 'no-store'
    }
  );

  const result = await response.json();

  if (result.status === 'PAID') {
    sessionStorage.removeItem('pendingPayment');
    location.replace(pendingPayment.returnPath);
    return;
  }

  if (result.status === 'CLOSED') {
    sessionStorage.removeItem('pendingPayment');
    showPaymentClosed();
    return;
  }

  showPaymentPendingActions();
};

页面还可以提供“我已完成支付”“重新支付”和“返回订单”等操作:

<div id="payment-actions" hidden>
  <button type="button" onclick="refreshOrderStatus()">
    我已完成支付
  </button>

  <button type="button" onclick="retryPayment()">
    重新支付
  </button>

  <button type="button" onclick="returnToOrder()">
    返回订单
  </button>
</div>

3. 保留原生关闭或返回入口

当前内置浏览器关闭了导航栏,而 iOS 没有系统级返回键。即使用户已经回到业务 App,也可能被困在支付中间页。

因此,至少要提供一种始终可用的退出方式:

  • 恢复内置浏览器导航栏;
  • 在 WebView 上方添加原生关闭按钮;
  • 在支付页中提供固定的“返回订单”按钮;
  • 通过 JS Bridge 调用原生代码关闭当前 WebView。

例如:

function closePaymentPage() {
  if (window.webkit?.messageHandlers?.closePaymentPage) {
    window.webkit.messageHandlers.closePaymentPage.postMessage({});
    return;
  }

  history.back();
}

原生端注册并处理该消息:

extension PaymentViewController: WKScriptMessageHandler {
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage
    ) {
        guard message.name == "closePaymentPage" else {
            return
        }

        navigationController?.popViewController(animated: true)
    }
}

业务 App 可以配置 Universal Link 或 URL Scheme,供外部重新打开 App,例如:

your-app-scheme://payment/result?orderNo=ORDER_202609090001

使用时需要注意:

  • 配置 Scheme 并不代表支付宝会在网站支付取消后调用它;
  • 不能假设把 Scheme 填入 quit_url,就能让支付宝 App 在取消支付后回跳;
  • 自定义 Scheme 能否用于某个支付参数,应以当前支付宝产品文档和开放平台配置要求为准;
  • URL 中不要携带支付密钥、签名私钥,也不要携带可直接确认支付成功的客户端参数。

注意事项

  • 支付成功必须以支付宝异步通知或服务端订单查询结果为准。
  • return_url、SDK 客户端回调和 H5 页面状态都不能单独作为入账依据。
  • App 恢复前台只说明用户回到了业务 App,无法证明用户已经取消或完成支付。
  • 用户可能在支付宝完成付款后手动切回业务 App,因此 App 恢复时应先查询订单,再根据结果显示支付成功、处理中或重新支付。
  • 查询接口应校验当前登录用户与订单的归属关系,不能只凭前端传入的订单号返回敏感信息。
  • 订单状态尚未确认时,不要立即创建新订单,以免造成重复支付。

对于“iOS App 内嵌 H5,再由 H5 唤起支付宝 App”的结构,建议改用原生 App 支付 SDK。如果必须保留网站支付,业务侧需要恢复导航能力,并在 App 恢复前台后通过服务端查单来恢复页面状态。

备注:内容仅供参考。