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).

Option
Android
iOS
External browser
Chrome
Safari
In-app browser
Chrome Custom Tabs
SFSafariViewController

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:

Environment
Google Pay
Apple Pay
In-app or external browser
Supported with no additional configuration in the app.
Supported in Safari and SFSafariViewController (iOS).
Android WebView
Supported if you configure the WebView and the device meets the requirements. See configuration.
Apple does not document support.
iOS WKWebView
Google documents a configuration, but states that the availability check always returns false. See considerations.
Supported since iOS 13 if your app does not inject JavaScript. See considerations.

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:

  1. Add the androidx.webkit dependency, version 1.14.0 or higher.
dependencies {
    implementation 'androidx.webkit:webkit:1.14.0'
}
  1. 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>
  1. Enable JavaScript and the Payment Request API in the WebView. If you use the CheckoutWebView component from the Android integration, add these lines next to the settings configuration.
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

settings.javaScriptEnabled = true
if (WebViewFeature.isFeatureSupported(WebViewFeature.PAYMENT_REQUEST)) {
    WebSettingsCompat.setPaymentRequestEnabled(settings, true)
}
  1. If you customize the WebView user agent, append the text GOOGLE_PAY_SUPPORTED to it.
  2. Do not intercept requests with shouldInterceptRequest to send them through a third-party HTTP client; Google does not support this setup.
  3. 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 WKWebView guide: append GOOGLE_PAY_SUPPORTED to the user agent and implement the WKUIDelegate methods webView(_:createWebViewWith:for:windowFeatures:) and webViewDidClose(_:) to display and close the pop-up windows opened by the payment flow.
  • In its FAQ, Google states that in iOS WebViews the isReadyToPay check always returns false due to iOS restrictions on third-party cookies. Even with the configuration above, Google Pay may not be available.

Apple Pay

  • WKWebView supports Apple Pay since iOS 13, but disables it if the app uses script injection APIs such as WKUserScript or evaluateJavaScript(_:completionHandler:) before the page uses Apple Pay. The restriction resets every time the top frame navigates (WebKit).
  • In react-native-webview, enable the enableApplePay={true} property. With it, injectedJavaScript, injectedJavaScriptBeforeContentLoaded, injectJavaScript, sharedCookiesEnabled, and HTML5 history stop working (reference).

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()
    })
}
Property
Description
JavaScriptEnabled
Enables the execution of JavaScript on the loading web page.
DomStorageEnabled
Enables web pages to store data locally, which can improve speed and performance.
ClearCache
Clears the WebView cache before loading a new URL.
WebChromeClient and WebViewClient
Configure the WebView's behavior for events such as URL loading and interaction with the page.
CookieManager
Allows the use of cookies in the WebView.

Integration example