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.
@@ -1,5 +1,5 @@
1
1
  import React, { createContext, useEffect, useState } from 'react';
2
- import { Platform } from 'react-native';
2
+ import { Platform, Linking } from 'react-native';
3
3
  import axios from 'axios';
4
4
  import AsyncStorage from '@react-native-async-storage/async-storage';
5
5
 
@@ -15,6 +15,7 @@ type CustomPurchase = {
15
15
  type T_DEEPLINK_IAP_CONTEXT = {
16
16
  referrerLink: string;
17
17
  userId: string;
18
+ iOSOfferCode: string | null;
18
19
  returnInsertAffiliateIdentifier: () => Promise<string | null>;
19
20
  validatePurchaseWithIapticAPI: (
20
21
  jsonIapPurchase: CustomPurchase,
@@ -31,7 +32,7 @@ type T_DEEPLINK_IAP_CONTEXT = {
31
32
  setInsertAffiliateIdentifier: (
32
33
  referringLink: string
33
34
  ) => Promise<void | string>;
34
- initialize: (code: string | null) => Promise<void>;
35
+ initialize: (code: string | null, verboseLogging?: boolean) => Promise<void>;
35
36
  isInitialized: boolean;
36
37
  };
37
38
 
@@ -57,12 +58,14 @@ const ASYNC_KEYS = {
57
58
  USER_ID: '@app_user_id',
58
59
  COMPANY_CODE: '@app_company_code',
59
60
  USER_ACCOUNT_TOKEN: '@app_user_account_token',
61
+ IOS_OFFER_CODE: '@app_ios_offer_code',
60
62
  };
61
63
 
62
64
  // STARTING CONTEXT IMPLEMENTATION
63
65
  export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
64
66
  referrerLink: '',
65
67
  userId: '',
68
+ iOSOfferCode: null,
66
69
  returnInsertAffiliateIdentifier: async () => '',
67
70
  validatePurchaseWithIapticAPI: async (
68
71
  jsonIapPurchase: CustomPurchase,
@@ -75,7 +78,7 @@ export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
75
78
  trackEvent: async (eventName: string) => {},
76
79
  setShortCode: async (shortCode: string) => {},
77
80
  setInsertAffiliateIdentifier: async (referringLink: string) => {},
78
- initialize: async (code: string | null) => {},
81
+ initialize: async (code: string | null, verboseLogging?: boolean) => {},
79
82
  isInitialized: false,
80
83
  });
81
84
 
@@ -86,9 +89,19 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
86
89
  const [userId, setUserId] = useState<string>('');
87
90
  const [companyCode, setCompanyCode] = useState<string | null>(null);
88
91
  const [isInitialized, setIsInitialized] = useState<boolean>(false);
92
+ const [verboseLogging, setVerboseLogging] = useState<boolean>(false);
93
+ const [iOSOfferCode, setIOSOfferCode] = useState<string | null>(null);
89
94
 
90
95
  // MARK: Initialize the SDK
91
- const initialize = async (companyCode: string | null): Promise<void> => {
96
+ const initialize = async (companyCode: string | null, verboseLogging: boolean = false): Promise<void> => {
97
+ setVerboseLogging(verboseLogging);
98
+
99
+ if (verboseLogging) {
100
+ console.log('[Insert Affiliate] [VERBOSE] Starting SDK initialization...');
101
+ console.log('[Insert Affiliate] [VERBOSE] Company code provided:', companyCode ? 'Yes' : 'No');
102
+ console.log('[Insert Affiliate] [VERBOSE] Verbose logging enabled');
103
+ }
104
+
92
105
  if (isInitialized) {
93
106
  console.error('[Insert Affiliate] SDK is already initialized.');
94
107
  return;
@@ -101,11 +114,18 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
101
114
  console.log(
102
115
  `[Insert Affiliate] SDK initialized with company code: ${companyCode}`
103
116
  );
117
+ if (verboseLogging) {
118
+ console.log('[Insert Affiliate] [VERBOSE] Company code saved to AsyncStorage');
119
+ console.log('[Insert Affiliate] [VERBOSE] SDK marked as initialized');
120
+ }
104
121
  } else {
105
122
  console.warn(
106
123
  '[Insert Affiliate] SDK initialized without a company code.'
107
124
  );
108
125
  setIsInitialized(true);
126
+ if (verboseLogging) {
127
+ console.log('[Insert Affiliate] [VERBOSE] No company code provided, SDK initialized in limited mode');
128
+ }
109
129
  }
110
130
  };
111
131
 
@@ -114,15 +134,35 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
114
134
  useEffect(() => {
115
135
  const fetchAsyncEssentials = async () => {
116
136
  try {
137
+ verboseLog('Loading stored data from AsyncStorage...');
117
138
  const uId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
118
139
  const refLink = await getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
140
+ const companyCodeFromStorage = await getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
141
+ const storedIOSOfferCode = await getValueFromAsync(ASYNC_KEYS.IOS_OFFER_CODE);
142
+
143
+ verboseLog(`User ID found: ${uId ? 'Yes' : 'No'}`);
144
+ verboseLog(`Referrer link found: ${refLink ? 'Yes' : 'No'}`);
145
+ verboseLog(`Company code found: ${companyCodeFromStorage ? 'Yes' : 'No'}`);
146
+ verboseLog(`iOS Offer Code found: ${storedIOSOfferCode ? 'Yes' : 'No'}`);
119
147
 
120
148
  if (uId && refLink) {
121
149
  setUserId(uId);
122
150
  setReferrerLink(refLink);
151
+ verboseLog('User ID and referrer link restored from storage');
152
+ }
153
+
154
+ if (companyCodeFromStorage) {
155
+ setCompanyCode(companyCodeFromStorage);
156
+ verboseLog('Company code restored from storage');
157
+ }
158
+
159
+ if (storedIOSOfferCode) {
160
+ setIOSOfferCode(storedIOSOfferCode);
161
+ verboseLog('iOS Offer Code restored from storage');
123
162
  }
124
163
  } catch (error) {
125
164
  errorLog(`ERROR ~ fetchAsyncEssentials: ${error}`);
165
+ verboseLog(`Error loading from AsyncStorage: ${error}`);
126
166
  }
127
167
  };
128
168
 
@@ -130,12 +170,17 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
130
170
  }, []);
131
171
 
132
172
  async function generateThenSetUserID() {
173
+ verboseLog('Getting or generating user ID...');
133
174
  let userId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
175
+
134
176
  if (!userId) {
177
+ verboseLog('No existing user ID found, generating new one...');
135
178
  userId = generateUserID();
136
179
  setUserId(userId);
137
180
  await saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
181
+ verboseLog(`Generated and saved new user ID: ${userId}`);
138
182
  } else {
183
+ verboseLog(`Found existing user ID: ${userId}`);
139
184
  setUserId(userId);
140
185
  }
141
186
 
@@ -173,6 +218,33 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
173
218
  await AsyncStorage.clear();
174
219
  };
175
220
 
221
+ // Helper function to get company code from state or storage
222
+ const getActiveCompanyCode = async (): Promise<string | null> => {
223
+ verboseLog('Getting active company code...');
224
+ let activeCompanyCode = companyCode;
225
+ verboseLog(`Company code in React state: ${activeCompanyCode || 'empty'}`);
226
+
227
+ if (!activeCompanyCode || (activeCompanyCode.trim() === '' && activeCompanyCode !== null)) {
228
+ verboseLog('Company code not in state, checking AsyncStorage...');
229
+ activeCompanyCode = await getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
230
+ verboseLog(`Company code in AsyncStorage: ${activeCompanyCode || 'empty'}`);
231
+
232
+ if (activeCompanyCode) {
233
+ // Update state for future use
234
+ setCompanyCode(activeCompanyCode);
235
+ verboseLog('Updated React state with company code from storage');
236
+ }
237
+ }
238
+ return activeCompanyCode;
239
+ };
240
+
241
+ // Helper function for verbose logging
242
+ const verboseLog = (message: string) => {
243
+ if (verboseLogging) {
244
+ console.log(`[Insert Affiliate] [VERBOSE] ${message}`);
245
+ }
246
+ };
247
+
176
248
  // Helper function to log errors
177
249
  const errorLog = (message: string, type?: 'error' | 'warn' | 'log') => {
178
250
  switch (type) {
@@ -190,9 +262,9 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
190
262
 
191
263
  // MARK: Short Codes
192
264
  const isShortCode = (referringLink: string): boolean => {
193
- // Short codes are less than 10 characters
194
- const isValidCharacters = /^[a-zA-Z0-9]+$/.test(referringLink);
195
- return isValidCharacters && referringLink.length < 25 && referringLink.length > 3;
265
+ // Short codes are 3-25 characters and can include underscores
266
+ const isValidCharacters = /^[a-zA-Z0-9_]+$/.test(referringLink);
267
+ return isValidCharacters && referringLink.length >= 3 && referringLink.length <= 25;
196
268
  };
197
269
 
198
270
  async function setShortCode(shortCode: string): Promise<void> {
@@ -243,11 +315,37 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
243
315
  };
244
316
 
245
317
  // MARK: Return Insert Affiliate Identifier
318
+ // Instead of just reading React state
246
319
  const returnInsertAffiliateIdentifier = async (): Promise<string | null> => {
247
320
  try {
248
- return `${referrerLink}-${userId}`;
321
+ verboseLog('Getting insert affiliate identifier...');
322
+ verboseLog(`React state - referrerLink: ${referrerLink || 'empty'}, userId: ${userId || 'empty'}`);
323
+
324
+ // Try React state first
325
+ if (referrerLink && userId) {
326
+ const identifier = `${referrerLink}-${userId}`;
327
+ verboseLog(`Found identifier in React state: ${identifier}`);
328
+ return identifier;
329
+ }
330
+
331
+ verboseLog('React state empty, checking AsyncStorage...');
332
+
333
+ // Fallback to async storage if React state is empty
334
+ const storedLink = await getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
335
+ const storedUserId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
336
+
337
+ verboseLog(`AsyncStorage - storedLink: ${storedLink || 'empty'}, storedUserId: ${storedUserId || 'empty'}`);
338
+
339
+ if (storedLink && storedUserId) {
340
+ const identifier = `${storedLink}-${storedUserId}`;
341
+ verboseLog(`Found identifier in AsyncStorage: ${identifier}`);
342
+ return identifier;
343
+ }
344
+
345
+ verboseLog('No affiliate identifier found in state or storage');
346
+ return null;
249
347
  } catch (error) {
250
- errorLog(`ERROR ~ returnInsertAffiliateIdentifier: ${error}`);
348
+ verboseLog(`Error getting affiliate identifier: ${error}`);
251
349
  return null;
252
350
  }
253
351
  };
@@ -258,85 +356,106 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
258
356
  referringLink: string
259
357
  ): Promise<void | string> {
260
358
  console.log('[Insert Affiliate] Setting affiliate identifier.');
359
+ verboseLog(`Input referringLink: ${referringLink}`);
261
360
 
262
361
  try {
362
+ verboseLog('Generating or retrieving user ID...');
263
363
  const customerID = await generateThenSetUserID();
264
364
  console.log(
265
365
  '[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.'
266
366
  );
367
+ verboseLog(`Customer ID: ${customerID}`);
267
368
 
268
369
  if (!referringLink) {
269
370
  console.warn('[Insert Affiliate] Referring link is invalid.');
270
- let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
371
+ verboseLog('Referring link is empty or invalid, storing as-is');
271
372
  await storeInsertAffiliateIdentifier({ link: referringLink });
272
- return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
373
+ return `${referringLink}-${customerID}`;
273
374
  }
274
375
 
275
- if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
276
- let companyCodeFromStorage = await getValueFromAsync(
277
- ASYNC_KEYS.COMPANY_CODE
376
+ // Get company code from state or storage
377
+ verboseLog('Getting company code...');
378
+ const activeCompanyCode = await getActiveCompanyCode();
379
+ verboseLog(`Active company code: ${activeCompanyCode || 'Not found'}`);
380
+
381
+ if (!activeCompanyCode) {
382
+ console.error(
383
+ '[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.'
278
384
  );
279
-
280
- if (companyCodeFromStorage !== null) {
281
- setCompanyCode(companyCodeFromStorage);
282
- } else {
283
- console.error(
284
- '[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.'
285
- );
286
- return;
287
- }
385
+ verboseLog('Company code missing, cannot proceed with API call');
386
+ return;
288
387
  }
289
388
 
290
389
  // Check if referring link is already a short code, if so save it and stop here.
390
+ verboseLog('Checking if referring link is already a short code...');
291
391
  if (isShortCode(referringLink)) {
292
392
  console.log(
293
393
  '[Insert Affiliate] Referring link is already a short code.'
294
394
  );
295
- let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
395
+ verboseLog('Link is already a short code, storing directly');
296
396
  await storeInsertAffiliateIdentifier({ link: referringLink });
297
- return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
397
+ return `${referringLink}-${customerID}`;
298
398
  }
299
399
 
400
+ verboseLog('Link is not a short code, will convert via API');
401
+
300
402
  // If the code is not already a short code, encode it raedy to send to our endpoint to return the short code. Save it before making the call in case something goes wrong
301
403
  // Encode the referring link
404
+ verboseLog('Encoding referring link for API call...');
302
405
  const encodedAffiliateLink = encodeURIComponent(referringLink);
303
406
  if (!encodedAffiliateLink) {
304
407
  console.error('[Insert Affiliate] Failed to encode affiliate link.');
305
-
306
- let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
408
+ verboseLog('Failed to encode link, storing original');
307
409
  await storeInsertAffiliateIdentifier({ link: referringLink });
308
- return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
410
+ return `${referringLink}-${customerID}`;
309
411
  }
310
412
 
311
413
  // Create the request URL
312
- const urlString = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encodedAffiliateLink}`;
414
+ const urlString = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${activeCompanyCode}&deepLinkUrl=${encodedAffiliateLink}`;
313
415
  console.log('[Insert Affiliate] urlString .', urlString);
416
+ verboseLog('Making API request to convert deep link to short code...');
417
+
314
418
  const response = await axios.get(urlString, {
315
419
  headers: {
316
420
  'Content-Type': 'application/json',
317
421
  },
318
422
  });
423
+
424
+ verboseLog(`API response status: ${response.status}`);
319
425
 
320
426
  // Call to the backend for the short code and save the resolse in valid
321
427
  if (response.status === 200 && response.data.shortLink) {
322
428
  const shortLink = response.data.shortLink;
323
429
  console.log('[Insert Affiliate] Short link received:', shortLink);
430
+ verboseLog(`Successfully converted to short link: ${shortLink}`);
431
+ verboseLog('Storing short link to AsyncStorage...');
432
+ await storeInsertAffiliateIdentifier({ link: shortLink });
433
+ verboseLog('Short link stored successfully');
324
434
  return `${shortLink}-${customerID}`;
325
435
  } else {
326
436
  console.warn('[Insert Affiliate] Unexpected response format.');
327
- let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
437
+ verboseLog(`Unexpected API response. Status: ${response.status}, Data: ${JSON.stringify(response.data)}`);
438
+ verboseLog('Storing original link as fallback');
328
439
  await storeInsertAffiliateIdentifier({ link: referringLink });
329
- return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
440
+ return `${referringLink}-${customerID}`;
330
441
  }
331
442
  } catch (error) {
332
443
  console.error('[Insert Affiliate] Error:', error);
444
+ verboseLog(`Error in setInsertAffiliateIdentifier: ${error}`);
333
445
  }
334
446
  };
335
447
 
336
448
  async function storeInsertAffiliateIdentifier({ link }: { link: string }) {
337
449
  console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
450
+ verboseLog(`Updating React state with referrer link: ${link}`);
338
451
  setReferrerLink(link);
452
+ verboseLog(`Saving referrer link to AsyncStorage...`);
339
453
  await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
454
+ verboseLog(`Referrer link saved to AsyncStorage successfully`);
455
+
456
+ // Automatically fetch and store offer code for any affiliate identifier
457
+ verboseLog('Attempting to fetch offer code for stored affiliate identifier...');
458
+ await retrieveAndStoreOfferCode(link);
340
459
  }
341
460
 
342
461
  const validatePurchaseWithIapticAPI = async (
@@ -419,26 +538,34 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
419
538
  };
420
539
 
421
540
  const storeExpectedStoreTransaction = async (purchaseToken: string): Promise<void> => {
422
- if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
541
+ verboseLog(`Storing expected store transaction with token: ${purchaseToken}`);
542
+
543
+ const activeCompanyCode = await getActiveCompanyCode();
544
+ if (!activeCompanyCode) {
423
545
  console.error("[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.");
546
+ verboseLog("Cannot store transaction: no company code available");
424
547
  return;
425
548
  }
426
549
 
427
550
  const shortCode = await returnInsertAffiliateIdentifier();
428
551
  if (!shortCode) {
429
552
  console.error("[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.");
553
+ verboseLog("Cannot store transaction: no affiliate identifier available");
430
554
  return;
431
555
  }
432
556
 
557
+ verboseLog(`Company code: ${activeCompanyCode}, Short code: ${shortCode}`);
558
+
433
559
  // Build JSON payload
434
560
  const payload = {
435
561
  UUID: purchaseToken,
436
- companyCode,
562
+ companyCode: activeCompanyCode,
437
563
  shortCode,
438
564
  storedDate: new Date().toISOString(), // ISO8601 format
439
565
  };
440
566
 
441
567
  console.log("[Insert Affiliate] Storing expected transaction: ", payload);
568
+ verboseLog("Making API call to store expected transaction...");
442
569
 
443
570
  try {
444
571
  const response = await fetch("https://api.insertaffiliate.com/v1/api/app-store-webhook/create-expected-transaction", {
@@ -449,40 +576,57 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
449
576
  body: JSON.stringify(payload),
450
577
  });
451
578
 
579
+ verboseLog(`API response status: ${response.status}`);
580
+
452
581
  if (response.ok) {
453
582
  console.info("[Insert Affiliate] Expected transaction stored successfully.");
583
+ verboseLog("Expected transaction stored successfully on server");
454
584
  } else {
455
585
  const errorText = await response.text();
456
586
  console.error(`[Insert Affiliate] Failed to store expected transaction with status code: ${response.status}. Response: ${errorText}`);
587
+ verboseLog(`API error response: ${errorText}`);
457
588
  }
458
589
  } catch (error) {
459
590
  console.error(`[Insert Affiliate] Error storing expected transaction: ${error}`);
591
+ verboseLog(`Network error storing transaction: ${error}`);
460
592
  }
461
593
  };
462
594
 
463
595
  // MARK: Track Event
464
596
  const trackEvent = async (eventName: string): Promise<void> => {
465
597
  try {
466
- if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
598
+ verboseLog(`Tracking event: ${eventName}`);
599
+
600
+ const activeCompanyCode = await getActiveCompanyCode();
601
+ if (!activeCompanyCode) {
467
602
  console.error("[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.");
603
+ verboseLog("Cannot track event: no company code available");
468
604
  return Promise.resolve();
469
605
  }
470
606
 
471
- console.log("track event called with - companyCode: ", companyCode);
607
+ console.log("track event called with - companyCode: ", activeCompanyCode);
472
608
 
473
609
  if (!referrerLink || !userId) {
474
610
  console.warn(
475
611
  '[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.'
476
612
  );
613
+ verboseLog("Cannot track event: no affiliate identifier available");
477
614
  return Promise.resolve();
478
615
  }
479
616
 
617
+ const deepLinkParam = `${referrerLink}-${userId}`;
618
+ verboseLog(`Deep link param: ${deepLinkParam}`);
619
+
480
620
  const payload = {
481
621
  eventName,
482
- deepLinkParam: `${referrerLink}-${userId}`,
483
- companyId: companyCode,
622
+ deepLinkParam: deepLinkParam,
623
+ companyId: activeCompanyCode,
484
624
  };
485
625
 
626
+ verboseLog(`Track event payload: ${JSON.stringify(payload)}`);
627
+
628
+ verboseLog("Making API call to track event...");
629
+
486
630
  const response = await axios.post(
487
631
  'https://api.insertaffiliate.com/v1/trackEvent',
488
632
  payload,
@@ -491,24 +635,109 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
491
635
  }
492
636
  );
493
637
 
638
+ verboseLog(`Track event API response status: ${response.status}`);
639
+
494
640
  if (response.status === 200) {
495
641
  console.log('[Insert Affiliate] Event tracked successfully');
642
+ verboseLog("Event tracked successfully on server");
496
643
  } else {
497
644
  console.error(
498
645
  `[Insert Affiliate] Failed to track event with status code: ${response.status}`
499
646
  );
647
+ verboseLog(`Track event API error: status ${response.status}, response: ${JSON.stringify(response.data)}`);
500
648
  }
501
649
  } catch (error) {
502
650
  console.error('[Insert Affiliate] Error tracking event:', error);
651
+ verboseLog(`Network error tracking event: ${error}`);
503
652
  return Promise.reject(error);
504
653
  }
505
654
  };
506
655
 
656
+ const fetchOfferCode = async (affiliateLink: string): Promise<string | null> => {
657
+ try {
658
+ const activeCompanyCode = await getActiveCompanyCode();
659
+ if (!activeCompanyCode) {
660
+ verboseLog('Cannot fetch offer code: no company code available');
661
+ return null;
662
+ }
663
+
664
+ const encodedAffiliateLink = encodeURIComponent(affiliateLink);
665
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${activeCompanyCode}/${encodedAffiliateLink}`;
666
+
667
+ verboseLog(`Fetching offer code from: ${url}`);
668
+
669
+ const response = await axios.get(url);
670
+
671
+ if (response.status === 200) {
672
+ const offerCode = response.data;
673
+
674
+ // Check for specific error strings from API
675
+ if (typeof offerCode === 'string' && (
676
+ offerCode.includes("errorofferCodeNotFound") ||
677
+ offerCode.includes("errorAffiliateoffercodenotfoundinanycompany") ||
678
+ offerCode.includes("errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas") ||
679
+ offerCode.includes("Routenotfound")
680
+ )) {
681
+ console.warn(`[Insert Affiliate] Offer code not found or invalid: ${offerCode}`);
682
+ verboseLog(`Offer code not found or invalid: ${offerCode}`);
683
+ return null;
684
+ }
685
+
686
+ const cleanedOfferCode = cleanOfferCode(offerCode);
687
+ verboseLog(`Successfully fetched and cleaned offer code: ${cleanedOfferCode}`);
688
+ return cleanedOfferCode;
689
+ } else {
690
+ console.error(`[Insert Affiliate] Failed to fetch offer code. Status code: ${response.status}, Response: ${JSON.stringify(response.data)}`);
691
+ verboseLog(`Failed to fetch offer code. Status code: ${response.status}, Response: ${JSON.stringify(response.data)}`);
692
+ return null;
693
+ }
694
+ } catch (error) {
695
+ console.error('[Insert Affiliate] Error fetching offer code:', error);
696
+ verboseLog(`Error fetching offer code: ${error}`);
697
+ return null;
698
+ }
699
+ };
700
+
701
+ const retrieveAndStoreOfferCode = async (affiliateLink: string): Promise<void> => {
702
+ try {
703
+ verboseLog(`Attempting to retrieve and store offer code for: ${affiliateLink}`);
704
+
705
+ const offerCode = await fetchOfferCode(affiliateLink);
706
+
707
+ if (offerCode && offerCode.length > 0) {
708
+ // Store in both AsyncStorage and state
709
+ await saveValueInAsync(ASYNC_KEYS.IOS_OFFER_CODE, offerCode);
710
+ setIOSOfferCode(offerCode);
711
+ verboseLog(`Successfully stored offer code: ${offerCode}`);
712
+ console.log('[Insert Affiliate] Offer code retrieved and stored successfully');
713
+ } else {
714
+ verboseLog('No valid offer code found to store');
715
+ // Clear stored offer code if none found
716
+ await saveValueInAsync(ASYNC_KEYS.IOS_OFFER_CODE, '');
717
+ setIOSOfferCode(null);
718
+ }
719
+ } catch (error) {
720
+ console.error('[Insert Affiliate] Error retrieving and storing offer code:', error);
721
+ verboseLog(`Error in retrieveAndStoreOfferCode: ${error}`);
722
+ }
723
+ };
724
+
725
+ const removeSpecialCharacters = (offerCode: string): string => {
726
+ // Remove special characters, keep only alphanumeric and underscores
727
+ return offerCode.replace(/[^a-zA-Z0-9_]/g, '');
728
+ };
729
+
730
+ const cleanOfferCode = (offerCode: string): string => {
731
+ // Remove special characters, keep only alphanumeric
732
+ return removeSpecialCharacters(offerCode);
733
+ };
734
+
507
735
  return (
508
736
  <DeepLinkIapContext.Provider
509
737
  value={{
510
738
  referrerLink,
511
739
  userId,
740
+ iOSOfferCode,
512
741
  setShortCode,
513
742
  returnInsertAffiliateIdentifier,
514
743
  storeExpectedStoreTransaction,
@@ -13,7 +13,8 @@ const useDeepLinkIapProvider = () => {
13
13
  setShortCode,
14
14
  setInsertAffiliateIdentifier,
15
15
  initialize,
16
- isInitialized
16
+ isInitialized,
17
+ iOSOfferCode,
17
18
  } = useContext(DeepLinkIapContext);
18
19
 
19
20
  return {
@@ -27,7 +28,8 @@ const useDeepLinkIapProvider = () => {
27
28
  setShortCode,
28
29
  setInsertAffiliateIdentifier,
29
30
  initialize,
30
- isInitialized
31
+ isInitialized,
32
+ iOSOfferCode,
31
33
  };
32
34
  };
33
35