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/.claude/settings.local.json +8 -0
- package/dist/DeepLinkIapProvider.d.ts +2 -1
- package/dist/DeepLinkIapProvider.js +223 -31
- package/dist/useDeepLinkIapProvider.d.ts +2 -1
- package/dist/useDeepLinkIapProvider.js +3 -2
- package/package.json +1 -1
- package/readme.md +449 -66
- package/src/DeepLinkIapProvider.tsx +266 -37
- package/src/useDeepLinkIapProvider.tsx +4 -2
|
@@ -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
|
|
194
|
-
const isValidCharacters = /^[a-zA-Z0-
|
|
195
|
-
return isValidCharacters && referringLink.length
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
371
|
+
verboseLog('Referring link is empty or invalid, storing as-is');
|
|
271
372
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
272
|
-
return `${
|
|
373
|
+
return `${referringLink}-${customerID}`;
|
|
273
374
|
}
|
|
274
375
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
395
|
+
verboseLog('Link is already a short code, storing directly');
|
|
296
396
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
297
|
-
return `${
|
|
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 `${
|
|
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=${
|
|
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
|
-
|
|
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 `${
|
|
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
|
-
|
|
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
|
-
|
|
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: ",
|
|
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:
|
|
483
|
-
companyId:
|
|
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
|
|