Integration with mobile application
General Recommendation
⚠️ Using WebView for the payment process is not recommended. While it is technically possible to integrate the checkout inside a
WebView, it is not the preferred option.
It is recommended to use real system browsers, as they use the native engine, share cookies with the device browser, and correctly maintain user context during redirects (3DS, authentications, wallets).
Why not WebView?
- Restrictions on user actions (autoplay, popups, wallets).
- Limitations on cross-domain redirects.
- Inconsistent behavior across operating system versions.
- Greater maintenance surface area.
Digital wallets in WebView
The conditions for using wallets inside a WebView depend on the platform: on Android, your app must enable the Payment Request API used by Google Pay; on iOS, WKWebView has its own restrictions for Google Pay and Apple Pay. If you offer wallets in your app, open Webcheckout in an in-app or external browser. If you need to keep the WebView, configure your app for each platform:
For Google Pay, the availability check and the errors that can occur during payment are described in Availability and payment errors.
Google Pay in Android WebView
According to Google’s Android WebView guide, the payer’s device must have:
- Google Play services 25.18.30 or higher.
- Android System WebView 137 or higher.
To enable Google Pay, make these changes in your app:
- Add the
androidx.webkitdependency, version 1.14.0 or higher.
dependencies {
implementation 'androidx.webkit:webkit:1.14.0'
}
- Declare the queries used by Google Pay in
AndroidManifest.xml.
<queries>
<intent>
<action android:name="org.chromium.intent.action.PAY"/>
</intent>
<intent>
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY"/>
</intent>
<intent>
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS"/>
</intent>
</queries>
- Enable JavaScript and the Payment Request API in the
WebView. If you use theCheckoutWebViewcomponent from the Android integration, add these lines next to thesettingsconfiguration.
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
settings.javaScriptEnabled = true
if (WebViewFeature.isFeatureSupported(WebViewFeature.PAYMENT_REQUEST)) {
WebSettingsCompat.setPaymentRequestEnabled(settings, true)
}
- If you customize the
WebViewuser agent, append the textGOOGLE_PAY_SUPPORTEDto it. - Do not intercept requests with
shouldInterceptRequestto send them through a third-party HTTP client; Google does not support this setup. - Complete the Google Pay integration publishing process for your app. Google requires it to allow Google Pay inside an app’s
WebView.
Google documents that, if the device does not meet the version requirements above, the availability check (isReadyToPay) returns false; in that case, Webcheckout does not display the button. If configuration is missing in the app, the button may be displayed and the flow may fail when starting, as happened in our tests.
Expo / React Native
react-native-webview does not expose a property to enable the Payment Request API. Apply steps 1 to 3 with native code, for example through a config plugin, and run the app as a development build: Expo Go does not include custom native code. If you customize the user agent, include GOOGLE_PAY_SUPPORTED, for example with applicationNameForUserAgent:
<WebView
source={{ uri: processUrl }}
javaScriptEnabled={true}
domStorageEnabled={true}
applicationNameForUserAgent="GOOGLE_PAY_SUPPORTED" // Appends GOOGLE_PAY_SUPPORTED to the user agent.
/>
Wallets in iOS WebView
On iOS, Apple documents Safari and SFSafariViewController as the supported contexts for Apple Pay on the web. If you offer wallets, the recommended option is the in-app browser, which uses SFSafariViewController on iOS. If you use WKWebView, keep the following in mind.
Google Pay
- Google publishes a
WKWebViewguide: appendGOOGLE_PAY_SUPPORTEDto the user agent and implement theWKUIDelegatemethodswebView(_:createWebViewWith:for:windowFeatures:)andwebViewDidClose(_:)to display and close the pop-up windows opened by the payment flow. - In its FAQ, Google states that in iOS WebViews the
isReadyToPaycheck always returnsfalsedue to iOS restrictions on third-party cookies. Even with the configuration above, Google Pay may not be available.
Apple Pay
WKWebViewsupports Apple Pay since iOS 13, but disables it if the app uses script injection APIs such asWKUserScriptorevaluateJavaScript(_:completionHandler:)before the page uses Apple Pay. The restriction resets every time the top frame navigates (WebKit).- In
react-native-webview, enable theenableApplePay={true}property. With it,injectedJavaScript,injectedJavaScriptBeforeContentLoaded,injectJavaScript,sharedCookiesEnabled, and HTML5 history stop working (reference).
Results from our tests. With a test app built with Expo (react-native-webview), we reproduced the Google Pay failure in an unconfigured Android WebView: the button appeared and the flow failed when starting. We confirmed it worked after applying the Android configuration. On the iPhone we tested, Google Pay did not work inside the WebView. This result comes from a single device and configuration: it does not replace the support documented by Google and Apple, which may vary by operating system version and your app’s configuration.
Integration with Expo / React Native
Once you have obtained a payment session from your backend service, you can start the payment process in the mobile application. Unlike native Android, in Expo you have three options for opening the processUrl, each with different levels of compatibility and user experience.
In-app browser (recommended)
Use expo-web-browser to open Chrome Custom Tabs (Android) or SFSafariViewController (iOS) without leaving the app. Supports 3DS and shares system cookies.
npx expo install expo-web-browser
import * as WebBrowser from 'expo-web-browser'
const result = await WebBrowser.openBrowserAsync(processUrl)
External browser
Opens the URL in the device's default browser. Use deep links with your returnUrl and cancelUrl so the app is notified when the process is complete.
import { Linking } from 'react-native'
await Linking.openURL(processUrl)
WebView (not recommended)
npx expo install react-native-webview
import { WebView } from 'react-native-webview'
;<WebView
source={{ uri: processUrl }}
javaScriptEnabled={true} // Required to run checkout scripts.
domStorageEnabled={true} // Maintains session state across redirects.
thirdPartyCookiesEnabled={true} // Required for 3DS and other authentication redirects.
mediaPlaybackRequiresUserAction={false} // Only required if your checkout needs to play video. Allows video autoplay without user interaction. On iOS, also requires the `muted` attribute on the video element.
allowsInlineMediaPlayback={true} // Use together with `mediaPlaybackRequiresUserAction={false}`.
/>
If you offer Google Pay or Apple Pay, review the additional configuration in Digital wallets in WebView.
Integration example
Before integrating into your app, you can validate the checkout behavior using the following tools. All of them allow you to enter the session URL and open it in different navigation modes (WebView, external browser, and in-app browser).
The entry point is App.js. The in-app browser logic (recommended option) is in the inapp mode, which uses WebBrowser.openBrowserAsync.
An Expo Snack is also available for quick execution without any local setup.
Android integration (Kotlin)
Once you have obtained a payment session from your backend service, you can start the payment process in the mobile application. See the general recommendation to choose the most suitable navigation mode.
If you decide to use WebView, you can load the processUrl using the loadUrl method of the WebView class. Make sure to enable both JavaScript and Cookies for the payment session to function correctly — without them, the session will not allow the process to move forward.
If you offer Google Pay, also complete the Google Pay in Android WebView configuration.
Configure the WebView
These settings help optimize and personalize the browsing experience within the WebView.
It is important that you can identify the Return URL and the Cancel URL to be able to close the WebView once the payment process is complete.
import android.annotation.SuppressLint
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.viewinterop.AndroidView
import com.placetopay.p2pr.utilities.Constants
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun CheckoutWebView(processUrl: String, returnUrl: String, cancelUrl: String, refreshWebView: Boolean, onFinished: () -> Unit) {
AndroidView(factory = {
WebView(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
clearCache(true)
CookieManager.getInstance().setAcceptCookie(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
}
webChromeClient = WebChromeClient()
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?, request: WebResourceRequest?
): Boolean {
if (request?.url.toString() == returnUrl || request?.url.toString() == cancelUrl)
onFinished()
return super.shouldOverrideUrlLoading(view, request)
}
}
loadUrl(processUrl)
}
}, update = {
it.loadUrl(processUrl)
if (refreshWebView) it.reload()
})
}