Mobile App Integration
For iOS
Comparison of iOS WebView
| Capability | WKWebView | SFSafariViewController |
|---|---|---|
| Embed inside your own UI | Yes | No (modal browser) |
Receive postMessage from Payment capture page in native code | Yes | No — use successUrl / cancelUrl redirects |
| Apple Pay | Yes (iOS 16+) | Yes (all supported iOS versions) |
| Google Pay | Yes | Limited / not supported officially |
| Initial setup effort | Moderate | Minimal |
SFSafariViewController
The simplest integration. iOS provides the full browser environment, so Apple Pay, 3DS and external scheme redirects all work without extra setup. successUrl and cancelUrl is mandatory to redirects users back to the app.
https://hosted-global-sandbox.ezypay.com/paymentmethod/embed?token=eyJhbGciOi...&countryCode=AU&successUrl=https%3A%2F%2Fexample.com%2Fsuccess&cancelUrl=https%3A%2F%2Fexample.com%2Fcancel
import SafariServices
let config = SFSafariViewController.Configuration()
config.entersReaderIfAvailable = false
config.barCollapsingEnabled = false
let vc = SFSafariViewController(url: pcpURL, configuration: config)
vc.dismissButtonStyle = .done
present(vc, animated: true)When Payment capture page finishes or is cancelled, it redirects to the successUrl / cancelUrl configured. Use a custom URL scheme or universal link to return control to your app, and handle it in your AppDelegate / SceneDelegate.
WKWebView
Use this integration when Payment capture page needs to be rendered inside a native screen. Apple Pay within WKWebView requires iOS 16 or later.
Required Setup
-
Set a Mobile Safari user agent
Payment capture page relies on user-agent detection to enable Google Pay and Safari-based payment APIs.
-
Set both
WKNavigationDelegateandWKUIDelegateThese delegates must:
- Forward non-HTTP(S) navigations (
tel:,mailto:, wallet schemes, bank deep links) toUIApplication.shared.open(_:). - Handle
window.open()by presenting the URL inSFSafariViewController. Payment capture page uses popups for 3DS challenges and some redirect flows.
- Forward non-HTTP(S) navigations (
-
Avoid injecting
WKUserScriptor registeringWKScriptMessageHandlerbefore page load when Apple Pay is required. Doing so disablesApplePaySessionfor that page load. If a JavaScript bridge is required, enable it only for flows that do not use Apple Pay.
Minimal WKWebView Setup
import WebKit
let safariUA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) " +
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 " +
"Mobile/15E148 Safari/604.1"
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = []
let webView = WKWebView(frame: .zero, configuration: config)
webView.customUserAgent = safariUA
webView.navigationDelegate = coordinator
webView.uiDelegate = coordinator
webView.load(URLRequest(url: pcpURL))Forwarding Non-Web Schemes
func webView(_ webView: WKWebView,
decidePolicyFor action: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = action.request.url,
let scheme = url.scheme?.lowercased(),
scheme != "https",
scheme != "http",
scheme != "about",
!scheme.isEmpty {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}Handling window.open (3DS Popups)
window.open (3DS Popups)func webView(_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for action: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
guard let url = action.request.url else { return nil }
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let root = scene.windows.first?.rootViewController {
root.present(SFSafariViewController(url: url), animated: true)
}
return nil
}Apple Pay Specific
-
Apple Pay merchant registration and domain verification are handled by Ezypay
No custom Apple Pay Merchant ID orcom.apple.developer.in-app-paymentsentitlement is required -
Payment capture page must be served from a verified domain.
-
Device requirements:
- A card is provisioned in Apple Wallet
- The user is signed in to iCloud
-
Apple Pay is supported in
WKWebViewfrom iOS 16 onward
UseSFSafariViewControllerfor earlier versions
Troubleshooting Apple Pay
- The page loads without JavaScript errors If
"ApplePaySession is not available"appears ,ensure noWKUserScriptwas injected before page load - Use a new access token when loading the page.
- If Google Pay button does not appears ensures Mobile Safari user agent is correctly set
- After tapping the Apple Pay button and authorising in the sheet, tap Create. The native
WKScriptMessageHandlerreceives a message with"type": "success"and"purpose": "PAYMENT_METHOD_TOKEN". If it doesn't arrive, confirm the bridge is being installed beforewindow.postMessage({actionType: "create"})is sent.
For Android
Comparison of Android WebView
| Capability | WebView | Custom Tabs |
|---|---|---|
| Embed inside your own UI | Yes | No (modal browser) |
Receive postMessage from Payment capture page in native code | Yes | No — use successUrl / cancelUrl redirects |
| Apple Pay | No | No |
| Google Pay | Yes | Yes |
| Initial setup effort | Moderate | Minimal |
WebView
-
System requirements
- Google Play services version 25.18.30 or higher
- Android System WebView (Chrome WebView) version 137 or higher
-
Apply required changes to the Android application. Follow Google’s official documentation:
https://developers.google.com/pay/api/android/guides/recipes/using-android-webview-
Update build dependency
https://developers.google.com/pay/api/android/guides/recipes/using-android-webview#add-build-dependency -
Update
AndroidManifest.xml
https://developers.google.com/pay/api/android/guides/recipes/using-android-webview#add-android-manifest-queries-tags -
Enable JavaScript and Payment Request API in the WebView
https://developers.google.com/pay/api/android/guides/recipes/using-android-webview#enable-payment-request-api
-
-
For Google Pay production usage:
- Register the application in the Google Pay & Wallet Console
- Follow the official documentation for publishing and approval:
https://developers.google.com/pay/api/android/guides/test-and-deploy/publish-your-integration#integrate-android-app.
Troubleshooting WebView
If opening the Google Pay sheet produces errors:
-
OR_BIBED_15
Indicates that required setup steps for enabling Google Pay in WebView may be incomplete. Verify that device requirements and WebView configuration steps have been completed. -
OR_BIBED_11
Occurs only in the Google Pay production environment. Verify that the application is registered and approved in the Google Pay & Wallet Console. -
Application not visible in Google Pay & Wallet Console
Ensure that the logged-in Google account has access to the application in the Google Play Console.
Custom Tabs
Apply the following changes in the Android application (Kotlin example). successUrl and cancelUrl is mandatory to redirects users back to the app.
https://hosted-global-sandbox.ezypay.com/paymentmethod/embed?token=eyJhbGciOi...&countryCode=AU&successUrl=https%3A%2F%2Fexample.com%2Fsuccess&cancelUrl=https%3A%2F%2Fexample.com%2Fcancel
1. Update build dependency
build.gradle.kts
dependencies {
implementation("androidx.browser:browser:<latest>")
}libs.versions.toml
[versions]
browser = "1.8.0"
[libraries]
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "browser" }2. Update AndroidManifest.xml
AndroidManifest.xml<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required because PCP is opened through HTTPS -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Recommended for browser intent handling -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
<application android:theme="@style/Theme.YourApp">
<activity android:name=".MainActivity" android:exported="true">
<!-- Launcher entry point -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Required for deep link return (success/cancel URLs) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Must match successUrl/cancelUrl -->
<data android:scheme="androidmobileapp" android:host="checkout" />
</intent-filter>
</activity>
</application>
</manifest>3. Launch Payment capture page URL with Custom Tabs
import android.content.Context
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
fun openPcpInCustomTab(context: Context, pcpUrl: String) {
val uri = Uri.parse(pcpUrl)
val customTabsIntent = CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
customTabsIntent.launchUrl(context, uri)
}Updated 1 day ago