@oobit/react-native-sdk 2.0.2 → 3.0.1
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.
- package/README.md +19 -341
- package/dist/WidgetSDK.d.ts +0 -2
- package/dist/WidgetSDK.d.ts.map +1 -1
- package/dist/WidgetSDK.js +25 -127
- package/dist/WidgetSDK.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +41 -148
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -33
- package/dist/types.js.map +1 -1
- package/dist/walletUtils.d.ts.map +1 -1
- package/dist/walletUtils.js +16 -64
- package/dist/walletUtils.js.map +1 -1
- package/package.json +1 -10
- package/src/WidgetSDK.tsx +26 -153
- package/src/index.ts +7 -9
- package/src/types.ts +42 -207
- package/src/walletUtils.ts +27 -42
package/src/WidgetSDK.tsx
CHANGED
|
@@ -21,26 +21,17 @@ import {
|
|
|
21
21
|
} from "react-native";
|
|
22
22
|
import { WebView, WebViewMessageEvent } from "react-native-webview";
|
|
23
23
|
import { getWidgetUrl, stripTokenPrefix } from "./config";
|
|
24
|
-
import {
|
|
24
|
+
import { WidgetMessage, WidgetSDKConfig } from "./types";
|
|
25
25
|
import { isWalletAvailable, openNativeWallet } from "./walletUtils";
|
|
26
|
-
import * as Clipboard from "expo-clipboard";
|
|
27
26
|
|
|
28
27
|
export interface WidgetSDKRef {
|
|
29
|
-
/** Navigate back within the widget */
|
|
30
28
|
navigateBack: () => void;
|
|
31
|
-
/** Reload the widget */
|
|
32
29
|
reload: () => void;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
36
33
|
(
|
|
37
|
-
{
|
|
38
|
-
accessToken,
|
|
39
|
-
userWalletAddress,
|
|
40
|
-
onError,
|
|
41
|
-
onClose,
|
|
42
|
-
onTransactionRequested,
|
|
43
|
-
},
|
|
34
|
+
{ accessToken, userWalletAddress, onClose, onTransactionRequested },
|
|
44
35
|
ref
|
|
45
36
|
) => {
|
|
46
37
|
const webViewRef = useRef<WebView>(null);
|
|
@@ -48,37 +39,23 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
48
39
|
const [isLoading, setIsLoading] = useState(true);
|
|
49
40
|
const hasLoadedOnce = useRef(false);
|
|
50
41
|
|
|
51
|
-
// Check wallet availability on mount
|
|
52
42
|
useEffect(() => {
|
|
53
|
-
|
|
43
|
+
isWalletAvailable().then(setWalletAvailable);
|
|
54
44
|
}, []);
|
|
55
45
|
|
|
56
|
-
// Handle Android hardware back button
|
|
57
46
|
useEffect(() => {
|
|
58
47
|
const backHandler = BackHandler.addEventListener(
|
|
59
48
|
"hardwareBackPress",
|
|
60
49
|
() => {
|
|
61
|
-
|
|
62
|
-
sendMessageToWidget({
|
|
63
|
-
type: "native:back-pressed",
|
|
64
|
-
timestamp: Date.now(),
|
|
65
|
-
});
|
|
66
|
-
// Return true to prevent default back behavior
|
|
50
|
+
sendMessageToWidget({ type: "native:back-pressed" });
|
|
67
51
|
return true;
|
|
68
52
|
}
|
|
69
53
|
);
|
|
70
|
-
|
|
71
54
|
return () => backHandler.remove();
|
|
72
55
|
}, []);
|
|
73
56
|
|
|
74
|
-
// Expose methods via ref
|
|
75
57
|
useImperativeHandle(ref, () => ({
|
|
76
|
-
navigateBack: () => {
|
|
77
|
-
sendMessageToWidget({
|
|
78
|
-
type: "native:navigate-back",
|
|
79
|
-
timestamp: Date.now(),
|
|
80
|
-
});
|
|
81
|
-
},
|
|
58
|
+
navigateBack: () => sendMessageToWidget({ type: "native:navigate-back" }),
|
|
82
59
|
reload: () => {
|
|
83
60
|
setIsLoading(true);
|
|
84
61
|
hasLoadedOnce.current = false;
|
|
@@ -86,29 +63,25 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
86
63
|
},
|
|
87
64
|
}));
|
|
88
65
|
|
|
89
|
-
const checkWalletAvailability = async () => {
|
|
90
|
-
const available = await isWalletAvailable();
|
|
91
|
-
setWalletAvailable(available);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Handle messages from the web widget
|
|
96
|
-
*/
|
|
97
66
|
const handleMessage = (event: WebViewMessageEvent) => {
|
|
98
67
|
try {
|
|
99
68
|
const message: WidgetMessage = JSON.parse(event.nativeEvent.data);
|
|
100
69
|
|
|
101
70
|
switch (message.type) {
|
|
102
71
|
case "widget:ready":
|
|
103
|
-
|
|
72
|
+
sendMessageToWidget({
|
|
73
|
+
type: "native:platform-info",
|
|
74
|
+
payload: { platform: Platform.OS, walletAvailable },
|
|
75
|
+
});
|
|
104
76
|
break;
|
|
105
77
|
|
|
106
78
|
case "widget:open-wallet":
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
79
|
+
openNativeWallet().then((success) => {
|
|
80
|
+
sendMessageToWidget({
|
|
81
|
+
type: "native:wallet-opened",
|
|
82
|
+
payload: { success },
|
|
83
|
+
});
|
|
84
|
+
});
|
|
112
85
|
break;
|
|
113
86
|
|
|
114
87
|
case "widget:close":
|
|
@@ -116,111 +89,27 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
116
89
|
break;
|
|
117
90
|
|
|
118
91
|
case "widget:transaction-requested":
|
|
119
|
-
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case "widget:token-expired":
|
|
123
|
-
onError?.("TOKEN_EXPIRED", "Access token has expired");
|
|
124
|
-
break;
|
|
125
|
-
|
|
126
|
-
case "widget:copy-to-clipboard":
|
|
127
|
-
handleCopyToClipboard(message as CopyToClipboardMessage);
|
|
92
|
+
onTransactionRequested(message.payload);
|
|
128
93
|
break;
|
|
129
94
|
}
|
|
130
95
|
} catch {
|
|
131
|
-
|
|
96
|
+
// Ignore parse errors
|
|
132
97
|
}
|
|
133
98
|
};
|
|
134
99
|
|
|
135
|
-
const handleReady = () => {
|
|
136
|
-
// Send platform info to widget
|
|
137
|
-
sendMessageToWidget({
|
|
138
|
-
type: "native:platform-info",
|
|
139
|
-
payload: {
|
|
140
|
-
platform: Platform.OS,
|
|
141
|
-
walletAvailable,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const handleOpenWallet = async () => {
|
|
147
|
-
const success = await openNativeWallet();
|
|
148
|
-
|
|
149
|
-
// Notify widget of result
|
|
150
|
-
sendMessageToWidget({
|
|
151
|
-
type: "native:wallet-opened",
|
|
152
|
-
payload: { success },
|
|
153
|
-
});
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const handleError = (message: WidgetMessage) => {
|
|
157
|
-
if (message.type !== "widget:error") return;
|
|
158
|
-
|
|
159
|
-
const { code, message: errorMessage } = message.payload;
|
|
160
|
-
onError?.(code, errorMessage);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const handleTransactionRequested = (message: WidgetMessage) => {
|
|
164
|
-
if (message.type !== "widget:transaction-requested") return;
|
|
165
|
-
|
|
166
|
-
const { token, cryptoAmount, depositAddress, depositAddressTag } =
|
|
167
|
-
message.payload;
|
|
168
|
-
|
|
169
|
-
onTransactionRequested(
|
|
170
|
-
token,
|
|
171
|
-
cryptoAmount,
|
|
172
|
-
depositAddress,
|
|
173
|
-
depositAddressTag
|
|
174
|
-
);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Handle clipboard copy request from widget
|
|
179
|
-
*/
|
|
180
|
-
const handleCopyToClipboard = async (message: CopyToClipboardMessage) => {
|
|
181
|
-
const { text, label } = message.payload;
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
await Clipboard.setStringAsync(text);
|
|
185
|
-
sendMessageToWidget({
|
|
186
|
-
type: "native:clipboard-result",
|
|
187
|
-
payload: { success: true, label },
|
|
188
|
-
});
|
|
189
|
-
} catch {
|
|
190
|
-
sendMessageToWidget({
|
|
191
|
-
type: "native:clipboard-result",
|
|
192
|
-
payload: {
|
|
193
|
-
success: false,
|
|
194
|
-
label,
|
|
195
|
-
error: "Clipboard operation failed",
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Send message to the web widget
|
|
203
|
-
*/
|
|
204
100
|
const sendMessageToWidget = (message: unknown) => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
`;
|
|
209
|
-
|
|
210
|
-
webViewRef.current?.injectJavaScript(js);
|
|
101
|
+
webViewRef.current?.injectJavaScript(
|
|
102
|
+
`window.postMessage(${JSON.stringify(message)}, '*'); true;`
|
|
103
|
+
);
|
|
211
104
|
};
|
|
212
105
|
|
|
213
|
-
/**
|
|
214
|
-
* Build the widget URL with query parameters
|
|
215
|
-
*/
|
|
216
106
|
const finalWidgetUrl = useMemo(() => {
|
|
217
107
|
const baseUrl = getWidgetUrl(accessToken);
|
|
218
108
|
const params = new URLSearchParams({
|
|
219
109
|
token: stripTokenPrefix(accessToken),
|
|
220
110
|
platform: Platform.OS,
|
|
221
|
-
userWalletAddress
|
|
111
|
+
userWalletAddress,
|
|
222
112
|
});
|
|
223
|
-
|
|
224
113
|
return `${baseUrl}?${params.toString()}`;
|
|
225
114
|
}, [accessToken, userWalletAddress]);
|
|
226
115
|
|
|
@@ -237,36 +126,24 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
237
126
|
setIsLoading(false);
|
|
238
127
|
}
|
|
239
128
|
}}
|
|
240
|
-
onError={() => {
|
|
241
|
-
setIsLoading(false);
|
|
242
|
-
onError?.("WEBVIEW_ERROR", "Failed to load widget");
|
|
243
|
-
}}
|
|
244
|
-
// Allow external URLs to open in native apps
|
|
245
129
|
onShouldStartLoadWithRequest={(request) => {
|
|
246
130
|
const { url } = request;
|
|
247
|
-
|
|
248
|
-
// Allow wallet URLs to open in native apps
|
|
249
131
|
if (
|
|
250
|
-
url.startsWith("shoebox://") ||
|
|
251
|
-
url.includes("pay.google.com") ||
|
|
132
|
+
url.startsWith("shoebox://") ||
|
|
133
|
+
url.includes("pay.google.com") ||
|
|
252
134
|
url.includes("wallet.google.com") ||
|
|
253
135
|
url.includes("wallet.apple.com")
|
|
254
136
|
) {
|
|
255
137
|
Linking.openURL(url).catch(() => {});
|
|
256
|
-
return false;
|
|
138
|
+
return false;
|
|
257
139
|
}
|
|
258
|
-
|
|
259
|
-
// Allow normal widget navigation
|
|
260
140
|
return true;
|
|
261
141
|
}}
|
|
262
|
-
// Security settings
|
|
263
142
|
javaScriptEnabled={true}
|
|
264
143
|
domStorageEnabled={true}
|
|
265
144
|
allowsInlineMediaPlayback={true}
|
|
266
|
-
// iOS specific
|
|
267
145
|
bounces={false}
|
|
268
146
|
allowsBackForwardNavigationGestures={true}
|
|
269
|
-
// Android specific
|
|
270
147
|
mixedContentMode="always"
|
|
271
148
|
/>
|
|
272
149
|
{isLoading && (
|
|
@@ -280,12 +157,8 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
280
157
|
);
|
|
281
158
|
|
|
282
159
|
const styles = StyleSheet.create({
|
|
283
|
-
container: {
|
|
284
|
-
|
|
285
|
-
},
|
|
286
|
-
webview: {
|
|
287
|
-
flex: 1,
|
|
288
|
-
},
|
|
160
|
+
container: { flex: 1 },
|
|
161
|
+
webview: { flex: 1 },
|
|
289
162
|
loadingOverlay: {
|
|
290
163
|
...StyleSheet.absoluteFillObject,
|
|
291
164
|
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Widget SDK - Public API
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { WidgetSDK } from "./WidgetSDK";
|
|
6
|
-
export type { WidgetSDKRef } from "./WidgetSDK";
|
|
7
|
-
|
|
8
1
|
export type {
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
EvmTransactionData,
|
|
3
|
+
EvmTransactionRequest,
|
|
4
|
+
SolanaTransactionRequest,
|
|
5
|
+
TransactionRequest,
|
|
6
|
+
TransactionTokenMetadata,
|
|
11
7
|
WidgetSDKConfig,
|
|
12
8
|
} from "./types";
|
|
9
|
+
export { WidgetSDK } from "./WidgetSDK";
|
|
10
|
+
export type { WidgetSDKRef } from "./WidgetSDK";
|
package/src/types.ts
CHANGED
|
@@ -1,210 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Messages that can be sent between the web widget and React Native
|
|
2
|
+
* Token metadata included with transaction requests
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Token information for transaction confirmation
|
|
8
|
-
* Minimal fields needed to display a send transaction confirmation page
|
|
9
|
-
*/
|
|
10
|
-
export interface DepositToken {
|
|
4
|
+
export interface TransactionTokenMetadata {
|
|
11
5
|
symbol: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
chainId: number;
|
|
6
|
+
amount: string;
|
|
7
|
+
decimals: number;
|
|
15
8
|
}
|
|
16
9
|
|
|
17
|
-
export type WidgetMessageType =
|
|
18
|
-
| "widget:ready"
|
|
19
|
-
| "widget:open-wallet"
|
|
20
|
-
| "widget:error"
|
|
21
|
-
| "widget:close"
|
|
22
|
-
| "widget:transaction-requested"
|
|
23
|
-
| "widget:token-expired"
|
|
24
|
-
| "widget:request-card-details-session"
|
|
25
|
-
| "widget:copy-to-clipboard";
|
|
26
|
-
|
|
27
10
|
/**
|
|
28
|
-
*
|
|
29
|
-
* Messages sent from the native app to the web widget
|
|
11
|
+
* EVM transaction data
|
|
30
12
|
*/
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
| "native:navigate-back"
|
|
36
|
-
| "native:card-details-session"
|
|
37
|
-
| "native:biometric-failed"
|
|
38
|
-
| "native:clipboard-result";
|
|
39
|
-
|
|
40
|
-
export interface NativeBackPressedMessage {
|
|
41
|
-
type: "native:back-pressed";
|
|
42
|
-
timestamp: number;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface NativeNavigateBackMessage {
|
|
46
|
-
type: "native:navigate-back";
|
|
47
|
-
timestamp: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface NativePlatformInfoMessage {
|
|
51
|
-
type: "native:platform-info";
|
|
52
|
-
payload: {
|
|
53
|
-
platform: "ios" | "android";
|
|
54
|
-
walletAvailable: boolean;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface NativeWalletOpenedMessage {
|
|
59
|
-
type: "native:wallet-opened";
|
|
60
|
-
payload: {
|
|
61
|
-
success: boolean;
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface NativeCardDetailsSessionMessage {
|
|
66
|
-
type: "native:card-details-session";
|
|
67
|
-
payload: {
|
|
68
|
-
sessionId: string; // RSA-encrypted session ID (Base64)
|
|
69
|
-
secretKey: string; // Secret key in hex format (32 chars)
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface NativeBiometricFailedMessage {
|
|
74
|
-
type: "native:biometric-failed";
|
|
75
|
-
payload: {
|
|
76
|
-
reason: "cancelled" | "failed" | "not_available" | "not_enrolled";
|
|
77
|
-
message?: string;
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface NativeClipboardResultMessage {
|
|
82
|
-
type: "native:clipboard-result";
|
|
83
|
-
payload: {
|
|
84
|
-
success: boolean;
|
|
85
|
-
label?: string; // Echo back the label for correlation
|
|
86
|
-
error?: string;
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export type NativeMessage =
|
|
91
|
-
| NativeBackPressedMessage
|
|
92
|
-
| NativeNavigateBackMessage
|
|
93
|
-
| NativePlatformInfoMessage
|
|
94
|
-
| NativeWalletOpenedMessage
|
|
95
|
-
| NativeCardDetailsSessionMessage
|
|
96
|
-
| NativeBiometricFailedMessage
|
|
97
|
-
| NativeClipboardResultMessage;
|
|
98
|
-
|
|
99
|
-
export interface BaseWidgetMessage {
|
|
100
|
-
type: WidgetMessageType;
|
|
101
|
-
timestamp: number;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface WidgetReadyMessage extends BaseWidgetMessage {
|
|
105
|
-
type: "widget:ready";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface OpenWalletMessage extends BaseWidgetMessage {
|
|
109
|
-
type: "widget:open-wallet";
|
|
110
|
-
payload: {
|
|
111
|
-
platform: "ios" | "android";
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface WidgetErrorMessage extends BaseWidgetMessage {
|
|
116
|
-
type: "widget:error";
|
|
117
|
-
payload: {
|
|
118
|
-
code: string;
|
|
119
|
-
message: string;
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export interface WidgetCloseMessage extends BaseWidgetMessage {
|
|
124
|
-
type: "widget:close";
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface TransactionRequestedMessage extends BaseWidgetMessage {
|
|
128
|
-
type: "widget:transaction-requested";
|
|
129
|
-
payload: {
|
|
130
|
-
token: DepositToken;
|
|
131
|
-
cryptoAmount: string;
|
|
132
|
-
depositAddress: string;
|
|
133
|
-
depositAddressTag: string | null;
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface TokenExpiredMessage extends BaseWidgetMessage {
|
|
138
|
-
type: "widget:token-expired";
|
|
139
|
-
payload?: {
|
|
140
|
-
reason?: string;
|
|
141
|
-
};
|
|
13
|
+
export interface EvmTransactionData {
|
|
14
|
+
to: string;
|
|
15
|
+
data: string;
|
|
16
|
+
value: string;
|
|
142
17
|
}
|
|
143
18
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
19
|
+
/**
|
|
20
|
+
* EVM transaction request payload
|
|
21
|
+
*/
|
|
22
|
+
export interface EvmTransactionRequest {
|
|
23
|
+
type: "evm";
|
|
24
|
+
chainId: number;
|
|
25
|
+
transaction: EvmTransactionData;
|
|
26
|
+
tokenMetadata: TransactionTokenMetadata;
|
|
149
27
|
}
|
|
150
28
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Solana transaction request payload
|
|
31
|
+
*/
|
|
32
|
+
export interface SolanaTransactionRequest {
|
|
33
|
+
type: "solana";
|
|
34
|
+
transaction: string;
|
|
35
|
+
tokenMetadata: TransactionTokenMetadata;
|
|
157
36
|
}
|
|
158
37
|
|
|
159
|
-
export type WidgetMessage =
|
|
160
|
-
| WidgetReadyMessage
|
|
161
|
-
| OpenWalletMessage
|
|
162
|
-
| WidgetErrorMessage
|
|
163
|
-
| WidgetCloseMessage
|
|
164
|
-
| TransactionRequestedMessage
|
|
165
|
-
| TokenExpiredMessage
|
|
166
|
-
| RequestCardDetailsSessionMessage
|
|
167
|
-
| CopyToClipboardMessage;
|
|
168
|
-
|
|
169
38
|
/**
|
|
170
|
-
*
|
|
171
|
-
* - TOKEN_EXPIRED: The access token has expired
|
|
172
|
-
* - PARSE_ERROR: Failed to parse a message from the widget
|
|
173
|
-
* - WEBVIEW_ERROR: The WebView failed to load
|
|
39
|
+
* Transaction request payload
|
|
174
40
|
*/
|
|
175
|
-
export type
|
|
41
|
+
export type TransactionRequest =
|
|
42
|
+
| EvmTransactionRequest
|
|
43
|
+
| SolanaTransactionRequest;
|
|
176
44
|
|
|
177
45
|
/**
|
|
178
46
|
* SDK Configuration
|
|
179
47
|
*/
|
|
180
48
|
export interface WidgetSDKConfig {
|
|
181
|
-
accessToken: string;
|
|
182
|
-
userWalletAddress: string;
|
|
183
|
-
/**
|
|
184
|
-
* Called when an error occurs
|
|
185
|
-
*/
|
|
186
|
-
onError?: (code: SDKErrorCode | string, message: string) => void;
|
|
187
|
-
/**
|
|
188
|
-
* Called when the widget requests to close
|
|
189
|
-
*/
|
|
49
|
+
accessToken: string;
|
|
50
|
+
userWalletAddress: string;
|
|
190
51
|
onClose?: () => void;
|
|
191
|
-
|
|
192
|
-
* Called when a crypto transaction is requested
|
|
193
|
-
*/
|
|
194
|
-
onTransactionRequested: (
|
|
195
|
-
token: DepositToken,
|
|
196
|
-
cryptoAmount: string,
|
|
197
|
-
depositAddress: string,
|
|
198
|
-
depositAddressTag: string | null
|
|
199
|
-
) => void;
|
|
52
|
+
onTransactionRequested: (transaction: TransactionRequest) => void;
|
|
200
53
|
}
|
|
201
54
|
|
|
202
55
|
/**
|
|
203
|
-
*
|
|
56
|
+
* Internal: Messages from widget
|
|
57
|
+
*/
|
|
58
|
+
export type WidgetMessage =
|
|
59
|
+
| { type: "widget:ready" }
|
|
60
|
+
| { type: "widget:open-wallet" }
|
|
61
|
+
| { type: "widget:close" }
|
|
62
|
+
| { type: "widget:transaction-requested"; payload: TransactionRequest };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Internal: Wallet URLs
|
|
204
66
|
*/
|
|
205
67
|
export const WALLET_URLS = {
|
|
206
68
|
ios: {
|
|
207
|
-
passkit: "shoebox://",
|
|
69
|
+
passkit: "shoebox://",
|
|
208
70
|
fallback: "https://wallet.apple.com",
|
|
209
71
|
},
|
|
210
72
|
android: {
|
|
@@ -212,30 +74,3 @@ export const WALLET_URLS = {
|
|
|
212
74
|
fallback: "https://wallet.google.com",
|
|
213
75
|
},
|
|
214
76
|
} as const;
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Message type constants that clients can use
|
|
218
|
-
* @example
|
|
219
|
-
* if (message.type === MessageTypes.READY) {
|
|
220
|
-
* // Handle ready
|
|
221
|
-
* }
|
|
222
|
-
*/
|
|
223
|
-
export const MessageTypes = {
|
|
224
|
-
// Widget → Native
|
|
225
|
-
READY: "widget:ready",
|
|
226
|
-
OPEN_WALLET: "widget:open-wallet",
|
|
227
|
-
ERROR: "widget:error",
|
|
228
|
-
CLOSE: "widget:close",
|
|
229
|
-
TRANSACTION_REQUESTED: "widget:transaction-requested",
|
|
230
|
-
TOKEN_EXPIRED: "widget:token-expired",
|
|
231
|
-
REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session",
|
|
232
|
-
COPY_TO_CLIPBOARD: "widget:copy-to-clipboard",
|
|
233
|
-
// Native → Widget
|
|
234
|
-
PLATFORM_INFO: "native:platform-info",
|
|
235
|
-
WALLET_OPENED: "native:wallet-opened",
|
|
236
|
-
BACK_PRESSED: "native:back-pressed",
|
|
237
|
-
NAVIGATE_BACK: "native:navigate-back",
|
|
238
|
-
CARD_DETAILS_SESSION: "native:card-details-session",
|
|
239
|
-
BIOMETRIC_FAILED: "native:biometric-failed",
|
|
240
|
-
CLIPBOARD_RESULT: "native:clipboard-result",
|
|
241
|
-
} as const;
|
package/src/walletUtils.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Handles opening Apple Wallet (iOS) and Google Wallet (Android)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as IntentLauncher from "expo-intent-launcher";
|
|
7
6
|
import { Alert, Linking, Platform } from "react-native";
|
|
8
7
|
import { WALLET_URLS } from "./types";
|
|
9
8
|
|
|
@@ -84,7 +83,7 @@ const openAppleWallet = async (): Promise<boolean> => {
|
|
|
84
83
|
|
|
85
84
|
/**
|
|
86
85
|
* Opens Google Wallet on Android
|
|
87
|
-
*
|
|
86
|
+
* Uses Linking API to launch the app or fall back to Play Store
|
|
88
87
|
*/
|
|
89
88
|
const openGoogleWallet = async (): Promise<boolean> => {
|
|
90
89
|
const GOOGLE_WALLET_PACKAGE = "com.google.android.apps.walletnfcrel";
|
|
@@ -92,8 +91,7 @@ const openGoogleWallet = async (): Promise<boolean> => {
|
|
|
92
91
|
try {
|
|
93
92
|
console.log("Attempting to open Google Wallet...");
|
|
94
93
|
|
|
95
|
-
//
|
|
96
|
-
// This is the most reliable way to open an installed app without knowing the exact activity
|
|
94
|
+
// Try using the market:// scheme to launch the app if it's installed
|
|
97
95
|
try {
|
|
98
96
|
const canOpen = await Linking.canOpenURL(`market://launch?id=${GOOGLE_WALLET_PACKAGE}`);
|
|
99
97
|
console.log("Can open market launch URL:", canOpen);
|
|
@@ -104,47 +102,34 @@ const openGoogleWallet = async (): Promise<boolean> => {
|
|
|
104
102
|
return true;
|
|
105
103
|
}
|
|
106
104
|
} catch (marketLaunchError) {
|
|
107
|
-
console.log("Market launch failed
|
|
105
|
+
console.log("Market launch failed:", marketLaunchError);
|
|
108
106
|
}
|
|
109
107
|
|
|
110
|
-
//
|
|
111
|
-
|
|
108
|
+
// App is not installed or market:// not available, open Play Store
|
|
109
|
+
console.log("Opening Play Store to install Google Wallet");
|
|
112
110
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.log("
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
console.log("Market URL failed, trying web Play Store:", marketError);
|
|
136
|
-
|
|
137
|
-
// Fallback to web Play Store
|
|
138
|
-
await Linking.openURL(
|
|
139
|
-
`https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
Alert.alert(
|
|
143
|
-
"Google Wallet",
|
|
144
|
-
"Google Wallet is not installed. Opening Play Store to install the app."
|
|
145
|
-
);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
111
|
+
// Try native Play Store app first
|
|
112
|
+
const playStoreUrl = `market://details?id=${GOOGLE_WALLET_PACKAGE}`;
|
|
113
|
+
await Linking.openURL(playStoreUrl);
|
|
114
|
+
|
|
115
|
+
Alert.alert(
|
|
116
|
+
"Google Wallet",
|
|
117
|
+
"Google Wallet is not installed. Opening Play Store to install the app."
|
|
118
|
+
);
|
|
119
|
+
return false;
|
|
120
|
+
} catch (marketError) {
|
|
121
|
+
console.log("Market URL failed, trying web Play Store:", marketError);
|
|
122
|
+
|
|
123
|
+
// Fallback to web Play Store
|
|
124
|
+
await Linking.openURL(
|
|
125
|
+
`https://play.google.com/store/apps/details?id=${GOOGLE_WALLET_PACKAGE}`
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
Alert.alert(
|
|
129
|
+
"Google Wallet",
|
|
130
|
+
"Google Wallet is not installed. Opening Play Store to install the app."
|
|
131
|
+
);
|
|
132
|
+
return false;
|
|
148
133
|
}
|
|
149
134
|
} catch (error) {
|
|
150
135
|
console.error("Failed to open Google Wallet:", error);
|