# Embedded Checkout (iFrame) — Mobile Implementation

Loading the Banxa checkout in a mobile WebView requires specific configuration to support camera access, payment methods, and KYC flows.

## Android

### Use Custom Chrome Tabs (recommended)

If your integration includes Google Pay, use **Custom Chrome Tabs** instead of a standard WebView. Standard WebView does not support GPAY or ACH.


```kotlin
import androidx.browser.customtabs.CustomTabsIntent

val customTabsIntent = CustomTabsIntent.Builder().build()
customTabsIntent.launchUrl(context, Uri.parse(checkoutUrl))
```

### WebView (for integrations without Google Pay)

If you are not using Google Pay, you can use a WebView with the following configuration:


```kotlin
webView.settings.apply {
    javaScriptEnabled = true
    domStorageEnabled = true          // Required: enables local storage
    mediaPlaybackRequiresUserGesture = false  // Required: enables video instructions
    allowFileAccess = true
    setSupportZoom(false)
}

// Enable camera access
webView.webChromeClient = object : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        request.grant(request.resources)  // Grant camera/microphone
    }

    // Required for HTML5 video playback
    override fun onShowCustomView(view: View, callback: CustomViewCallback) {
        // Handle fullscreen video
    }
}
```

Declare permissions in `AndroidManifest.xml`:


```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
```

## iOS

### SFSafariViewController (recommended)

Use `SFSafariViewController` when your integration includes KYC liveness checks (Sumsub). Liveness checks cannot complete in a standard `WKWebView`.


```swift
import SafariServices

let safariVC = SFSafariViewController(url: URL(string: checkoutUrl)!)
present(safariVC, animated: true)
```

### WKWebView

If liveness checks are not required, `WKWebView` can be used with the following configuration:


```swift
import WebKit

let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true          // Required
config.mediaTypesRequiringUserActionForPlayback = []  // Required: allow autoplay for video instructions

let webView = WKWebView(frame: .zero, configuration: config)

// Request camera permission
webView.uiDelegate = self  // Implement WKUIDelegate
```

Implement `WKUIDelegate` to handle permission requests:


```swift
func webView(_ webView: WKWebView,
             requestMediaCapturePermissionFor origin: WKSecurityOrigin,
             initiatedByFrame frame: WKFrameInfo,
             type: WKMediaCaptureType,
             decisionHandler: @escaping (WKPermissionDecision) -> Void) {
    decisionHandler(.grant)
}
```

Add to `Info.plist`:


```xml
<key>NSCameraUsageDescription</key>
<string>Required for identity verification</string>
<key>NSMicrophoneUsageDescription</key>
<string>Required for identity verification</string>
```

## React Native

Use `react-native-webview` with the following props:


```jsx
import { WebView } from 'react-native-webview';

<WebView
  source={{ uri: checkoutUrl }}
  allowsInlineMediaPlayback={true}        // Required: inline video for instructions
  mediaPlaybackRequiresUserAction={false} // Required: allow autoplay
  domStorageEnabled={true}               // Required: local storage (Android)
  enableApplePay={true}                  // Required: Apple Pay on iOS
  onNavigationStateChange={(navState) => {
    // Detect redirect to your redirectUrl
    if (navState.url.startsWith('https://yourapp.com/order-complete')) {
      // Dismiss WebView and check order status
    }
  }}
/>
```

Camera and microphone access is controlled by OS-level permissions — set in `AndroidManifest.xml` and `Info.plist` as shown below, not via WebView props.

## Flutter

Use `webview_flutter` with the following configuration:


```dart
import 'package:webview_flutter/webview_flutter.dart';

late final WebViewController controller;

controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..setNavigationDelegate(NavigationDelegate(
    onNavigationRequest: (NavigationRequest request) {
      if (request.url.startsWith('https://yourapp.com/order-complete')) {
        // Dismiss WebView
        return NavigationDecision.prevent;
      }
      return NavigationDecision.navigate;
    },
  ))
  ..loadRequest(Uri.parse(checkoutUrl));
```

For camera access on Android, add to `AndroidManifest.xml`:


```xml
<uses-permission android:name="android.permission.CAMERA" />
```

## Payment methods that redirect out of the WebView

iDEAL, Klarna, and PayPal cannot complete inside a WebView. When a customer selects one of these methods, they will be redirected to an external browser to complete their payment. After payment, the customer is returned to the Banxa order status page — they will not be returned automatically to your WebView.

Design your post-payment UX accordingly: a confirmation screen with a link back to your app is the recommended pattern.

## Configuration checklist

| Requirement | Android | iOS |
|  --- | --- | --- |
| Local storage | `domStorageEnabled = true` | Enabled by default in WKWebView |
| Camera access | `onPermissionRequest` grant | `WKUIDelegate` + Info.plist |
| Video playback | `WebChromeClient` + `mediaPlaybackRequiresUserGesture = false` | `allowsInlineMediaPlayback = true` |
| KYC liveness | Custom Chrome Tabs or WebView | SFSafariViewController required |
| Google Pay | Custom Chrome Tabs required | Supported in WKWebView |