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.

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}`.
/>

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.

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