Skip to content

WebView bridge

A web page inside a native shell cannot reach StoreKit or Play Billing. The bridge lets the page call getProducts, purchase, restore, getCustomer and ads.showRewarded through postMessage. The native side runs them with the SDK and posts the result back.

import { useMemo, useRef } from "react";
import { WebView } from "react-native-webview";
import { Kasskeep, KASSKEEP_WEBVIEW_INJECTED_JS, createKasskeepWebViewBridge } from "@kasskeep/react-native";
export function GameScreen() {
const ref = useRef<WebView>(null);
const bridge = useMemo(
() =>
createKasskeepWebViewBridge({
webViewRef: ref,
allowedOrigins: ["https://game.example.com"],
methods: ["getProducts", "purchase", "restore", "getCustomer", "setAppUserId"],
onAppUserId: (id) => {
if (Kasskeep.appUserId) Kasskeep.setAppUserId(id);
else Kasskeep.configure({ apiKey: "pk_...", baseUrl: "https://api.kasskeep.com", appUserId: id });
},
}),
[],
);
return (
<WebView
ref={ref}
source={{ uri: "https://game.example.com/" }}
originWhitelist={["https://game.example.com"]}
injectedJavaScriptBeforeContentLoaded={KASSKEEP_WEBVIEW_INJECTED_JS}
onMessage={(e) => {
if (bridge.onMessage(e)) return;
}}
/>
);
}

createKasskeepWebViewBridge(options) returns { onMessage(event): boolean, dispose() }. onMessage returns false for messages that are not bridge messages, so the shell can handle its own.

Option Meaning
webViewRef Ref to the WebView
allowedOrigins Required. Exact scheme://host[:port] origins. Messages from other origins are dropped without an answer
methods Allow-list. Default getProducts, purchase, restore, getCustomer, ads.showRewarded. setAppUserId only when listed
onAppUserId(id) Called for setAppUserId, typically to call Kasskeep.configure after the page logs in
onRejected(r) Called with { reason: "origin" | "version" | "method" | "malformed", origin, method? }

Version 1. The page posts:

{"tag": "kasskeep", "v": 1, "id": "<request id>", "method": "purchase", "params": {}}

The native side answers by calling window.__kasskeepBridgeReceive with {"tag": "kasskeep", "v": 1, "id": "<same id>", "ok": true, "result": ...} or {"ok": false, "error": {"code", "message", "retryable?", "status?"}}. The answer is delivered only while location.origin is still the origin of the request. Results are JSON-escaped so they cannot break out of the injected script.

On the page side use kasskeep-web.js, which implements this protocol.