@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.
- package/README.md +568 -0
- package/dist/WidgetSDK.d.ts +11 -0
- package/dist/WidgetSDK.d.ts.map +1 -0
- package/dist/WidgetSDK.js +249 -0
- package/dist/WidgetSDK.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +43 -0
- package/dist/types.js.map +1 -0
- package/dist/walletUtils.d.ts +14 -0
- package/dist/walletUtils.d.ts.map +1 -0
- package/dist/walletUtils.js +192 -0
- package/dist/walletUtils.js.map +1 -0
- package/package.json +62 -0
- package/src/WidgetSDK.tsx +292 -0
- package/src/config.ts +22 -0
- package/src/index.ts +40 -0
- package/src/types.ts +179 -0
- package/src/walletUtils.ts +170 -0
package/README.md
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
# Widget SDK
|
|
2
|
+
|
|
3
|
+
A React Native/Expo SDK that makes it easy to embed your card issuance widget into mobile apps. Just drop in the component and handle callbacks - no WebView complexity needed.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @your-company/widget-sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @your-company/widget-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
In this monorepo:
|
|
14
|
+
```typescript
|
|
15
|
+
import { WidgetSDK } from '@sdk';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { WidgetSDK } from '@sdk';
|
|
22
|
+
import { View, Alert } from 'react-native';
|
|
23
|
+
|
|
24
|
+
function MyApp() {
|
|
25
|
+
const widgetUrl = 'https://your-widget.com?token=xyz'; // Your authenticated widget URL
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<View style={{ flex: 1 }}>
|
|
29
|
+
<WidgetSDK
|
|
30
|
+
apiKey="your-api-key"
|
|
31
|
+
environment="production"
|
|
32
|
+
widgetUrl={widgetUrl}
|
|
33
|
+
onReady={() => {
|
|
34
|
+
console.log('Widget loaded successfully');
|
|
35
|
+
}}
|
|
36
|
+
onCardCreated={(cardId, cardType, last4) => {
|
|
37
|
+
Alert.alert('Success', `Card ${last4} created!`);
|
|
38
|
+
// Save card info, navigate to card details, etc.
|
|
39
|
+
}}
|
|
40
|
+
onError={(code, message) => {
|
|
41
|
+
Alert.alert('Error', message);
|
|
42
|
+
// Log to error tracking, show user-friendly message, etc.
|
|
43
|
+
}}
|
|
44
|
+
onClose={() => {
|
|
45
|
+
console.log('User closed the widget');
|
|
46
|
+
// Navigate back, clean up state, etc.
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
</View>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Props
|
|
55
|
+
|
|
56
|
+
### Required Props
|
|
57
|
+
|
|
58
|
+
#### `apiKey: string`
|
|
59
|
+
Your API key for authentication.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
<WidgetSDK apiKey="your-api-key" />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### `environment: 'sandbox' | 'production'`
|
|
66
|
+
The environment to use.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
<WidgetSDK environment="production" />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### `widgetUrl: string`
|
|
73
|
+
The URL of your widget (typically includes a JWT token for authentication).
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
<WidgetSDK widgetUrl="https://widget.yourapp.com?token=jwt_token_here" />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Optional Callback Props
|
|
80
|
+
|
|
81
|
+
#### `onReady?: () => void`
|
|
82
|
+
Called when the widget has finished loading and is ready for interaction.
|
|
83
|
+
|
|
84
|
+
**Use this to:**
|
|
85
|
+
- Hide loading indicators
|
|
86
|
+
- Track analytics events
|
|
87
|
+
- Initialize app state
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
<WidgetSDK
|
|
91
|
+
onReady={() => {
|
|
92
|
+
console.log('Widget is ready!');
|
|
93
|
+
analytics.track('widget_loaded');
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `onCardCreated?: (cardId: string, cardType: string, last4: string) => void`
|
|
99
|
+
Called when a card is successfully created in the widget.
|
|
100
|
+
|
|
101
|
+
**Parameters:**
|
|
102
|
+
- `cardId` - Unique identifier for the created card
|
|
103
|
+
- `cardType` - Type of card: `'virtual'` or `'physical'`
|
|
104
|
+
- `last4` - Last 4 digits of the card number
|
|
105
|
+
|
|
106
|
+
**Use this to:**
|
|
107
|
+
- Save card info to your app's state
|
|
108
|
+
- Navigate to card details screen
|
|
109
|
+
- Show success message to user
|
|
110
|
+
- Trigger analytics events
|
|
111
|
+
- Close the widget
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
<WidgetSDK
|
|
115
|
+
onCardCreated={(cardId, cardType, last4) => {
|
|
116
|
+
console.log(`Card created: ${cardId}, type: ${cardType}, ending in ${last4}`);
|
|
117
|
+
|
|
118
|
+
// Save to state
|
|
119
|
+
setUserCard({ id: cardId, type: cardType, last4 });
|
|
120
|
+
|
|
121
|
+
// Navigate to card details
|
|
122
|
+
navigation.navigate('CardDetails', { cardId });
|
|
123
|
+
|
|
124
|
+
// Track analytics
|
|
125
|
+
analytics.track('card_created', { cardType });
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `onError?: (code: string, message: string) => void`
|
|
131
|
+
Called when an error occurs in the widget.
|
|
132
|
+
|
|
133
|
+
**Parameters:**
|
|
134
|
+
- `code` - Error code (e.g., `'INVALID_CARD'`, `'NETWORK_ERROR'`)
|
|
135
|
+
- `message` - Human-readable error message
|
|
136
|
+
|
|
137
|
+
**Use this to:**
|
|
138
|
+
- Show error alerts to user
|
|
139
|
+
- Log errors to monitoring service
|
|
140
|
+
- Handle specific error cases
|
|
141
|
+
- Retry operations
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
<WidgetSDK
|
|
145
|
+
onError={(code, message) => {
|
|
146
|
+
console.error(`Widget error [${code}]: ${message}`);
|
|
147
|
+
|
|
148
|
+
// Show user-friendly error
|
|
149
|
+
Alert.alert('Error', message);
|
|
150
|
+
|
|
151
|
+
// Log to error tracking
|
|
152
|
+
Sentry.captureException(new Error(`Widget error: ${code} - ${message}`));
|
|
153
|
+
|
|
154
|
+
// Handle specific errors
|
|
155
|
+
if (code === 'SESSION_EXPIRED') {
|
|
156
|
+
// Refresh authentication and reload widget
|
|
157
|
+
refreshAuth();
|
|
158
|
+
}
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### `onClose?: () => void`
|
|
164
|
+
Called when the user requests to close the widget (via a close button in the widget).
|
|
165
|
+
|
|
166
|
+
**Use this to:**
|
|
167
|
+
- Navigate back to previous screen
|
|
168
|
+
- Clean up state
|
|
169
|
+
- Track analytics events
|
|
170
|
+
- Hide the widget
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
<WidgetSDK
|
|
174
|
+
onClose={() => {
|
|
175
|
+
console.log('User closed widget');
|
|
176
|
+
|
|
177
|
+
// Navigate back
|
|
178
|
+
navigation.goBack();
|
|
179
|
+
|
|
180
|
+
// Or hide widget
|
|
181
|
+
setShowWidget(false);
|
|
182
|
+
|
|
183
|
+
// Track analytics
|
|
184
|
+
analytics.track('widget_closed');
|
|
185
|
+
}}
|
|
186
|
+
/>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Complete Example
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import React, { useState } from 'react';
|
|
193
|
+
import { View, Button, StyleSheet, Alert, ActivityIndicator } from 'react-native';
|
|
194
|
+
import { WidgetSDK } from '@sdk';
|
|
195
|
+
|
|
196
|
+
export default function CardIssuanceScreen() {
|
|
197
|
+
const [showWidget, setShowWidget] = useState(false);
|
|
198
|
+
const [widgetUrl, setWidgetUrl] = useState('');
|
|
199
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
200
|
+
|
|
201
|
+
const launchWidget = async () => {
|
|
202
|
+
try {
|
|
203
|
+
setIsLoading(true);
|
|
204
|
+
|
|
205
|
+
// Get authenticated widget URL from your backend
|
|
206
|
+
const response = await fetch('https://your-api.com/widget/token', {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
headers: { 'Authorization': 'Bearer your-token' }
|
|
209
|
+
});
|
|
210
|
+
const { url } = await response.json();
|
|
211
|
+
|
|
212
|
+
setWidgetUrl(url);
|
|
213
|
+
setShowWidget(true);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
Alert.alert('Error', 'Failed to load widget');
|
|
216
|
+
} finally {
|
|
217
|
+
setIsLoading(false);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (showWidget && widgetUrl) {
|
|
222
|
+
return (
|
|
223
|
+
<View style={styles.container}>
|
|
224
|
+
<WidgetSDK
|
|
225
|
+
apiKey="your-api-key"
|
|
226
|
+
environment="production"
|
|
227
|
+
widgetUrl={widgetUrl}
|
|
228
|
+
onReady={() => {
|
|
229
|
+
console.log('Widget ready');
|
|
230
|
+
}}
|
|
231
|
+
onCardCreated={(cardId, cardType, last4) => {
|
|
232
|
+
Alert.alert(
|
|
233
|
+
'Card Created!',
|
|
234
|
+
`Your ${cardType} card ending in ${last4} is ready.`,
|
|
235
|
+
[
|
|
236
|
+
{
|
|
237
|
+
text: 'View Card',
|
|
238
|
+
onPress: () => {
|
|
239
|
+
setShowWidget(false);
|
|
240
|
+
// Navigate to card details
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
);
|
|
245
|
+
}}
|
|
246
|
+
onError={(code, message) => {
|
|
247
|
+
Alert.alert('Error', message);
|
|
248
|
+
}}
|
|
249
|
+
onClose={() => {
|
|
250
|
+
setShowWidget(false);
|
|
251
|
+
}}
|
|
252
|
+
/>
|
|
253
|
+
</View>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<View style={styles.container}>
|
|
259
|
+
<Button
|
|
260
|
+
title={isLoading ? 'Loading...' : 'Create Card'}
|
|
261
|
+
onPress={launchWidget}
|
|
262
|
+
disabled={isLoading}
|
|
263
|
+
/>
|
|
264
|
+
{isLoading && <ActivityIndicator style={{ marginTop: 20 }} />}
|
|
265
|
+
</View>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const styles = StyleSheet.create({
|
|
270
|
+
container: {
|
|
271
|
+
flex: 1,
|
|
272
|
+
justifyContent: 'center',
|
|
273
|
+
padding: 20,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## TypeScript Support
|
|
279
|
+
|
|
280
|
+
The SDK is fully typed. Import types as needed:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import type { WidgetSDKConfig } from '@sdk';
|
|
284
|
+
|
|
285
|
+
const config: WidgetSDKConfig = {
|
|
286
|
+
apiKey: 'your-key',
|
|
287
|
+
environment: 'production',
|
|
288
|
+
widgetUrl: 'https://...',
|
|
289
|
+
onCardCreated: (cardId, cardType, last4) => {
|
|
290
|
+
// Fully typed parameters
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Message Type Constants
|
|
296
|
+
|
|
297
|
+
For advanced use cases where you need to handle messages directly:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { MessageTypes } from '@sdk';
|
|
301
|
+
|
|
302
|
+
// Use constants instead of strings
|
|
303
|
+
if (message.type === MessageTypes.CARD_CREATED) {
|
|
304
|
+
// Handle card creation
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Available constants:
|
|
309
|
+
|
|
310
|
+
**Widget → Native:**
|
|
311
|
+
- `MessageTypes.READY` - Widget is ready
|
|
312
|
+
- `MessageTypes.CARD_CREATED` - Card was created
|
|
313
|
+
- `MessageTypes.ERROR` - An error occurred
|
|
314
|
+
- `MessageTypes.CLOSE` - User closed widget
|
|
315
|
+
- `MessageTypes.OPEN_WALLET` - User wants to add card to wallet
|
|
316
|
+
|
|
317
|
+
**Native → Widget:**
|
|
318
|
+
- `MessageTypes.PLATFORM_INFO` - Platform info sent to widget
|
|
319
|
+
- `MessageTypes.WALLET_OPENED` - Wallet open result
|
|
320
|
+
- `MessageTypes.BACK_PRESSED` - User pressed back button/gesture
|
|
321
|
+
- `MessageTypes.NAVIGATE_BACK` - Programmatic back navigation
|
|
322
|
+
|
|
323
|
+
## Platform Support
|
|
324
|
+
|
|
325
|
+
| Platform | Supported | Notes |
|
|
326
|
+
|----------|-----------|-------|
|
|
327
|
+
| iOS | ✅ | Includes Apple Wallet integration |
|
|
328
|
+
| Android | ✅ | Includes Google Pay integration |
|
|
329
|
+
| Web | ❌ | React Native only |
|
|
330
|
+
|
|
331
|
+
## Native Wallet Integration
|
|
332
|
+
|
|
333
|
+
The SDK automatically handles adding cards to Apple Wallet (iOS) and Google Pay (Android) when requested by your widget.
|
|
334
|
+
|
|
335
|
+
**Your widget can trigger wallet addition:**
|
|
336
|
+
```javascript
|
|
337
|
+
// In your web widget
|
|
338
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
339
|
+
type: 'widget:open-wallet',
|
|
340
|
+
timestamp: Date.now(),
|
|
341
|
+
payload: { platform: 'ios' } // or 'android'
|
|
342
|
+
}));
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**The SDK handles it automatically** - no callback needed from your app!
|
|
346
|
+
|
|
347
|
+
You can also manually open the wallet from your React Native app:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { openNativeWallet, isWalletAvailable } from '@sdk';
|
|
351
|
+
|
|
352
|
+
// Check if wallet is available
|
|
353
|
+
const available = await isWalletAvailable();
|
|
354
|
+
|
|
355
|
+
if (available) {
|
|
356
|
+
// Open native wallet
|
|
357
|
+
await openNativeWallet();
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Back Navigation
|
|
362
|
+
|
|
363
|
+
The SDK handles back navigation gestures and forwards them to your web widget, allowing the widget to control its own internal navigation.
|
|
364
|
+
|
|
365
|
+
### How It Works
|
|
366
|
+
|
|
367
|
+
1. **Android**: Hardware back button press is intercepted and forwarded to the widget
|
|
368
|
+
2. **iOS**: Swipe-back gestures are enabled via `allowsBackForwardNavigationGestures`
|
|
369
|
+
|
|
370
|
+
The widget receives a `native:back-pressed` message and decides whether to:
|
|
371
|
+
- Navigate back internally (if there's navigation history)
|
|
372
|
+
- Close the widget (if on the first screen)
|
|
373
|
+
|
|
374
|
+
### Programmatic Back Navigation
|
|
375
|
+
|
|
376
|
+
You can programmatically trigger back navigation using a ref:
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { useRef } from 'react';
|
|
380
|
+
import { WidgetSDK, WidgetSDKRef } from '@sdk';
|
|
381
|
+
|
|
382
|
+
function MyScreen() {
|
|
383
|
+
const widgetRef = useRef<WidgetSDKRef>(null);
|
|
384
|
+
|
|
385
|
+
const handleTimeout = () => {
|
|
386
|
+
// Programmatically navigate widget back
|
|
387
|
+
widgetRef.current?.navigateBack();
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<WidgetSDK
|
|
392
|
+
ref={widgetRef}
|
|
393
|
+
widgetUrl={widgetUrl}
|
|
394
|
+
accessToken={token}
|
|
395
|
+
onClose={() => setShowWidget(false)}
|
|
396
|
+
/>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Web Widget Implementation
|
|
402
|
+
|
|
403
|
+
Your web widget must handle these messages from the native app:
|
|
404
|
+
|
|
405
|
+
```javascript
|
|
406
|
+
// In your web widget
|
|
407
|
+
window.addEventListener('message', (event) => {
|
|
408
|
+
try {
|
|
409
|
+
const message = JSON.parse(event.data);
|
|
410
|
+
|
|
411
|
+
if (message.type === 'native:back-pressed' || message.type === 'native:navigate-back') {
|
|
412
|
+
// Check if on first screen (login, etc.)
|
|
413
|
+
if (isOnFirstScreen()) {
|
|
414
|
+
// Close widget - return to native app
|
|
415
|
+
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
416
|
+
type: 'widget:close',
|
|
417
|
+
timestamp: Date.now()
|
|
418
|
+
}));
|
|
419
|
+
} else {
|
|
420
|
+
// Navigate back internally
|
|
421
|
+
window.history.back();
|
|
422
|
+
// OR: router.back() if using React Router
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} catch (e) {
|
|
426
|
+
// Ignore non-JSON messages
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Message Types
|
|
432
|
+
|
|
433
|
+
| Message | Direction | Description |
|
|
434
|
+
|---------|-----------|-------------|
|
|
435
|
+
| `native:back-pressed` | Native → Widget | User pressed back button/gesture |
|
|
436
|
+
| `native:navigate-back` | Native → Widget | Programmatic back navigation request |
|
|
437
|
+
| `widget:close` | Widget → Native | Widget requests to close |
|
|
438
|
+
|
|
439
|
+
## Dependencies
|
|
440
|
+
|
|
441
|
+
### Required Peer Dependencies
|
|
442
|
+
|
|
443
|
+
```json
|
|
444
|
+
{
|
|
445
|
+
"react": ">=18.0.0",
|
|
446
|
+
"react-native": ">=0.70.0",
|
|
447
|
+
"react-native-webview": ">=13.0.0"
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Optional Dependencies
|
|
452
|
+
|
|
453
|
+
For full wallet integration:
|
|
454
|
+
- `expo-intent-launcher` - Android wallet integration
|
|
455
|
+
- `expo-linking` - Deep link handling
|
|
456
|
+
|
|
457
|
+
Install with:
|
|
458
|
+
```bash
|
|
459
|
+
expo install expo-intent-launcher expo-linking
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Common Use Cases
|
|
463
|
+
|
|
464
|
+
### Show Widget After Authentication
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
468
|
+
const [widgetUrl, setWidgetUrl] = useState('');
|
|
469
|
+
|
|
470
|
+
useEffect(() => {
|
|
471
|
+
if (isAuthenticated) {
|
|
472
|
+
fetchWidgetUrl().then(setWidgetUrl);
|
|
473
|
+
}
|
|
474
|
+
}, [isAuthenticated]);
|
|
475
|
+
|
|
476
|
+
if (!isAuthenticated) {
|
|
477
|
+
return <LoginScreen onLogin={() => setIsAuthenticated(true)} />;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return <WidgetSDK widgetUrl={widgetUrl} {...props} />;
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Close Widget and Navigate
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
<WidgetSDK
|
|
487
|
+
onCardCreated={(cardId) => {
|
|
488
|
+
// Navigate to card details immediately
|
|
489
|
+
navigation.replace('CardDetails', { cardId });
|
|
490
|
+
}}
|
|
491
|
+
onClose={() => {
|
|
492
|
+
// Go back to previous screen
|
|
493
|
+
navigation.goBack();
|
|
494
|
+
}}
|
|
495
|
+
/>
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Track Analytics
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
<WidgetSDK
|
|
502
|
+
onReady={() => {
|
|
503
|
+
analytics.track('widget_viewed');
|
|
504
|
+
}}
|
|
505
|
+
onCardCreated={(cardId, cardType) => {
|
|
506
|
+
analytics.track('card_created', {
|
|
507
|
+
cardId,
|
|
508
|
+
cardType,
|
|
509
|
+
source: 'mobile_app'
|
|
510
|
+
});
|
|
511
|
+
}}
|
|
512
|
+
onError={(code, message) => {
|
|
513
|
+
analytics.track('widget_error', { code, message });
|
|
514
|
+
}}
|
|
515
|
+
/>
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Show Loading State
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
const [isWidgetReady, setIsWidgetReady] = useState(false);
|
|
522
|
+
|
|
523
|
+
<View style={{ flex: 1 }}>
|
|
524
|
+
{!isWidgetReady && (
|
|
525
|
+
<View style={styles.loadingOverlay}>
|
|
526
|
+
<ActivityIndicator size="large" />
|
|
527
|
+
<Text>Loading widget...</Text>
|
|
528
|
+
</View>
|
|
529
|
+
)}
|
|
530
|
+
<WidgetSDK
|
|
531
|
+
{...props}
|
|
532
|
+
onReady={() => setIsWidgetReady(true)}
|
|
533
|
+
/>
|
|
534
|
+
</View>
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Troubleshooting
|
|
538
|
+
|
|
539
|
+
### Widget Not Loading
|
|
540
|
+
|
|
541
|
+
**Check these:**
|
|
542
|
+
1. Is `widgetUrl` accessible?
|
|
543
|
+
2. Is the JWT token valid?
|
|
544
|
+
3. Check React Native debugger for errors
|
|
545
|
+
4. Verify `react-native-webview` is installed
|
|
546
|
+
|
|
547
|
+
### Callbacks Not Firing
|
|
548
|
+
|
|
549
|
+
**Make sure:**
|
|
550
|
+
1. Your widget is sending proper postMessage events
|
|
551
|
+
2. Message format matches expected structure (see web widget integration docs)
|
|
552
|
+
3. Check console for `[WidgetSDK]` logs
|
|
553
|
+
|
|
554
|
+
### App Crashes on Wallet Integration
|
|
555
|
+
|
|
556
|
+
**iOS:** Ensure Apple Wallet app is installed (may require real device)
|
|
557
|
+
**Android:** Ensure Google Play Services is configured on the device/emulator
|
|
558
|
+
|
|
559
|
+
## Support
|
|
560
|
+
|
|
561
|
+
For integration help:
|
|
562
|
+
- [Architecture Docs](../SDK_ARCHITECTURE.md)
|
|
563
|
+
- [Quick Start Guide](../SDK_QUICKSTART.md)
|
|
564
|
+
- [Web Widget Integration](../WIDGET_INTEGRATION.md)
|
|
565
|
+
|
|
566
|
+
## License
|
|
567
|
+
|
|
568
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget SDK Component
|
|
3
|
+
* Embeds the web widget in a WebView and handles bidirectional communication
|
|
4
|
+
*/
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { WidgetSDKConfig } from "./types";
|
|
7
|
+
export interface WidgetSDKRef {
|
|
8
|
+
navigateBack: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const WidgetSDK: React.ForwardRefExoticComponent<WidgetSDKConfig & React.RefAttributes<WidgetSDKRef>>;
|
|
11
|
+
//# sourceMappingURL=WidgetSDK.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WidgetSDK.d.ts","sourceRoot":"","sources":["../src/WidgetSDK.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AAIf,OAAO,EAAiB,eAAe,EAAE,MAAM,SAAS,CAAC;AAGzD,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,eAAO,MAAM,SAAS,sFA6PrB,CAAC"}
|