noboarding 0.1.0-alpha
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 +515 -0
- package/REVENUECAT_SETUP.md +756 -0
- package/SETUP_GUIDE.md +873 -0
- package/cusomte_screens.md +1964 -0
- package/lib/OnboardingFlow.d.ts +3 -0
- package/lib/OnboardingFlow.js +235 -0
- package/lib/analytics.d.ts +25 -0
- package/lib/analytics.js +72 -0
- package/lib/api.d.ts +31 -0
- package/lib/api.js +149 -0
- package/lib/components/ElementRenderer.d.ts +13 -0
- package/lib/components/ElementRenderer.js +521 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +18 -0
- package/lib/types.d.ts +185 -0
- package/lib/types.js +2 -0
- package/lib/variableUtils.d.ts +17 -0
- package/lib/variableUtils.js +118 -0
- package/logic.md +2095 -0
- package/package.json +44 -0
- package/src/OnboardingFlow.tsx +276 -0
- package/src/analytics.ts +84 -0
- package/src/api.ts +173 -0
- package/src/components/ElementRenderer.tsx +627 -0
- package/src/index.ts +32 -0
- package/src/types.ts +242 -0
- package/src/variableUtils.ts +133 -0
- package/tsconfig.json +20 -0
package/SETUP_GUIDE.md
ADDED
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
# Noboarding SDK - Complete Setup Guide
|
|
2
|
+
|
|
3
|
+
Choose your setup method:
|
|
4
|
+
|
|
5
|
+
- **[AI Setup](#ai-setup)** - Copy/paste instructions for your AI coding assistant
|
|
6
|
+
- **[Normal Setup](#normal-setup)** - Step-by-step manual instructions
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## AI Setup
|
|
11
|
+
|
|
12
|
+
> **For developers using AI coding assistants (Claude Code, Cursor, GitHub Copilot, etc.)**
|
|
13
|
+
|
|
14
|
+
Copy the instructions below and paste them into your AI coding assistant to automatically set up Noboarding SDK with RevenueCat integration.
|
|
15
|
+
|
|
16
|
+
### Prerequisites Checklist
|
|
17
|
+
|
|
18
|
+
Before giving these instructions to your AI assistant, ensure you have:
|
|
19
|
+
|
|
20
|
+
- ✅ RevenueCat account already configured
|
|
21
|
+
- ✅ Products/subscriptions set up in RevenueCat
|
|
22
|
+
- ✅ RevenueCat API keys (iOS + Android)
|
|
23
|
+
- ✅ Noboarding account and API key
|
|
24
|
+
- ✅ React Native app project ready
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
### Instructions for AI Assistant
|
|
29
|
+
|
|
30
|
+
**Copy everything below this line and paste it to your AI coding assistant:**
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
I need you to integrate the Noboarding SDK into my React Native app with RevenueCat paywall support. Here are the requirements:
|
|
36
|
+
|
|
37
|
+
## Context
|
|
38
|
+
- I already have RevenueCat configured in my app with API keys
|
|
39
|
+
- I have a Noboarding account with API key: [PASTE_YOUR_NOBOARDING_API_KEY_HERE]
|
|
40
|
+
- My RevenueCat iOS key: [PASTE_YOUR_IOS_KEY_HERE]
|
|
41
|
+
- My RevenueCat Android key: [PASTE_YOUR_ANDROID_KEY_HERE]
|
|
42
|
+
|
|
43
|
+
## Task 1: Install Noboarding SDK
|
|
44
|
+
|
|
45
|
+
1. Install the Noboarding SDK package:
|
|
46
|
+
```bash
|
|
47
|
+
npm install noboarding
|
|
48
|
+
# or
|
|
49
|
+
yarn add noboarding
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
2. Install peer dependencies if not already installed:
|
|
53
|
+
```bash
|
|
54
|
+
npm install @react-native-async-storage/async-storage
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Task 2: Create RevenueCat Paywall Screen
|
|
58
|
+
|
|
59
|
+
Create a new file at `src/screens/PaywallScreen.tsx` with the following implementation:
|
|
60
|
+
|
|
61
|
+
Requirements for the PaywallScreen component:
|
|
62
|
+
- Import CustomScreenProps from 'noboarding'
|
|
63
|
+
- Import necessary RevenueCat types (PurchasesOffering, PurchasesPackage)
|
|
64
|
+
- Use analytics.track() for these events:
|
|
65
|
+
- paywall_viewed (on mount)
|
|
66
|
+
- paywall_loaded (when offerings load)
|
|
67
|
+
- paywall_purchase_started
|
|
68
|
+
- paywall_conversion (on successful purchase)
|
|
69
|
+
- paywall_purchase_failed
|
|
70
|
+
- paywall_dismissed (when user skips)
|
|
71
|
+
- Implement preview mode that shows a placeholder UI when preview={true}
|
|
72
|
+
- Load offerings from RevenueCat using Purchases.getOfferings()
|
|
73
|
+
- Handle purchase with Purchases.purchasePackage()
|
|
74
|
+
- Check for premium entitlement after purchase
|
|
75
|
+
- Call onDataUpdate() with purchase info on successful purchase
|
|
76
|
+
- Call onNext() to continue after purchase
|
|
77
|
+
- Call onSkip() or onNext() when user dismisses
|
|
78
|
+
|
|
79
|
+
The component should:
|
|
80
|
+
- Show loading state while fetching offerings
|
|
81
|
+
- Display all available packages from the current offering
|
|
82
|
+
- Show package title, price, and any intro pricing
|
|
83
|
+
- Have a "Restore Purchases" button
|
|
84
|
+
- Have a skip/dismiss button (conditionally shown if onSkip is provided)
|
|
85
|
+
- Use Alert.alert() to show success/error messages
|
|
86
|
+
- Style it nicely with a modern, clean UI
|
|
87
|
+
|
|
88
|
+
## Task 3: Integrate SDK in App.tsx
|
|
89
|
+
|
|
90
|
+
Update the main App.tsx file:
|
|
91
|
+
|
|
92
|
+
1. Import OnboardingFlow from 'noboarding'
|
|
93
|
+
2. Import the PaywallScreen component
|
|
94
|
+
3. Import Purchases from 'react-native-purchases'
|
|
95
|
+
4. Add state to manage onboarding visibility: useState(true)
|
|
96
|
+
5. In useEffect, configure RevenueCat with the API keys I provided above
|
|
97
|
+
6. Render OnboardingFlow component with:
|
|
98
|
+
- apiKey prop set to my Noboarding API key
|
|
99
|
+
- customComponents={{ PaywallScreen: PaywallScreen }}
|
|
100
|
+
- onUserIdGenerated callback that calls Purchases.logIn(userId) - CRITICAL for attribution
|
|
101
|
+
- onComplete callback that logs userData and hides onboarding
|
|
102
|
+
- onSkip callback (optional)
|
|
103
|
+
7. Show main app content when onboarding is complete
|
|
104
|
+
|
|
105
|
+
## Task 4: Configure RevenueCat Webhook
|
|
106
|
+
|
|
107
|
+
After you complete the code changes, provide me with instructions to configure the RevenueCat webhook. The webhook should point to:
|
|
108
|
+
|
|
109
|
+
**Webhook URL:** https://hhmmzmrsptegprfztqtq.supabase.co/functions/v1/revenuecat-webhook
|
|
110
|
+
**Authorization Header:** Bearer c3373PFzv9wPpISOu5XFz22zABeLjpzYwGqmclXR60o=
|
|
111
|
+
|
|
112
|
+
Tell me:
|
|
113
|
+
1. Where to find the webhook settings in RevenueCat dashboard
|
|
114
|
+
2. Which events to select (minimum: INITIAL_PURCHASE, RENEWAL, CANCELLATION)
|
|
115
|
+
3. How to test the webhook
|
|
116
|
+
|
|
117
|
+
## Task 5: Usage Instructions
|
|
118
|
+
|
|
119
|
+
After completing the integration, provide me with:
|
|
120
|
+
1. How to add the PaywallScreen to my onboarding flow in the Noboarding dashboard
|
|
121
|
+
2. How to test the integration locally
|
|
122
|
+
3. What analytics events I can expect to see
|
|
123
|
+
4. How conversions will be tracked
|
|
124
|
+
|
|
125
|
+
## Important Notes
|
|
126
|
+
- Use TypeScript for all files
|
|
127
|
+
- Follow React Native best practices
|
|
128
|
+
- Add proper error handling
|
|
129
|
+
- Include loading states
|
|
130
|
+
- Make the UI accessible
|
|
131
|
+
- Add comments explaining critical parts (especially the onUserIdGenerated callback)
|
|
132
|
+
|
|
133
|
+
Please implement all of this and let me know when you're done. If you need any clarification on my existing code structure, ask me first before making assumptions.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
**After your AI assistant completes the setup:**
|
|
139
|
+
|
|
140
|
+
1. **Add PaywallScreen to Dashboard:**
|
|
141
|
+
- Log in to your Noboarding dashboard
|
|
142
|
+
- Go to your onboarding flow
|
|
143
|
+
- Click "Add Custom Screen"
|
|
144
|
+
- Enter component name: `PaywallScreen`
|
|
145
|
+
- Position it in your flow
|
|
146
|
+
- Save and publish
|
|
147
|
+
|
|
148
|
+
2. **Configure RevenueCat Webhook:**
|
|
149
|
+
- Go to [RevenueCat Dashboard](https://app.revenuecat.com/)
|
|
150
|
+
- Navigate to: Integrations → Webhooks
|
|
151
|
+
- Click "Add New Webhook"
|
|
152
|
+
- URL: `https://hhmmzmrsptegprfztqtq.supabase.co/functions/v1/revenuecat-webhook`
|
|
153
|
+
- Authorization: `Bearer c3373PFzv9wPpISOu5XFz22zABeLjpzYwGqmclXR60o=`
|
|
154
|
+
- Events: Select INITIAL_PURCHASE, RENEWAL, CANCELLATION
|
|
155
|
+
- Save and test
|
|
156
|
+
|
|
157
|
+
3. **Test Your Integration:**
|
|
158
|
+
```bash
|
|
159
|
+
npm start
|
|
160
|
+
# Test in development with sandbox purchases
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
4. **Deploy to Production:**
|
|
164
|
+
- Build your app
|
|
165
|
+
- Submit to App Store / Google Play
|
|
166
|
+
- After approval, publish your onboarding flow in the dashboard
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Normal Setup
|
|
171
|
+
|
|
172
|
+
> **For developers who prefer step-by-step manual instructions**
|
|
173
|
+
|
|
174
|
+
### Table of Contents
|
|
175
|
+
|
|
176
|
+
1. [Installation](#installation)
|
|
177
|
+
2. [Basic Integration](#basic-integration)
|
|
178
|
+
3. [RevenueCat Paywall Integration](#revenuecat-paywall-integration)
|
|
179
|
+
4. [Webhook Configuration](#webhook-configuration)
|
|
180
|
+
5. [Dashboard Setup](#dashboard-setup)
|
|
181
|
+
6. [Testing](#testing)
|
|
182
|
+
7. [Deployment](#deployment)
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Installation
|
|
187
|
+
|
|
188
|
+
Install the Noboarding SDK:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install noboarding
|
|
192
|
+
# or
|
|
193
|
+
yarn add noboarding
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Install peer dependencies:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm install @react-native-async-storage/async-storage
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
For RevenueCat integration, also install:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm install react-native-purchases
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**iOS Setup:**
|
|
209
|
+
```bash
|
|
210
|
+
cd ios && pod install
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### Basic Integration
|
|
216
|
+
|
|
217
|
+
#### Step 1: Import and Configure
|
|
218
|
+
|
|
219
|
+
In your `App.tsx`:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import React, { useState } from 'react';
|
|
223
|
+
import { OnboardingFlow } from 'noboarding';
|
|
224
|
+
|
|
225
|
+
export default function App() {
|
|
226
|
+
const [showOnboarding, setShowOnboarding] = useState(true);
|
|
227
|
+
|
|
228
|
+
if (showOnboarding) {
|
|
229
|
+
return (
|
|
230
|
+
<OnboardingFlow
|
|
231
|
+
apiKey="sk_live_your_api_key_here"
|
|
232
|
+
onComplete={(userData) => {
|
|
233
|
+
console.log('Onboarding complete:', userData);
|
|
234
|
+
setShowOnboarding(false);
|
|
235
|
+
}}
|
|
236
|
+
onSkip={() => {
|
|
237
|
+
setShowOnboarding(false);
|
|
238
|
+
}}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return <YourMainApp />;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Step 2: Get Your API Key
|
|
248
|
+
|
|
249
|
+
1. Log in to your [Noboarding Dashboard](https://dashboard.noboarding.com)
|
|
250
|
+
2. Go to Settings → API Keys
|
|
251
|
+
3. Copy your API key (starts with `sk_live_`)
|
|
252
|
+
4. Replace `sk_live_your_api_key_here` in the code above
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### RevenueCat Paywall Integration
|
|
257
|
+
|
|
258
|
+
#### Step 1: Configure RevenueCat
|
|
259
|
+
|
|
260
|
+
In your `App.tsx`, initialize RevenueCat:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { useEffect } from 'react';
|
|
264
|
+
import { Platform } from 'react-native';
|
|
265
|
+
import Purchases from 'react-native-purchases';
|
|
266
|
+
|
|
267
|
+
export default function App() {
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
// Configure RevenueCat with your API keys
|
|
270
|
+
Purchases.configure({
|
|
271
|
+
apiKey: Platform.OS === 'ios'
|
|
272
|
+
? 'appl_YOUR_IOS_KEY'
|
|
273
|
+
: 'goog_YOUR_ANDROID_KEY',
|
|
274
|
+
});
|
|
275
|
+
}, []);
|
|
276
|
+
|
|
277
|
+
// Rest of your app...
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Where to find RevenueCat API keys:**
|
|
282
|
+
- Go to [RevenueCat Dashboard](https://app.revenuecat.com/)
|
|
283
|
+
- Select your project
|
|
284
|
+
- Go to Settings → API Keys
|
|
285
|
+
- Copy iOS and Android keys
|
|
286
|
+
|
|
287
|
+
#### Step 2: Create Paywall Screen Component
|
|
288
|
+
|
|
289
|
+
Create a new file `src/screens/PaywallScreen.tsx`:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import React, { useState, useEffect } from 'react';
|
|
293
|
+
import {
|
|
294
|
+
View,
|
|
295
|
+
Text,
|
|
296
|
+
TouchableOpacity,
|
|
297
|
+
ActivityIndicator,
|
|
298
|
+
StyleSheet,
|
|
299
|
+
Alert,
|
|
300
|
+
} from 'react-native';
|
|
301
|
+
import Purchases, { PurchasesOffering, PurchasesPackage } from 'react-native-purchases';
|
|
302
|
+
import type { CustomScreenProps } from 'noboarding';
|
|
303
|
+
|
|
304
|
+
export const PaywallScreen: React.FC<CustomScreenProps> = ({
|
|
305
|
+
analytics,
|
|
306
|
+
onNext,
|
|
307
|
+
onSkip,
|
|
308
|
+
preview,
|
|
309
|
+
onDataUpdate,
|
|
310
|
+
}) => {
|
|
311
|
+
const [loading, setLoading] = useState(true);
|
|
312
|
+
const [purchasing, setPurchasing] = useState(false);
|
|
313
|
+
const [offering, setOffering] = useState<PurchasesOffering | null>(null);
|
|
314
|
+
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
analytics.track('paywall_viewed');
|
|
317
|
+
loadOffering();
|
|
318
|
+
}, []);
|
|
319
|
+
|
|
320
|
+
const loadOffering = async () => {
|
|
321
|
+
try {
|
|
322
|
+
const offerings = await Purchases.getOfferings();
|
|
323
|
+
setOffering(offerings.current);
|
|
324
|
+
|
|
325
|
+
analytics.track('paywall_loaded', {
|
|
326
|
+
offering_id: offerings.current?.identifier,
|
|
327
|
+
packages_count: offerings.current?.availablePackages.length,
|
|
328
|
+
});
|
|
329
|
+
} catch (error: any) {
|
|
330
|
+
analytics.track('paywall_error', { error: error.message });
|
|
331
|
+
} finally {
|
|
332
|
+
setLoading(false);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const handlePurchase = async (pkg: PurchasesPackage) => {
|
|
337
|
+
try {
|
|
338
|
+
setPurchasing(true);
|
|
339
|
+
|
|
340
|
+
analytics.track('paywall_purchase_started', {
|
|
341
|
+
package_id: pkg.identifier,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const { customerInfo } = await Purchases.purchasePackage(pkg);
|
|
345
|
+
const isPremium = customerInfo.entitlements.active['premium'] !== undefined;
|
|
346
|
+
|
|
347
|
+
if (isPremium) {
|
|
348
|
+
analytics.track('paywall_conversion', {
|
|
349
|
+
package_id: pkg.identifier,
|
|
350
|
+
price: pkg.product.priceString,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
onDataUpdate?.({
|
|
354
|
+
premium: true,
|
|
355
|
+
package_id: pkg.identifier,
|
|
356
|
+
purchase_timestamp: new Date().toISOString(),
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
Alert.alert('Welcome to Premium!', '', [
|
|
360
|
+
{ text: 'Continue', onPress: onNext }
|
|
361
|
+
]);
|
|
362
|
+
}
|
|
363
|
+
} catch (error: any) {
|
|
364
|
+
const cancelled = error.userCancelled;
|
|
365
|
+
|
|
366
|
+
analytics.track('paywall_purchase_failed', {
|
|
367
|
+
package_id: pkg.identifier,
|
|
368
|
+
cancelled,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
if (!cancelled) {
|
|
372
|
+
Alert.alert('Purchase Failed', 'Please try again.');
|
|
373
|
+
}
|
|
374
|
+
} finally {
|
|
375
|
+
setPurchasing(false);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const handleSkip = () => {
|
|
380
|
+
analytics.track('paywall_dismissed');
|
|
381
|
+
onSkip?.() || onNext();
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const handleRestorePurchases = async () => {
|
|
385
|
+
try {
|
|
386
|
+
setPurchasing(true);
|
|
387
|
+
const { customerInfo } = await Purchases.restorePurchases();
|
|
388
|
+
const isPremium = customerInfo.entitlements.active['premium'] !== undefined;
|
|
389
|
+
|
|
390
|
+
if (isPremium) {
|
|
391
|
+
Alert.alert('Purchases Restored', 'Your premium access has been restored!', [
|
|
392
|
+
{ text: 'Continue', onPress: onNext }
|
|
393
|
+
]);
|
|
394
|
+
} else {
|
|
395
|
+
Alert.alert('No Purchases Found', 'We couldn\'t find any previous purchases.');
|
|
396
|
+
}
|
|
397
|
+
} catch (error) {
|
|
398
|
+
Alert.alert('Restore Failed', 'Please try again.');
|
|
399
|
+
} finally {
|
|
400
|
+
setPurchasing(false);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// PREVIEW MODE (for dashboard)
|
|
405
|
+
if (preview) {
|
|
406
|
+
return (
|
|
407
|
+
<View style={styles.previewContainer}>
|
|
408
|
+
<Text style={styles.previewEmoji}>💎</Text>
|
|
409
|
+
<Text style={styles.previewTitle}>Paywall Preview</Text>
|
|
410
|
+
<Text style={styles.previewNote}>(Real paywall in app)</Text>
|
|
411
|
+
<TouchableOpacity style={styles.button} onPress={onNext}>
|
|
412
|
+
<Text style={styles.buttonText}>Continue</Text>
|
|
413
|
+
</TouchableOpacity>
|
|
414
|
+
</View>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (loading) {
|
|
419
|
+
return (
|
|
420
|
+
<View style={styles.centerContainer}>
|
|
421
|
+
<ActivityIndicator size="large" color="#007AFF" />
|
|
422
|
+
<Text style={styles.loadingText}>Loading options...</Text>
|
|
423
|
+
</View>
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return (
|
|
428
|
+
<View style={styles.container}>
|
|
429
|
+
<Text style={styles.title}>Unlock Premium</Text>
|
|
430
|
+
<Text style={styles.subtitle}>Get full access to all features</Text>
|
|
431
|
+
|
|
432
|
+
{offering?.availablePackages.map((pkg) => (
|
|
433
|
+
<TouchableOpacity
|
|
434
|
+
key={pkg.identifier}
|
|
435
|
+
style={styles.packageCard}
|
|
436
|
+
onPress={() => handlePurchase(pkg)}
|
|
437
|
+
disabled={purchasing}
|
|
438
|
+
>
|
|
439
|
+
<Text style={styles.packageTitle}>
|
|
440
|
+
{pkg.product.title?.replace(/\(.*?\)/, '').trim()}
|
|
441
|
+
</Text>
|
|
442
|
+
<Text style={styles.packagePrice}>{pkg.product.priceString}</Text>
|
|
443
|
+
</TouchableOpacity>
|
|
444
|
+
))}
|
|
445
|
+
|
|
446
|
+
{purchasing && (
|
|
447
|
+
<View style={styles.purchasingOverlay}>
|
|
448
|
+
<ActivityIndicator size="large" color="#FFFFFF" />
|
|
449
|
+
<Text style={styles.purchasingText}>Processing...</Text>
|
|
450
|
+
</View>
|
|
451
|
+
)}
|
|
452
|
+
|
|
453
|
+
<TouchableOpacity onPress={handleRestorePurchases} style={styles.restoreButton}>
|
|
454
|
+
<Text style={styles.restoreText}>Restore Purchases</Text>
|
|
455
|
+
</TouchableOpacity>
|
|
456
|
+
|
|
457
|
+
<TouchableOpacity onPress={handleSkip} style={styles.skipButton}>
|
|
458
|
+
<Text style={styles.skipText}>Maybe Later</Text>
|
|
459
|
+
</TouchableOpacity>
|
|
460
|
+
</View>
|
|
461
|
+
);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const styles = StyleSheet.create({
|
|
465
|
+
container: {
|
|
466
|
+
flex: 1,
|
|
467
|
+
padding: 20,
|
|
468
|
+
backgroundColor: '#FFFFFF',
|
|
469
|
+
},
|
|
470
|
+
centerContainer: {
|
|
471
|
+
flex: 1,
|
|
472
|
+
justifyContent: 'center',
|
|
473
|
+
alignItems: 'center',
|
|
474
|
+
},
|
|
475
|
+
previewContainer: {
|
|
476
|
+
flex: 1,
|
|
477
|
+
justifyContent: 'center',
|
|
478
|
+
alignItems: 'center',
|
|
479
|
+
padding: 20,
|
|
480
|
+
backgroundColor: '#F5F5F5',
|
|
481
|
+
},
|
|
482
|
+
previewEmoji: {
|
|
483
|
+
fontSize: 64,
|
|
484
|
+
marginBottom: 16,
|
|
485
|
+
},
|
|
486
|
+
previewTitle: {
|
|
487
|
+
fontSize: 24,
|
|
488
|
+
fontWeight: 'bold',
|
|
489
|
+
marginBottom: 8,
|
|
490
|
+
},
|
|
491
|
+
previewNote: {
|
|
492
|
+
fontSize: 14,
|
|
493
|
+
color: '#999',
|
|
494
|
+
marginBottom: 24,
|
|
495
|
+
},
|
|
496
|
+
title: {
|
|
497
|
+
fontSize: 32,
|
|
498
|
+
fontWeight: 'bold',
|
|
499
|
+
marginBottom: 8,
|
|
500
|
+
textAlign: 'center',
|
|
501
|
+
},
|
|
502
|
+
subtitle: {
|
|
503
|
+
fontSize: 18,
|
|
504
|
+
color: '#666',
|
|
505
|
+
marginBottom: 32,
|
|
506
|
+
textAlign: 'center',
|
|
507
|
+
},
|
|
508
|
+
loadingText: {
|
|
509
|
+
marginTop: 16,
|
|
510
|
+
fontSize: 16,
|
|
511
|
+
color: '#666',
|
|
512
|
+
},
|
|
513
|
+
packageCard: {
|
|
514
|
+
backgroundColor: '#F5F5F5',
|
|
515
|
+
borderRadius: 12,
|
|
516
|
+
padding: 20,
|
|
517
|
+
marginBottom: 12,
|
|
518
|
+
},
|
|
519
|
+
packageTitle: {
|
|
520
|
+
fontSize: 18,
|
|
521
|
+
fontWeight: '600',
|
|
522
|
+
marginBottom: 8,
|
|
523
|
+
},
|
|
524
|
+
packagePrice: {
|
|
525
|
+
fontSize: 24,
|
|
526
|
+
fontWeight: 'bold',
|
|
527
|
+
color: '#007AFF',
|
|
528
|
+
},
|
|
529
|
+
button: {
|
|
530
|
+
backgroundColor: '#007AFF',
|
|
531
|
+
borderRadius: 12,
|
|
532
|
+
padding: 16,
|
|
533
|
+
alignItems: 'center',
|
|
534
|
+
},
|
|
535
|
+
buttonText: {
|
|
536
|
+
color: '#FFFFFF',
|
|
537
|
+
fontSize: 18,
|
|
538
|
+
fontWeight: '600',
|
|
539
|
+
},
|
|
540
|
+
restoreButton: {
|
|
541
|
+
marginTop: 16,
|
|
542
|
+
padding: 12,
|
|
543
|
+
alignItems: 'center',
|
|
544
|
+
},
|
|
545
|
+
restoreText: {
|
|
546
|
+
fontSize: 16,
|
|
547
|
+
color: '#007AFF',
|
|
548
|
+
},
|
|
549
|
+
skipButton: {
|
|
550
|
+
marginTop: 8,
|
|
551
|
+
padding: 12,
|
|
552
|
+
alignItems: 'center',
|
|
553
|
+
},
|
|
554
|
+
skipText: {
|
|
555
|
+
fontSize: 16,
|
|
556
|
+
color: '#666',
|
|
557
|
+
},
|
|
558
|
+
purchasingOverlay: {
|
|
559
|
+
...StyleSheet.absoluteFillObject,
|
|
560
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
561
|
+
justifyContent: 'center',
|
|
562
|
+
alignItems: 'center',
|
|
563
|
+
},
|
|
564
|
+
purchasingText: {
|
|
565
|
+
color: '#FFFFFF',
|
|
566
|
+
marginTop: 16,
|
|
567
|
+
fontSize: 16,
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### Step 3: Register Paywall Screen
|
|
573
|
+
|
|
574
|
+
Update your `App.tsx` to register the custom screen:
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
import { OnboardingFlow } from 'noboarding';
|
|
578
|
+
import Purchases from 'react-native-purchases';
|
|
579
|
+
import { PaywallScreen } from './src/screens/PaywallScreen';
|
|
580
|
+
|
|
581
|
+
export default function App() {
|
|
582
|
+
const [showOnboarding, setShowOnboarding] = useState(true);
|
|
583
|
+
|
|
584
|
+
useEffect(() => {
|
|
585
|
+
// Configure RevenueCat
|
|
586
|
+
Purchases.configure({
|
|
587
|
+
apiKey: Platform.OS === 'ios'
|
|
588
|
+
? 'appl_YOUR_IOS_KEY'
|
|
589
|
+
: 'goog_YOUR_ANDROID_KEY',
|
|
590
|
+
});
|
|
591
|
+
}, []);
|
|
592
|
+
|
|
593
|
+
if (showOnboarding) {
|
|
594
|
+
return (
|
|
595
|
+
<OnboardingFlow
|
|
596
|
+
apiKey="sk_live_your_api_key"
|
|
597
|
+
customComponents={{
|
|
598
|
+
PaywallScreen: PaywallScreen, // Register custom screen
|
|
599
|
+
}}
|
|
600
|
+
// CRITICAL: Sync user ID with RevenueCat for proper attribution
|
|
601
|
+
onUserIdGenerated={(userId) => {
|
|
602
|
+
Purchases.logIn(userId);
|
|
603
|
+
}}
|
|
604
|
+
onComplete={(userData) => {
|
|
605
|
+
console.log('Onboarding complete:', userData);
|
|
606
|
+
// userData.premium will be true if they purchased
|
|
607
|
+
setShowOnboarding(false);
|
|
608
|
+
}}
|
|
609
|
+
/>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return <YourMainApp />;
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**⚠️ Critical: User ID Sync**
|
|
618
|
+
|
|
619
|
+
The `onUserIdGenerated` callback is **essential** for tracking conversions:
|
|
620
|
+
- The SDK generates a unique user ID for analytics
|
|
621
|
+
- You must use the **same ID** in RevenueCat
|
|
622
|
+
- This allows the webhook to attribute purchases to onboarding sessions
|
|
623
|
+
- Without this, A/B test conversion metrics won't work
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
### Webhook Configuration
|
|
628
|
+
|
|
629
|
+
#### Step 1: Configure RevenueCat Webhook
|
|
630
|
+
|
|
631
|
+
1. Go to [RevenueCat Dashboard](https://app.revenuecat.com/)
|
|
632
|
+
2. Navigate to **Integrations → Webhooks**
|
|
633
|
+
3. Click **"Add New Webhook"**
|
|
634
|
+
4. Enter the following:
|
|
635
|
+
|
|
636
|
+
**Webhook URL:**
|
|
637
|
+
```
|
|
638
|
+
https://hhmmzmrsptegprfztqtq.supabase.co/functions/v1/revenuecat-webhook
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Authorization Header:**
|
|
642
|
+
```
|
|
643
|
+
Bearer c3373PFzv9wPpISOu5XFz22zABeLjpzYwGqmclXR60o=
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
5. Select events to send (minimum):
|
|
647
|
+
- ✅ INITIAL_PURCHASE
|
|
648
|
+
- ✅ RENEWAL
|
|
649
|
+
- ✅ CANCELLATION
|
|
650
|
+
- ✅ BILLING_ISSUE (optional but recommended)
|
|
651
|
+
|
|
652
|
+
6. Click **"Save"**
|
|
653
|
+
|
|
654
|
+
#### Step 2: Test the Webhook
|
|
655
|
+
|
|
656
|
+
RevenueCat provides a test button. Click it to verify the webhook is working.
|
|
657
|
+
|
|
658
|
+
You can also test manually:
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
curl -X POST https://hhmmzmrsptegprfztqtq.supabase.co/functions/v1/revenuecat-webhook \
|
|
662
|
+
-H "Authorization: Bearer c3373PFzv9wPpISOu5XFz22zABeLjpzYwGqmclXR60o=" \
|
|
663
|
+
-H "Content-Type: application/json" \
|
|
664
|
+
-d '{
|
|
665
|
+
"api_version":"1.0",
|
|
666
|
+
"event":{
|
|
667
|
+
"id":"test_123",
|
|
668
|
+
"type":"TEST",
|
|
669
|
+
"app_user_id":"test_user",
|
|
670
|
+
"product_id":"test",
|
|
671
|
+
"transaction_id":"txn_123",
|
|
672
|
+
"original_transaction_id":"txn_123",
|
|
673
|
+
"purchased_at_ms":1707584400000,
|
|
674
|
+
"is_family_share":false,
|
|
675
|
+
"country_code":"US",
|
|
676
|
+
"store":"APP_STORE",
|
|
677
|
+
"environment":"SANDBOX"
|
|
678
|
+
}
|
|
679
|
+
}'
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
Expected response: `{"success":true}`
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
### Dashboard Setup
|
|
687
|
+
|
|
688
|
+
#### Step 1: Add Custom Screen to Flow
|
|
689
|
+
|
|
690
|
+
1. Log in to your [Noboarding Dashboard](https://dashboard.noboarding.com)
|
|
691
|
+
2. Go to **Flows** and select or create a flow
|
|
692
|
+
3. Click **"Add Custom Screen"**
|
|
693
|
+
4. Enter:
|
|
694
|
+
- **Component Name:** `PaywallScreen` (must match exactly)
|
|
695
|
+
- **Description:** "Premium subscription paywall"
|
|
696
|
+
5. **Drag to position** the screen where you want it in the flow
|
|
697
|
+
6. Click **"Save Draft"**
|
|
698
|
+
|
|
699
|
+
**Example flow order:**
|
|
700
|
+
```
|
|
701
|
+
1. Welcome Screen (SDK)
|
|
702
|
+
2. Feature Tour (SDK)
|
|
703
|
+
3. PaywallScreen (Custom) ← Your paywall
|
|
704
|
+
4. Setup Complete (SDK)
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
#### Step 2: Publish Flow
|
|
708
|
+
|
|
709
|
+
**Important:** Only publish after your app with the custom screen is live in app stores.
|
|
710
|
+
|
|
711
|
+
1. Review your flow
|
|
712
|
+
2. Click **"Publish"**
|
|
713
|
+
3. Users will now see the paywall in their onboarding
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
### Testing
|
|
718
|
+
|
|
719
|
+
#### Local Testing (Development)
|
|
720
|
+
|
|
721
|
+
1. **Start your app:**
|
|
722
|
+
```bash
|
|
723
|
+
npm start
|
|
724
|
+
# or
|
|
725
|
+
yarn start
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
2. **Use sandbox mode:**
|
|
729
|
+
- iOS: Use a sandbox Apple ID for testing
|
|
730
|
+
- Android: Use a test Google Play account
|
|
731
|
+
|
|
732
|
+
3. **Test the flow:**
|
|
733
|
+
- Complete onboarding
|
|
734
|
+
- Navigate to paywall screen
|
|
735
|
+
- Make a test purchase
|
|
736
|
+
- Verify analytics events in dashboard
|
|
737
|
+
|
|
738
|
+
#### Verify Analytics
|
|
739
|
+
|
|
740
|
+
Check your Noboarding dashboard for these events:
|
|
741
|
+
- ✅ `paywall_viewed`
|
|
742
|
+
- ✅ `paywall_loaded`
|
|
743
|
+
- ✅ `paywall_purchase_started`
|
|
744
|
+
- ✅ `paywall_conversion`
|
|
745
|
+
- ✅ `onboarding_completed`
|
|
746
|
+
|
|
747
|
+
#### Check Revenue Tracking
|
|
748
|
+
|
|
749
|
+
Go to **Analytics → Paywall Performance** to see:
|
|
750
|
+
- Paywall views
|
|
751
|
+
- Conversions
|
|
752
|
+
- Conversion rate
|
|
753
|
+
- Total revenue
|
|
754
|
+
|
|
755
|
+
---
|
|
756
|
+
|
|
757
|
+
### Deployment
|
|
758
|
+
|
|
759
|
+
#### Step 1: Build Your App
|
|
760
|
+
|
|
761
|
+
**iOS:**
|
|
762
|
+
```bash
|
|
763
|
+
cd ios
|
|
764
|
+
pod install
|
|
765
|
+
cd ..
|
|
766
|
+
npx react-native run-ios --configuration Release
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Android:**
|
|
770
|
+
```bash
|
|
771
|
+
npx react-native run-android --variant=release
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
#### Step 2: Submit to App Stores
|
|
775
|
+
|
|
776
|
+
1. Build production app bundle
|
|
777
|
+
2. Submit to App Store Connect / Google Play Console
|
|
778
|
+
3. Wait for review and approval (typically 1-3 days)
|
|
779
|
+
|
|
780
|
+
#### Step 3: Publish Onboarding Flow
|
|
781
|
+
|
|
782
|
+
**After your app is approved and live:**
|
|
783
|
+
|
|
784
|
+
1. Go to your Noboarding dashboard
|
|
785
|
+
2. Click **"Publish"** on your flow
|
|
786
|
+
3. Users downloading the new version will see the paywall
|
|
787
|
+
|
|
788
|
+
---
|
|
789
|
+
|
|
790
|
+
### Analytics & A/B Testing
|
|
791
|
+
|
|
792
|
+
Once deployed, you can:
|
|
793
|
+
|
|
794
|
+
#### Track Conversion Metrics
|
|
795
|
+
- View paywall conversion rates
|
|
796
|
+
- See revenue by variant
|
|
797
|
+
- Compare different paywall placements
|
|
798
|
+
- Analyze drop-off points
|
|
799
|
+
|
|
800
|
+
#### Run A/B Tests
|
|
801
|
+
1. Create multiple variants with different paywall positions
|
|
802
|
+
2. Set traffic allocation
|
|
803
|
+
3. Select primary metric: `paywall_conversion`
|
|
804
|
+
4. Run experiment
|
|
805
|
+
5. View results in dashboard
|
|
806
|
+
|
|
807
|
+
**Example A/B test:**
|
|
808
|
+
- **Variant A:** Paywall after welcome screen
|
|
809
|
+
- **Variant B:** Paywall after feature tour
|
|
810
|
+
- **Metric:** Conversion rate + Revenue per user
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
### Troubleshooting
|
|
815
|
+
|
|
816
|
+
#### Paywall Not Showing
|
|
817
|
+
|
|
818
|
+
**Check:**
|
|
819
|
+
- Component registered: `customComponents={{ PaywallScreen }}`
|
|
820
|
+
- Name matches exactly (case-sensitive)
|
|
821
|
+
- App includes the custom screen code
|
|
822
|
+
- Dashboard flow is published
|
|
823
|
+
|
|
824
|
+
**Debug:**
|
|
825
|
+
```typescript
|
|
826
|
+
console.log('Registered components:', Object.keys(customComponents));
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
#### No Conversions Tracked
|
|
830
|
+
|
|
831
|
+
**Check:**
|
|
832
|
+
- User ID synced with RevenueCat via `onUserIdGenerated`
|
|
833
|
+
- Webhook configured correctly in RevenueCat
|
|
834
|
+
- Authorization header is correct
|
|
835
|
+
- Events selected in RevenueCat webhook settings
|
|
836
|
+
|
|
837
|
+
**Debug:**
|
|
838
|
+
```sql
|
|
839
|
+
-- Check analytics events
|
|
840
|
+
SELECT * FROM analytics_events
|
|
841
|
+
WHERE user_id = 'your_user_id'
|
|
842
|
+
ORDER BY timestamp DESC;
|
|
843
|
+
|
|
844
|
+
-- Check RevenueCat events
|
|
845
|
+
SELECT * FROM revenuecat_events
|
|
846
|
+
WHERE app_user_id = 'your_user_id'
|
|
847
|
+
ORDER BY purchased_at DESC;
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
#### Revenue Not Showing
|
|
851
|
+
|
|
852
|
+
**Check:**
|
|
853
|
+
- `price` and `currency` in webhook payload
|
|
854
|
+
- Event type is `INITIAL_PURCHASE`
|
|
855
|
+
- Migration created `revenuecat_events` table
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
### Next Steps
|
|
860
|
+
|
|
861
|
+
- 📖 Read [Custom Screens Guide](./cusomte_screens.md) for advanced customization
|
|
862
|
+
- 🎨 Customize your paywall UI
|
|
863
|
+
- 📊 Set up A/B tests in dashboard
|
|
864
|
+
- 💡 Experiment with paywall placement and messaging
|
|
865
|
+
|
|
866
|
+
**Need Help?**
|
|
867
|
+
- Documentation: https://docs.noboarding.com
|
|
868
|
+
- Support: support@noboarding.com
|
|
869
|
+
- Community: [Discord](#)
|
|
870
|
+
|
|
871
|
+
---
|
|
872
|
+
|
|
873
|
+
**Happy building! 🚀**
|