From a3a190b5e15134737ea3705ab53d931abcd0317c Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:13:48 -0300 Subject: [PATCH 1/2] docs(payments): implement API-23 deeplink overload in Android PayWebView sample Promote the "if you support minSdk < 24, also override the deprecated overload" note into actual code: add a shared handleWalletDeeplink helper and override both shouldOverrideUrlLoading overloads. On API 23 only the deprecated String overload fires, so without it wc: wallet deeplinks load inside the WebView instead of opening the wallet. Mirrors the reown-kotlin dapp sample implementation (reown-kotlin PR #419). --- payments/wallets/webview.mdx | 42 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/payments/wallets/webview.mdx b/payments/wallets/webview.mdx index 02e8cd7..bd83e35 100644 --- a/payments/wallets/webview.mdx +++ b/payments/wallets/webview.mdx @@ -208,6 +208,7 @@ const styles = StyleSheet.create({webview: {flex: 1}}); ```kotlin import android.content.ActivityNotFoundException +import android.content.Context import android.content.Intent import android.net.Uri import android.os.Handler @@ -232,6 +233,21 @@ fun isWalletDeeplink(url: String): Boolean = try { Uri.parse(url).getQueryParameter("uri")?.startsWith("wc:") == true } catch (_: Exception) { false } +// Shared by both shouldOverrideUrlLoading overloads: forward wc: wallet deeplinks to the OS +// (return true = handled), load everything else in-place (return false). +private fun handleWalletDeeplink(context: Context, url: String?): Boolean { + if (url == null) return false + if (isWalletDeeplink(url)) { + try { + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + } catch (_: ActivityNotFoundException) { + // No app can handle the wallet deeplink (wallet not installed). + } + return true + } + return false +} + @Composable fun PayWebView( gatewayUrl: String, @@ -274,22 +290,16 @@ fun PayWebView( "ReactNativeWebView" ) webViewClient = object : WebViewClient() { - // This WebResourceRequest overload is API 24+. If you support minSdk < 24, - // also override the deprecated shouldOverrideUrlLoading(view, url: String) and - // run the same isWalletDeeplink check — otherwise deeplink interception - // silently no-ops on API 23. - override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { - val reqUrl = request?.url?.toString() ?: return false - if (isWalletDeeplink(reqUrl)) { - try { - context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(reqUrl))) - } catch (_: ActivityNotFoundException) { - // No app can handle the wallet deeplink (wallet not installed). - } - return true - } - return false - } + // API 24+ overload. + override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean = + handleWalletDeeplink(context, request?.url?.toString()) + + // Deprecated pre-API-24 overload — still the only one called on API 23, so + // without it deeplink interception silently no-ops there and wc: links would + // load inside the WebView instead of opening the wallet. + @Suppress("DEPRECATION") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean = + handleWalletDeeplink(context, url) } loadUrl(buildPayUrl(gatewayUrl, appDeepLink)) } From e8176330d4f302f801bc6fcd2770ce848ae363e5 Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:58:33 -0300 Subject: [PATCH 2/2] docs(payments): block non-https navigations in RN/Kotlin/Flutter PayWebView samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prose (Wallet Deeplink Interception + Best Practices) and the iOS sample block non-https schemes, but the React Native, Kotlin, and Flutter samples allowed them. Align all three: forward wc: wallet deeplinks, allow https + about:, and block any other scheme (tel:, sms:, intent:, …) so the page can't drive the OS into arbitrary native apps. Addresses the Copilot review comment on the Android sample. --- payments/wallets/webview.mdx | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/payments/wallets/webview.mdx b/payments/wallets/webview.mdx index bd83e35..da42d04 100644 --- a/payments/wallets/webview.mdx +++ b/payments/wallets/webview.mdx @@ -165,7 +165,14 @@ export function PayWebView({gatewayUrl, appDeepLink, onSuccess, onFailure}: PayW Linking.openURL(request.url).catch(console.warn); return false; } - return true; + // Allow https (the checkout) and about:; block any other scheme so the + // page can't drive the OS into arbitrary native schemes (tel:, sms:, …). + try { + const scheme = new URL(request.url).protocol; + return scheme === 'https:' || scheme === 'about:'; + } catch { + return false; + } }, [], ); @@ -233,8 +240,10 @@ fun isWalletDeeplink(url: String): Boolean = try { Uri.parse(url).getQueryParameter("uri")?.startsWith("wc:") == true } catch (_: Exception) { false } -// Shared by both shouldOverrideUrlLoading overloads: forward wc: wallet deeplinks to the OS -// (return true = handled), load everything else in-place (return false). +// Shared by both shouldOverrideUrlLoading overloads. Returns true = handled (don't load +// in the WebView), false = let the WebView load it. Forward wc: wallet deeplinks to the OS; +// allow https (the checkout) and about: to load; block any other scheme (tel:, sms:, +// intent:, …) so the page can't drive the OS into arbitrary native apps. private fun handleWalletDeeplink(context: Context, url: String?): Boolean { if (url == null) return false if (isWalletDeeplink(url)) { @@ -245,7 +254,8 @@ private fun handleWalletDeeplink(context: Context, url: String?): Boolean { } return true } - return false + val scheme = Uri.parse(url).scheme?.lowercase() + return scheme != "https" && scheme != "about" } @Composable @@ -468,7 +478,13 @@ class _PayWebViewState extends State { } return NavigationDecision.prevent; } - return NavigationDecision.navigate; + // Allow https (the checkout) and about:; block any other scheme so the + // page can't drive the OS into arbitrary native schemes (tel:, sms:, …). + final scheme = Uri.tryParse(request.url)?.scheme.toLowerCase(); + if (scheme == 'https' || scheme == 'about') { + return NavigationDecision.navigate; + } + return NavigationDecision.prevent; }, )) ..addJavaScriptChannel('ReactNativeWebView', onMessageReceived: (msg) {