PAYATHON 2026

Revolut Pay SDK 在 WebView 支付重定向后因 return URI 崩溃

支付小周

结论

崩溃发生在 Revolut Pay SDK 处理 WebView 回调时,不是在 MainActivity.onNewIntent() 中。

myapp://revolut-pay 是合法的 Android hierarchical URI,Manifest 配置也能与它匹配。异常表明,SDK 在 WebViewConfirmationActivity.shouldOverrideUrlLoading() 中对一个实际为 opaque URI 的地址调用了 getQueryParameterNames()。正常情况下,SDK 应先检查 uri.isHierarchical。结合堆栈判断,可能是 SDK 没有正确处理 MAIN 环境返回的地址,也可能是 MAIN 环境实际下发的重定向 URL 与配置不一致。

异常发生在 SDK 自己的 WebView Activity 内部,因此在应用层为 handleDeepLink() 添加 try/catch 或 URI 校验,也无法阻止这次崩溃。

为什么会崩溃

Android 的 Uri.getQueryParameterNames() 只能处理 hierarchical URI,例如:

myapp://revolut-pay?status=success
https://example.com/callback?status=success

没有 // 的 URI 属于 opaque URI,例如:

myapp:revolut-pay?status=success

如果对 opaque URI 调用:

uri.getQueryParameterNames()

系统会直接抛出:

UnsupportedOperationException: This isn't a hierarchical URI.

堆栈中的关键位置是:

WebViewConfirmationActivity$b.shouldOverrideUrlLoading

这说明 WebView 在拦截跳转 URL 时发生了崩溃。此时系统还没有把 deep link 交给 MainActivity,所以即使注释掉 handleDeepLink(intent),崩溃仍会出现。

首先确认生产环境实际返回的 URL

除了检查传给 init() 的 returnUri,还需要确认 MAIN 环境中的 WebView 最终导航到了哪个完整 URL。尤其要检查实际地址是否变成了:

myapp:revolut-pay

而不是:

myapp://revolut-pay

同时检查以下情况:

  • URL 是否经过二次编码或错误解码;
  • MAIN 商户后台配置的 return URI 是否与 SDK 中的值一致;
  • 重定向经过中间页面后,URI 格式是否发生变化;
  • WebView 跳转到回调地址前,是否先触发了其他自定义 scheme;
  • 生产环境支付页面是否与当前 Android SDK 版本存在兼容问题。

可以使用 Chrome 的 WebView 远程调试、网络代理或 Revolut 提供的诊断日志,确认实际跳转地址。不要在日志或工单中公开支付令牌、订单信息和客户数据。

建议调整 return URI

原地址按照 Android 规则已经是 hierarchical URI。不过,可以尝试改用包含明确路径的形式,减少服务端拼接或解析时的歧义:

private const val RETURN_URI = "myapp://revolut-pay/callback"

初始化时必须使用完全相同的值:

RevolutPayments.revolutPay.init(
    environment = RevolutPayEnvironment.MAIN,
    returnUri = Uri.parse(RETURN_URI),
    merchantPublicKey = "...",
    requestShipping = false,
    customer = Customer("", "", "", null, null)
)

Manifest 中也要配置对应的 path:

<activity
    android:name=".ui.activity.MainActivity"
    android:launchMode="singleTop"
    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="myapp"
            android:host="revolut-pay"
            android:path="/callback" />
    </intent-filter>
</activity>

如果还需要在 Revolut 商户后台登记生产环境回调地址,那里也必须填写相同的值:

myapp://revolut-pay/callback

结尾斜杠、大小写和路径是否敏感,应以当前 Revolut Pay 的配置规则为准。不能认为 SANDBOX 中可用的地址会自动同步到 MAIN。

正确处理冷启动和已有实例

SDK 内部的崩溃解决后,应用仍需同时处理冷启动,以及 singleTop 模式下传入的新 Intent:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleDeepLink(intent)
}

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    setIntent(intent)
    handleDeepLink(intent)
}

private fun handleDeepLink(intent: Intent?) {
    val uri = intent?.data ?: return

    if (!uri.isHierarchical) {
        Log.e("RevolutDeepLink", "Ignoring opaque URI: $uri")
        return
    }

    if (uri.scheme != "myapp" || uri.host != "revolut-pay") {
        return
    }

    runCatching {
        RevolutPayments.revolutPay.handle(uri)
    }.onFailure { error ->
        Log.e("RevolutDeepLink", "Error handling Revolut Pay URI", error)
    }
}

这段校验只能保护 deep link 进入应用后的处理逻辑,无法捕获 Revolut SDK 的 WebViewConfirmationActivity 在 shouldOverrideUrlLoading() 中抛出的异常。

推荐的解决顺序

  1. 升级到 Revolut Pay Android SDK 当前可用的稳定版本,并查看发布说明中是否提到 WebView、deep link 或 return URI 修复。
  2. 逐字符核对 MAIN 商户配置和代码中的 returnUri。
  3. 获取 WebView 最后访问的几个 URL,确认其中是否包含 opaque URI。
  4. 尝试使用带固定 path 的回调地址,例如 myapp://revolut-pay/callback。
  5. 如果问题仍然存在,将 SDK 版本、Android 版本、MAIN 环境、脱敏后的实际跳转 URL 和完整堆栈提交给 Revolut 支持团队。

SDK 内部较合理的处理方式类似:

val uri = Uri.parse(url)

if (!uri.isHierarchical) {
    // 忽略、交给系统处理或返回明确错误
    return false
}

val parameterNames = uri.queryParameterNames

但这段逻辑位于 Revolut SDK 内,应用无法通过自己的 onNewIntent() 修复。

综合这些现象,问题更可能来自 MAIN 环境的 WebView 重定向格式异常,或 SDK 缺少必要的 URI 防御性检查,而不是当前 Activity 的 deep-link handler。使用带 path 的 return URI 可以作为兼容性尝试,但如果实际 URL 仍被生成为 opaque URI,最终还需要 Revolut 修复服务端重定向或发布新版 SDK。

备注:内容仅供参考。