支付宝H5支付缺失fromAPPurlScheme无法返回第三方APP
结论
第三方 App 需要通过支付宝支持的 URL Scheme 拉起收银台,并在链接中传入来源 App 的 Scheme。常用参数名为区分大小写的 fromAppUrlScheme,参数值必须与第三方 App 已注册的 URL Scheme 完全一致。
如果只在 H5 页面中打开普通的 https:// 支付地址,或直接跳转到支付宝下载页、中间页,支付宝可能无法识别来源 App。即使支付成功,也可能无法自动返回原 App。
正确的链接结构
典型的支付宝客户端拉起链接如下:
alipays://platformapi/startapp?appId=20000067&url=<ENCODED_H5_PAY_URL>&fromAppUrlScheme=<APP_SCHEME>
也可以使用:
alipay://platformapi/startapp?appId=20000067&url=<ENCODED_H5_PAY_URL>&fromAppUrlScheme=<APP_SCHEME>
参数含义如下:
url是经过 URL 编码的支付宝 H5 收银台地址。fromAppUrlScheme是第三方 App 注册的 Scheme,通常只填写 Scheme 名称,例如myshop,不要随意填写回调网页地址。- 参数名称及大小写必须符合当前接入方案。常用写法是
fromAppUrlScheme,不要自行改成fromAPPurlScheme。 appId=20000067是常见的支付宝钱包 H5 收银台拉起入口。如果支付宝当前接入文档或返回的跳转模板使用了其他值,应以实际模板为准,不要硬编码替换。
假设第三方 App 注册的 Scheme 是 myshop,原始收银台地址为:
https://mclient.alipay.com/cashier/mobilepay.htm?trade_no=xxx
可以使用下面的 JavaScript 生成拉起地址:
const cashierUrl =
"https://mclient.alipay.com/cashier/mobilepay.htm?trade_no=xxx";
const appScheme = "myshop";
const alipayUrl =
"alipays://platformapi/startapp" +
"?appId=20000067" +
"&url=" + encodeURIComponent(cashierUrl) +
"&fromAppUrlScheme=" + encodeURIComponent(appScheme);
window.location.href = alipayUrl;
不要直接拼接未经编码的 cashierUrl。支付地址通常包含 ?、&、= 等字符,不编码会破坏外层拉起链接的参数结构。
注册第三方 App 的 Scheme
只有系统能够通过指定 Scheme 打开第三方 App 时,fromAppUrlScheme 才会生效。假设使用:
myshop://
iOS 和 Android 都需要注册 myshop。
iOS 配置
在 Info.plist 中注册 URL Types:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.example.myshop</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myshop</string>
</array>
</dict>
</array>
应用还需要在相应的生命周期回调中处理 URL:
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
guard url.scheme == "myshop" else {
return false
}
// 恢复支付页面,并主动查询订单状态。
return true
}
如果项目使用 Scene 生命周期,则应在对应的 scene(_:openURLContexts:) 回调中处理。
Android 配置
在接收返回地址的 Activity 中配置 intent-filter:
<activity
android:name=".PaymentResultActivity"
android:exported="true">
<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="myshop" />
</intent-filter>
</activity>
然后在 Activity 中处理回跳 Intent:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handlePaymentReturn(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
handlePaymentReturn(intent)
}
private fun handlePaymentReturn(intent: Intent?) {
val uri = intent?.data ?: return
if (uri.scheme != "myshop") return
// 恢复支付页面,并通过服务端查询订单状态。
}
完整处理流程
可以按以下顺序配置和排查:
- 为第三方 App 确定一个不会与其他应用冲突的 Scheme,例如
myshop。 - 在 iOS 和 Android 工程中注册该 Scheme。
- 获取支付宝返回的真实 H5 收银台地址。
- 对整个 H5 收银台地址进行一次 URL 编码。
- 构造以
alipays://或alipay://开头的拉起地址。 - 在外层拉起地址中加入
fromAppUrlScheme=myshop。 - 在真机上确认
myshop://可以单独打开第三方 App。 - 支付完成并返回 App 后,由 App 或服务端查询订单状态。
注意事项
不要混淆两类返回地址
fromAppUrlScheme 用于从支付宝客户端切回第三方 App。支付接口中的 return_url 通常用于支付完成后的网页跳转,两者用途不同。
只配置 return_url,不能保证支付宝客户端会返回第三方 App。只配置 fromAppUrlScheme,也不能代替支付结果通知。
支付结果必须以服务端为准
App 被重新打开只说明发生了回跳,不能据此认定支付成功。用户可能取消支付,也可能通过伪造 Scheme 主动打开 App。
应按以下方式确认支付结果:
- 服务端接收支付宝异步通知并完成验签。
- App 返回前台后,请求业务服务端查询订单。
- 业务服务端调用支付宝查询接口,或读取已经确认的订单状态。
- 不要直接信任回跳 URL 中的支付状态。
Scheme 必须完全一致
以下几处的值必须保持一致:
fromAppUrlScheme=myshop
iOS CFBundleURLSchemes: myshop
Android android:scheme="myshop"
不要在一处填写 myshop,另一处却填写 myshop://、MyShop 或带路径的完整 URL,除非当前支付宝接入文档明确要求使用这些格式。
不要重复编码
通常只需对内层 H5 收银台地址编码一次:
encodeURIComponent(cashierUrl)
如果页面再次对整个拉起链接进行编码,可能产生 %253A、%252F 等内容,导致支付宝无法正确还原收银台地址。
WebView 需要放行自定义 Scheme
如果支付页运行在 App 内的 WebView 中,WebView 遇到 alipays:// 或 alipay:// 时可能不会自动跳转。这时需要由原生层拦截链接,再交给系统打开。同时,只允许可信的支付宝 Scheme 和支付域名,避免系统执行任意外部地址。
部分浏览器、系统版本或支付宝客户端版本还可能限制没有用户手势的 Scheme 跳转。因此,最好让用户点击”打开支付宝支付”按钮触发跳转,不要在页面加载后立即自动跳转。
备注:内容仅供参考。
