insert-affiliate-react-native-sdk 1.5.0 → 1.6.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
@@ -15,9 +15,9 @@ The **InsertAffiliateReactNative SDK** is designed for React Native applications
15
15
  To get started with the InsertAffiliateReactNative SDK:
16
16
 
17
17
  1. [Install the SDK](#installation)
18
- 2. [Initialise the SDK in App.tsx](#basic-usage)
18
+ 2. [Set up the provider in Index.js and initialize the SDK in App.tsx](#basic-usage)
19
19
  3. [Set up in-app purchases (Required)](#in-app-purchase-setup-required)
20
- 4. [Set up deep linking (Required)](#deep-link-setup-required)
20
+ 4. [Set up deep linking in Index.js (Required)](#deep-link-setup-required)
21
21
 
22
22
  ## Installation
23
23
 
@@ -28,45 +28,109 @@ To integrate the InsertAffiliateReactNative SDK into your app:
28
28
  npm install insert-affiliate-react-native-sdk
29
29
  ```
30
30
 
31
+ ## Architecture Overview
32
+
33
+ The SDK uses a clean, two-file architecture:
34
+
35
+ - **`index.js`** (Entry Point): Provider wrapper and deep link handling
36
+ - **`App.tsx`** (UI Logic): SDK initialization and your app components
37
+
38
+ This separation ensures clean code organization and proper initialization timing.
39
+
31
40
  ## Basic Usage
32
41
 
33
42
  Follow the steps below to install the SDK.
34
43
 
35
- #### Step 1: Initialisation in `App.tsx`
44
+ ### Step 1: Entry Point in `Index.js`
45
+ ```javascript
46
+ import React from 'react';
47
+ import {AppRegistry} from 'react-native';
48
+ import App from './App';
49
+ import {name as appName} from './app.json';
50
+ import {DeepLinkIapProvider} from 'insert-affiliate-react-native-sdk';
51
+
52
+ const RootComponent = () => {
53
+ return (
54
+ <DeepLinkIapProvider>
55
+ <App />
56
+ </DeepLinkIapProvider>
57
+ );
58
+ };
59
+
60
+ AppRegistry.registerComponent(appName, () => RootComponent);
61
+ ```
62
+
63
+ #### Step 2: SDK initialization in `App.tsx`
36
64
 
37
65
  First, wrap your with our provider and call the `initialize` method early in your app's lifecycle:
38
66
 
39
67
  ```javascript
40
68
  const Child = () => {
41
- const {
42
- referrerLink,
43
- subscriptions,
44
- iapLoading,
45
- validatePurchaseWithIapticAPI,
46
- userId,
47
- userPurchase,
48
- trackEvent,
49
- initialize,
50
- isInitialized,
51
- } = useDeepLinkIapProvider();
69
+ const { initialize, isInitialized } = useDeepLinkIapProvider();
52
70
 
53
71
  useEffect(() => {
54
- initialize("{{ your-company-code }}");
72
+ if (!isInitialized) {
73
+ initialize("{{ your-company-code }}");
74
+ }
55
75
  }, [initialize, isInitialized]);
56
-
57
- // ...
58
76
  }
59
77
 
60
78
  const App = () => {
61
- return (
62
- <DeepLinkIapProvider>
63
- <Child />
64
- </DeepLinkIapProvider>
65
- );
79
+ return <Child />;
66
80
  };
67
81
  ```
68
82
  - Replace `{{ your_company_code }}` with the unique company code associated with your Insert Affiliate account. You can find this code in your dashboard under [Settings](http://app.insertaffiliate.com/settings).
69
83
 
84
+ ### Verbose Logging (Optional)
85
+
86
+ For debugging and troubleshooting, you can enable verbose logging to get detailed insights into the SDK's operations:
87
+
88
+ ```javascript
89
+ const Child = () => {
90
+ const { initialize, isInitialized } = useDeepLinkIapProvider();
91
+
92
+ useEffect(() => {
93
+ if (!isInitialized) {
94
+ // Enable verbose logging (second parameter)
95
+ initialize("{{ your-company-code }}", true);
96
+ }
97
+ }, [initialize, isInitialized]);
98
+ }
99
+ ```
100
+
101
+ **When verbose logging is enabled, you'll see detailed logs with the `[Insert Affiliate] [VERBOSE]` prefix that show:**
102
+
103
+ - **Initialization Process**: SDK startup, company code validation, AsyncStorage operations
104
+ - **Data Management**: User ID generation, referrer link storage, company code state management
105
+ - **Deep Link Processing**: Input validation, short code detection, API conversion process
106
+ - **API Communication**: Request/response details for all server calls
107
+ - **Event Tracking**: Event parameters, payload construction, success/failure status
108
+ - **Purchase Operations**: Transaction storage, token validation, webhook processing
109
+
110
+ **Example verbose output:**
111
+ ```
112
+ [Insert Affiliate] [VERBOSE] Starting SDK initialization...
113
+ [Insert Affiliate] [VERBOSE] Company code provided: Yes
114
+ [Insert Affiliate] [VERBOSE] Verbose logging enabled
115
+ [Insert Affiliate] SDK initialized with company code: your-company-code
116
+ [Insert Affiliate] [VERBOSE] Company code saved to AsyncStorage
117
+ [Insert Affiliate] [VERBOSE] SDK marked as initialized
118
+ [Insert Affiliate] [VERBOSE] Loading stored data from AsyncStorage...
119
+ [Insert Affiliate] [VERBOSE] User ID found: Yes
120
+ [Insert Affiliate] [VERBOSE] Referrer link found: Yes
121
+ [Insert Affiliate] [VERBOSE] Company code found: Yes
122
+ ```
123
+
124
+ **Benefits of verbose logging:**
125
+ - **Debug Deep Linking Issues**: See exactly what links are being processed and how they're converted
126
+ - **Monitor API Communication**: Track all server requests, responses, and error details
127
+ - **Identify Storage Problems**: Understand AsyncStorage read/write operations and state sync
128
+ - **Performance Insights**: Monitor async operation timing and identify bottlenecks
129
+ - **Integration Troubleshooting**: Quickly identify configuration or setup issues
130
+
131
+ ⚠️ **Important**: Disable verbose logging in production builds to avoid exposing sensitive debugging information and to optimize performance.
132
+
133
+
70
134
  ## In-App Purchase Setup [Required]
71
135
  Insert Affiliate requires a Receipt Verification platform to validate in-app purchases. You must choose **one** of our supported partners:
72
136
  - [RevenueCat](https://www.revenuecat.com/)
@@ -79,10 +143,12 @@ Insert Affiliate requires a Receipt Verification platform to validate in-app pur
79
143
  First, complete the [RevenueCat SDK installation](https://www.revenuecat.com/docs/getting-started/installation/reactnative). Then modify your `App.tsx`:
80
144
 
81
145
  ```javascript
82
- import {
83
- DeepLinkIapProvider,
84
- useDeepLinkIapProvider,
85
- } from 'insert-affiliate-react-native-sdk';
146
+ import React, {useEffect} from 'react';
147
+ import {AppRegistry} from 'react-native';
148
+ import branch from 'react-native-branch';
149
+ import App from './App';
150
+ import {name as appName} from './app.json';
151
+ import {useDeepLinkIapProvider, DeepLinkIapProvider} from 'insert-affiliate-react-native-sdk';
86
152
 
87
153
  // ... //
88
154
  const {
@@ -186,7 +252,7 @@ const Child = () => {
186
252
  <Button
187
253
  disabled={iapLoading}
188
254
  title={`Click to Buy Subscription`}
189
- onPress={() => handleBuySubscription("oneMonthSubscriptionTwo")}
255
+ onPress={() => handleBuySubscription("oneMonthSubscription")}
190
256
  />
191
257
  {iapLoading && <ActivityIndicator size={"small"} color={"black"} />}
192
258
  </View>
@@ -195,10 +261,7 @@ const Child = () => {
195
261
 
196
262
  const App = () => {
197
263
  return (
198
- // Wrapped application code from the previous step...
199
- <DeepLinkIapProvider>
200
- <Child />
201
- </DeepLinkIapProvider>
264
+ <Child />
202
265
  );
203
266
  };
204
267
 
@@ -238,7 +301,7 @@ Ensure you import the necessary dependencies, including `Platform` and `useDeepL
238
301
 
239
302
  ```javascript
240
303
  import { Platform } from 'react-native';
241
- import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
304
+ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
242
305
  import { requestSubscription } from 'react-native-iap';
243
306
 
244
307
  const { returnUserAccountTokenAndStoreExpectedTransaction } = useDeepLinkIapProvider();
@@ -322,38 +385,33 @@ To set up deep linking with Branch.io, follow these steps:
322
385
 
323
386
  1. Create a deep link in Branch and pass it to our dashboard when an affiliate signs up.
324
387
  - Example: [Create Affiliate](https://docs.insertaffiliate.com/create-affiliate).
325
- 2. Modify Your Deep Link Handling in `App.tsx`
326
- - After setting up your Branch integration, add the following code to initialise our SDK in your app:
388
+ 2. Modify Your Deep Link Handling in `Index.js`
389
+ - After setting up your Branch integration, add the following code to your app:
327
390
 
328
391
 
329
392
  #### Example with RevenueCat
330
393
  ```javascript
331
- import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
332
- import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
394
+ import {useDeepLinkIapProvider, DeepLinkIapProvider} from 'insert-affiliate-react-native-sdk';
333
395
 
334
396
  //...
397
+ const DeepLinkHandler = () => {
335
398
  const {setInsertAffiliateIdentifier} = useDeepLinkIapProvider();
336
399
 
337
400
  useEffect(() => {
338
- if (!isInitialized) return;
339
-
340
401
  const branchSubscription = branch.subscribe(async ({error, params}) => {
341
402
  if (error) {
342
403
  console.error('Error from Branch:', error);
343
404
  return;
344
405
  }
345
406
 
346
- if (!params) {
347
- return
348
- }
349
407
  if (params['+clicked_branch_link']) {
350
408
  const referringLink = params['~referring_link'];
351
409
  if (referringLink) {
352
410
  try {
353
- let insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
411
+ let insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
354
412
 
355
413
  if (insertAffiliateIdentifier) {
356
- await Purchases.setAttributes({"insert_affiliate" : affiliateIdentifier});
414
+ await Purchases.setAttributes({"insert_affiliate" : insertAffiliateIdentifier});
357
415
  }
358
416
 
359
417
  } catch (err) {
@@ -363,35 +421,68 @@ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
363
421
  }
364
422
  });
365
423
 
366
- // Cleanup the subscription on component unmount
367
424
  return () => {
368
425
  branchSubscription();
369
426
  };
370
- }, [setInsertAffiliateIdentifier, isInitialized]);
427
+ }, [setInsertAffiliateIdentifier]);
428
+
429
+ return <App />;
430
+ };
431
+
432
+ const RootComponent = () => {
433
+ return (
434
+ <DeepLinkIapProvider>
435
+ <DeepLinkHandler />
436
+ </DeepLinkIapProvider>
437
+ );
438
+ };
439
+
440
+ AppRegistry.registerComponent(appName, () => RootComponent);
441
+
371
442
  //...
372
443
  ```
373
444
 
374
445
  #### Example with Iaptic / App Store Direct Integration / Google Play Direct Integration
375
446
  ```javascript
376
447
  import branch from 'react-native-branch';
377
- import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
378
- const {setInsertAffiliateIdentifier} = useDeepLinkIapProvider();
448
+ import {useDeepLinkIapProvider, DeepLinkIapProvider} from 'insert-affiliate-react-native-sdk';
379
449
 
380
- branch.subscribe(async ({ error, params }) => {
381
- if (error) {
382
- console.error('Error from Branch: ' + error);
383
- return;
384
- }
385
-
386
- if (params['+clicked_branch_link']) {
387
- if (params["~referring_link"]) {
388
- setInsertAffiliateIdentifier(params["~referring_link"], (shortLink) => {
389
- console.log("Insert Affiliate - setInsertAffiliateIdentifier: ", params["~referring_link"], " - Stored shortLink ", shortLink);
390
- });
450
+ const DeepLinkHandler = () => {
451
+ const {setInsertAffiliateIdentifier} = useDeepLinkIapProvider();
452
+
453
+ React.useEffect(() => {
454
+ const branchSubscription = branch.subscribe(async ({error, params}) => {
455
+ if (error) {
456
+ console.error('Error from Branch:', error);
457
+ return;
458
+ }
459
+
460
+ if (params['+clicked_branch_link']) {
461
+ const referringLink = params['~referring_link'];
462
+ if (referringLink) {
463
+ try {
464
+ await setInsertAffiliateIdentifier(referringLink);
465
+ console.log('Affiliate identifier set successfully.');
466
+ } catch (err) {
467
+ console.error('Error setting affiliate identifier:', err);
468
+ }
391
469
  }
392
- }
393
- });
470
+ }
471
+ });
472
+
473
+ return () => branchSubscription();
474
+ }, [setInsertAffiliateIdentifier]);
475
+
476
+ return <App />;
477
+ };
394
478
 
479
+ const RootComponent = () => {
480
+ return (
481
+ <DeepLinkIapProvider>
482
+ <DeepLinkHandler />
483
+ </DeepLinkIapProvider>
484
+ );
485
+ };
395
486
  ```
396
487
 
397
488
  ## Additional Features
@@ -407,7 +498,7 @@ At this stage, we cannot guarantee that this feature is fully resistant to tampe
407
498
 
408
499
  #### Using `trackEvent`
409
500
 
410
- To track an event, use the `trackEvent` function. Make sure to set an affiliate identifier first; otherwise, event tracking wont work. Heres an example:
501
+ To track an event, use the `trackEvent` function. Make sure to set an affiliate identifier first; otherwise, event tracking won't work. Here's an example:
411
502
 
412
503
  ```javascript
413
504
  const {
@@ -429,11 +520,301 @@ const {
429
520
  />
430
521
  ```
431
522
 
432
- ### 2. Short Codes (Beta)
523
+ ### 2. Discounts for Users → Offer Codes / Dynamic Product IDs
524
+
525
+ The SDK allows you to apply dynamic modifiers to in-app purchases based on whether the app was installed via an affiliate. These modifiers can be used to swap the default product ID for a discounted or trial-based one - similar to applying an offer code.
526
+
527
+ > **Note:** Offer Codes are currently supported on **iOS only**.
528
+
529
+ #### How It Works
530
+
531
+ When a user clicks an affiliate link or enters a short code linked to an offer (set up in the **Insert Affiliate Dashboard**), the SDK auto-populates the `iOSOfferCode` field with a relevant modifier (e.g., `_oneWeekFree`). You can append this to your base product ID to dynamically display the correct subscription.
532
+
533
+
534
+ #### Basic Usage
535
+
536
+ ##### 1. Automatic Offer Code Fetching
537
+ If an affiliate short code is stored, the SDK automatically fetches and saves the associated offer code modifier.
538
+
539
+ ##### 2. Access the Offer Code Modifier
540
+ The offer code modifier is available through the context:
541
+
542
+ ```javascript
543
+ const { iOSOfferCode } = useDeepLinkIapProvider();
544
+ ```
545
+
546
+ ##### Setup Requirements
547
+
548
+ #### App Store Connect Configuration
549
+ 1. Create both a base and a promotional product:
550
+ - Base product: `oneMonthSubscription`
551
+ - Promo product: `oneMonthSubscription_oneWeekFree`
552
+ 2. Ensure **both** products are approved and available for sale.
553
+
554
+
555
+ **Product Naming Pattern:**
556
+ - Follow the pattern: `{baseProductId}{iOSOfferCode}`
557
+ - Example: `oneMonthSubscription` + `_oneWeekFree` = `oneMonthSubscription_oneWeekFree`
558
+
559
+ ---
560
+
561
+ #### RevenueCat Dashboard Configuration
562
+
563
+ #### RevenueCat Dashboard Configuration:
564
+ 1. Create separate offerings:
565
+ - Base offering: `premium_monthly`
566
+ - Modified offering: `premium_monthly_oneWeekFree`
567
+
568
+ 2. Add both product IDs under different offerings in RevenueCat.
569
+
570
+ 3. Ensure modified products follow this naming pattern: {baseProductId}_{cleanOfferCode}. e.g. premium_monthly_oneWeekFree
571
+
572
+
573
+ ### Integration Example
574
+ ```javascript
575
+ import React, { useEffect, useState } from 'react';
576
+ import { View, Button, Text } from 'react-native';
577
+ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
578
+ import Purchases from 'react-native-purchases';
579
+
580
+ const PurchaseHandler = () => {
581
+ const { iOSOfferCode } = useDeepLinkIapProvider();
582
+ const [subscriptions, setSubscriptions] = useState([]);
583
+
584
+ const fetchSubscriptions = async () => {
585
+ const offerings = await Purchases.getOfferings();
586
+ let packagesToUse = [];
587
+
588
+ if (iOSOfferCode) {
589
+
590
+
591
+ // Construct modified product IDs from base products
592
+ const baseProducts = offerings.current.availablePackages;
593
+
594
+ for (const basePackage of baseProducts) {
595
+ const baseProductId = basePackage.product.identifier;
596
+ const modifiedProductId = `${baseProductId}_${iOSOfferCode}`;
597
+
598
+ // Search all offerings for the modified product
599
+ const allOfferings = Object.values(offerings.all);
600
+ let foundModified = false;
601
+
602
+ for (const offering of allOfferings) {
603
+ const modifiedPackage = offering.availablePackages.find(pkg =>
604
+ pkg.product.identifier === modifiedProductId
605
+ );
606
+
607
+ if (modifiedPackage) {
608
+ packagesToUse.push(modifiedPackage);
609
+ foundModified = true;
610
+ break;
611
+ }
612
+ }
613
+
614
+ // Fallback to base product if no modified version
615
+ if (!foundModified) {
616
+ packagesToUse.push(basePackage);
617
+ }
618
+ }
619
+ } else {
620
+ packagesToUse = offerings.current.availablePackages;
621
+ }
622
+
623
+ setSubscriptions(packagesToUse);
624
+ };
625
+
626
+ const handlePurchase = async (subscriptionPackage) => {
627
+ try {
628
+ await Purchases.purchasePackage(subscriptionPackage);
629
+ } catch (error) {
630
+ console.error('Purchase failed:', error);
631
+ }
632
+ };
633
+
634
+ useEffect(() => {
635
+ fetchSubscriptions();
636
+ }, [iOSOfferCode]);
637
+
638
+ return (
639
+ <View>
640
+ {subscriptions.map((pkg) => (
641
+ <Button
642
+ key={pkg.identifier}
643
+ title={`Buy: ${pkg.product.identifier}`}
644
+ onPress={() => handlePurchase(pkg)}
645
+ />
646
+ ))}
647
+ {iOSOfferCode && (
648
+ <Text>Special offer applied: {iOSOfferCode}</Text>
649
+ )}
650
+ </View>
651
+ );
652
+ };
653
+ ```
654
+ ---
655
+
656
+ #### Native Receipt Verification Example
657
+
658
+ For apps using `react-native-iap` directly:
659
+
660
+ ```javascript
661
+ import React, { useState, useEffect } from 'react';
662
+ import { View, Text, Button, Platform } from 'react-native';
663
+ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
664
+ import {
665
+ initConnection,
666
+ getSubscriptions,
667
+ requestSubscription,
668
+ useIAP
669
+ } from 'react-native-iap';
670
+
671
+ const NativeIAPPurchaseView = () => {
672
+ const { iOSOfferCode, returnUserAccountTokenAndStoreExpectedTransaction } = useDeepLinkIapProvider();
673
+ const [availableProducts, setAvailableProducts] = useState([]);
674
+ const [loading, setLoading] = useState(false);
675
+ const { currentPurchase, connected } = useIAP();
676
+
677
+ const baseProductIdentifier = "oneMonthSubscription";
678
+
679
+ // Dynamic product identifier that includes offer code
680
+ const dynamicProductIdentifier = iOSOfferCode
681
+ ? `${baseProductIdentifier}${iOSOfferCode}` // e.g., "oneMonthSubscription_oneWeekFree"
682
+ : baseProductIdentifier;
683
+
684
+ const fetchProducts = async () => {
685
+ try {
686
+ setLoading(true);
687
+
688
+ // Try to fetch the dynamic product first
689
+ let productIds = [dynamicProductIdentifier];
690
+
691
+ // Also include base product as fallback
692
+ if (iOSOfferCode) {
693
+ productIds.push(baseProductIdentifier);
694
+ }
695
+
696
+ const products = await getSubscriptions({ skus: productIds });
697
+
698
+ // Prioritize the dynamic product if it exists
699
+ let sortedProducts = products;
700
+ if (iOSOfferCode && products.length > 1) {
701
+ sortedProducts = products.sort((a, b) =>
702
+ a.productId === dynamicProductIdentifier ? -1 : 1
703
+ );
704
+ }
705
+
706
+ setAvailableProducts(sortedProducts);
707
+ console.log(`Loaded products for: ${productIds.join(', ')}`);
708
+
709
+ } catch (error) {
710
+ try {
711
+ // Fallback logic
712
+ const baseProducts = await getSubscriptions({ skus: [baseProductIdentifier] });
713
+ setAvailableProducts(baseProducts);
714
+ } catch (fallbackError) {
715
+ console.error('Failed to fetch base products:', fallbackError);
716
+ }
717
+ } finally {
718
+ setLoading(false);
719
+ }
720
+ };
721
+
722
+ const handlePurchase = async (productId) => {
723
+ // Implement the purchase handling logic as outlined in the remaining SDK integration steps.
724
+ };
725
+
726
+ useEffect(() => {
727
+ if (connected) {
728
+ fetchProducts();
729
+ }
730
+ }, [connected, iOSOfferCode]);;
731
+
732
+ const primaryProduct = availableProducts[0];
733
+
734
+ return (
735
+ <View style={{ padding: 20 }}>
736
+ <Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 10 }}>
737
+ Premium Subscription
738
+ </Text>
739
+
740
+ {iOSOfferCode && (
741
+ <View style={{ backgroundColor: '#e3f2fd', padding: 10, marginBottom: 15, borderRadius: 8 }}>
742
+ <Text style={{ color: '#1976d2', fontWeight: 'bold' }}>
743
+ 🎉 Special Offer Applied: {iOSOfferCode}
744
+ </Text>
745
+ </View>
746
+ )}
747
+
748
+ {loading ? (
749
+ <Text>Loading products...</Text>
750
+ ) : primaryProduct ? (
751
+ <View>
752
+ <Text style={{ fontSize: 16, marginBottom: 5 }}>
753
+ {primaryProduct.title}
754
+ </Text>
755
+ <Text style={{ fontSize: 14, color: '#666', marginBottom: 5 }}>
756
+ Price: {primaryProduct.localizedPrice}
757
+ </Text>
758
+ <Text style={{ fontSize: 12, color: '#999', marginBottom: 15 }}>
759
+ Product ID: {primaryProduct.productId}
760
+ </Text>
761
+
762
+ <Button
763
+ title={loading ? "Processing..." : "Subscribe Now"}
764
+ onPress={() => handlePurchase(primaryProduct.productId)}
765
+ disabled={loading}
766
+ />
767
+
768
+ {primaryProduct.productId === dynamicProductIdentifier && iOSOfferCode && (
769
+ <Text style={{ fontSize: 12, color: '#4caf50', marginTop: 10 }}>
770
+ ✓ Promotional pricing applied
771
+ </Text>
772
+ )}
773
+ </View>
774
+ ) : (
775
+ <View>
776
+ <Text style={{ color: '#f44336', marginBottom: 10 }}>
777
+ Product not found: {dynamicProductIdentifier}
778
+ </Text>
779
+ <Button
780
+ title="Retry"
781
+ onPress={fetchProducts}
782
+ />
783
+ </View>
784
+ )}
785
+
786
+ {availableProducts.length > 1 && (
787
+ <View style={{ marginTop: 20 }}>
788
+ <Text style={{ fontSize: 14, fontWeight: 'bold', marginBottom: 10 }}>
789
+ Other Options:
790
+ </Text>
791
+ {availableProducts.slice(1).map((product) => (
792
+ <Button
793
+ key={product.productId}
794
+ title={`${product.title} - ${product.localizedPrice}`}
795
+ onPress={() => handlePurchase(product.productId)}
796
+ />
797
+ ))}
798
+ </View>
799
+ )}
800
+ </View>
801
+ );
802
+ };
803
+ ```
804
+
805
+ ##### Key Features of Native IAP Integration:
806
+
807
+ 1. **Dynamic Product Loading**: Automatically constructs product IDs using the offer code modifier
808
+ 2. **Fallback Strategy**: If the promotional product isn't found, falls back to the base product
809
+ 3. **Visual Feedback**: Shows users when promotional pricing is applied
810
+ 4. **Error Handling**: Graceful handling when products aren't available
811
+
812
+
813
+ ### 3. Short Codes (Beta)
433
814
 
434
815
  #### What are Short Codes?
435
816
 
436
- Short codes are unique, 10-character alphanumeric identifiers that affiliates can use to promote products or subscriptions. These codes are ideal for influencers or partners, making them easier to share than long URLs.
817
+ Short codes are unique identifiers that affiliates can use to promote products or subscriptions. These codes are ideal for influencers or partners, making them easier to share than long URLs.
437
818
 
438
819
  **Example Use Case**: An influencer promotes a subscription with the short code "JOIN12345" within their TikTok video's description. When users enter this code within your app during sign-up or before purchase, the app tracks the subscription back to the influencer for commission payouts.
439
820
 
@@ -444,10 +825,12 @@ For more information, visit the [Insert Affiliate Short Codes Documentation](htt
444
825
  Use the `setShortCode` method to associate a short code with an affiliate. This is ideal for scenarios where users enter the code via an input field, pop-up, or similar UI element.
445
826
 
446
827
  Short codes must meet the following criteria:
447
- - Exactly **10 characters long**.
448
- - Contain only **letters and numbers** (alphanumeric characters).
828
+ - Between **3-25 characters long**.
829
+ - Contain only **letters, numbers, and underscores** (alphanumeric characters and underscores).
449
830
  - Replace {{ user_entered_short_code }} with the short code the user enters through your chosen input method, i.e. an input field / pop up element
450
831
 
832
+ When a short code is set, the SDK automatically attempts to fetch and store any associated offer codes for iOS users.
833
+
451
834
  ```javascript
452
835
  import {
453
836
  DeepLinkIapProvider,
@@ -459,6 +842,6 @@ Short codes must meet the following criteria:
459
842
 
460
843
  <Button
461
844
  title={'Set Short Code'}
462
- onPress={() => setShortCode('JOIN123456')}
845
+ onPress={() => setShortCode('JOIN_123')}
463
846
  />
464
847
  ```