@oobit/react-native-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ /**
3
+ * Wallet Integration Utilities
4
+ * Handles opening Apple Wallet (iOS) and Google Wallet (Android)
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.isWalletAvailable = exports.openNativeWallet = void 0;
41
+ const IntentLauncher = __importStar(require("expo-intent-launcher"));
42
+ const react_native_1 = require("react-native");
43
+ const types_1 = require("./types");
44
+ /**
45
+ * Opens the native wallet app based on platform
46
+ * @returns Promise that resolves when wallet opens successfully
47
+ */
48
+ const openNativeWallet = async () => {
49
+ try {
50
+ if (react_native_1.Platform.OS === "ios") {
51
+ return await openAppleWallet();
52
+ }
53
+ else if (react_native_1.Platform.OS === "android") {
54
+ return await openGoogleWallet();
55
+ }
56
+ else {
57
+ console.warn("Wallet opening not supported on this platform");
58
+ return false;
59
+ }
60
+ }
61
+ catch (error) {
62
+ console.error("Failed to open wallet:", error);
63
+ react_native_1.Alert.alert("Error", "Could not open wallet. Please check if the app is installed.");
64
+ return false;
65
+ }
66
+ };
67
+ exports.openNativeWallet = openNativeWallet;
68
+ /**
69
+ * Opens Apple Wallet on iOS
70
+ * Uses multiple URL schemes in order of reliability
71
+ */
72
+ const openAppleWallet = async () => {
73
+ const { passkit, fallback } = types_1.WALLET_URLS.ios;
74
+ try {
75
+ console.log("Attempting to open Apple Wallet...");
76
+ // Method 1: Try wallet:// scheme (documented as working in recent iOS)
77
+ try {
78
+ const canOpenWallet = await react_native_1.Linking.canOpenURL("wallet://");
79
+ if (canOpenWallet) {
80
+ await react_native_1.Linking.openURL("wallet://");
81
+ console.log("Successfully opened Apple Wallet via wallet:// scheme");
82
+ return true;
83
+ }
84
+ }
85
+ catch (walletError) {
86
+ console.log("wallet:// scheme failed, trying shoebox://:", walletError);
87
+ }
88
+ // Method 2: Try shoebox:// scheme (undocumented but widely used)
89
+ // Note: This is unofficial and could potentially cause App Store rejection
90
+ try {
91
+ const canOpenShoebox = await react_native_1.Linking.canOpenURL(passkit);
92
+ if (canOpenShoebox) {
93
+ await react_native_1.Linking.openURL(passkit);
94
+ console.log("Successfully opened Apple Wallet via shoebox:// scheme");
95
+ return true;
96
+ }
97
+ }
98
+ catch (shoeboxError) {
99
+ console.log("shoebox:// scheme failed:", shoeboxError);
100
+ }
101
+ // Method 3: Fallback to wallet.apple.com (opens in browser)
102
+ console.log("Direct schemes failed, falling back to web URL");
103
+ const canOpenFallback = await react_native_1.Linking.canOpenURL(fallback);
104
+ if (canOpenFallback) {
105
+ await react_native_1.Linking.openURL(fallback);
106
+ console.log("Opened wallet.apple.com in browser");
107
+ return true;
108
+ }
109
+ throw new Error("Apple Wallet not available");
110
+ }
111
+ catch (error) {
112
+ console.error("Failed to open Apple Wallet:", error);
113
+ throw error;
114
+ }
115
+ };
116
+ /**
117
+ * Opens Google Wallet on Android
118
+ * Matches the proven Android native implementation from LinksUtils.kt
119
+ */
120
+ const openGoogleWallet = async () => {
121
+ const GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
122
+ try {
123
+ console.log("Attempting to open Google Wallet...");
124
+ // First, try using the market:// scheme to just launch the app if it's installed
125
+ // This is the most reliable way to open an installed app without knowing the exact activity
126
+ try {
127
+ const canOpen = await react_native_1.Linking.canOpenURL(`market://launch?id=${GOOGLE_WALLET_PACKAGE}`);
128
+ console.log("Can open market launch URL:", canOpen);
129
+ if (canOpen) {
130
+ await react_native_1.Linking.openURL(`market://launch?id=${GOOGLE_WALLET_PACKAGE}`);
131
+ console.log("Successfully opened Google Wallet via market:// scheme");
132
+ return true;
133
+ }
134
+ }
135
+ catch (marketLaunchError) {
136
+ console.log("Market launch failed, trying intent launcher:", marketLaunchError);
137
+ }
138
+ // Fallback: Try intent launcher with ACTION_VIEW (no explicit className)
139
+ // This should open the app if it handles the VIEW action
140
+ try {
141
+ await IntentLauncher.startActivityAsync("android.intent.action.VIEW", {
142
+ packageName: GOOGLE_WALLET_PACKAGE,
143
+ flags: 268435456, // FLAG_ACTIVITY_NEW_TASK (0x10000000)
144
+ });
145
+ console.log("Successfully opened Google Wallet with VIEW intent");
146
+ return true;
147
+ }
148
+ catch (intentError) {
149
+ console.log("Intent launcher failed:", intentError);
150
+ // App is not installed, open Play Store
151
+ console.log("App appears to not be installed, opening Play Store");
152
+ try {
153
+ // Try native Play Store app first
154
+ const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
155
+ await react_native_1.Linking.openURL(playStoreUrl);
156
+ react_native_1.Alert.alert("Google Wallet", "Google Wallet is not installed. Opening Play Store to install the app.");
157
+ return false;
158
+ }
159
+ catch (marketError) {
160
+ console.log("Market URL failed, trying web Play Store:", marketError);
161
+ // Fallback to web Play Store
162
+ await react_native_1.Linking.openURL(`https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`);
163
+ react_native_1.Alert.alert("Google Wallet", "Google Wallet is not installed. Opening Play Store to install the app.");
164
+ return false;
165
+ }
166
+ }
167
+ }
168
+ catch (error) {
169
+ console.error("Failed to open Google Wallet:", error);
170
+ throw error;
171
+ }
172
+ };
173
+ /**
174
+ * Check if wallet is available on the current platform
175
+ */
176
+ const isWalletAvailable = async () => {
177
+ try {
178
+ if (react_native_1.Platform.OS === "ios") {
179
+ return await react_native_1.Linking.canOpenURL(types_1.WALLET_URLS.ios.passkit);
180
+ }
181
+ else if (react_native_1.Platform.OS === "android") {
182
+ return await react_native_1.Linking.canOpenURL(types_1.WALLET_URLS.android.googlePay);
183
+ }
184
+ return false;
185
+ }
186
+ catch (error) {
187
+ console.error("Error checking wallet availability:", error);
188
+ return false;
189
+ }
190
+ };
191
+ exports.isWalletAvailable = isWalletAvailable;
192
+ //# sourceMappingURL=walletUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walletUtils.js","sourceRoot":"","sources":["../src/walletUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,qEAAuD;AACvD,+CAAwD;AACxD,mCAAsC;AAEtC;;;GAGG;AACI,MAAM,gBAAgB,GAAG,KAAK,IAAsB,EAAE;IAC3D,IAAI,CAAC;QACH,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,MAAM,eAAe,EAAE,CAAC;QACjC,CAAC;aAAM,IAAI,uBAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,MAAM,gBAAgB,EAAE,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAC9D,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC/C,oBAAK,CAAC,KAAK,CACT,OAAO,EACP,8DAA8D,CAC/D,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAlBW,QAAA,gBAAgB,oBAkB3B;AAEF;;;GAGG;AACH,MAAM,eAAe,GAAG,KAAK,IAAsB,EAAE;IACnD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,mBAAW,CAAC,GAAG,CAAC;IAE9C,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAElD,uEAAuE;QACvE,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,sBAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC5D,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,sBAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;gBACrE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,OAAO,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,6CAA6C,EAAE,WAAW,CAAC,CAAC;QAC1E,CAAC;QAED,iEAAiE;QACjE,2EAA2E;QAC3E,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,MAAM,sBAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACzD,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,sBAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;gBACtE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,YAAY,CAAC,CAAC;QACzD,CAAC;QAED,4DAA4D;QAC5D,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;QAC9D,MAAM,eAAe,GAAG,MAAM,sBAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC3D,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,sBAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACrD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,gBAAgB,GAAG,KAAK,IAAsB,EAAE;IACpD,MAAM,qBAAqB,GAAG,sCAAsC,CAAC;IAErE,IAAI,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QAEnD,iFAAiF;QACjF,4FAA4F;QAC5F,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,sBAAO,CAAC,UAAU,CAAC,sBAAsB,qBAAqB,EAAE,CAAC,CAAC;YACxF,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;YAEpD,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,sBAAO,CAAC,OAAO,CAAC,sBAAsB,qBAAqB,EAAE,CAAC,CAAC;gBACrE,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;gBACtE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,OAAO,iBAAiB,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,+CAA+C,EAAE,iBAAiB,CAAC,CAAC;QAClF,CAAC;QAED,yEAAyE;QACzE,yDAAyD;QACzD,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,kBAAkB,CAAC,4BAA4B,EAAE;gBACpE,WAAW,EAAE,qBAAqB;gBAClC,KAAK,EAAE,SAAS,EAAE,sCAAsC;aACzD,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,WAAW,CAAC,CAAC;YAEpD,wCAAwC;YACxC,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;YACnE,IAAI,CAAC;gBACH,kCAAkC;gBAClC,MAAM,YAAY,GAAG,uBAAuB,qBAAqB,EAAE,CAAC;gBACpE,MAAM,sBAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;gBAEpC,oBAAK,CAAC,KAAK,CACT,eAAe,EACf,wEAAwE,CACzE,CAAC;gBACF,OAAO,KAAK,CAAC;YACf,CAAC;YAAC,OAAO,WAAW,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,WAAW,CAAC,CAAC;gBAEtE,6BAA6B;gBAC7B,MAAM,sBAAO,CAAC,OAAO,CACnB,iDAAiD,qBAAqB,EAAE,CACzE,CAAC;gBAEF,oBAAK,CAAC,KAAK,CACT,eAAe,EACf,wEAAwE,CACzE,CAAC;gBACF,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACI,MAAM,iBAAiB,GAAG,KAAK,IAAsB,EAAE;IAC5D,IAAI,CAAC;QACH,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,MAAM,sBAAO,CAAC,UAAU,CAAC,mBAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;aAAM,IAAI,uBAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,MAAM,sBAAO,CAAC,UAAU,CAAC,mBAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAZW,QAAA,iBAAiB,qBAY5B"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@oobit/react-native-sdk",
3
+ "version": "1.0.0",
4
+ "description": "React Native/Expo SDK for embedding web widgets with native wallet integration",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "react-native": "dist/index.js",
9
+ "source": "src/index.ts",
10
+ "keywords": [
11
+ "react-native",
12
+ "expo",
13
+ "widget",
14
+ "webview",
15
+ "wallet",
16
+ "sdk"
17
+ ],
18
+ "author": "Oobit",
19
+ "license": "UNLICENSED",
20
+ "private": false,
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/oobit-tech/react-native-SDK.git"
27
+ },
28
+ "peerDependencies": {
29
+ "expo-linking": ">=6.0.0",
30
+ "react": ">=18.0.0",
31
+ "react-native": ">=0.70.0",
32
+ "react-native-webview": ">=13.0.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "expo-intent-launcher": {
36
+ "optional": true
37
+ }
38
+ },
39
+ "devDependencies": {
40
+ "@types/react": "~19.1.0",
41
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
42
+ "@typescript-eslint/parser": "^8.47.0",
43
+ "eslint": "^9.39.1",
44
+ "expo-intent-launcher": "~13.0.7",
45
+ "expo-linking": "~8.0.8",
46
+ "react": "19.1.0",
47
+ "react-native": "0.81.5",
48
+ "react-native-webview": "13.15.0",
49
+ "typescript": "~5.9.2"
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "src",
54
+ "README.md"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsc",
58
+ "prepublishOnly": "npm run build",
59
+ "typecheck": "tsc --noEmit",
60
+ "clean": "rm -rf dist"
61
+ }
62
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Widget SDK Component
3
+ * Embeds the web widget in a WebView and handles bidirectional communication
4
+ */
5
+
6
+ import React, {
7
+ forwardRef,
8
+ useEffect,
9
+ useImperativeHandle,
10
+ useMemo,
11
+ useRef,
12
+ useState,
13
+ } from "react";
14
+ import { ActivityIndicator, BackHandler, Linking, Platform, StyleSheet, View } from "react-native";
15
+ import { WebView, WebViewMessageEvent } from "react-native-webview";
16
+ import { WIDGET_CONFIG } from "./config";
17
+ import { WidgetMessage, WidgetSDKConfig } from "./types";
18
+ import { isWalletAvailable, openNativeWallet } from "./walletUtils";
19
+
20
+ export interface WidgetSDKRef {
21
+ navigateBack: () => void;
22
+ }
23
+
24
+ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
25
+ (
26
+ {
27
+ accessToken,
28
+ userWalletAddress,
29
+ onReady,
30
+ onCardCreated,
31
+ onError,
32
+ onClose,
33
+ onTransactionRequested,
34
+ },
35
+ ref
36
+ ) => {
37
+ const webViewRef = useRef<WebView>(null);
38
+ const [walletAvailable, setWalletAvailable] = useState(false);
39
+ const [isLoading, setIsLoading] = useState(true);
40
+ const hasLoadedOnce = useRef(false);
41
+
42
+ // Check wallet availability on mount
43
+ useEffect(() => {
44
+ checkWalletAvailability();
45
+ }, []);
46
+
47
+ // Handle Android hardware back button
48
+ useEffect(() => {
49
+ const backHandler = BackHandler.addEventListener(
50
+ "hardwareBackPress",
51
+ () => {
52
+ // Send back pressed message to widget
53
+ sendMessageToWidget({
54
+ type: "native:back-pressed",
55
+ timestamp: Date.now(),
56
+ });
57
+ // Return true to prevent default back behavior
58
+ return true;
59
+ }
60
+ );
61
+
62
+ return () => backHandler.remove();
63
+ }, []);
64
+
65
+ // Expose navigateBack method via ref
66
+ useImperativeHandle(ref, () => ({
67
+ navigateBack: () => {
68
+ sendMessageToWidget({
69
+ type: "native:navigate-back",
70
+ timestamp: Date.now(),
71
+ });
72
+ },
73
+ }));
74
+
75
+ const checkWalletAvailability = async () => {
76
+ const available = await isWalletAvailable();
77
+ setWalletAvailable(available);
78
+ };
79
+
80
+ /**
81
+ * Handle messages from the web widget
82
+ */
83
+ const handleMessage = (event: WebViewMessageEvent) => {
84
+ try {
85
+ const message: WidgetMessage = JSON.parse(event.nativeEvent.data);
86
+
87
+ console.log("[WidgetSDK] Received message:", message.type);
88
+
89
+ switch (message.type) {
90
+ case "widget:ready":
91
+ handleReady();
92
+ break;
93
+
94
+ case "widget:open-wallet":
95
+ handleOpenWallet();
96
+ break;
97
+
98
+ case "widget:card-created":
99
+ handleCardCreated(message);
100
+ break;
101
+
102
+ case "widget:error":
103
+ handleError(message);
104
+ break;
105
+
106
+ case "widget:close":
107
+ handleClose();
108
+ break;
109
+
110
+ case "widget:transaction-requested":
111
+ handleTransactionRequested(message);
112
+ break;
113
+
114
+ case "widget:token-expired":
115
+ console.error("[WidgetSDK] Access token expired");
116
+ onError?.("TOKEN_EXPIRED", "Access token has expired");
117
+ break;
118
+
119
+ default:
120
+ console.warn("[WidgetSDK] Unknown message type:", message);
121
+ }
122
+ } catch (error) {
123
+ console.error("[WidgetSDK] Failed to parse message:", error);
124
+ onError?.("PARSE_ERROR", "Failed to parse widget message");
125
+ }
126
+ };
127
+
128
+ const handleReady = () => {
129
+ console.log("[WidgetSDK] Widget ready");
130
+ onReady?.();
131
+
132
+ // Send platform info to widget
133
+ sendMessageToWidget({
134
+ type: "native:platform-info",
135
+ payload: {
136
+ platform: Platform.OS,
137
+ walletAvailable,
138
+ },
139
+ });
140
+ };
141
+
142
+ const handleOpenWallet = async () => {
143
+ console.log("[WidgetSDK] Opening native wallet...");
144
+
145
+ const success = await openNativeWallet();
146
+
147
+ // Notify widget of result
148
+ sendMessageToWidget({
149
+ type: "native:wallet-opened",
150
+ payload: { success },
151
+ });
152
+ };
153
+
154
+ const handleCardCreated = (message: WidgetMessage) => {
155
+ if (message.type !== "widget:card-created") return;
156
+
157
+ const { cardId, cardType, last4 } = message.payload;
158
+ console.log("[WidgetSDK] Card created:", cardId);
159
+
160
+ onCardCreated?.(cardId, cardType, last4);
161
+ };
162
+
163
+ const handleError = (message: WidgetMessage) => {
164
+ if (message.type !== "widget:error") return;
165
+
166
+ const { code, message: errorMessage } = message.payload;
167
+ console.error("[WidgetSDK] Widget error:", code, errorMessage);
168
+
169
+ onError?.(code, errorMessage);
170
+ };
171
+
172
+ const handleClose = () => {
173
+ console.log("[WidgetSDK] Widget closed");
174
+ onClose?.();
175
+ };
176
+
177
+ const handleTransactionRequested = (message: WidgetMessage) => {
178
+ if (message.type !== "widget:transaction-requested") return;
179
+
180
+ const { token, cryptoAmount, depositAddress, depositAddressTag } = message.payload;
181
+ console.log("[WidgetSDK] Transaction requested:", {
182
+ token,
183
+ cryptoAmount,
184
+ depositAddress,
185
+ depositAddressTag,
186
+ });
187
+
188
+ onTransactionRequested?.(token, cryptoAmount, depositAddress, depositAddressTag);
189
+ };
190
+
191
+ /**
192
+ * Send message to the web widget
193
+ */
194
+ const sendMessageToWidget = (message: any) => {
195
+ const js = `
196
+ window.postMessage(${JSON.stringify(message)}, '*');
197
+ true;
198
+ `;
199
+
200
+ webViewRef.current?.injectJavaScript(js);
201
+ };
202
+
203
+ /**
204
+ * Build the widget URL with query parameters
205
+ */
206
+ const finalWidgetUrl = useMemo(() => {
207
+ // Construct URL with token, platform, and wallet address
208
+ const params = new URLSearchParams({
209
+ token: accessToken,
210
+ platform: Platform.OS,
211
+ userWalletAddress: userWalletAddress,
212
+ });
213
+
214
+ const url = `${WIDGET_CONFIG.URL}?${params.toString()}`;
215
+ console.log("[WidgetSDK] Widget URL:", url.substring(0, 100) + "...");
216
+ return url;
217
+ }, [accessToken, userWalletAddress]);
218
+
219
+ return (
220
+ <View style={styles.container}>
221
+ <WebView
222
+ ref={webViewRef}
223
+ style={styles.webview}
224
+ source={{ uri: finalWidgetUrl }}
225
+ onMessage={handleMessage}
226
+ onLoadEnd={() => {
227
+ if (!hasLoadedOnce.current) {
228
+ hasLoadedOnce.current = true;
229
+ setIsLoading(false);
230
+ }
231
+ }}
232
+ onError={(syntheticEvent) => {
233
+ const { nativeEvent } = syntheticEvent;
234
+ console.error("[WidgetSDK] WebView error:", nativeEvent);
235
+ setIsLoading(false);
236
+ onError?.("WEBVIEW_ERROR", "Failed to load widget");
237
+ }}
238
+ // IMPORTANT: Allow external URLs to open in native apps
239
+ onShouldStartLoadWithRequest={(request) => {
240
+ const { url } = request;
241
+
242
+ // Allow wallet URLs to open in native apps
243
+ if (
244
+ url.startsWith("shoebox://") || // Apple Wallet
245
+ url.includes("pay.google.com") || // Google Wallet
246
+ url.includes("wallet.google.com") ||
247
+ url.includes("wallet.apple.com")
248
+ ) {
249
+ console.log("[WidgetSDK] Opening external URL:", url);
250
+ Linking.openURL(url).catch((err) => {
251
+ console.error("[WidgetSDK] Failed to open URL:", err);
252
+ });
253
+ return false; // Don't load in WebView
254
+ }
255
+
256
+ // Allow normal widget navigation
257
+ return true;
258
+ }}
259
+ // Security settings
260
+ javaScriptEnabled={true}
261
+ domStorageEnabled={true}
262
+ allowsInlineMediaPlayback={true}
263
+ // iOS specific
264
+ bounces={false}
265
+ allowsBackForwardNavigationGestures={true}
266
+ // Android specific
267
+ mixedContentMode="always"
268
+ />
269
+ {isLoading && (
270
+ <View style={styles.loadingOverlay}>
271
+ <ActivityIndicator size="large" color="#007AFF" />
272
+ </View>
273
+ )}
274
+ </View>
275
+ );
276
+ }
277
+ );
278
+
279
+ const styles = StyleSheet.create({
280
+ container: {
281
+ flex: 1,
282
+ },
283
+ webview: {
284
+ flex: 1,
285
+ },
286
+ loadingOverlay: {
287
+ ...StyleSheet.absoluteFillObject,
288
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
289
+ justifyContent: 'center',
290
+ alignItems: 'center',
291
+ },
292
+ });
package/src/config.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Widget SDK Configuration
3
+ * Environment-based configuration for the widget URL
4
+ */
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ /**
9
+ * Widget URL configuration
10
+ * - Development: Uses localhost (update IP for your network)
11
+ * - Production: Uses production widget URL
12
+ */
13
+ export const WIDGET_CONFIG = {
14
+ /**
15
+ * Base widget URL
16
+ * In development (__DEV__ = true), uses localhost
17
+ * In production (__DEV__ = false), uses production URL
18
+ */
19
+ URL: __DEV__
20
+ ? "https://oobit-widget-dev.web.app" // Development: Vite dev server default
21
+ : "https://widget.oobit.com", // Production: Oobit widget URL
22
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Widget SDK - Public API
3
+ * Export all public components, types, and utilities
4
+ */
5
+
6
+ export { WidgetSDK } from './WidgetSDK';
7
+ export type { WidgetSDKRef } from './WidgetSDK';
8
+
9
+ // Export all message types for clients to use
10
+ export type {
11
+ // Configuration
12
+ WidgetSDKConfig,
13
+ DepositToken,
14
+
15
+ // Widget → Native message types
16
+ WidgetMessageType,
17
+ BaseWidgetMessage,
18
+ WidgetMessage,
19
+ WidgetReadyMessage,
20
+ OpenWalletMessage,
21
+ CardCreatedMessage,
22
+ WidgetErrorMessage,
23
+ WidgetCloseMessage,
24
+ TransactionRequestedMessage,
25
+
26
+ // Native → Widget message types
27
+ NativeMessageType,
28
+ NativeMessage,
29
+ NativeBackPressedMessage,
30
+ NativeNavigateBackMessage,
31
+ NativePlatformInfoMessage,
32
+ NativeWalletOpenedMessage,
33
+ } from './types';
34
+
35
+ // Export constants
36
+ export { WALLET_URLS, MessageTypes } from './types';
37
+ export { WIDGET_CONFIG } from './config';
38
+
39
+ // Export wallet utilities
40
+ export { openNativeWallet, isWalletAvailable } from './walletUtils';