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/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! 🚀**