@oobit/react-native-sdk 3.0.2 → 3.1.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,11 @@
1
+ import React from 'react';
2
+ import { WidgetSDKConfig } from '@oobit/core';
3
+ export { EvmTransactionRequest, SolanaTransactionRequest, TransactionRequest, TransactionTokenMetadata, WidgetSDKConfig } from '@oobit/core';
4
+
5
+ interface WidgetSDKRef {
6
+ navigateBack: () => void;
7
+ reload: () => void;
8
+ }
9
+ declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
10
+
11
+ export { WidgetSDK, type WidgetSDKRef };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,11 @@
1
- export { WidgetSDK } from './components/WidgetSDK';
2
- export type { WidgetSDKRef } from './components/WidgetSDK';
3
- export type { TransactionRequest, EvmTransactionRequest, SolanaTransactionRequest, TransactionTokenMetadata, WidgetSDKConfig, } from '@oobit/core';
4
- //# sourceMappingURL=index.d.ts.map
1
+ import React from 'react';
2
+ import { WidgetSDKConfig } from '@oobit/core';
3
+ export { EvmTransactionRequest, SolanaTransactionRequest, TransactionRequest, TransactionTokenMetadata, WidgetSDKConfig } from '@oobit/core';
4
+
5
+ interface WidgetSDKRef {
6
+ navigateBack: () => void;
7
+ reload: () => void;
8
+ }
9
+ declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
10
+
11
+ export { WidgetSDK, type WidgetSDKRef };
package/dist/index.js CHANGED
@@ -1,6 +1,244 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WidgetSDK = void 0;
4
- var WidgetSDK_1 = require("./components/WidgetSDK");
5
- Object.defineProperty(exports, "WidgetSDK", { enumerable: true, get: function () { return WidgetSDK_1.WidgetSDK; } });
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var reactNative = require('react-native');
5
+ var reactNativeWebview = require('react-native-webview');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+
11
+ // src/components/WidgetSDK.tsx
12
+
13
+ // ../core/dist/index.mjs
14
+ var WALLET_URLS = {
15
+ ios: {
16
+ passkit: "shoebox://",
17
+ fallback: "https://wallet.apple.com"
18
+ },
19
+ android: {
20
+ googlePay: "https://pay.google.com/gp/w/home/wallet",
21
+ fallback: "https://wallet.google.com"
22
+ }
23
+ };
24
+ var TOKEN_PREFIX = {
25
+ SANDBOX: "sbx_",
26
+ PRODUCTION: "prod_"
27
+ };
28
+ var WIDGET_URLS = {
29
+ sandbox: "https://oobit-widget-dev.web.app",
30
+ production: "https://oobit-widget.web.app"
31
+ };
32
+ function getWidgetUrl(accessToken) {
33
+ if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
34
+ return WIDGET_URLS.sandbox;
35
+ }
36
+ if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
37
+ return WIDGET_URLS.production;
38
+ }
39
+ return WIDGET_URLS.production;
40
+ }
41
+ function stripTokenPrefix(accessToken) {
42
+ if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
43
+ return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);
44
+ }
45
+ if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
46
+ return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);
47
+ }
48
+ return accessToken;
49
+ }
50
+ var GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
51
+ async function openNativeWallet() {
52
+ try {
53
+ if (reactNative.Platform.OS === "ios") {
54
+ return await openAppleWallet();
55
+ }
56
+ if (reactNative.Platform.OS === "android") {
57
+ return await openGoogleWallet();
58
+ }
59
+ return false;
60
+ } catch {
61
+ reactNative.Alert.alert(
62
+ "Error",
63
+ "Could not open wallet. Please check if the app is installed."
64
+ );
65
+ return false;
66
+ }
67
+ }
68
+ async function openAppleWallet() {
69
+ const { passkit, fallback } = WALLET_URLS.ios;
70
+ if (await tryOpenUrl("wallet://")) {
71
+ return true;
72
+ }
73
+ if (await tryOpenUrl(passkit)) {
74
+ return true;
75
+ }
76
+ if (await tryOpenUrl(fallback)) {
77
+ return true;
78
+ }
79
+ throw new Error("Apple Wallet not available");
80
+ }
81
+ async function openGoogleWallet() {
82
+ const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;
83
+ if (await tryOpenUrl(launchUrl)) {
84
+ return true;
85
+ }
86
+ const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
87
+ const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;
88
+ const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);
89
+ if (opened) {
90
+ reactNative.Alert.alert(
91
+ "Google Wallet",
92
+ "Google Wallet is not installed. Opening Play Store to install the app."
93
+ );
94
+ }
95
+ return false;
96
+ }
97
+ async function tryOpenUrl(url) {
98
+ try {
99
+ const canOpen = await reactNative.Linking.canOpenURL(url);
100
+ if (canOpen) {
101
+ await reactNative.Linking.openURL(url);
102
+ return true;
103
+ }
104
+ } catch {
105
+ }
106
+ return false;
107
+ }
108
+ async function isWalletAvailable() {
109
+ try {
110
+ if (reactNative.Platform.OS === "ios") {
111
+ return await reactNative.Linking.canOpenURL(WALLET_URLS.ios.passkit);
112
+ }
113
+ if (reactNative.Platform.OS === "android") {
114
+ return await reactNative.Linking.canOpenURL(WALLET_URLS.android.googlePay);
115
+ }
116
+ return false;
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
122
+ // src/components/WidgetSDK.tsx
123
+ var WidgetSDK = React.forwardRef(
124
+ ({ accessToken, userWalletAddress, onClose, onTransactionRequested }, ref) => {
125
+ const webViewRef = React.useRef(null);
126
+ const [walletAvailable, setWalletAvailable] = React.useState(false);
127
+ const [isLoading, setIsLoading] = React.useState(true);
128
+ const hasLoadedOnce = React.useRef(false);
129
+ React.useEffect(() => {
130
+ isWalletAvailable().then(setWalletAvailable);
131
+ }, []);
132
+ React.useEffect(() => {
133
+ const backHandler = reactNative.BackHandler.addEventListener(
134
+ "hardwareBackPress",
135
+ () => {
136
+ sendMessageToWidget({ type: "native:back-pressed" });
137
+ return true;
138
+ }
139
+ );
140
+ return () => backHandler.remove();
141
+ }, []);
142
+ React.useImperativeHandle(ref, () => ({
143
+ navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
144
+ reload: () => {
145
+ setIsLoading(true);
146
+ hasLoadedOnce.current = false;
147
+ webViewRef.current?.reload();
148
+ }
149
+ }));
150
+ const handleMessage = (event) => {
151
+ try {
152
+ const message = JSON.parse(event.nativeEvent.data);
153
+ switch (message.type) {
154
+ case "widget:ready":
155
+ sendMessageToWidget({
156
+ type: "native:platform-info",
157
+ payload: { platform: reactNative.Platform.OS, walletAvailable }
158
+ });
159
+ break;
160
+ case "widget:open-wallet":
161
+ openNativeWallet().then((success) => {
162
+ sendMessageToWidget({
163
+ type: "native:wallet-opened",
164
+ payload: { success }
165
+ });
166
+ });
167
+ break;
168
+ case "widget:close":
169
+ onClose?.();
170
+ break;
171
+ case "widget:transaction-requested":
172
+ onTransactionRequested(message.payload);
173
+ break;
174
+ }
175
+ } catch {
176
+ }
177
+ };
178
+ const sendMessageToWidget = (message) => {
179
+ webViewRef.current?.injectJavaScript(
180
+ `window.postMessage(${JSON.stringify(message)}, '*'); true;`
181
+ );
182
+ };
183
+ const finalWidgetUrl = React.useMemo(() => {
184
+ const baseUrl = getWidgetUrl(accessToken);
185
+ const params = new URLSearchParams({
186
+ token: stripTokenPrefix(accessToken),
187
+ platform: reactNative.Platform.OS,
188
+ userWalletAddress,
189
+ sdk: "react-native"
190
+ });
191
+ return `${baseUrl}?${params.toString()}`;
192
+ }, [accessToken, userWalletAddress]);
193
+ return /* @__PURE__ */ React__default.default.createElement(reactNative.View, { style: styles.container }, /* @__PURE__ */ React__default.default.createElement(
194
+ reactNativeWebview.WebView,
195
+ {
196
+ ref: webViewRef,
197
+ style: styles.webview,
198
+ source: { uri: finalWidgetUrl },
199
+ onMessage: handleMessage,
200
+ onLoadEnd: () => {
201
+ if (!hasLoadedOnce.current) {
202
+ hasLoadedOnce.current = true;
203
+ setIsLoading(false);
204
+ }
205
+ },
206
+ onShouldStartLoadWithRequest: (request) => {
207
+ const { url } = request;
208
+ if (url.startsWith("shoebox://") || url.includes("pay.google.com") || url.includes("wallet.google.com") || url.includes("wallet.apple.com")) {
209
+ reactNative.Linking.openURL(url).catch(() => {
210
+ });
211
+ return false;
212
+ }
213
+ return true;
214
+ },
215
+ onOpenWindow: (syntheticEvent) => {
216
+ const { nativeEvent } = syntheticEvent;
217
+ const { targetUrl } = nativeEvent;
218
+ reactNative.Linking.openURL(targetUrl).catch(() => {
219
+ });
220
+ },
221
+ javaScriptEnabled: true,
222
+ domStorageEnabled: true,
223
+ allowsInlineMediaPlayback: true,
224
+ bounces: false,
225
+ allowsBackForwardNavigationGestures: true,
226
+ mixedContentMode: "always"
227
+ }
228
+ ), isLoading && /* @__PURE__ */ React__default.default.createElement(reactNative.View, { style: styles.loadingOverlay }, /* @__PURE__ */ React__default.default.createElement(reactNative.ActivityIndicator, { size: "large", color: "#007AFF" })));
229
+ }
230
+ );
231
+ var styles = reactNative.StyleSheet.create({
232
+ container: { flex: 1 },
233
+ webview: { flex: 1 },
234
+ loadingOverlay: {
235
+ ...reactNative.StyleSheet.absoluteFillObject,
236
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
237
+ justifyContent: "center",
238
+ alignItems: "center"
239
+ }
240
+ });
241
+
242
+ exports.WidgetSDK = WidgetSDK;
243
+ //# sourceMappingURL=index.js.map
6
244
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oDAAmD;AAA1C,sGAAA,SAAS,OAAA"}
1
+ {"version":3,"sources":["../../core/src/types/messages.ts","../../core/src/config/environment.ts","../src/utils/walletUtils.ts","../src/components/WidgetSDK.tsx"],"names":["Platform","Alert","Linking","forwardRef","useRef","useState","useEffect","BackHandler","useImperativeHandle","useMemo","React","View","WebView","ActivityIndicator","StyleSheet"],"mappings":"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","file":"index.js","sourcesContent":["import type { TransactionRequest } from './transactions';\n\n/**\n * Internal: Messages from widget\n */\nexport type WidgetMessage =\n | { type: \"widget:ready\" }\n | { type: \"widget:open-wallet\" }\n | { type: \"widget:close\" }\n | { type: \"widget:transaction-requested\"; payload: TransactionRequest };\n\n/**\n * Internal: Wallet URLs\n */\nexport const WALLET_URLS = {\n ios: {\n passkit: \"shoebox://\",\n fallback: \"https://wallet.apple.com\",\n },\n android: {\n googlePay: \"https://pay.google.com/gp/w/home/wallet\",\n fallback: \"https://wallet.google.com\",\n },\n} as const;\n","/**\n * Widget SDK Configuration\n * Environment detection based on access token prefix\n */\n\n/**\n * Token prefixes that determine the environment\n */\nconst TOKEN_PREFIX = {\n SANDBOX: \"sbx_\",\n PRODUCTION: \"prod_\",\n} as const;\n\n/**\n * Widget URLs by environment\n */\nexport const WIDGET_URLS = {\n sandbox: \"https://oobit-widget-dev.web.app\",\n production: \"https://oobit-widget.web.app\",\n} as const;\n\n/**\n * Get the widget URL based on the access token prefix\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The widget URL for the corresponding environment\n */\nexport function getWidgetUrl(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return WIDGET_URLS.sandbox;\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return WIDGET_URLS.production;\n }\n // Default to production for backwards compatibility\n return WIDGET_URLS.production;\n}\n\n/**\n * Strip the environment prefix from an access token\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The token without the environment prefix\n */\nexport function stripTokenPrefix(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);\n }\n return accessToken;\n}\n","import { Alert, Linking, Platform } from \"react-native\";\nimport { WALLET_URLS } from \"@oobit/core\";\n\nconst GOOGLE_WALLET_PACKAGE = \"com.google.android.apps.walletnfcrel\";\n\nexport async function openNativeWallet(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await openAppleWallet();\n }\n if (Platform.OS === \"android\") {\n return await openGoogleWallet();\n }\n return false;\n } catch {\n Alert.alert(\n \"Error\",\n \"Could not open wallet. Please check if the app is installed.\"\n );\n return false;\n }\n}\n\nasync function openAppleWallet(): Promise<boolean> {\n const { passkit, fallback } = WALLET_URLS.ios;\n\n // Try wallet:// scheme first (documented for recent iOS)\n if (await tryOpenUrl(\"wallet://\")) {\n return true;\n }\n\n // Try shoebox:// scheme (unofficial but widely used)\n if (await tryOpenUrl(passkit)) {\n return true;\n }\n\n // Fallback to wallet.apple.com (opens in browser)\n if (await tryOpenUrl(fallback)) {\n return true;\n }\n\n throw new Error(\"Apple Wallet not available\");\n}\n\nasync function openGoogleWallet(): Promise<boolean> {\n const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;\n\n // Try launching the app directly\n if (await tryOpenUrl(launchUrl)) {\n return true;\n }\n\n // App not installed - open Play Store\n const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;\n const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;\n\n const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);\n\n if (opened) {\n Alert.alert(\n \"Google Wallet\",\n \"Google Wallet is not installed. Opening Play Store to install the app.\"\n );\n }\n\n return false;\n}\n\nasync function tryOpenUrl(url: string): Promise<boolean> {\n try {\n const canOpen = await Linking.canOpenURL(url);\n if (canOpen) {\n await Linking.openURL(url);\n return true;\n }\n } catch {\n // URL scheme not available\n }\n return false;\n}\n\nexport async function isWalletAvailable(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await Linking.canOpenURL(WALLET_URLS.ios.passkit);\n }\n if (Platform.OS === \"android\") {\n return await Linking.canOpenURL(WALLET_URLS.android.googlePay);\n }\n return false;\n } catch {\n return false;\n }\n}\n","import React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n ActivityIndicator,\n BackHandler,\n Linking,\n Platform,\n StyleSheet,\n View,\n} from \"react-native\";\nimport { WebView, WebViewMessageEvent } from \"react-native-webview\";\nimport type { WidgetMessage, WidgetSDKConfig } from \"@oobit/core\";\nimport { getWidgetUrl, stripTokenPrefix } from \"@oobit/core\";\nimport { isWalletAvailable, openNativeWallet } from \"../utils/walletUtils\";\n\nexport interface WidgetSDKRef {\n navigateBack: () => void;\n reload: () => void;\n}\n\nexport const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(\n (\n { accessToken, userWalletAddress, onClose, onTransactionRequested },\n ref\n ) => {\n const webViewRef = useRef<WebView>(null);\n const [walletAvailable, setWalletAvailable] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n const hasLoadedOnce = useRef(false);\n\n useEffect(() => {\n isWalletAvailable().then(setWalletAvailable);\n }, []);\n\n useEffect(() => {\n const backHandler = BackHandler.addEventListener(\n \"hardwareBackPress\",\n () => {\n sendMessageToWidget({ type: \"native:back-pressed\" });\n return true;\n }\n );\n return () => backHandler.remove();\n }, []);\n\n useImperativeHandle(ref, () => ({\n navigateBack: () => sendMessageToWidget({ type: \"native:navigate-back\" }),\n reload: () => {\n setIsLoading(true);\n hasLoadedOnce.current = false;\n webViewRef.current?.reload();\n },\n }));\n\n const handleMessage = (event: WebViewMessageEvent) => {\n try {\n const message: WidgetMessage = JSON.parse(event.nativeEvent.data);\n\n switch (message.type) {\n case \"widget:ready\":\n sendMessageToWidget({\n type: \"native:platform-info\",\n payload: { platform: Platform.OS, walletAvailable },\n });\n break;\n\n case \"widget:open-wallet\":\n openNativeWallet().then((success) => {\n sendMessageToWidget({\n type: \"native:wallet-opened\",\n payload: { success },\n });\n });\n break;\n\n case \"widget:close\":\n onClose?.();\n break;\n\n case \"widget:transaction-requested\":\n onTransactionRequested(message.payload);\n break;\n }\n } catch {\n // Invalid JSON from WebView - ignore\n }\n };\n\n const sendMessageToWidget = (message: unknown) => {\n webViewRef.current?.injectJavaScript(\n `window.postMessage(${JSON.stringify(message)}, '*'); true;`\n );\n };\n\n const finalWidgetUrl = useMemo(() => {\n const baseUrl = getWidgetUrl(accessToken);\n const params = new URLSearchParams({\n token: stripTokenPrefix(accessToken),\n platform: Platform.OS,\n userWalletAddress,\n sdk: 'react-native',\n });\n return `${baseUrl}?${params.toString()}`;\n }, [accessToken, userWalletAddress]);\n\n return (\n <View style={styles.container}>\n <WebView\n ref={webViewRef}\n style={styles.webview}\n source={{ uri: finalWidgetUrl }}\n onMessage={handleMessage}\n onLoadEnd={() => {\n if (!hasLoadedOnce.current) {\n hasLoadedOnce.current = true;\n setIsLoading(false);\n }\n }}\n onShouldStartLoadWithRequest={(request) => {\n const { url } = request;\n if (\n url.startsWith(\"shoebox://\") ||\n url.includes(\"pay.google.com\") ||\n url.includes(\"wallet.google.com\") ||\n url.includes(\"wallet.apple.com\")\n ) {\n Linking.openURL(url).catch(() => {});\n return false;\n }\n return true;\n }}\n onOpenWindow={(syntheticEvent) => {\n const { nativeEvent } = syntheticEvent;\n const { targetUrl } = nativeEvent;\n Linking.openURL(targetUrl).catch(() => {});\n }}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n allowsInlineMediaPlayback={true}\n bounces={false}\n allowsBackForwardNavigationGestures={true}\n mixedContentMode=\"always\"\n />\n {isLoading && (\n <View style={styles.loadingOverlay}>\n <ActivityIndicator size=\"large\" color=\"#007AFF\" />\n </View>\n )}\n </View>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n webview: { flex: 1 },\n loadingOverlay: {\n ...StyleSheet.absoluteFillObject,\n backgroundColor: \"rgba(255, 255, 255, 0.9)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n});\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,238 @@
1
+ import React, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo } from 'react';
2
+ import { BackHandler, Platform, View, Linking, ActivityIndicator, StyleSheet, Alert } from 'react-native';
3
+ import { WebView } from 'react-native-webview';
4
+
5
+ // src/components/WidgetSDK.tsx
6
+
7
+ // ../core/dist/index.mjs
8
+ var WALLET_URLS = {
9
+ ios: {
10
+ passkit: "shoebox://",
11
+ fallback: "https://wallet.apple.com"
12
+ },
13
+ android: {
14
+ googlePay: "https://pay.google.com/gp/w/home/wallet",
15
+ fallback: "https://wallet.google.com"
16
+ }
17
+ };
18
+ var TOKEN_PREFIX = {
19
+ SANDBOX: "sbx_",
20
+ PRODUCTION: "prod_"
21
+ };
22
+ var WIDGET_URLS = {
23
+ sandbox: "https://oobit-widget-dev.web.app",
24
+ production: "https://oobit-widget.web.app"
25
+ };
26
+ function getWidgetUrl(accessToken) {
27
+ if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
28
+ return WIDGET_URLS.sandbox;
29
+ }
30
+ if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
31
+ return WIDGET_URLS.production;
32
+ }
33
+ return WIDGET_URLS.production;
34
+ }
35
+ function stripTokenPrefix(accessToken) {
36
+ if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {
37
+ return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);
38
+ }
39
+ if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {
40
+ return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);
41
+ }
42
+ return accessToken;
43
+ }
44
+ var GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
45
+ async function openNativeWallet() {
46
+ try {
47
+ if (Platform.OS === "ios") {
48
+ return await openAppleWallet();
49
+ }
50
+ if (Platform.OS === "android") {
51
+ return await openGoogleWallet();
52
+ }
53
+ return false;
54
+ } catch {
55
+ Alert.alert(
56
+ "Error",
57
+ "Could not open wallet. Please check if the app is installed."
58
+ );
59
+ return false;
60
+ }
61
+ }
62
+ async function openAppleWallet() {
63
+ const { passkit, fallback } = WALLET_URLS.ios;
64
+ if (await tryOpenUrl("wallet://")) {
65
+ return true;
66
+ }
67
+ if (await tryOpenUrl(passkit)) {
68
+ return true;
69
+ }
70
+ if (await tryOpenUrl(fallback)) {
71
+ return true;
72
+ }
73
+ throw new Error("Apple Wallet not available");
74
+ }
75
+ async function openGoogleWallet() {
76
+ const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;
77
+ if (await tryOpenUrl(launchUrl)) {
78
+ return true;
79
+ }
80
+ const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
81
+ const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;
82
+ const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);
83
+ if (opened) {
84
+ Alert.alert(
85
+ "Google Wallet",
86
+ "Google Wallet is not installed. Opening Play Store to install the app."
87
+ );
88
+ }
89
+ return false;
90
+ }
91
+ async function tryOpenUrl(url) {
92
+ try {
93
+ const canOpen = await Linking.canOpenURL(url);
94
+ if (canOpen) {
95
+ await Linking.openURL(url);
96
+ return true;
97
+ }
98
+ } catch {
99
+ }
100
+ return false;
101
+ }
102
+ async function isWalletAvailable() {
103
+ try {
104
+ if (Platform.OS === "ios") {
105
+ return await Linking.canOpenURL(WALLET_URLS.ios.passkit);
106
+ }
107
+ if (Platform.OS === "android") {
108
+ return await Linking.canOpenURL(WALLET_URLS.android.googlePay);
109
+ }
110
+ return false;
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ // src/components/WidgetSDK.tsx
117
+ var WidgetSDK = forwardRef(
118
+ ({ accessToken, userWalletAddress, onClose, onTransactionRequested }, ref) => {
119
+ const webViewRef = useRef(null);
120
+ const [walletAvailable, setWalletAvailable] = useState(false);
121
+ const [isLoading, setIsLoading] = useState(true);
122
+ const hasLoadedOnce = useRef(false);
123
+ useEffect(() => {
124
+ isWalletAvailable().then(setWalletAvailable);
125
+ }, []);
126
+ useEffect(() => {
127
+ const backHandler = BackHandler.addEventListener(
128
+ "hardwareBackPress",
129
+ () => {
130
+ sendMessageToWidget({ type: "native:back-pressed" });
131
+ return true;
132
+ }
133
+ );
134
+ return () => backHandler.remove();
135
+ }, []);
136
+ useImperativeHandle(ref, () => ({
137
+ navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
138
+ reload: () => {
139
+ setIsLoading(true);
140
+ hasLoadedOnce.current = false;
141
+ webViewRef.current?.reload();
142
+ }
143
+ }));
144
+ const handleMessage = (event) => {
145
+ try {
146
+ const message = JSON.parse(event.nativeEvent.data);
147
+ switch (message.type) {
148
+ case "widget:ready":
149
+ sendMessageToWidget({
150
+ type: "native:platform-info",
151
+ payload: { platform: Platform.OS, walletAvailable }
152
+ });
153
+ break;
154
+ case "widget:open-wallet":
155
+ openNativeWallet().then((success) => {
156
+ sendMessageToWidget({
157
+ type: "native:wallet-opened",
158
+ payload: { success }
159
+ });
160
+ });
161
+ break;
162
+ case "widget:close":
163
+ onClose?.();
164
+ break;
165
+ case "widget:transaction-requested":
166
+ onTransactionRequested(message.payload);
167
+ break;
168
+ }
169
+ } catch {
170
+ }
171
+ };
172
+ const sendMessageToWidget = (message) => {
173
+ webViewRef.current?.injectJavaScript(
174
+ `window.postMessage(${JSON.stringify(message)}, '*'); true;`
175
+ );
176
+ };
177
+ const finalWidgetUrl = useMemo(() => {
178
+ const baseUrl = getWidgetUrl(accessToken);
179
+ const params = new URLSearchParams({
180
+ token: stripTokenPrefix(accessToken),
181
+ platform: Platform.OS,
182
+ userWalletAddress,
183
+ sdk: "react-native"
184
+ });
185
+ return `${baseUrl}?${params.toString()}`;
186
+ }, [accessToken, userWalletAddress]);
187
+ return /* @__PURE__ */ React.createElement(View, { style: styles.container }, /* @__PURE__ */ React.createElement(
188
+ WebView,
189
+ {
190
+ ref: webViewRef,
191
+ style: styles.webview,
192
+ source: { uri: finalWidgetUrl },
193
+ onMessage: handleMessage,
194
+ onLoadEnd: () => {
195
+ if (!hasLoadedOnce.current) {
196
+ hasLoadedOnce.current = true;
197
+ setIsLoading(false);
198
+ }
199
+ },
200
+ onShouldStartLoadWithRequest: (request) => {
201
+ const { url } = request;
202
+ if (url.startsWith("shoebox://") || url.includes("pay.google.com") || url.includes("wallet.google.com") || url.includes("wallet.apple.com")) {
203
+ Linking.openURL(url).catch(() => {
204
+ });
205
+ return false;
206
+ }
207
+ return true;
208
+ },
209
+ onOpenWindow: (syntheticEvent) => {
210
+ const { nativeEvent } = syntheticEvent;
211
+ const { targetUrl } = nativeEvent;
212
+ Linking.openURL(targetUrl).catch(() => {
213
+ });
214
+ },
215
+ javaScriptEnabled: true,
216
+ domStorageEnabled: true,
217
+ allowsInlineMediaPlayback: true,
218
+ bounces: false,
219
+ allowsBackForwardNavigationGestures: true,
220
+ mixedContentMode: "always"
221
+ }
222
+ ), isLoading && /* @__PURE__ */ React.createElement(View, { style: styles.loadingOverlay }, /* @__PURE__ */ React.createElement(ActivityIndicator, { size: "large", color: "#007AFF" })));
223
+ }
224
+ );
225
+ var styles = StyleSheet.create({
226
+ container: { flex: 1 },
227
+ webview: { flex: 1 },
228
+ loadingOverlay: {
229
+ ...StyleSheet.absoluteFillObject,
230
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
231
+ justifyContent: "center",
232
+ alignItems: "center"
233
+ }
234
+ });
235
+
236
+ export { WidgetSDK };
237
+ //# sourceMappingURL=index.mjs.map
238
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/types/messages.ts","../../core/src/config/environment.ts","../src/utils/walletUtils.ts","../src/components/WidgetSDK.tsx"],"names":["Platform","Linking"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { TransactionRequest } from './transactions';\n\n/**\n * Internal: Messages from widget\n */\nexport type WidgetMessage =\n | { type: \"widget:ready\" }\n | { type: \"widget:open-wallet\" }\n | { type: \"widget:close\" }\n | { type: \"widget:transaction-requested\"; payload: TransactionRequest };\n\n/**\n * Internal: Wallet URLs\n */\nexport const WALLET_URLS = {\n ios: {\n passkit: \"shoebox://\",\n fallback: \"https://wallet.apple.com\",\n },\n android: {\n googlePay: \"https://pay.google.com/gp/w/home/wallet\",\n fallback: \"https://wallet.google.com\",\n },\n} as const;\n","/**\n * Widget SDK Configuration\n * Environment detection based on access token prefix\n */\n\n/**\n * Token prefixes that determine the environment\n */\nconst TOKEN_PREFIX = {\n SANDBOX: \"sbx_\",\n PRODUCTION: \"prod_\",\n} as const;\n\n/**\n * Widget URLs by environment\n */\nexport const WIDGET_URLS = {\n sandbox: \"https://oobit-widget-dev.web.app\",\n production: \"https://oobit-widget.web.app\",\n} as const;\n\n/**\n * Get the widget URL based on the access token prefix\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The widget URL for the corresponding environment\n */\nexport function getWidgetUrl(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return WIDGET_URLS.sandbox;\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return WIDGET_URLS.production;\n }\n // Default to production for backwards compatibility\n return WIDGET_URLS.production;\n}\n\n/**\n * Strip the environment prefix from an access token\n * @param accessToken - The access token (prefixed with sbx_ or prod_)\n * @returns The token without the environment prefix\n */\nexport function stripTokenPrefix(accessToken: string): string {\n if (accessToken.startsWith(TOKEN_PREFIX.SANDBOX)) {\n return accessToken.slice(TOKEN_PREFIX.SANDBOX.length);\n }\n if (accessToken.startsWith(TOKEN_PREFIX.PRODUCTION)) {\n return accessToken.slice(TOKEN_PREFIX.PRODUCTION.length);\n }\n return accessToken;\n}\n","import { Alert, Linking, Platform } from \"react-native\";\nimport { WALLET_URLS } from \"@oobit/core\";\n\nconst GOOGLE_WALLET_PACKAGE = \"com.google.android.apps.walletnfcrel\";\n\nexport async function openNativeWallet(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await openAppleWallet();\n }\n if (Platform.OS === \"android\") {\n return await openGoogleWallet();\n }\n return false;\n } catch {\n Alert.alert(\n \"Error\",\n \"Could not open wallet. Please check if the app is installed.\"\n );\n return false;\n }\n}\n\nasync function openAppleWallet(): Promise<boolean> {\n const { passkit, fallback } = WALLET_URLS.ios;\n\n // Try wallet:// scheme first (documented for recent iOS)\n if (await tryOpenUrl(\"wallet://\")) {\n return true;\n }\n\n // Try shoebox:// scheme (unofficial but widely used)\n if (await tryOpenUrl(passkit)) {\n return true;\n }\n\n // Fallback to wallet.apple.com (opens in browser)\n if (await tryOpenUrl(fallback)) {\n return true;\n }\n\n throw new Error(\"Apple Wallet not available\");\n}\n\nasync function openGoogleWallet(): Promise<boolean> {\n const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;\n\n // Try launching the app directly\n if (await tryOpenUrl(launchUrl)) {\n return true;\n }\n\n // App not installed - open Play Store\n const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;\n const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;\n\n const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);\n\n if (opened) {\n Alert.alert(\n \"Google Wallet\",\n \"Google Wallet is not installed. Opening Play Store to install the app.\"\n );\n }\n\n return false;\n}\n\nasync function tryOpenUrl(url: string): Promise<boolean> {\n try {\n const canOpen = await Linking.canOpenURL(url);\n if (canOpen) {\n await Linking.openURL(url);\n return true;\n }\n } catch {\n // URL scheme not available\n }\n return false;\n}\n\nexport async function isWalletAvailable(): Promise<boolean> {\n try {\n if (Platform.OS === \"ios\") {\n return await Linking.canOpenURL(WALLET_URLS.ios.passkit);\n }\n if (Platform.OS === \"android\") {\n return await Linking.canOpenURL(WALLET_URLS.android.googlePay);\n }\n return false;\n } catch {\n return false;\n }\n}\n","import React, {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n ActivityIndicator,\n BackHandler,\n Linking,\n Platform,\n StyleSheet,\n View,\n} from \"react-native\";\nimport { WebView, WebViewMessageEvent } from \"react-native-webview\";\nimport type { WidgetMessage, WidgetSDKConfig } from \"@oobit/core\";\nimport { getWidgetUrl, stripTokenPrefix } from \"@oobit/core\";\nimport { isWalletAvailable, openNativeWallet } from \"../utils/walletUtils\";\n\nexport interface WidgetSDKRef {\n navigateBack: () => void;\n reload: () => void;\n}\n\nexport const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(\n (\n { accessToken, userWalletAddress, onClose, onTransactionRequested },\n ref\n ) => {\n const webViewRef = useRef<WebView>(null);\n const [walletAvailable, setWalletAvailable] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n const hasLoadedOnce = useRef(false);\n\n useEffect(() => {\n isWalletAvailable().then(setWalletAvailable);\n }, []);\n\n useEffect(() => {\n const backHandler = BackHandler.addEventListener(\n \"hardwareBackPress\",\n () => {\n sendMessageToWidget({ type: \"native:back-pressed\" });\n return true;\n }\n );\n return () => backHandler.remove();\n }, []);\n\n useImperativeHandle(ref, () => ({\n navigateBack: () => sendMessageToWidget({ type: \"native:navigate-back\" }),\n reload: () => {\n setIsLoading(true);\n hasLoadedOnce.current = false;\n webViewRef.current?.reload();\n },\n }));\n\n const handleMessage = (event: WebViewMessageEvent) => {\n try {\n const message: WidgetMessage = JSON.parse(event.nativeEvent.data);\n\n switch (message.type) {\n case \"widget:ready\":\n sendMessageToWidget({\n type: \"native:platform-info\",\n payload: { platform: Platform.OS, walletAvailable },\n });\n break;\n\n case \"widget:open-wallet\":\n openNativeWallet().then((success) => {\n sendMessageToWidget({\n type: \"native:wallet-opened\",\n payload: { success },\n });\n });\n break;\n\n case \"widget:close\":\n onClose?.();\n break;\n\n case \"widget:transaction-requested\":\n onTransactionRequested(message.payload);\n break;\n }\n } catch {\n // Invalid JSON from WebView - ignore\n }\n };\n\n const sendMessageToWidget = (message: unknown) => {\n webViewRef.current?.injectJavaScript(\n `window.postMessage(${JSON.stringify(message)}, '*'); true;`\n );\n };\n\n const finalWidgetUrl = useMemo(() => {\n const baseUrl = getWidgetUrl(accessToken);\n const params = new URLSearchParams({\n token: stripTokenPrefix(accessToken),\n platform: Platform.OS,\n userWalletAddress,\n sdk: 'react-native',\n });\n return `${baseUrl}?${params.toString()}`;\n }, [accessToken, userWalletAddress]);\n\n return (\n <View style={styles.container}>\n <WebView\n ref={webViewRef}\n style={styles.webview}\n source={{ uri: finalWidgetUrl }}\n onMessage={handleMessage}\n onLoadEnd={() => {\n if (!hasLoadedOnce.current) {\n hasLoadedOnce.current = true;\n setIsLoading(false);\n }\n }}\n onShouldStartLoadWithRequest={(request) => {\n const { url } = request;\n if (\n url.startsWith(\"shoebox://\") ||\n url.includes(\"pay.google.com\") ||\n url.includes(\"wallet.google.com\") ||\n url.includes(\"wallet.apple.com\")\n ) {\n Linking.openURL(url).catch(() => {});\n return false;\n }\n return true;\n }}\n onOpenWindow={(syntheticEvent) => {\n const { nativeEvent } = syntheticEvent;\n const { targetUrl } = nativeEvent;\n Linking.openURL(targetUrl).catch(() => {});\n }}\n javaScriptEnabled={true}\n domStorageEnabled={true}\n allowsInlineMediaPlayback={true}\n bounces={false}\n allowsBackForwardNavigationGestures={true}\n mixedContentMode=\"always\"\n />\n {isLoading && (\n <View style={styles.loadingOverlay}>\n <ActivityIndicator size=\"large\" color=\"#007AFF\" />\n </View>\n )}\n </View>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: { flex: 1 },\n webview: { flex: 1 },\n loadingOverlay: {\n ...StyleSheet.absoluteFillObject,\n backgroundColor: \"rgba(255, 255, 255, 0.9)\",\n justifyContent: \"center\",\n alignItems: \"center\",\n },\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oobit/react-native-sdk",
3
- "version": "3.0.2",
3
+ "version": "3.1.0",
4
4
  "description": "React Native SDK for integrating Oobit crypto payments into wallet apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -44,20 +44,21 @@
44
44
  "react-native-webview": ">=13.0.0"
45
45
  },
46
46
  "dependencies": {
47
- "node-forge": "^1.3.1",
48
- "@oobit/core": "3.0.2"
47
+ "node-forge": "^1.3.1"
49
48
  },
50
49
  "devDependencies": {
50
+ "@testing-library/react-native": "^12.4.0",
51
51
  "@types/node-forge": "^1.3.11",
52
52
  "@types/react": "~19.1.0",
53
53
  "react": "19.1.0",
54
54
  "react-native": "0.81.5",
55
55
  "react-native-webview": "13.15.0",
56
+ "tsup": "^8.0.1",
56
57
  "typescript": "~5.9.2",
57
58
  "vitest": "^1.2.0",
58
- "@testing-library/react-native": "^12.4.0",
59
- "@oobit/typescript-config": "1.0.0",
60
- "@oobit/eslint-config": "1.0.0"
59
+ "@oobit/core": "3.1.0",
60
+ "@oobit/eslint-config": "1.0.0",
61
+ "@oobit/typescript-config": "1.0.0"
61
62
  },
62
63
  "files": [
63
64
  "dist",
@@ -69,8 +70,8 @@
69
70
  "LICENSE"
70
71
  ],
71
72
  "scripts": {
72
- "build": "tsc",
73
- "dev": "tsc --watch",
73
+ "build": "tsup",
74
+ "dev": "tsup --watch",
74
75
  "typecheck": "tsc --noEmit",
75
76
  "clean": "rm -rf dist",
76
77
  "lint": "eslint src/",
@@ -104,6 +104,7 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
104
104
  token: stripTokenPrefix(accessToken),
105
105
  platform: Platform.OS,
106
106
  userWalletAddress,
107
+ sdk: 'react-native',
107
108
  });
108
109
  return `${baseUrl}?${params.toString()}`;
109
110
  }, [accessToken, userWalletAddress]);
@@ -1,8 +0,0 @@
1
- import React from "react";
2
- import type { WidgetSDKConfig } from "@oobit/core";
3
- export interface WidgetSDKRef {
4
- navigateBack: () => void;
5
- reload: () => void;
6
- }
7
- export declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
8
- //# sourceMappingURL=WidgetSDK.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"WidgetSDK.d.ts","sourceRoot":"","sources":["../../src/components/WidgetSDK.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAUf,OAAO,KAAK,EAAiB,eAAe,EAAE,MAAM,aAAa,CAAC;AAIlE,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,eAAO,MAAM,SAAS,sFAkIrB,CAAC"}
@@ -1,143 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.WidgetSDK = void 0;
37
- const react_1 = __importStar(require("react"));
38
- const react_native_1 = require("react-native");
39
- const react_native_webview_1 = require("react-native-webview");
40
- const core_1 = require("@oobit/core");
41
- const walletUtils_1 = require("../utils/walletUtils");
42
- exports.WidgetSDK = (0, react_1.forwardRef)(({ accessToken, userWalletAddress, onClose, onTransactionRequested }, ref) => {
43
- const webViewRef = (0, react_1.useRef)(null);
44
- const [walletAvailable, setWalletAvailable] = (0, react_1.useState)(false);
45
- const [isLoading, setIsLoading] = (0, react_1.useState)(true);
46
- const hasLoadedOnce = (0, react_1.useRef)(false);
47
- (0, react_1.useEffect)(() => {
48
- (0, walletUtils_1.isWalletAvailable)().then(setWalletAvailable);
49
- }, []);
50
- (0, react_1.useEffect)(() => {
51
- const backHandler = react_native_1.BackHandler.addEventListener("hardwareBackPress", () => {
52
- sendMessageToWidget({ type: "native:back-pressed" });
53
- return true;
54
- });
55
- return () => backHandler.remove();
56
- }, []);
57
- (0, react_1.useImperativeHandle)(ref, () => ({
58
- navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
59
- reload: () => {
60
- setIsLoading(true);
61
- hasLoadedOnce.current = false;
62
- webViewRef.current?.reload();
63
- },
64
- }));
65
- const handleMessage = (event) => {
66
- try {
67
- const message = JSON.parse(event.nativeEvent.data);
68
- switch (message.type) {
69
- case "widget:ready":
70
- sendMessageToWidget({
71
- type: "native:platform-info",
72
- payload: { platform: react_native_1.Platform.OS, walletAvailable },
73
- });
74
- break;
75
- case "widget:open-wallet":
76
- (0, walletUtils_1.openNativeWallet)().then((success) => {
77
- sendMessageToWidget({
78
- type: "native:wallet-opened",
79
- payload: { success },
80
- });
81
- });
82
- break;
83
- case "widget:close":
84
- onClose?.();
85
- break;
86
- case "widget:transaction-requested":
87
- onTransactionRequested(message.payload);
88
- break;
89
- }
90
- }
91
- catch {
92
- // Invalid JSON from WebView - ignore
93
- }
94
- };
95
- const sendMessageToWidget = (message) => {
96
- webViewRef.current?.injectJavaScript(`window.postMessage(${JSON.stringify(message)}, '*'); true;`);
97
- };
98
- const finalWidgetUrl = (0, react_1.useMemo)(() => {
99
- const baseUrl = (0, core_1.getWidgetUrl)(accessToken);
100
- const params = new URLSearchParams({
101
- token: (0, core_1.stripTokenPrefix)(accessToken),
102
- platform: react_native_1.Platform.OS,
103
- userWalletAddress,
104
- });
105
- return `${baseUrl}?${params.toString()}`;
106
- }, [accessToken, userWalletAddress]);
107
- return (<react_native_1.View style={styles.container}>
108
- <react_native_webview_1.WebView ref={webViewRef} style={styles.webview} source={{ uri: finalWidgetUrl }} onMessage={handleMessage} onLoadEnd={() => {
109
- if (!hasLoadedOnce.current) {
110
- hasLoadedOnce.current = true;
111
- setIsLoading(false);
112
- }
113
- }} onShouldStartLoadWithRequest={(request) => {
114
- const { url } = request;
115
- if (url.startsWith("shoebox://") ||
116
- url.includes("pay.google.com") ||
117
- url.includes("wallet.google.com") ||
118
- url.includes("wallet.apple.com")) {
119
- react_native_1.Linking.openURL(url).catch(() => { });
120
- return false;
121
- }
122
- return true;
123
- }} onOpenWindow={(syntheticEvent) => {
124
- const { nativeEvent } = syntheticEvent;
125
- const { targetUrl } = nativeEvent;
126
- react_native_1.Linking.openURL(targetUrl).catch(() => { });
127
- }} javaScriptEnabled={true} domStorageEnabled={true} allowsInlineMediaPlayback={true} bounces={false} allowsBackForwardNavigationGestures={true} mixedContentMode="always"/>
128
- {isLoading && (<react_native_1.View style={styles.loadingOverlay}>
129
- <react_native_1.ActivityIndicator size="large" color="#007AFF"/>
130
- </react_native_1.View>)}
131
- </react_native_1.View>);
132
- });
133
- const styles = react_native_1.StyleSheet.create({
134
- container: { flex: 1 },
135
- webview: { flex: 1 },
136
- loadingOverlay: {
137
- ...react_native_1.StyleSheet.absoluteFillObject,
138
- backgroundColor: "rgba(255, 255, 255, 0.9)",
139
- justifyContent: "center",
140
- alignItems: "center",
141
- },
142
- });
143
- //# sourceMappingURL=WidgetSDK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"WidgetSDK.js","sourceRoot":"","sources":["../../src/components/WidgetSDK.tsx"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,GAChB,MAAM,aAAa,CAAC"}
@@ -1,3 +0,0 @@
1
- export declare function openNativeWallet(): Promise<boolean>;
2
- export declare function isWalletAvailable(): Promise<boolean>;
3
- //# sourceMappingURL=walletUtils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"walletUtils.d.ts","sourceRoot":"","sources":["../../src/utils/walletUtils.ts"],"names":[],"mappings":"AAKA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAgBzD;AA4DD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAY1D"}
@@ -1,81 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openNativeWallet = openNativeWallet;
4
- exports.isWalletAvailable = isWalletAvailable;
5
- const react_native_1 = require("react-native");
6
- const core_1 = require("@oobit/core");
7
- const GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
8
- async function openNativeWallet() {
9
- try {
10
- if (react_native_1.Platform.OS === "ios") {
11
- return await openAppleWallet();
12
- }
13
- if (react_native_1.Platform.OS === "android") {
14
- return await openGoogleWallet();
15
- }
16
- return false;
17
- }
18
- catch {
19
- react_native_1.Alert.alert("Error", "Could not open wallet. Please check if the app is installed.");
20
- return false;
21
- }
22
- }
23
- async function openAppleWallet() {
24
- const { passkit, fallback } = core_1.WALLET_URLS.ios;
25
- // Try wallet:// scheme first (documented for recent iOS)
26
- if (await tryOpenUrl("wallet://")) {
27
- return true;
28
- }
29
- // Try shoebox:// scheme (unofficial but widely used)
30
- if (await tryOpenUrl(passkit)) {
31
- return true;
32
- }
33
- // Fallback to wallet.apple.com (opens in browser)
34
- if (await tryOpenUrl(fallback)) {
35
- return true;
36
- }
37
- throw new Error("Apple Wallet not available");
38
- }
39
- async function openGoogleWallet() {
40
- const launchUrl = `market://launch?id=${GOOGLE_WALLET_PACKAGE}`;
41
- // Try launching the app directly
42
- if (await tryOpenUrl(launchUrl)) {
43
- return true;
44
- }
45
- // App not installed - open Play Store
46
- const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
47
- const webPlayStoreUrl = `https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`;
48
- const opened = await tryOpenUrl(playStoreUrl) || await tryOpenUrl(webPlayStoreUrl);
49
- if (opened) {
50
- react_native_1.Alert.alert("Google Wallet", "Google Wallet is not installed. Opening Play Store to install the app.");
51
- }
52
- return false;
53
- }
54
- async function tryOpenUrl(url) {
55
- try {
56
- const canOpen = await react_native_1.Linking.canOpenURL(url);
57
- if (canOpen) {
58
- await react_native_1.Linking.openURL(url);
59
- return true;
60
- }
61
- }
62
- catch {
63
- // URL scheme not available
64
- }
65
- return false;
66
- }
67
- async function isWalletAvailable() {
68
- try {
69
- if (react_native_1.Platform.OS === "ios") {
70
- return await react_native_1.Linking.canOpenURL(core_1.WALLET_URLS.ios.passkit);
71
- }
72
- if (react_native_1.Platform.OS === "android") {
73
- return await react_native_1.Linking.canOpenURL(core_1.WALLET_URLS.android.googlePay);
74
- }
75
- return false;
76
- }
77
- catch {
78
- return false;
79
- }
80
- }
81
- //# sourceMappingURL=walletUtils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"walletUtils.js","sourceRoot":"","sources":["../../src/utils/walletUtils.ts"],"names":[],"mappings":";;AAKA,4CAgBC;AA4DD,8CAYC;AA7FD,+CAAwD;AACxD,sCAA0C;AAE1C,MAAM,qBAAqB,GAAG,sCAAsC,CAAC;AAE9D,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,MAAM,eAAe,EAAE,CAAC;QACjC,CAAC;QACD,IAAI,uBAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,MAAM,gBAAgB,EAAE,CAAC;QAClC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,oBAAK,CAAC,KAAK,CACT,OAAO,EACP,8DAA8D,CAC/D,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,kBAAW,CAAC,GAAG,CAAC;IAE9C,yDAAyD;IACzD,IAAI,MAAM,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qDAAqD;IACrD,IAAI,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kDAAkD;IAClD,IAAI,MAAM,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;AAChD,CAAC;AAED,KAAK,UAAU,gBAAgB;IAC7B,MAAM,SAAS,GAAG,sBAAsB,qBAAqB,EAAE,CAAC;IAEhE,iCAAiC;IACjC,IAAI,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,MAAM,YAAY,GAAG,uBAAuB,qBAAqB,EAAE,CAAC;IACpE,MAAM,eAAe,GAAG,iDAAiD,qBAAqB,EAAE,CAAC;IAEjG,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,IAAI,MAAM,UAAU,CAAC,eAAe,CAAC,CAAC;IAEnF,IAAI,MAAM,EAAE,CAAC;QACX,oBAAK,CAAC,KAAK,CACT,eAAe,EACf,wEAAwE,CACzE,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,sBAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,sBAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,MAAM,sBAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,uBAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,MAAM,sBAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}