insert-affiliate-react-native-sdk 1.9.0 → 1.10.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.
@@ -54,6 +54,8 @@ const ASYNC_KEYS = {
54
54
  USER_ACCOUNT_TOKEN: '@app_user_account_token',
55
55
  IOS_OFFER_CODE: '@app_ios_offer_code',
56
56
  AFFILIATE_STORED_DATE: '@app_affiliate_stored_date',
57
+ SDK_INIT_REPORTED: '@app_sdk_init_reported',
58
+ REPORTED_AFFILIATE_ASSOCIATIONS: '@app_reported_affiliate_associations',
57
59
  };
58
60
  // STARTING CONTEXT IMPLEMENTATION
59
61
  exports.DeepLinkIapContext = (0, react_1.createContext)({
@@ -112,6 +114,8 @@ const DeepLinkIapProvider = ({ children, }) => {
112
114
  console.log('[Insert Affiliate] [VERBOSE] Company code saved to AsyncStorage');
113
115
  console.log('[Insert Affiliate] [VERBOSE] SDK marked as initialized');
114
116
  }
117
+ // Report SDK initialization for onboarding verification (fire and forget)
118
+ reportSdkInitIfNeeded(companyCode, verboseLogging);
115
119
  }
116
120
  else {
117
121
  console.warn('[Insert Affiliate] SDK initialized without a company code.');
@@ -330,12 +334,24 @@ const DeepLinkIapProvider = ({ children, }) => {
330
334
  verboseLog('Invalid URL provided to handleInsertLinkAndroid');
331
335
  return false;
332
336
  }
333
- // Parse the URL to extract query parameters
334
- const urlObj = new URL(url);
335
- const insertAffiliate = urlObj.searchParams.get('insertAffiliate');
337
+ // Parse the URL to extract query parameters (React Native compatible)
338
+ // URLSearchParams is not available in React Native, so parse manually
339
+ let insertAffiliate = null;
340
+ const queryIndex = url.indexOf('?');
341
+ if (queryIndex !== -1) {
342
+ const queryString = url.substring(queryIndex + 1);
343
+ const params = queryString.split('&');
344
+ for (const param of params) {
345
+ const [key, value] = param.split('=');
346
+ if (key === 'insertAffiliate' && value) {
347
+ insertAffiliate = decodeURIComponent(value);
348
+ break;
349
+ }
350
+ }
351
+ }
336
352
  if (insertAffiliate && insertAffiliate.length > 0) {
337
353
  verboseLog(`Found insertAffiliate parameter: ${insertAffiliate}`);
338
- yield storeInsertAffiliateIdentifier({ link: insertAffiliate });
354
+ yield storeInsertAffiliateIdentifier({ link: insertAffiliate, source: 'deep_link_android' });
339
355
  return true;
340
356
  }
341
357
  else {
@@ -449,7 +465,7 @@ const DeepLinkIapProvider = ({ children, }) => {
449
465
  // If we have insertAffiliate parameter, use it as the affiliate identifier
450
466
  if (insertAffiliate && insertAffiliate.length > 0) {
451
467
  verboseLog(`Found insertAffiliate parameter, setting as affiliate identifier: ${insertAffiliate}`);
452
- yield storeInsertAffiliateIdentifier({ link: insertAffiliate });
468
+ yield storeInsertAffiliateIdentifier({ link: insertAffiliate, source: 'install_referrer' });
453
469
  return true;
454
470
  }
455
471
  else {
@@ -513,7 +529,7 @@ const DeepLinkIapProvider = ({ children, }) => {
513
529
  console.log(`[Insert Affiliate] Warning: URL company code (${companyCode}) doesn't match initialized company code (${activeCompanyCode})`);
514
530
  }
515
531
  // If URL scheme is used, we can straight away store the short code as the referring link
516
- yield storeInsertAffiliateIdentifier({ link: shortCode });
532
+ yield storeInsertAffiliateIdentifier({ link: shortCode, source: 'deep_link_ios' });
517
533
  // Collect and send enhanced system info to backend
518
534
  try {
519
535
  const enhancedSystemInfo = yield getEnhancedSystemInfo();
@@ -630,6 +646,87 @@ const DeepLinkIapProvider = ({ children, }) => {
630
646
  break;
631
647
  }
632
648
  };
649
+ // Reports a new affiliate association to the backend for tracking.
650
+ // Only reports each unique affiliateIdentifier once to prevent duplicates.
651
+ const reportAffiliateAssociationIfNeeded = (affiliateIdentifier, source) => __awaiter(void 0, void 0, void 0, function* () {
652
+ try {
653
+ const activeCompanyCode = yield getActiveCompanyCode();
654
+ if (!activeCompanyCode) {
655
+ verboseLog('Cannot report affiliate association: no company code available');
656
+ return;
657
+ }
658
+ // Get the set of already-reported affiliate identifiers
659
+ const reportedAssociationsJson = yield async_storage_1.default.getItem(ASYNC_KEYS.REPORTED_AFFILIATE_ASSOCIATIONS);
660
+ const reportedAssociations = reportedAssociationsJson ? JSON.parse(reportedAssociationsJson) : [];
661
+ // Check if this affiliate identifier has already been reported
662
+ if (reportedAssociations.includes(affiliateIdentifier)) {
663
+ verboseLog(`Affiliate association already reported for: ${affiliateIdentifier}, skipping`);
664
+ return;
665
+ }
666
+ verboseLog(`Reporting new affiliate association: ${affiliateIdentifier} (source: ${source})`);
667
+ const response = yield fetch('https://api.insertaffiliate.com/V1/onboarding/affiliate-associated', {
668
+ method: 'POST',
669
+ headers: {
670
+ 'Content-Type': 'application/json',
671
+ },
672
+ body: JSON.stringify({
673
+ companyId: activeCompanyCode,
674
+ affiliateIdentifier: affiliateIdentifier,
675
+ source: source,
676
+ timestamp: new Date().toISOString(),
677
+ }),
678
+ });
679
+ if (response.ok) {
680
+ // Add to reported set and persist
681
+ reportedAssociations.push(affiliateIdentifier);
682
+ yield async_storage_1.default.setItem(ASYNC_KEYS.REPORTED_AFFILIATE_ASSOCIATIONS, JSON.stringify(reportedAssociations));
683
+ verboseLog(`Affiliate association reported successfully for: ${affiliateIdentifier}`);
684
+ }
685
+ else {
686
+ verboseLog(`Affiliate association report failed with status: ${response.status}`);
687
+ }
688
+ }
689
+ catch (error) {
690
+ // Silently fail - this is non-critical telemetry
691
+ verboseLog(`Affiliate association report error: ${error}`);
692
+ }
693
+ });
694
+ // Reports SDK initialization to the backend for onboarding verification.
695
+ // Only reports once per install to minimize server load.
696
+ const reportSdkInitIfNeeded = (companyCode, verboseLogging) => __awaiter(void 0, void 0, void 0, function* () {
697
+ try {
698
+ // Only report once per install
699
+ const alreadyReported = yield async_storage_1.default.getItem(ASYNC_KEYS.SDK_INIT_REPORTED);
700
+ if (alreadyReported === 'true') {
701
+ return;
702
+ }
703
+ if (verboseLogging) {
704
+ console.log('[Insert Affiliate] Reporting SDK initialization for onboarding verification...');
705
+ }
706
+ const response = yield fetch('https://api.insertaffiliate.com/V1/onboarding/sdk-init', {
707
+ method: 'POST',
708
+ headers: {
709
+ 'Content-Type': 'application/json',
710
+ },
711
+ body: JSON.stringify({ companyId: companyCode }),
712
+ });
713
+ if (response.ok) {
714
+ yield async_storage_1.default.setItem(ASYNC_KEYS.SDK_INIT_REPORTED, 'true');
715
+ if (verboseLogging) {
716
+ console.log('[Insert Affiliate] SDK initialization reported successfully');
717
+ }
718
+ }
719
+ else if (verboseLogging) {
720
+ console.log(`[Insert Affiliate] SDK initialization report failed with status: ${response.status}`);
721
+ }
722
+ }
723
+ catch (error) {
724
+ // Silently fail - this is non-critical telemetry
725
+ if (verboseLogging) {
726
+ console.log(`[Insert Affiliate] SDK initialization report error: ${error}`);
727
+ }
728
+ }
729
+ });
633
730
  // MARK: - Deep Linking Utilities
634
731
  // Retrieves and validates clipboard content for UUID format
635
732
  const getClipboardUUID = () => __awaiter(void 0, void 0, void 0, function* () {
@@ -995,7 +1092,7 @@ const DeepLinkIapProvider = ({ children, }) => {
995
1092
  if (matchFound && response.data.matched_affiliate_shortCode && response.data.matched_affiliate_shortCode.length > 0) {
996
1093
  const matchedShortCode = response.data.matched_affiliate_shortCode;
997
1094
  verboseLog(`Storing Matched short code from backend: ${matchedShortCode}`);
998
- yield storeInsertAffiliateIdentifier({ link: matchedShortCode });
1095
+ yield storeInsertAffiliateIdentifier({ link: matchedShortCode, source: 'clipboard_match' });
999
1096
  }
1000
1097
  }
1001
1098
  // Check for a successful response
@@ -1109,7 +1206,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1109
1206
  const exists = yield checkAffiliateExists(capitalisedShortCode);
1110
1207
  if (exists) {
1111
1208
  // If affiliate exists, set the Insert Affiliate Identifier
1112
- yield storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
1209
+ yield storeInsertAffiliateIdentifier({ link: capitalisedShortCode, source: 'short_code_manual' });
1113
1210
  console.log(`[Insert Affiliate] Short code ${capitalisedShortCode} validated and stored successfully.`);
1114
1211
  return true;
1115
1212
  }
@@ -1262,7 +1359,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1262
1359
  if (!referringLink) {
1263
1360
  console.warn('[Insert Affiliate] Referring link is invalid.');
1264
1361
  verboseLog('Referring link is empty or invalid, storing as-is');
1265
- yield storeInsertAffiliateIdentifier({ link: referringLink });
1362
+ yield storeInsertAffiliateIdentifier({ link: referringLink, source: 'referring_link' });
1266
1363
  return `${referringLink}-${customerID}`;
1267
1364
  }
1268
1365
  // Get company code from state or storage
@@ -1279,7 +1376,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1279
1376
  if (isShortCode(referringLink)) {
1280
1377
  console.log('[Insert Affiliate] Referring link is already a short code.');
1281
1378
  verboseLog('Link is already a short code, storing directly');
1282
- yield storeInsertAffiliateIdentifier({ link: referringLink });
1379
+ yield storeInsertAffiliateIdentifier({ link: referringLink, source: 'referring_link' });
1283
1380
  return `${referringLink}-${customerID}`;
1284
1381
  }
1285
1382
  verboseLog('Link is not a short code, will convert via API');
@@ -1290,7 +1387,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1290
1387
  if (!encodedAffiliateLink) {
1291
1388
  console.error('[Insert Affiliate] Failed to encode affiliate link.');
1292
1389
  verboseLog('Failed to encode link, storing original');
1293
- yield storeInsertAffiliateIdentifier({ link: referringLink });
1390
+ yield storeInsertAffiliateIdentifier({ link: referringLink, source: 'referring_link' });
1294
1391
  return `${referringLink}-${customerID}`;
1295
1392
  }
1296
1393
  // Create the request URL
@@ -1309,7 +1406,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1309
1406
  console.log('[Insert Affiliate] Short link received:', shortLink);
1310
1407
  verboseLog(`Successfully converted to short link: ${shortLink}`);
1311
1408
  verboseLog('Storing short link to AsyncStorage...');
1312
- yield storeInsertAffiliateIdentifier({ link: shortLink });
1409
+ yield storeInsertAffiliateIdentifier({ link: shortLink, source: 'referring_link' });
1313
1410
  verboseLog('Short link stored successfully');
1314
1411
  return `${shortLink}-${customerID}`;
1315
1412
  }
@@ -1317,7 +1414,7 @@ const DeepLinkIapProvider = ({ children, }) => {
1317
1414
  console.warn('[Insert Affiliate] Unexpected response format.');
1318
1415
  verboseLog(`Unexpected API response. Status: ${response.status}, Data: ${JSON.stringify(response.data)}`);
1319
1416
  verboseLog('Storing original link as fallback');
1320
- yield storeInsertAffiliateIdentifier({ link: referringLink });
1417
+ yield storeInsertAffiliateIdentifier({ link: referringLink, source: 'referring_link' });
1321
1418
  return `${referringLink}-${customerID}`;
1322
1419
  }
1323
1420
  }
@@ -1329,8 +1426,8 @@ const DeepLinkIapProvider = ({ children, }) => {
1329
1426
  }
1330
1427
  ;
1331
1428
  function storeInsertAffiliateIdentifier(_a) {
1332
- return __awaiter(this, arguments, void 0, function* ({ link }) {
1333
- console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
1429
+ return __awaiter(this, arguments, void 0, function* ({ link, source }) {
1430
+ console.log(`[Insert Affiliate] Storing affiliate identifier: ${link} (source: ${source})`);
1334
1431
  // Check if we're trying to store the same link (prevent duplicate storage)
1335
1432
  const existingLink = yield getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
1336
1433
  if (existingLink === link) {
@@ -1355,6 +1452,11 @@ const DeepLinkIapProvider = ({ children, }) => {
1355
1452
  verboseLog(`Triggering callback with identifier: ${currentIdentifier}`);
1356
1453
  insertAffiliateIdentifierChangeCallbackRef.current(currentIdentifier);
1357
1454
  }
1455
+ // Report this new affiliate association to the backend (fire and forget)
1456
+ const fullIdentifier = yield returnInsertAffiliateIdentifier();
1457
+ if (fullIdentifier) {
1458
+ reportAffiliateAssociationIfNeeded(fullIdentifier, source);
1459
+ }
1358
1460
  });
1359
1461
  }
1360
1462
  const validatePurchaseWithIapticAPI = (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () {
@@ -0,0 +1,364 @@
1
+ # AppsFlyer Deep Linking Integration
2
+
3
+ This guide shows how to integrate InsertAffiliateReactNative SDK with AppsFlyer for deep linking attribution.
4
+
5
+ ## Prerequisites
6
+
7
+ - [AppsFlyer SDK for React Native](https://dev.appsflyer.com/hc/docs/react-native-plugin) installed and configured
8
+ - Create an AppsFlyer OneLink and provide it to affiliates via the [Insert Affiliate dashboard](https://app.insertaffiliate.com/affiliates)
9
+
10
+ ## Platform Setup
11
+
12
+ Complete the deep linking setup for AppsFlyer by following their official documentation:
13
+ - [AppsFlyer Deferred Deep Link Integration Guide](https://dev.appsflyer.com/hc/docs/deeplinkintegrate)
14
+
15
+ This covers all platform-specific configurations including:
16
+ - iOS: Info.plist configuration, AppDelegate setup, and universal links
17
+ - Android: AndroidManifest.xml intent filters, MainActivity setup, and App Links
18
+ - Testing and troubleshooting for both platforms
19
+
20
+ ## Integration Examples
21
+
22
+ Choose the example that matches your IAP verification platform:
23
+
24
+ ### Example with RevenueCat
25
+
26
+ ```javascript
27
+ import React, { useEffect } from 'react';
28
+ import { AppRegistry, Platform } from 'react-native';
29
+ import appsFlyer from 'react-native-appsflyer';
30
+ import Purchases from 'react-native-purchases';
31
+ import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
32
+ import App from './App';
33
+ import { name as appName } from './app.json';
34
+
35
+ const DeepLinkHandler = () => {
36
+ const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
37
+
38
+ useEffect(() => {
39
+ if (!isInitialized) return;
40
+
41
+ // Initialize AppsFlyer
42
+ const initAppsFlyer = async () => {
43
+ try {
44
+ const initOptions = {
45
+ devKey: 'YOUR_APPSFLYER_DEV_KEY',
46
+ isDebug: true,
47
+ appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
48
+ };
49
+
50
+ await appsFlyer.initSdk(initOptions);
51
+ } catch (error) {
52
+ console.error('AppsFlyer initialization error:', error);
53
+ }
54
+ };
55
+
56
+ // Handle deep link data
57
+ const handleDeepLink = async (deepLinkData) => {
58
+ if (deepLinkData && deepLinkData.data) {
59
+ const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
60
+
61
+ if (referringLink) {
62
+ try {
63
+ const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
64
+
65
+ if (insertAffiliateIdentifier) {
66
+ await Purchases.setAttributes({ "insert_affiliate": insertAffiliateIdentifier });
67
+ }
68
+ } catch (err) {
69
+ console.error('Error setting affiliate identifier:', err);
70
+ }
71
+ }
72
+ }
73
+ };
74
+
75
+ // Listen for all deep link types
76
+ appsFlyer.onDeepLink(handleDeepLink);
77
+ appsFlyer.onAppOpenAttribution(handleDeepLink);
78
+ appsFlyer.onInstallConversionData(handleDeepLink);
79
+
80
+ initAppsFlyer();
81
+ }, [setInsertAffiliateIdentifier, isInitialized]);
82
+
83
+ return <App />;
84
+ };
85
+
86
+ const RootComponent = () => {
87
+ return (
88
+ <DeepLinkIapProvider>
89
+ <DeepLinkHandler />
90
+ </DeepLinkIapProvider>
91
+ );
92
+ };
93
+
94
+ AppRegistry.registerComponent(appName, () => RootComponent);
95
+ ```
96
+
97
+ **Replace the following:**
98
+ - `YOUR_APPSFLYER_DEV_KEY` with your AppsFlyer Dev Key
99
+ - `YOUR_IOS_APP_ID` with your iOS App ID (numbers only, e.g., "123456789")
100
+ - `YOUR_ANDROID_PACKAGE_NAME` with your Android package name
101
+
102
+ ### Example with Adapty
103
+
104
+ ```javascript
105
+ import React, { useEffect } from 'react';
106
+ import { AppRegistry, Platform } from 'react-native';
107
+ import appsFlyer from 'react-native-appsflyer';
108
+ import { adapty } from 'react-native-adapty';
109
+ import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
110
+ import App from './App';
111
+ import { name as appName } from './app.json';
112
+
113
+ const DeepLinkHandler = () => {
114
+ const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
115
+
116
+ useEffect(() => {
117
+ if (!isInitialized) return;
118
+
119
+ const initAppsFlyer = async () => {
120
+ try {
121
+ const initOptions = {
122
+ devKey: 'YOUR_APPSFLYER_DEV_KEY',
123
+ isDebug: true,
124
+ appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
125
+ };
126
+
127
+ await appsFlyer.initSdk(initOptions);
128
+ } catch (error) {
129
+ console.error('AppsFlyer initialization error:', error);
130
+ }
131
+ };
132
+
133
+ const handleDeepLink = async (deepLinkData) => {
134
+ if (deepLinkData && deepLinkData.data) {
135
+ const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
136
+
137
+ if (referringLink) {
138
+ try {
139
+ const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
140
+
141
+ if (insertAffiliateIdentifier) {
142
+ await adapty.updateProfile({
143
+ codableCustomAttributes: {
144
+ insert_affiliate: insertAffiliateIdentifier,
145
+ },
146
+ });
147
+ }
148
+ } catch (err) {
149
+ console.error('Error setting affiliate identifier:', err);
150
+ }
151
+ }
152
+ }
153
+ };
154
+
155
+ appsFlyer.onDeepLink(handleDeepLink);
156
+ appsFlyer.onAppOpenAttribution(handleDeepLink);
157
+ appsFlyer.onInstallConversionData(handleDeepLink);
158
+
159
+ initAppsFlyer();
160
+ }, [setInsertAffiliateIdentifier, isInitialized]);
161
+
162
+ return <App />;
163
+ };
164
+
165
+ const RootComponent = () => {
166
+ return (
167
+ <DeepLinkIapProvider>
168
+ <DeepLinkHandler />
169
+ </DeepLinkIapProvider>
170
+ );
171
+ };
172
+
173
+ AppRegistry.registerComponent(appName, () => RootComponent);
174
+ ```
175
+
176
+ ### Example with Apphud
177
+
178
+ ```javascript
179
+ import React, { useEffect } from 'react';
180
+ import { AppRegistry, Platform } from 'react-native';
181
+ import appsFlyer from 'react-native-appsflyer';
182
+ import Apphud from 'react-native-apphud';
183
+ import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
184
+ import App from './App';
185
+ import { name as appName } from './app.json';
186
+
187
+ const DeepLinkHandler = () => {
188
+ const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
189
+
190
+ useEffect(() => {
191
+ if (!isInitialized) return;
192
+
193
+ const initAppsFlyer = async () => {
194
+ try {
195
+ const initOptions = {
196
+ devKey: 'YOUR_APPSFLYER_DEV_KEY',
197
+ isDebug: true,
198
+ appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
199
+ };
200
+
201
+ await appsFlyer.initSdk(initOptions);
202
+ } catch (error) {
203
+ console.error('AppsFlyer initialization error:', error);
204
+ }
205
+ };
206
+
207
+ const handleDeepLink = async (deepLinkData) => {
208
+ if (deepLinkData && deepLinkData.data) {
209
+ const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
210
+
211
+ if (referringLink) {
212
+ try {
213
+ const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
214
+
215
+ if (insertAffiliateIdentifier) {
216
+ await Apphud.setUserProperty("insert_affiliate", insertAffiliateIdentifier, false);
217
+ }
218
+ } catch (err) {
219
+ console.error('Error setting affiliate identifier:', err);
220
+ }
221
+ }
222
+ }
223
+ };
224
+
225
+ appsFlyer.onDeepLink(handleDeepLink);
226
+ appsFlyer.onAppOpenAttribution(handleDeepLink);
227
+ appsFlyer.onInstallConversionData(handleDeepLink);
228
+
229
+ initAppsFlyer();
230
+ }, [setInsertAffiliateIdentifier, isInitialized]);
231
+
232
+ return <App />;
233
+ };
234
+
235
+ const RootComponent = () => {
236
+ return (
237
+ <DeepLinkIapProvider>
238
+ <DeepLinkHandler />
239
+ </DeepLinkIapProvider>
240
+ );
241
+ };
242
+
243
+ AppRegistry.registerComponent(appName, () => RootComponent);
244
+ ```
245
+
246
+ ### Example with Iaptic / Store Direct Integration
247
+
248
+ ```javascript
249
+ import React, { useEffect } from 'react';
250
+ import { AppRegistry, Platform } from 'react-native';
251
+ import appsFlyer from 'react-native-appsflyer';
252
+ import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
253
+ import App from './App';
254
+ import { name as appName } from './app.json';
255
+
256
+ const DeepLinkHandler = () => {
257
+ const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
258
+
259
+ useEffect(() => {
260
+ if (!isInitialized) return;
261
+
262
+ const initAppsFlyer = async () => {
263
+ try {
264
+ const initOptions = {
265
+ devKey: 'YOUR_APPSFLYER_DEV_KEY',
266
+ isDebug: true,
267
+ appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
268
+ };
269
+
270
+ await appsFlyer.initSdk(initOptions);
271
+ } catch (error) {
272
+ console.error('AppsFlyer initialization error:', error);
273
+ }
274
+ };
275
+
276
+ const handleDeepLink = async (deepLinkData) => {
277
+ if (deepLinkData && deepLinkData.data) {
278
+ const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
279
+
280
+ if (referringLink) {
281
+ try {
282
+ await setInsertAffiliateIdentifier(referringLink);
283
+ // Affiliate identifier is stored automatically for Iaptic/direct store integration
284
+ } catch (err) {
285
+ console.error('Error setting affiliate identifier:', err);
286
+ }
287
+ }
288
+ }
289
+ };
290
+
291
+ appsFlyer.onDeepLink(handleDeepLink);
292
+ appsFlyer.onAppOpenAttribution(handleDeepLink);
293
+ appsFlyer.onInstallConversionData(handleDeepLink);
294
+
295
+ initAppsFlyer();
296
+ }, [setInsertAffiliateIdentifier, isInitialized]);
297
+
298
+ return <App />;
299
+ };
300
+
301
+ const RootComponent = () => {
302
+ return (
303
+ <DeepLinkIapProvider>
304
+ <DeepLinkHandler />
305
+ </DeepLinkIapProvider>
306
+ );
307
+ };
308
+
309
+ AppRegistry.registerComponent(appName, () => RootComponent);
310
+ ```
311
+
312
+ ## Deep Link Listener Types
313
+
314
+ AppsFlyer provides three types of deep link callbacks:
315
+
316
+ | Callback | When It Fires | Use Case |
317
+ |----------|---------------|----------|
318
+ | `onDeepLink` | App opened via deep link (app already installed) | Direct attribution |
319
+ | `onAppOpenAttribution` | App opened via deep link with attribution data | Re-engagement campaigns |
320
+ | `onInstallConversionData` | First app launch after install | Deferred deep linking |
321
+
322
+ For comprehensive affiliate tracking, we recommend listening to all three as shown in the examples above.
323
+
324
+ ## Testing
325
+
326
+ Test your AppsFlyer deep link integration:
327
+
328
+ ```bash
329
+ # Test with your OneLink URL (iOS Simulator)
330
+ xcrun simctl openurl booted "https://your-app.onelink.me/abc123"
331
+
332
+ # Test with your OneLink URL (Android Emulator)
333
+ adb shell am start -W -a android.intent.action.VIEW -d "https://your-app.onelink.me/abc123"
334
+ ```
335
+
336
+ ## Troubleshooting
337
+
338
+ **Problem:** Attribution callback not firing
339
+ - **Solution:** Ensure AppsFlyer SDK is initialized with correct dev key and app ID
340
+ - Check AppsFlyer dashboard to verify OneLink is active
341
+ - Verify `isInitialized` is `true` before setting up listeners
342
+
343
+ **Problem:** Deep link parameters not captured
344
+ - **Solution:** Verify deep link contains correct parameters in AppsFlyer dashboard
345
+ - Check Info.plist has correct URL schemes and associated domains (iOS)
346
+ - Check AndroidManifest.xml has correct intent filters (Android)
347
+
348
+ **Problem:** Deferred deep linking not working
349
+ - **Solution:** Make sure `onInstallConversionData` listener is set up
350
+ - Test with a fresh app install (uninstall/reinstall)
351
+ - Verify AppsFlyer's "Deferred Deep Linking" is enabled in dashboard
352
+
353
+ **Problem:** `deep_link_value` is undefined
354
+ - **Solution:** Ensure you're accessing the correct property path in the callback data
355
+ - Log the full `deepLinkData` object to see available fields
356
+
357
+ ## Next Steps
358
+
359
+ After completing AppsFlyer integration:
360
+ 1. Test deep link attribution with a test affiliate link
361
+ 2. Verify affiliate identifier is stored correctly
362
+ 3. Make a test purchase to confirm tracking works end-to-end
363
+
364
+ [Back to Main README](../readme.md)