On the open web, tapping "Pay" opens the browser's payment sheet — Apple Pay on Safari, Google Pay on Chrome, or the Payment Request API across browsers. Inside a WebView, those buttons silently disappear. Apple Pay is simply not available in WKWebView, and Android disables the Payment Request API unless the host explicitly re-enables it. A host-owned runtime bridges native payment SDKs — one checkout call that resolves to Apple Pay, Google Pay, or the platform wallet on every engine.

Apple Pay: Safari and SFSafariViewController only

Apple's own documentation and the wider payment ecosystem are unambiguous: Apple Pay on the web is supported only in Safari and SFSafariViewController. Apple directs apps that load checkout in a WKWebView to move the payment request into native code. Apple Pay JS (window.ApplePaySession) is effectively unavailable in an embedded WebView; the sheet that opens natively in Safari is gone, and the page gets no usable payment method — the option just isn't offered.

iOS 13 added a narrow path for Apple Pay inside WKWebView, but with a hard catch: if the app calls WKUserScript or evaluateJavaScript before the page uses Apple Pay, the transaction is disabled. With tiered WebKit, more WKWebViews are now restricted from script injection entirely. For a hybrid app, the practical outcome is the same: you cannot rely on the web payment sheet and must bridge Apple Pay through PKPaymentAuthorizationController in native code.

Android: Payment Request is off by default

Google's own Google Pay documentation states it plainly: "By default, WebView disables the Payment Request API." When a web checkout is embedded in an Android WebView, the Payment Request API — which Google Pay's web flow uses to launch the payment sheet — is turned off. To re-enable it the host must add the AndroidX WebKit dependency, declare the meta-data flag android.webkit.WebView.enablePaymentRequest, and call WebSettingsCompat.setPaymentRequestEnabled(settings, true) at runtime.

And the requirements are strict: Google Pay inside WebView needs Google Play services 25.18.30+ and Android WebView (Chrome) 137+, and isReadyToPay returns false when they are not met. Stripe has deprecated its Payment Request Button, and providers consistently steer apps toward native SDKs precisely because the embedded web path is unreliable.

What breaks when payment dies

  • The wallet button never appears. A checkout that shows Apple Pay or Google Pay in the browser shows neither in the WebView — no error, just a missing option.
  • Conversion collapses. Users who pay exclusively with a wallet are stuck re-typing card details, or abandon.
  • Federated sign-in breaks alongside it. The same embedded WebView also loses Google sign-in (disallowed_useragent), so login and pay both stall.
  • Social in-app browsers hide it too. WeChat, Douyin, Instagram, and Facebook WebViews impose their own restrictions, so the break never shows up in desktop QA.

The runtime fix

A host-owned runtime bridges the payment layer. The page calls one uniform API — runtime.requestPayment(methods, details) — and the host resolves it through the platform-native payment SDK: Apple Pay via PKPaymentAuthorizationController on iOS, Google Pay / the native payment sheet on Android. Merchant session, sheet presentation, and token return are handled natively and consistently. One integration, every engine, one completed checkout.

DimensionWebViewHost-owned runtime
Apple Pay (iOS)Unsupported in WKWebViewNative PKPaymentAuthorizationController
Google Pay (Android)Payment Request disabled by defaultNative Google Pay SDK
ActivationHost flags + AndroidX WebKitOne runtime.requestPayment()
Sheet presentationBrowser-owned, absentHost-owned, reliable
FallbackManual card onlyWallet or card, both wired
ConsistencyPer-engineUniform

A native app completes a wallet checkout on every device. A stock WebView completes it on almost none. A runtime gives the page one pay action — and a real authorization at the end of it.

Sources