React Native Integration
This example demonstrates how to integrate the Hubble Gift Card Store SDK in a React Native application.
Parameters
| Parameter | Required | Description |
|---|---|---|
clientId | Yes | Client ID provided by the Hubble team |
appSecret | Yes | App secret provided by the Hubble team |
token | No | Auth token that uniquely identifies your user. Optional if lazy login is enabled. |
wrap-plt | Yes | Platform identifier. Use rn. |
appVersion | No | App version string. Defaults to "10000". |
deviceId | No | Device identifier. |
Deep Links
To navigate directly to a specific page, append the route path before the query parameters. See the Deep Links page for the full route mapping.
| Page | URL Format |
|---|---|
| Home (default) | https://sdk.myhubble.money/?clientId=... |
| Brand purchase | https://sdk.myhubble.money/buy/{brandId}?clientId=... |
| Search | https://sdk.myhubble.money/search?clientId=... |
| Transactions | https://sdk.myhubble.money/transactions?clientId=... |
| Help | https://sdk.myhubble.money/help?clientId=... |
Events
The SDK communicates with the host app via window.ReactNativeWebView.postMessage(json). Events are JSON objects with a type field:
Action events — SDK lifecycle and navigation:
{"type": "action", "action": "close"}
{"type": "action", "action": "app_ready"}
{"type": "action", "action": "error"}
Analytics events — user interaction tracking:
{ "type": "analytics", "event": "event_name", "properties": { "key": "value" } }
| Action | Description |
|---|---|
close | User tapped close. Dismiss the WebView. |
app_ready | SDK loaded successfully. |
error | SDK failed to load. Show an error state. |
See the Events page for the full list of analytics events.
Events Not Reaching Analytics
If you are not seeing SDK events in your analytics dashboard, check the following:
- Verify your event listener is set up BEFORE loading the SDK (events fire immediately on load).
- On native platforms, ensure the JavaScript bridge is registered before the WebView loads the URL.
- Check for CORS issues if your analytics provider requires domain whitelisting.
Platform-Specific Configuration
React Native runs on both iOS and Android, so you may need platform-specific configuration for UPI payments:
- iOS: See the iOS Integration Guide for
Info.plistconfiguration to detect installed UPI apps. - Android: See the Android Integration Guide for
AndroidManifest.xmlconfiguration required for UPI intents on Android 11+.
Full Example
import { router } from "expo-router";
import { useEffect, useRef, useState } from "react";
import { BackHandler, Linking, SafeAreaView } from "react-native";
import WebView, { WebViewMessageEvent } from "react-native-webview";
import { ShouldStartLoadRequest } from "react-native-webview/lib/WebViewTypes";
type HubbleParams = {
token?: string;
clientId: string;
appSecret: string;
"wrap-plt": string;
appVersion?: string;
deviceId?: string;
};
type HubbleActionEvent = {
type: "action";
action: "close" | "app_ready" | "error";
};
type HubbleAnalyticsEvent = {
type: "analytics";
event: string;
properties: Record<string, any>;
};
type HubbleEvent = HubbleActionEvent | HubbleAnalyticsEvent;
export default function HubbleWebView() {
const params: HubbleParams = {
token: "your_auth_token", // optional for lazy login
clientId: "id_given_by_hubble",
appSecret: "secret_given_by_hubble",
"wrap-plt": "rn",
// appVersion: "10000", // optional, defaults to 10000
// deviceId: "device_id", // optional
};
const baseUrl = "https://sdk.dev.myhubble.money/";
// prod baseUrl: https://sdk.myhubble.money/
const sourceUrl = `${baseUrl}?clientId=${params.clientId}&appSecret=${params.appSecret}&token=${params.token}&wrap-plt=${params["wrap-plt"]}`;
// Deep link example: to open a brand page directly, append the route path:
// const sourceUrl = `${baseUrl}buy/uber?clientId=${params.clientId}&appSecret=${params.appSecret}&token=${params.token}&wrap-plt=${params["wrap-plt"]}`;
const webViewRef = useRef<WebView>(null);
// maintain a variable to keep track of state, if it can be popped or not
const [canGoBack, setCanGoBack] = useState(false);
useEffect(() => {
const backAction = () => {
if (canGoBack && webViewRef.current) {
webViewRef.current.goBack();
return true; // prevent default behavior (app exit)
}
return false; // allow default behavior
};
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction,
);
return () => backHandler.remove();
}, [canGoBack]);
const onShouldStartLoadWithRequest = (e: ShouldStartLoadRequest) => {
if (
e.url?.startsWith(baseUrl) ||
e.url?.startsWith("https://api.razorpay.com")
) {
return true;
} else {
Linking.openURL(e.url);
return false;
}
};
const handleEvent = (e: WebViewMessageEvent) => {
console.log("event", e.nativeEvent.data);
const eventData: HubbleEvent = JSON.parse(e.nativeEvent.data);
if (eventData.type === "action") {
if (eventData.action === "close") {
router.back();
} else if (eventData.action === "app_ready") {
console.log("Hubble SDK is ready");
} else if (eventData.action === "error") {
console.log("Hubble SDK failed to load");
}
} else if (eventData.type === "analytics") {
logEvent(eventData.event, eventData.properties);
}
};
function logEvent(eventType: string, eventParams: Record<string, any>) {
// your implementation of sending events to analytics
console.log("eventType", eventType);
console.log("eventParams", eventParams);
}
return (
<SafeAreaView style={{ flex: 1 }}>
<WebView
ref={webViewRef}
showsVerticalScrollIndicator={false}
javaScriptEnabled={true}
startInLoadingState={true}
webviewDebuggingEnabled={true} // Disable at your end
cacheEnabled={false} // Disable at your end
source={{ uri: sourceUrl }}
setSupportMultipleWindows={true}
javaScriptCanOpenWindowsAutomatically={true} // has to be true
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
onMessage={handleEvent}
onNavigationStateChange={(navState) => {
setCanGoBack(navState.canGoBack);
}}
/>
</SafeAreaView>
);
}