@influto/react-native-sdk 1.0.0 → 1.2.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 CHANGED
@@ -234,11 +234,59 @@ if (attribution.attributed) {
234
234
  - Make sure you call `identifyUser()` after user completes onboarding
235
235
  - Verify user ID matches what RevenueCat sends in webhooks
236
236
 
237
+ ## Promo Code Integration
238
+
239
+ InfluTo supports **manual promo code entry** for users who hear about codes via social media, podcasts, or word-of-mouth.
240
+
241
+ ### Quick Example (Pre-built UI)
242
+
243
+ ```typescript
244
+ import { ReferralCodeInput } from '@influto/react-native-sdk/ui';
245
+
246
+ <ReferralCodeInput
247
+ onValidated={(result) => {
248
+ if (result.valid) {
249
+ navigation.navigate('Paywall');
250
+ }
251
+ }}
252
+ onSkip={() => navigation.navigate('Paywall')}
253
+ />
254
+ ```
255
+
256
+ ### Quick Example (Headless)
257
+
258
+ ```typescript
259
+ import InfluTo from '@influto/react-native-sdk';
260
+
261
+ // User enters code
262
+ const result = await InfluTo.applyCode('FITGURU30', userId);
263
+
264
+ if (result.valid && result.applied) {
265
+ // Code is validated AND set in RevenueCat automatically
266
+ showPaywall(result.campaign);
267
+ }
268
+ ```
269
+
270
+ **📚 [Complete Promo Code Guide](./PROMO_CODES.md)** - Examples, customization, best practices
271
+
272
+ ---
273
+
274
+ ## New in v1.1.0
275
+
276
+ ✅ **Manual promo code entry** - Users can type codes in your app
277
+ ✅ **Pre-built UI component** - `<ReferralCodeInput />` with full customization
278
+ ✅ **Headless API** - Build your own UI with `validateCode()`, `setReferralCode()`, `applyCode()`
279
+ ✅ **Auto-prefill** - Automatically fills code if user came via link
280
+ ✅ **Custom callbacks** - React to validation results
281
+ ✅ **Full internationalization** - Customize all labels and messages
282
+
283
+ ---
284
+
237
285
  ## Support
238
286
 
239
287
  - **Documentation:** https://docs.influ.to
240
- - **Email:** support@influ.to
241
- - **Discord:** https://discord.gg/influto
288
+ - **Help Center:** https://influ.to/help
289
+ - **Email:** hello@influ.to
242
290
 
243
291
  ## License
244
292
 
package/lib/InfluTo.d.ts CHANGED
@@ -20,7 +20,7 @@
20
20
  * }
21
21
  * ```
22
22
  */
23
- import type { InfluToConfig, AttributionResult, Campaign, TrackEventOptions } from './types';
23
+ import type { InfluToConfig, AttributionResult, Campaign, TrackEventOptions, CodeValidationResult, SetCodeResult } from './types';
24
24
  declare class InfluToSDK {
25
25
  private config;
26
26
  private apiUrl;
@@ -62,6 +62,87 @@ declare class InfluToSDK {
62
62
  * Get stored referral code (if any)
63
63
  */
64
64
  getReferralCode(): Promise<string | null>;
65
+ /**
66
+ * Get prefilled referral code (if user came via attribution link)
67
+ *
68
+ * Use this to pre-fill a promo code input field
69
+ *
70
+ * @returns Referral code if attribution exists, null otherwise
71
+ */
72
+ getPrefilledCode(): Promise<string | null>;
73
+ /**
74
+ * Validate a referral/promo code
75
+ *
76
+ * Check if a code is valid without applying it.
77
+ * Use this when user manually enters a code in your app.
78
+ *
79
+ * @param code - The referral code to validate
80
+ * @returns Validation result with campaign info if valid
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const result = await InfluTo.validateCode('FITGURU30');
85
+ *
86
+ * if (result.valid) {
87
+ * console.log('Campaign:', result.campaign.name);
88
+ * console.log('Commission:', result.campaign.commission_percentage + '%');
89
+ * // Show custom offer based on campaign
90
+ * } else {
91
+ * console.log('Invalid code:', result.error);
92
+ * }
93
+ * ```
94
+ */
95
+ validateCode(code: string): Promise<CodeValidationResult>;
96
+ /**
97
+ * Manually set a referral code
98
+ *
99
+ * Use this when user enters a promo code manually (not from link click).
100
+ * This will:
101
+ * 1. Store the code locally
102
+ * 2. Set it in RevenueCat attributes automatically
103
+ * 3. Record the attribution with backend
104
+ *
105
+ * @param code - The referral code to set
106
+ * @param appUserId - Optional user ID (if available)
107
+ * @returns Result with success status
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * // User enters code manually
112
+ * const result = await InfluTo.setReferralCode('FITGURU30');
113
+ *
114
+ * if (result.success) {
115
+ * console.log('Code applied successfully');
116
+ * // Show appropriate paywall/offer
117
+ * }
118
+ * ```
119
+ */
120
+ setReferralCode(code: string, appUserId?: string): Promise<SetCodeResult>;
121
+ /**
122
+ * Validate and apply a referral code (combined operation)
123
+ *
124
+ * This is a convenience method that validates a code and applies it if valid.
125
+ * Use this for one-step validation + application.
126
+ *
127
+ * @param code - The referral code to validate and apply
128
+ * @param appUserId - Optional user ID
129
+ * @returns Validation result. If valid, code is automatically applied.
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const result = await InfluTo.applyCode('FITGURU30', userId);
134
+ *
135
+ * if (result.valid) {
136
+ * // Code validated AND applied automatically
137
+ * showCustomOffer(result.campaign);
138
+ * } else {
139
+ * Alert.alert('Invalid Code', result.error);
140
+ * }
141
+ * ```
142
+ */
143
+ applyCode(code: string, appUserId?: string): Promise<CodeValidationResult & {
144
+ applied?: boolean;
145
+ }>;
65
146
  /**
66
147
  * Clear stored attribution data
67
148
  *
package/lib/InfluTo.js CHANGED
@@ -37,7 +37,7 @@ const STORAGE_KEYS = {
37
37
  class InfluToSDK {
38
38
  constructor() {
39
39
  this.config = null;
40
- this.apiUrl = 'https://api.influ.to/api';
40
+ this.apiUrl = 'https://influ.to/api';
41
41
  this.isInitialized = false;
42
42
  }
43
43
  /**
@@ -117,6 +117,25 @@ class InfluToSDK {
117
117
  // Store for future use
118
118
  await async_storage_1.default.setItem(STORAGE_KEYS.ATTRIBUTION, JSON.stringify(attribution));
119
119
  await async_storage_1.default.setItem(STORAGE_KEYS.REFERRAL_CODE, response.referral_code);
120
+ // 🎯 AUTO-INTEGRATION: Set RevenueCat attribute if available
121
+ try {
122
+ // @ts-ignore - RevenueCat might not be installed
123
+ const Purchases = require('react-native-purchases').default;
124
+ if (Purchases && Purchases.setAttributes) {
125
+ await Purchases.setAttributes({
126
+ referral_code: response.referral_code
127
+ });
128
+ if (this.config?.debug) {
129
+ console.log('[InfluTo] ✅ Referral code set in RevenueCat automatically');
130
+ }
131
+ }
132
+ }
133
+ catch (e) {
134
+ // RevenueCat not installed - that's okay, developer can set manually
135
+ if (this.config?.debug) {
136
+ console.log('[InfluTo] RevenueCat not found - set referral_code manually');
137
+ }
138
+ }
120
139
  if (this.config?.debug) {
121
140
  console.log('[InfluTo] ✅ Attribution found:', response.referral_code);
122
141
  }
@@ -214,6 +233,185 @@ class InfluToSDK {
214
233
  async getReferralCode() {
215
234
  return await async_storage_1.default.getItem(STORAGE_KEYS.REFERRAL_CODE);
216
235
  }
236
+ /**
237
+ * Get prefilled referral code (if user came via attribution link)
238
+ *
239
+ * Use this to pre-fill a promo code input field
240
+ *
241
+ * @returns Referral code if attribution exists, null otherwise
242
+ */
243
+ async getPrefilledCode() {
244
+ const attribution = await async_storage_1.default.getItem(STORAGE_KEYS.ATTRIBUTION);
245
+ if (attribution) {
246
+ const parsed = JSON.parse(attribution);
247
+ return parsed.attributed ? parsed.referralCode : null;
248
+ }
249
+ return null;
250
+ }
251
+ /**
252
+ * Validate a referral/promo code
253
+ *
254
+ * Check if a code is valid without applying it.
255
+ * Use this when user manually enters a code in your app.
256
+ *
257
+ * @param code - The referral code to validate
258
+ * @returns Validation result with campaign info if valid
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const result = await InfluTo.validateCode('FITGURU30');
263
+ *
264
+ * if (result.valid) {
265
+ * console.log('Campaign:', result.campaign.name);
266
+ * console.log('Commission:', result.campaign.commission_percentage + '%');
267
+ * // Show custom offer based on campaign
268
+ * } else {
269
+ * console.log('Invalid code:', result.error);
270
+ * }
271
+ * ```
272
+ */
273
+ async validateCode(code) {
274
+ if (!this.isInitialized) {
275
+ return {
276
+ valid: false,
277
+ error: 'SDK not initialized',
278
+ error_code: 'NETWORK_ERROR'
279
+ };
280
+ }
281
+ try {
282
+ const response = await this.apiRequest('/sdk/validate-code', {
283
+ method: 'POST',
284
+ body: JSON.stringify({ code: code.trim().toUpperCase() })
285
+ });
286
+ return response;
287
+ }
288
+ catch (error) {
289
+ if (this.config?.debug) {
290
+ console.error('[InfluTo] Code validation failed:', error);
291
+ }
292
+ return {
293
+ valid: false,
294
+ error: 'Network error or invalid response',
295
+ error_code: 'NETWORK_ERROR'
296
+ };
297
+ }
298
+ }
299
+ /**
300
+ * Manually set a referral code
301
+ *
302
+ * Use this when user enters a promo code manually (not from link click).
303
+ * This will:
304
+ * 1. Store the code locally
305
+ * 2. Set it in RevenueCat attributes automatically
306
+ * 3. Record the attribution with backend
307
+ *
308
+ * @param code - The referral code to set
309
+ * @param appUserId - Optional user ID (if available)
310
+ * @returns Result with success status
311
+ *
312
+ * @example
313
+ * ```typescript
314
+ * // User enters code manually
315
+ * const result = await InfluTo.setReferralCode('FITGURU30');
316
+ *
317
+ * if (result.success) {
318
+ * console.log('Code applied successfully');
319
+ * // Show appropriate paywall/offer
320
+ * }
321
+ * ```
322
+ */
323
+ async setReferralCode(code, appUserId) {
324
+ if (!this.isInitialized) {
325
+ return {
326
+ success: false,
327
+ message: 'SDK not initialized'
328
+ };
329
+ }
330
+ const normalizedCode = code.trim().toUpperCase();
331
+ try {
332
+ // Store locally
333
+ await async_storage_1.default.setItem(STORAGE_KEYS.REFERRAL_CODE, normalizedCode);
334
+ // Store attribution record
335
+ const attribution = {
336
+ attributed: true,
337
+ referralCode: normalizedCode,
338
+ attributionMethod: 'manual_entry',
339
+ clickedAt: new Date().toISOString(),
340
+ message: 'Manually entered code'
341
+ };
342
+ await async_storage_1.default.setItem(STORAGE_KEYS.ATTRIBUTION, JSON.stringify(attribution));
343
+ // Set in RevenueCat automatically
344
+ try {
345
+ const Purchases = require('react-native-purchases').default;
346
+ if (Purchases && Purchases.setAttributes) {
347
+ await Purchases.setAttributes({
348
+ referral_code: normalizedCode
349
+ });
350
+ if (this.config?.debug) {
351
+ console.log('[InfluTo] ✅ Referral code set in RevenueCat');
352
+ }
353
+ }
354
+ }
355
+ catch (e) {
356
+ if (this.config?.debug) {
357
+ console.warn('[InfluTo] RevenueCat not available - set manually');
358
+ }
359
+ }
360
+ // Record with backend
361
+ const response = await this.apiRequest('/sdk/set-referral-code', {
362
+ method: 'POST',
363
+ body: JSON.stringify({
364
+ code: normalizedCode,
365
+ app_user_id: appUserId
366
+ })
367
+ });
368
+ return response;
369
+ }
370
+ catch (error) {
371
+ if (this.config?.debug) {
372
+ console.error('[InfluTo] Failed to set referral code:', error);
373
+ }
374
+ return {
375
+ success: false,
376
+ message: 'Failed to set code: ' + error.message
377
+ };
378
+ }
379
+ }
380
+ /**
381
+ * Validate and apply a referral code (combined operation)
382
+ *
383
+ * This is a convenience method that validates a code and applies it if valid.
384
+ * Use this for one-step validation + application.
385
+ *
386
+ * @param code - The referral code to validate and apply
387
+ * @param appUserId - Optional user ID
388
+ * @returns Validation result. If valid, code is automatically applied.
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * const result = await InfluTo.applyCode('FITGURU30', userId);
393
+ *
394
+ * if (result.valid) {
395
+ * // Code validated AND applied automatically
396
+ * showCustomOffer(result.campaign);
397
+ * } else {
398
+ * Alert.alert('Invalid Code', result.error);
399
+ * }
400
+ * ```
401
+ */
402
+ async applyCode(code, appUserId) {
403
+ // First validate
404
+ const validation = await this.validateCode(code);
405
+ if (!validation.valid) {
406
+ return { ...validation, applied: false };
407
+ }
408
+ // If valid, apply it
409
+ const setResult = await this.setReferralCode(code, appUserId);
410
+ return {
411
+ ...validation,
412
+ applied: setResult.success
413
+ };
414
+ }
217
415
  /**
218
416
  * Clear stored attribution data
219
417
  *
@@ -0,0 +1,125 @@
1
+ /**
2
+ * ReferralCodeInput - Pre-built UI component for promo code entry
3
+ *
4
+ * Features:
5
+ * - Auto-prefills if user came via attribution link
6
+ * - Real-time validation
7
+ * - Customizable styling (colors, fonts, spacing)
8
+ * - Loading & success/error states
9
+ * - Optional skip button
10
+ * - Callback on validation
11
+ * - Fully accessible
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { ReferralCodeInput } from '@influto/react-native-sdk/ui';
16
+ *
17
+ * <ReferralCodeInput
18
+ * onValidated={(result) => {
19
+ * if (result.valid) {
20
+ * navigation.navigate('Paywall', { campaign: result.campaign });
21
+ * }
22
+ * }}
23
+ * onSkip={() => navigation.navigate('Paywall')}
24
+ * />
25
+ * ```
26
+ */
27
+ import React from 'react';
28
+ import { ViewStyle, TextStyle } from 'react-native';
29
+ import type { CodeValidationResult } from '../types';
30
+ export interface ReferralCodeInputProps {
31
+ /**
32
+ * Auto-prefill code if user came via attribution link
33
+ * @default true
34
+ */
35
+ autoPrefill?: boolean;
36
+ /**
37
+ * Auto-validate code on mount (if prefilled)
38
+ * @default false
39
+ */
40
+ autoValidate?: boolean;
41
+ /**
42
+ * Callback when code is validated (valid or invalid)
43
+ */
44
+ onValidated?: (result: CodeValidationResult) => void;
45
+ /**
46
+ * Callback when user skips code entry
47
+ */
48
+ onSkip?: () => void;
49
+ /**
50
+ * Callback when code is successfully applied
51
+ */
52
+ onApplied?: (result: CodeValidationResult) => void;
53
+ /**
54
+ * App user ID (if available) - used for attribution tracking
55
+ */
56
+ appUserId?: string;
57
+ /**
58
+ * Custom color scheme
59
+ */
60
+ colors?: {
61
+ primary?: string;
62
+ success?: string;
63
+ error?: string;
64
+ text?: string;
65
+ textSecondary?: string;
66
+ background?: string;
67
+ border?: string;
68
+ inputBackground?: string;
69
+ };
70
+ /**
71
+ * Custom fonts
72
+ */
73
+ fonts?: {
74
+ family?: string;
75
+ sizeTitle?: number;
76
+ sizeInput?: number;
77
+ sizeButton?: number;
78
+ sizeMessage?: number;
79
+ };
80
+ /**
81
+ * Custom text labels (for internationalization)
82
+ */
83
+ labels?: {
84
+ title?: string;
85
+ subtitle?: string;
86
+ placeholder?: string;
87
+ validateButton?: string;
88
+ skipButton?: string;
89
+ validatingMessage?: string;
90
+ validMessage?: string;
91
+ invalidMessage?: string;
92
+ errorMessage?: string;
93
+ prefilledMessage?: string;
94
+ };
95
+ /**
96
+ * Custom styles for fine-grained control
97
+ */
98
+ style?: {
99
+ container?: ViewStyle;
100
+ titleContainer?: ViewStyle;
101
+ title?: TextStyle;
102
+ subtitle?: TextStyle;
103
+ inputContainer?: ViewStyle;
104
+ input?: TextStyle;
105
+ buttonContainer?: ViewStyle;
106
+ validateButton?: ViewStyle;
107
+ validateButtonText?: TextStyle;
108
+ skipButton?: ViewStyle;
109
+ skipButtonText?: TextStyle;
110
+ messageContainer?: ViewStyle;
111
+ messageText?: TextStyle;
112
+ };
113
+ /**
114
+ * Show skip button
115
+ * @default true
116
+ */
117
+ showSkipButton?: boolean;
118
+ /**
119
+ * Validate on blur (when user leaves input)
120
+ * @default true
121
+ */
122
+ validateOnBlur?: boolean;
123
+ }
124
+ export declare const ReferralCodeInput: React.FC<ReferralCodeInputProps>;
125
+ export default ReferralCodeInput;