PAYATHON 2026

iOS 中 H5 无法拉起支付宝支付并签约怎么办?

支付阿杰

结论

iOS 上点击 H5 后能打开支付宝,但没有进入收银台或签约页,通常说明支付宝已经被成功唤起,问题多半出在后续环节:

  • 服务端生成的支付或签约请求无效,如签名、参数、接口权限或业务参数有误。
  • 跳转地址没有正确进行 URL 编码,参数在 iOS、WebView 或重定向过程中丢失。
  • 页面运行在 WKWebView、微信等内置浏览器中,外部 Scheme 或 Universal Link 被拦截。
  • 商户把支付和签约当作普通 H5 支付处理,但实际上没有开通相应的支付签约产品。
  • 支付宝回跳地址配置错误,导致流程看起来没有完成。

支付宝被打开,并不代表接口调用成功。如果客户端只进入支付宝首页,通常是因为支付宝识别了唤起链接,却没有解析出有效的业务指令。

先确认业务模式

先确认需要实现哪种流程:

  1. 普通 H5 支付;
  2. 独立签约;
  3. 先签约,再支付;
  4. 支付时同时完成代扣协议签约。

这些模式对应不同的支付宝产品和 API。普通 H5 支付一般使用 alipay.trade.wap.pay;独立签约可能使用 alipay.user.agreement.page.sign 等签约类接口。具体使用哪个接口,取决于商户已经开通的支付宝产品。不能直接把签约参数附加到普通支付请求中。

如果需要一次操作同时完成支付和签约,应先到支付宝商户平台确认是否开通了支持该模式的产品,再按照对应产品文档接入。没有相关产品权限时,即使参数格式正确,也可能无法进入预期页面。

排查服务端请求

支付参数和签名应由服务端生成,不要把支付宝私钥保存在 H5 或 iOS 客户端中。

以普通手机网站支付为例,服务端请求通常需要检查以下参数:

method=alipay.trade.wap.pay
app_id=商户应用ID
format=JSON
charset=utf-8
sign_type=RSA2
timestamp=yyyy-MM-dd HH:mm:ss
version=1.0
notify_url=服务端异步通知地址
return_url=支付完成后的页面地址
biz_content=业务参数JSON
sign=签名结果

biz_content 至少要重点核对这些内容:

{
  "out_trade_no": "唯一商户订单号",
  "total_amount": "0.01",
  "subject": "订单说明",
  "product_code": "QUICK_WAP_WAY"
}

逐项确认:

  • app_id、应用私钥和支付宝公钥属于同一个应用。
  • sign_type 与实际签名算法一致,通常为 RSA2。
  • out_trade_no 在商户侧保持唯一。
  • 金额使用字符串表示,并符合接口要求。
  • biz_content 序列化后参与签名的内容,与实际提交的内容完全一致。
  • 应用、商户账号和接口产品均处于可用状态。
  • 签约接口中的协议号、个人信息和回跳地址等参数符合所接产品的要求。

建议直接使用支付宝服务端 SDK 生成请求表单,不要自行拼接 alipays:// 地址。

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
request.setBizContent("""
{
  "out_trade_no":"ORDER_202609090001",
  "total_amount":"0.01",
  "subject":"测试订单",
  "product_code":"QUICK_WAP_WAY"
}
""");

String form = alipayClient.pageExecute(request).getBody();

服务端把生成的 HTML 表单返回给浏览器,再由页面直接提交:

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>正在跳转到支付宝</title>
</head>
<body>
  <!-- 输出支付宝 SDK 生成的 form -->
</body>
</html>

这段代码只用于说明调用方式。SDK 类名和初始化参数应以项目实际使用的支付宝 Java SDK 为准。

检查 URL 编码

如果业务必须经过自定义跳转页或 Scheme,应把业务 URL 作为一个整体进行百分号编码。漏编码或重复编码,都可能造成支付宝客户端被打开,却无法解析支付或签约指令。

JavaScript 示例:

const targetUrl = "https://example.com/alipay/cashier?...";
const encodedTargetUrl = encodeURIComponent(targetUrl);

// schemeBase 需要来自所接支付宝产品的正式接入方案
const alipayUrl = `${schemeBase}${encodedTargetUrl}`;
window.location.href = alipayUrl;

需要重点检查:

  • ?、&、=、+、# 等字符是否已正确编码。
  • 是否先对完整业务 URL 编码,再将其拼接到外层跳转地址。
  • 中间是否经过短链、网关或 CDN 重定向。
  • JavaScript、后端框架或 WebView 是否重复执行了编码或解码。
  • 重定向是否把 POST 请求转换成 GET,造成表单字段丢失。

不要直接照搬网上的 alipays://platformapi/startapp 示例。示例中的 appId、路径和参数通常对应特定的支付宝能力,组合错误时可能只能打开支付宝首页。

检查 iOS WebView 拦截

如果 H5 页面运行在 App 的 WKWebView 中,需要允许系统接管支付宝 Scheme。

Swift 示例:

import WebKit
import UIKit

func webView(
    _ webView: WKWebView,
    decidePolicyFor navigationAction: WKNavigationAction,
    decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
    guard let url = navigationAction.request.url else {
        decisionHandler(.cancel)
        return
    }

    if let scheme = url.scheme?.lowercased(),
       scheme == "alipays" || scheme == "alipay" {
        UIApplication.shared.open(url, options: [:]) { success in
            if !success {
                // 可在这里提示用户安装支付宝或改用浏览器支付
            }
        }
        decisionHandler(.cancel)
        return
    }

    decisionHandler(.allow)
}

如果代码通过 canOpenURL(_:) 判断支付宝是否安装,还需要在 Info.plist 中声明要查询的 Scheme:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>alipays</string>
    <string>alipay</string>
</array>

LSApplicationQueriesSchemes 只影响 canOpenURL(_:) 查询。完成这项配置,并不意味着支付流程会自动执行。实际使用哪个 Scheme,应以当前支付宝接入文档和真实跳转地址为准。

跳转还应由用户点击直接触发。对于定时器、异步回调、隐藏 iframe 或页面加载后自动跳转等非用户主动发起的操作,iOS 或内置浏览器可能会拦截外部应用唤起。

区分浏览器环境

分别在以下环境中测试:

  • iPhone Safari;
  • 自有 App 的 WKWebView;
  • 微信、钉钉等内置浏览器;
  • 已安装支付宝和未安装支付宝两种状态。

如果 Safari 正常,而 WKWebView 异常,应重点检查导航代理和 Scheme 拦截。如果 Safari 和 WebView 都只能打开支付宝首页,应优先检查服务端请求、URL 编码和支付宝产品权限。

部分内置浏览器会限制直接唤起外部 App。此时可以提示用户使用 Safari 打开,或改用支付宝当前产品支持的标准 H5 跳转方式。不要用反复跳转或隐藏页面来规避浏览器的安全策略。

正确处理回调

return_url 只负责支付或签约完成后的前端跳转,不能用来判断最终业务结果。订单支付状态应以服务端异步通知或主动查询结果为准。

服务端处理异步通知时,至少需要:

  1. 验证支付宝签名;
  2. 校验 app_id、商户身份、订单号和金额;
  3. 判断交易状态;
  4. 确保重复通知能够被幂等处理;
  5. 处理成功后,按接口要求返回响应。

签约流程也不能只根据前端页面判断是否成功。服务端应保存协议号,并通过通知或查询接口确认协议状态。

建议的定位顺序

按以下顺序排查,可以较快缩小问题范围:

  1. 在 Safari 中直接打开服务端生成的支付或签约页面。
  2. 查看服务端调用支付宝 SDK 时返回的错误信息和响应内容。
  3. 使用同一组请求参数,在支付宝提供的调试环境或官方工具中验签。
  4. 记录重定向前后的完整 URL,比较参数是否丢失或被重复编码。
  5. 检查 WKWebView 是否拦截 alipays、alipay 等非 HTTP Scheme。
  6. 核对商户是否已经开通目标支付、签约或代扣产品。
  7. 分别验证支付和签约接口,确认各自能够成功后,再串联业务流程。
  8. 根据异步通知或查询结果判断最终状态。

如果支付宝已经打开,但只显示首页,应优先检查业务请求是否由官方 SDK 正确生成、跳转 URL 是否完整、签名是否有效,以及所需的签约产品是否已经开通。

备注:内容仅供参考。