支付宝网站支付唤起app后放弃支付如何返回自身页面
结论
quit_url 不能处理“从网站支付唤起支付宝 App 后,用户放弃支付”的返回场景。网站支付接口通常也没有等价参数,能在用户取消支付后强制支付宝 App 自动跳回业务 App 或指定的 H5 页面。
当前页面运行在 iOS App 的内置浏览器中,可以选择以下方案:
- 优先改用支付宝 App 支付 SDK。由原生 App 发起支付,并通过 SDK 回调接收取消、成功等结果。
- 继续使用网站支付。业务 App 需要保留关闭或返回入口,在 App 恢复前台后查询服务端订单状态,不能依赖支付宝在取消支付后自动回跳。
- 为业务 App 配置 Universal Link 或 URL Scheme。这只能解决“如何打开业务 App”,不能保证支付宝会在网站支付取消后主动调用该链接。
为什么 quit_url 无法生效
quit_url 用于支付宝网页收银台。只要用户仍在网页收银台中,就可以通过页面上的返回入口跳转到这个地址。
网页唤起支付宝 App 后,支付流程便进入支付宝客户端。用户取消支付属于客户端内部操作,原来的网站支付页面无法可靠地收到取消事件,quit_url 也不会因此变成 App 之间的回跳地址。
return_url 也不能当作取消回调使用。它通常负责支付完成后的页面跳转,而且跳转结果不能作为支付成功的最终依据。订单是否支付成功,仍要以支付宝异步通知或服务端主动查询结果为准。
此外,iOS 不允许后台 App 强制把自己切回前台。如果用户取消支付后仍停留在支付宝 App,业务 App 无法通过网页脚本主动回到前台。
推荐方案:改用 App 支付 SDK
支付页面本来就嵌套在 iOS App 中,更适合采用下面的流程:
- H5 通过 JS Bridge 请求原生 App 发起支付。
- 业务服务端创建 App 支付订单,生成签名后的订单字符串。
- iOS 原生代码调用支付宝 App 支付 SDK。
- 支付结束或用户取消后,SDK 通过业务 App 配置的回跳能力返回结果。
- 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)
}
}
关于 Universal Link 和 URL Scheme
业务 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 恢复前台后通过服务端查单来恢复页面状态。
备注:内容仅供参考。
