WebView and native hosts
A mobile app can show the checkout web page inside a WebView (a browser component inside the app). @checkout-kit/webview-bridge lets the page and the app talk to each other safely: the page reports what is happening with the payment, and the app can send commands, such as "cancel".
The package has three entry points. The native side imports /host or /protocol. These work without a browser and without a React Native dependency.
Install from npm
npm install @checkout-kit/core @checkout-kit/webview-bridgenpm package · All installation options
Install these packages in both the web project and native host. Add the WebView component separately in your mobile app; for Expo use npx expo install react-native-webview.
| Import | Where it runs |
|---|---|
@checkout-kit/webview-bridge | On the web page: createWebViewBridge(engine, options). |
@checkout-kit/webview-bridge/host | In the app: handling messages, sending commands, navigation rules, return links. |
@checkout-kit/webview-bridge/protocol | Anywhere: types, the protocol version and strict message parsers. |
On the web page
import { createWebViewBridge } from '@checkout-kit/webview-bridge'
const bridge = createWebViewBridge(engine, { reportHeight: true })
// Register once for this document, and call bridge.stop() when disposing the checkout.- The bridge finds
window.ReactNativeWebViewby itself. In a normal browser it does nothing. - It sends
PAYMENT_READY, state and step events, and checked results. - The data in each message is picked field by field. It never contains card data.
onErrorcan report that a message could not be delivered to the app. This does not affect the payment.
In the app
import {
createBridgeCommand,
createCheckoutMessageHandler,
createCommandScript,
} from '@checkout-kit/webview-bridge/host'
const handle = createCheckoutMessageHandler({
PAYMENT_SUCCEEDED: (event) => showReceipt(event.payload.intentId),
})
// WebView.onMessage: handle(event.nativeEvent.data), only for the merchant checkout URL.
if (handle.sessionId) {
const command = createBridgeCommand(
'PAYMENT_CANCEL',
{},
{
sessionId: handle.sessionId,
id: 'native:1', // unique within this session
},
)
webview.injectJavaScript(createCommandScript(command))
}How sessions and commands work:
- Every command must carry the session id from the
PAYMENT_READYmessage, not some id you made up in the app. - The page rejects commands from another session, commands with a repeated id, broken data and commands sent from provider frames.
- A
pingcommand getsPAYMENT_READYback, with the command id incorrelationId. - The app forgets the old session only when a new
PAYMENT_READYarrives. Repeated events are dropped.
Safety rules:
- Build commands with
createCommandScript. Do not paste a return token into a JavaScript string yourself. The function passes parameters as data and works withWebView.injectJavaScript. - In
onMessage, accept messages only from your checkout page address — even if the WebView is allowed to open bank pages.
Navigation. createNavigationPolicy({ allow, openExternally, returnScheme }) compares HTTPS origins and whole path folders: allowing /checkout does not allow /checkout-admin. When a return link with your scheme arrives, parse it with parseReturnDeepLink and send PAYMENT_RESUME with the parsed parameters. The page then reads the order from your server again. The return link itself does not mean that the payment succeeded.
Mobile app example · WebView guide
Navigation and return links
For every address the WebView wants to open, make one of four decisions:
- Allow in the WebView — your checkout pages and the provider pages you allowed.
- Open outside — in the phone's system browser or another app.
- Block and resume — for a return link with your own scheme: do not open it, parse it and send
PAYMENT_RESUME. - Reject — everything else.
Compare exact origins and whole path folders. Addresses with a login or password inside are not allowed.
Set up the return link handler in the app before any SDK step or redirect starts:
- handle both the link that opened the app (initial
LinkingURL) and links that arrive later; - keep return links that arrive before
PAYMENT_READYin a queue; - reset the "ready" flag when a new page loads in the WebView.
A POST redirect also sends form fields. You cannot replace it by just opening its address.
Check on a real device
The React Native example shows session handling, the queue of return links and filtering in onMessage. The React Native and WebView dependencies and the registration of your link scheme belong to your app. Build the app and try it on iOS and Android: browser and Node tests do not build a mobile app.