insert-affiliate-js-sdk 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,98 +19,547 @@ interface ExpectedTransactionPayload {
19
19
  storedDate: string;
20
20
  }
21
21
 
22
+ export interface AffiliateDetails {
23
+ affiliateName: string;
24
+ affiliateShortCode: string;
25
+ deeplinkUrl: string;
26
+ }
27
+
28
+ export type InsertAffiliateIdentifierChangeCallback = (identifier: string | null) => void;
29
+
22
30
  export class InsertAffiliate {
23
31
  private static isInitialized: boolean = false;
24
32
  private static companyCode: string | null = null;
33
+ private static verboseLogging: boolean = false;
34
+ private static insertAffiliateIdentifierChangeCallback: InsertAffiliateIdentifierChangeCallback | null = null;
35
+ private static affiliateAttributionActiveTime: number | null = null; // in milliseconds
36
+ private static offerCode: string | null = null;
37
+
38
+ private static verboseLog(message: string): void {
39
+ if (this.verboseLogging) {
40
+ console.log(`[Insert Affiliate] [VERBOSE] ${message}`);
41
+ }
42
+ }
43
+
44
+ static async initialize(code: string | null, verboseLogging: boolean = false, affiliateAttributionActiveTime?: number): Promise<void> {
45
+ this.verboseLogging = verboseLogging;
46
+
47
+ if (verboseLogging) {
48
+ this.verboseLog('Starting SDK initialization...');
49
+ this.verboseLog(`Company code provided: ${code ? 'Yes' : 'No'}`);
50
+ this.verboseLog('Verbose logging enabled');
51
+ }
25
52
 
26
- static async initialize(code: string | null): Promise<void> {
27
53
  if (this.isInitialized) {
28
- console.warn('[Insert Affiliate] SDK already initialized.');
54
+ this.verboseLog('SDK already initialized, skipping');
29
55
  return;
30
56
  }
31
57
  this.companyCode = code;
32
58
  await saveValue('companyCode', code || '');
59
+
60
+ // Set attribution timeout if provided
61
+ if (affiliateAttributionActiveTime !== undefined) {
62
+ this.affiliateAttributionActiveTime = affiliateAttributionActiveTime;
63
+ await saveValue('affiliateAttributionActiveTime', affiliateAttributionActiveTime.toString());
64
+ this.verboseLog(`Attribution timeout set to: ${affiliateAttributionActiveTime}ms`);
65
+ }
66
+
33
67
  this.isInitialized = true;
34
- console.log(`[Insert Affiliate] SDK initialized ${code ? `with company code: ${code}` : 'without a company code.'}`);
68
+ this.verboseLog(`SDK initialized ${code ? `with company code: ${code}` : 'without a company code.'}`);
69
+ this.verboseLog('Company code saved to storage');
70
+ this.verboseLog('SDK marked as initialized');
71
+
72
+ // Check for insertAffiliate URL parameter
73
+ await this.checkForInsertAffiliateParam();
74
+ }
75
+
76
+ private static async checkForInsertAffiliateParam(): Promise<void> {
77
+ this.verboseLog('Checking for insertAffiliate URL parameter...');
78
+
79
+ try {
80
+ // Only run in browser environments
81
+ if (typeof window === 'undefined' || !window.location) {
82
+ this.verboseLog('Not in browser environment, skipping URL parameter check');
83
+ return;
84
+ }
85
+
86
+ this.verboseLog(`Current URL: ${window.location.href}`);
87
+ const urlParams = new URLSearchParams(window.location.search);
88
+ const insertAffiliateParam = urlParams.get('insertAffiliate');
89
+
90
+ if (insertAffiliateParam) {
91
+ this.verboseLog(`Found insertAffiliate URL parameter: ${insertAffiliateParam}`);
92
+ await this.setShortCode(insertAffiliateParam);
93
+ this.verboseLog('Successfully processed insertAffiliate URL parameter');
94
+ } else {
95
+ this.verboseLog('No insertAffiliate URL parameter found');
96
+ }
97
+ } catch (error) {
98
+ this.verboseLog(`Error checking for insertAffiliate URL parameter: ${error}`);
99
+ }
35
100
  }
36
101
 
37
- static async returnInsertAffiliateIdentifier(): Promise<string | null> {
102
+ static async returnInsertAffiliateIdentifier(ignoreTimeout: boolean = false): Promise<string | null> {
103
+ this.verboseLog('Getting insert affiliate identifier...');
38
104
  const userId = await this.getOrCreateUserID();
39
105
  const referrerLink = await getValue('referrerLink');
40
- if (!referrerLink) return null;
41
- return `${referrerLink}-${userId}`;
106
+ this.verboseLog(`User ID: ${userId || 'empty'}, Referrer link: ${referrerLink || 'empty'}`);
107
+
108
+ if (!referrerLink) {
109
+ this.verboseLog('No referrer link found, returning null');
110
+ return null;
111
+ }
112
+
113
+ // Check attribution timeout unless explicitly ignored
114
+ if (!ignoreTimeout) {
115
+ const isValid = await this.isAffiliateAttributionValid();
116
+ if (!isValid) {
117
+ this.verboseLog('Attribution has expired, returning null');
118
+ return null;
119
+ }
120
+ } else {
121
+ this.verboseLog('Ignoring attribution timeout');
122
+ }
123
+
124
+ const identifier = `${referrerLink}-${userId}`;
125
+ this.verboseLog(`Returning affiliate identifier: ${identifier}`);
126
+ return identifier;
42
127
  }
43
128
 
44
129
  static async setInsertAffiliateIdentifier(referringLink: string): Promise<string | null> {
130
+ this.verboseLog(`Setting affiliate identifier. Input referringLink: ${referringLink}`);
131
+
45
132
  const userId = await this.getOrCreateUserID();
46
- const shortCode = /^[a-zA-Z0-9]{3,25}$/.test(referringLink)
133
+ this.verboseLog(`User ID: ${userId}`);
134
+
135
+ // Check if it's already a short code
136
+ const isShortCode = /^[a-zA-Z0-9]{3,25}$/.test(referringLink);
137
+ this.verboseLog(`Is short code: ${isShortCode}`);
138
+
139
+ const shortCode = isShortCode
47
140
  ? referringLink
48
141
  : await this.fetchShortLink(referringLink);
49
142
 
50
- if (!shortCode) return null;
143
+ if (!shortCode) {
144
+ this.verboseLog('No short code found or generated, returning null');
145
+ return null;
146
+ }
147
+
148
+ // Check if the same short code is already stored
149
+ const existingShortCode = await getValue('referrerLink');
150
+ if (existingShortCode === shortCode) {
151
+ this.verboseLog(`Short code ${shortCode} is already set, not updating attribution date`);
152
+ const identifier = `${shortCode}-${userId}`;
153
+ this.verboseLog(`Returning existing identifier: ${identifier}`);
154
+ return identifier;
155
+ }
51
156
 
157
+ this.verboseLog(`Saving short code to storage: ${shortCode}`);
52
158
  await saveValue('referrerLink', shortCode);
53
- return `${shortCode}-${userId}`;
159
+
160
+ // Store the attribution date
161
+ const storedDate = new Date().toISOString();
162
+ await saveValue('affiliateStoredDate', storedDate);
163
+ this.verboseLog(`Short code saved successfully with stored date: ${storedDate}`);
164
+
165
+ const identifier = `${shortCode}-${userId}`;
166
+ this.verboseLog(`Returning identifier: ${identifier}`);
167
+
168
+ // Auto-fetch and store offer code (use just the short code, not the full identifier)
169
+ await this.fetchAndStoreOfferCode(shortCode);
170
+
171
+ // Trigger callback if one is registered
172
+ if (this.insertAffiliateIdentifierChangeCallback) {
173
+ this.verboseLog(`Triggering callback with identifier: ${identifier}`);
174
+ this.insertAffiliateIdentifierChangeCallback(identifier);
175
+ }
176
+
177
+ return identifier;
54
178
  }
55
179
 
56
- static async setShortCode(shortCode: string): Promise<void> {
180
+ /**
181
+ * Validates and sets a short code for affiliate tracking
182
+ * Validates the short code against the API before storing
183
+ * @param shortCode The short code to validate and set
184
+ * @returns true if the code exists and was successfully validated and stored, false otherwise
185
+ */
186
+ static async setShortCode(shortCode: string): Promise<boolean> {
187
+ this.verboseLog(`Setting short code. Input: ${shortCode}`);
188
+
57
189
  const valid = /^[a-zA-Z0-9]{3,25}$/.test(shortCode);
190
+ this.verboseLog(`Short code validation: ${valid ? 'Valid' : 'Invalid'}`);
191
+
58
192
  if (!valid) {
59
- console.warn('[Insert Affiliate] Invalid short code.');
60
- return;
193
+ this.verboseLog('Invalid short code format, aborting');
194
+ return false;
195
+ }
196
+
197
+ // Validate that the short code exists in the system
198
+ const affiliateDetails = await this.getAffiliateDetails(shortCode);
199
+ if (!affiliateDetails) {
200
+ this.verboseLog(`Short code '${shortCode}' does not exist or validation failed`);
201
+ console.error(`[Insert Affiliate] Error: Short code '${shortCode}' does not exist or validation failed.`);
202
+ return false;
61
203
  }
204
+
205
+ this.verboseLog(`Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
206
+ console.log(`[Insert Affiliate] Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
207
+
208
+ // If validation passes, set the Insert Affiliate Identifier
209
+ this.verboseLog('Calling setInsertAffiliateIdentifier with short code');
62
210
  await this.setInsertAffiliateIdentifier(shortCode);
211
+ return true;
212
+ }
213
+
214
+ static setInsertAffiliateIdentifierChangeCallback(callback: InsertAffiliateIdentifierChangeCallback | null): void {
215
+ this.verboseLog(`Setting affiliate identifier change callback: ${callback ? 'callback provided' : 'callback cleared'}`);
216
+ this.insertAffiliateIdentifierChangeCallback = callback;
217
+ }
218
+
219
+ static async isAffiliateAttributionValid(): Promise<boolean> {
220
+ this.verboseLog('Checking if affiliate attribution is valid...');
221
+
222
+ const storedDateStr = await getValue('affiliateStoredDate');
223
+ if (!storedDateStr) {
224
+ this.verboseLog('No stored date found, attribution invalid');
225
+ return false;
226
+ }
227
+
228
+ // Get timeout value from storage or class property
229
+ let timeoutMs = this.affiliateAttributionActiveTime;
230
+ if (timeoutMs === null) {
231
+ const storedTimeout = await getValue('affiliateAttributionActiveTime');
232
+ timeoutMs = storedTimeout ? parseInt(storedTimeout, 10) : null;
233
+ }
234
+
235
+ // If no timeout is set, attribution never expires
236
+ if (timeoutMs === null) {
237
+ this.verboseLog('No attribution timeout configured, attribution is valid');
238
+ return true;
239
+ }
240
+
241
+ const storedDate = new Date(storedDateStr);
242
+ const currentDate = new Date();
243
+ const elapsedMs = currentDate.getTime() - storedDate.getTime();
244
+
245
+ const isValid = elapsedMs <= timeoutMs;
246
+ this.verboseLog(`Attribution stored: ${storedDateStr}, elapsed: ${elapsedMs}ms, timeout: ${timeoutMs}ms, valid: ${isValid}`);
247
+
248
+ return isValid;
249
+ }
250
+
251
+ static async getAffiliateStoredDate(): Promise<string | null> {
252
+ this.verboseLog('Getting affiliate stored date...');
253
+ const storedDate = await getValue('affiliateStoredDate');
254
+ this.verboseLog(`Stored date: ${storedDate || 'none'}`);
255
+ return storedDate;
256
+ }
257
+
258
+ /**
259
+ * Retrieve detailed information about an affiliate by their short code or deep link
260
+ * This method queries the API and does not store or set the affiliate identifier
261
+ * @param affiliateCode The short code or deep link to look up
262
+ * @returns AffiliateDetails if found, null otherwise
263
+ */
264
+ static async getAffiliateDetails(affiliateCode: string): Promise<AffiliateDetails | null> {
265
+ this.verboseLog(`Getting affiliate details for: ${affiliateCode}`);
266
+
267
+ const companyCode = this.companyCode || await getValue('companyCode');
268
+ if (!companyCode) {
269
+ this.verboseLog('Cannot get affiliate details: no company code available');
270
+ return null;
271
+ }
272
+
273
+ // Strip UUID from code if present (e.g., "ABC123-uuid" becomes "ABC123")
274
+ const cleanCode = affiliateCode.includes('-') ? affiliateCode.split('-')[0] : affiliateCode;
275
+ this.verboseLog(`Clean code: ${cleanCode}`);
276
+
277
+ try {
278
+ const url = 'https://api.insertaffiliate.com/V1/checkAffiliateExists';
279
+ const payload = {
280
+ companyId: companyCode,
281
+ affiliateCode: cleanCode,
282
+ };
283
+
284
+ this.verboseLog(`Making API call to: ${url}`);
285
+ this.verboseLog(`Payload: ${JSON.stringify(payload)}`);
286
+
287
+ const response = await fetch(url, {
288
+ method: 'POST',
289
+ headers: { 'Content-Type': 'application/json' },
290
+ body: JSON.stringify(payload),
291
+ });
292
+
293
+ this.verboseLog(`API response status: ${response.status}`);
294
+
295
+ if (!response.ok) {
296
+ this.verboseLog(`Failed to get affiliate details, status: ${response.status}`);
297
+ return null;
298
+ }
299
+
300
+ const data = await response.json();
301
+ this.verboseLog(`API response data: ${JSON.stringify(data)}`);
302
+
303
+ if (data.exists && data.affiliate) {
304
+ const details: AffiliateDetails = {
305
+ affiliateName: data.affiliate.affiliateName,
306
+ affiliateShortCode: data.affiliate.affiliateShortCode,
307
+ deeplinkUrl: data.affiliate.deeplinkurl,
308
+ };
309
+
310
+ this.verboseLog(`Successfully retrieved affiliate details for: ${details.affiliateName}`);
311
+ return details;
312
+ }
313
+
314
+ this.verboseLog('Affiliate does not exist');
315
+ return null;
316
+ } catch (error) {
317
+ this.verboseLog(`Error fetching affiliate details: ${error}`);
318
+ return null;
319
+ }
320
+ }
321
+
322
+ static async returnCompanyId(): Promise<string | null> {
323
+ this.verboseLog('Getting company ID...');
324
+ const companyCode = this.companyCode || await getValue('companyCode');
325
+ this.verboseLog(`Company ID: ${companyCode || 'none'}`);
326
+ return companyCode;
327
+ }
328
+
329
+ /**
330
+ * Get the offer code for the current affiliate
331
+ * @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
332
+ * @returns The offer code for the specified platform, or null if not found
333
+ */
334
+ static async getOfferCode(platformType: 'ios' | 'android' | 'stripe' = 'stripe'): Promise<string | null> {
335
+ this.verboseLog(`Getting offer code for platform: ${platformType}...`);
336
+
337
+ const storageKey = platformType === 'stripe' ? 'offerCode' : `offerCode_${platformType}`;
338
+
339
+ // Return cached offer code if available (only for default Stripe)
340
+ if (platformType === 'stripe' && this.offerCode) {
341
+ this.verboseLog(`Returning cached offer code: ${this.offerCode}`);
342
+ return this.offerCode;
343
+ }
344
+
345
+ // Try to get from storage
346
+ const storedOfferCode = await getValue(storageKey);
347
+ if (storedOfferCode) {
348
+ this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
349
+ if (platformType === 'stripe') {
350
+ this.offerCode = storedOfferCode;
351
+ }
352
+ return storedOfferCode;
353
+ }
354
+
355
+ // If not in storage, try to fetch it
356
+ const shortCode = await getValue('referrerLink');
357
+ if (shortCode) {
358
+ this.verboseLog(`No stored offer code, fetching for short code: ${shortCode}`);
359
+ const fetchedCode = await this.fetchOfferCodeForPlatform(shortCode, platformType);
360
+ return fetchedCode;
361
+ }
362
+
363
+ this.verboseLog('No offer code found');
364
+ return null;
365
+ }
366
+
367
+ /**
368
+ * Get the Stripe coupon/promo code for the current affiliate
369
+ * Convenience method that calls getOfferCode with platformType='stripe'
370
+ * @returns The Stripe coupon/promo code, or null if not found
371
+ */
372
+ static async getStripeCouponCode(): Promise<string | null> {
373
+ return this.getOfferCode('stripe');
374
+ }
375
+
376
+ /**
377
+ * Fetch offer code for a specific platform
378
+ * @param shortCode The affiliate short code
379
+ * @param platformType The platform type: 'ios', 'android', or 'stripe'
380
+ * @returns The offer code for the specified platform, or null if not found
381
+ */
382
+ private static async fetchOfferCodeForPlatform(shortCode: string, platformType: 'ios' | 'android' | 'stripe'): Promise<string | null> {
383
+ this.verboseLog(`Fetching offer code for platform: ${platformType}, short code: ${shortCode}`);
384
+
385
+ try {
386
+ const companyCode = this.companyCode || await getValue('companyCode');
387
+ if (!companyCode) {
388
+ this.verboseLog('Cannot fetch offer code: no company code available');
389
+ return null;
390
+ }
391
+
392
+ const encoded = encodeURIComponent(shortCode);
393
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}?platformType=${platformType}`;
394
+ this.verboseLog(`Making API call to: ${url}`);
395
+
396
+ const response = await fetch(url);
397
+ this.verboseLog(`API response status: ${response.status}`);
398
+
399
+ if (!response.ok) {
400
+ this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
401
+ return null;
402
+ }
403
+
404
+ const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, '');
405
+ this.verboseLog(`Received offer code: ${offerCode}`);
406
+
407
+ const errorCodes = [
408
+ 'errorofferCodeNotFound',
409
+ 'errorAffiliateoffercodenotfoundinanycompany',
410
+ 'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas',
411
+ 'Routenotfound'
412
+ ];
413
+
414
+ if (errorCodes.includes(offerCode)) {
415
+ this.verboseLog('Offer code not found or invalid');
416
+ return null;
417
+ }
418
+
419
+ // Store offer code with platform-specific key
420
+ const storageKey = platformType === 'stripe' ? 'offerCode' : `offerCode_${platformType}`;
421
+ await saveValue(storageKey, offerCode);
422
+ if (platformType === 'stripe') {
423
+ this.offerCode = offerCode;
424
+ }
425
+ this.verboseLog(`Offer code stored successfully with key ${storageKey}: ${offerCode}`);
426
+
427
+ return offerCode;
428
+ } catch (error) {
429
+ this.verboseLog(`Error fetching offer code: ${error}`);
430
+ return null;
431
+ }
432
+ }
433
+
434
+ private static async fetchAndStoreOfferCode(shortCode: string): Promise<void> {
435
+ this.verboseLog(`Fetching offer code for short code: ${shortCode}`);
436
+
437
+ try {
438
+ // Get company code
439
+ const companyCode = this.companyCode || await getValue('companyCode');
440
+ if (!companyCode) {
441
+ this.verboseLog('Cannot fetch offer code: no company code available');
442
+ return;
443
+ }
444
+
445
+ // Use the more efficient endpoint with company code and just the short code
446
+ const encoded = encodeURIComponent(shortCode);
447
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}`;
448
+ this.verboseLog(`Making API call to: ${url}`);
449
+
450
+ const response = await fetch(url);
451
+ this.verboseLog(`API response status: ${response.status}`);
452
+
453
+ if (!response.ok) {
454
+ this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
455
+ return;
456
+ }
457
+
458
+ const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, '');
459
+ this.verboseLog(`Received offer code: ${offerCode}`);
460
+
461
+ // Check for error codes
462
+ const errorCodes = [
463
+ 'errorofferCodeNotFound',
464
+ 'errorAffiliateoffercodenotfoundinanycompany',
465
+ 'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas',
466
+ 'Routenotfound'
467
+ ];
468
+
469
+ if (errorCodes.includes(offerCode)) {
470
+ this.verboseLog('Offer code not found or invalid');
471
+ return;
472
+ }
473
+
474
+ // Store offer code
475
+ this.offerCode = offerCode;
476
+ await saveValue('offerCode', offerCode);
477
+ this.verboseLog(`Offer code stored successfully: ${offerCode}`);
478
+ } catch (error) {
479
+ this.verboseLog(`Error fetching offer code: ${error}`);
480
+ }
63
481
  }
64
482
 
65
483
  static async trackEvent(eventName: string): Promise<void> {
484
+ this.verboseLog(`Tracking event: ${eventName}`);
485
+
66
486
  const id = await this.returnInsertAffiliateIdentifier();
67
487
 
68
488
  if (!id) {
69
- console.warn('[Insert Affiliate] No affiliate identifier found.');
489
+ this.verboseLog('Cannot track event: no affiliate identifier available');
70
490
  return;
71
491
  }
72
492
 
73
493
  const companyCode = this.companyCode || await getValue('companyCode');
494
+ this.verboseLog(`Company code: ${companyCode || 'empty'}`);
74
495
 
75
- if (!companyCode) return;
496
+ if (!companyCode) {
497
+ this.verboseLog('Cannot track event: no company code available');
498
+ return;
499
+ }
500
+
501
+ const payload = {
502
+ eventName,
503
+ deepLinkParam: id,
504
+ companyId: companyCode,
505
+ };
506
+
507
+ this.verboseLog(`Track event payload: ${JSON.stringify(payload)}`);
508
+ this.verboseLog('Making API call to track event...');
76
509
 
77
510
  try {
78
- await fetch('https://api.insertaffiliate.com/v1/trackEvent', {
511
+ const response = await fetch('https://api.insertaffiliate.com/v1/trackEvent', {
79
512
  method: 'POST',
80
513
  headers: { 'Content-Type': 'application/json' },
81
- body: JSON.stringify(
82
- {
83
- eventName,
84
- deepLinkParam: id,
85
- companyId: companyCode,
86
- }),
514
+ body: JSON.stringify(payload),
87
515
  });
88
- console.log('[Insert Affiliate] Event tracked:', eventName);
516
+
517
+ this.verboseLog(`Track event API response status: ${response.status}`);
518
+
519
+ if (response.ok) {
520
+ this.verboseLog(`Event tracked successfully: ${eventName}`);
521
+ } else {
522
+ this.verboseLog(`Failed to track event with status code: ${response.status}`);
523
+ }
89
524
  } catch (err) {
90
- console.error('[Insert Affiliate] Failed to track event:', err);
525
+ this.verboseLog(`Network error tracking event: ${err}`);
91
526
  }
92
527
  }
93
528
 
94
529
  static async returnUserAccountTokenAndStoreExpectedTransaction(): Promise<string | null> {
530
+ this.verboseLog('Getting user account token and storing expected transaction...');
531
+
95
532
  const shortCode = await this.returnInsertAffiliateIdentifier();
96
- if (!shortCode) return null;
533
+ if (!shortCode) {
534
+ this.verboseLog('No affiliate identifier found, not saving expected transaction');
535
+ return null;
536
+ }
97
537
 
98
538
  let token = await getValue('userAccountToken');
539
+ this.verboseLog(`Existing token: ${token || 'none'}`);
540
+
99
541
  if (!token) {
542
+ this.verboseLog('Generating new user account token...');
100
543
  token = generateUUID();
101
544
  await saveValue('userAccountToken', token);
545
+ this.verboseLog(`Generated and saved new token: ${token}`);
102
546
  }
103
547
 
548
+ this.verboseLog(`User account token: ${token}`);
104
549
  await this.storeExpectedStoreTransaction(token);
105
550
  return token;
106
551
  }
107
552
 
108
553
  static async storeExpectedStoreTransaction(userAccountToken: string): Promise<void> {
554
+ this.verboseLog(`Storing expected store transaction with token: ${userAccountToken}`);
555
+
109
556
  const companyCode = this.companyCode || await getValue('companyCode');
110
557
  const shortCode = await this.returnInsertAffiliateIdentifier();
111
558
 
559
+ this.verboseLog(`Company code: ${companyCode || 'empty'}, Short code: ${shortCode || 'empty'}`);
560
+
112
561
  if (!companyCode || !shortCode) {
113
- console.error('[Insert Affiliate] Missing company code or identifier.');
562
+ this.verboseLog('Cannot store transaction: missing company code or identifier');
114
563
  return;
115
564
  }
116
565
 
@@ -121,6 +570,9 @@ export class InsertAffiliate {
121
570
  storedDate: new Date().toISOString(),
122
571
  };
123
572
 
573
+ this.verboseLog(`Payload: ${JSON.stringify(payload)}`);
574
+ this.verboseLog('Making API call to store expected transaction...');
575
+
124
576
  try {
125
577
  const res = await fetch('https://api.insertaffiliate.com/v1/api/app-store-webhook/create-expected-transaction', {
126
578
  method: 'POST',
@@ -128,13 +580,15 @@ export class InsertAffiliate {
128
580
  body: JSON.stringify(payload),
129
581
  });
130
582
 
583
+ this.verboseLog(`API response status: ${res.status}`);
584
+
131
585
  if (res.status === 200) {
132
- console.log('[Insert Affiliate] Stored expected transaction');
586
+ this.verboseLog('Expected transaction stored successfully on server');
133
587
  } else {
134
- console.warn('[Insert Affiliate] Failed storing transaction:', res.status);
588
+ this.verboseLog(`Failed to store transaction with status: ${res.status}`);
135
589
  }
136
590
  } catch (error) {
137
- console.error('[Insert Affiliate] Error storing transaction:', error);
591
+ this.verboseLog(`Network error storing transaction: ${error}`);
138
592
  }
139
593
  }
140
594
 
@@ -145,7 +599,9 @@ export class InsertAffiliate {
145
599
  iapticPublicKey: string
146
600
  ): Promise<boolean> {
147
601
  try {
602
+ this.verboseLog('Starting Iaptic purchase validation...');
148
603
  const isIOS = typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
604
+ this.verboseLog(`Platform detected: ${isIOS ? 'iOS' : 'Android'}`);
149
605
 
150
606
  const baseRequest = {
151
607
  id: iapticAppId,
@@ -155,12 +611,14 @@ export class InsertAffiliate {
155
611
  let transaction: any;
156
612
 
157
613
  if (isIOS) {
614
+ this.verboseLog('Creating iOS transaction payload');
158
615
  transaction = {
159
616
  id: iapticAppId,
160
617
  type: 'ios-appstore',
161
618
  appStoreReceipt: jsonIapPurchase.transactionReceipt,
162
619
  };
163
620
  } else {
621
+ this.verboseLog('Creating Android transaction payload');
164
622
  const receiptJson: IapticAndroidReceipt = JSON.parse(atob(jsonIapPurchase.transactionReceipt));
165
623
  transaction = {
166
624
  id: receiptJson.orderId,
@@ -172,6 +630,7 @@ export class InsertAffiliate {
172
630
  }
173
631
 
174
632
  const insertAffiliateIdentifier = await this.returnInsertAffiliateIdentifier();
633
+ this.verboseLog(`Affiliate identifier: ${insertAffiliateIdentifier || 'none'}`);
175
634
 
176
635
  const payload = {
177
636
  ...baseRequest,
@@ -181,6 +640,7 @@ export class InsertAffiliate {
181
640
  : undefined,
182
641
  };
183
642
 
643
+ this.verboseLog('Making API call to Iaptic validator...');
184
644
  const response = await fetch('https://validator.iaptic.com/v1/validate', {
185
645
  method: 'POST',
186
646
  headers: {
@@ -190,19 +650,43 @@ export class InsertAffiliate {
190
650
  body: JSON.stringify(payload),
191
651
  });
192
652
 
193
- return response.status === 200;
653
+ this.verboseLog(`Iaptic validation response status: ${response.status}`);
654
+
655
+ if (response.status === 200) {
656
+ this.verboseLog('Purchase validated successfully');
657
+ return true;
658
+ } else {
659
+ this.verboseLog(`Validation failed with status: ${response.status}`);
660
+ return false;
661
+ }
194
662
  } catch (error) {
195
- console.error('[Insert Affiliate] Purchase validation failed:', error);
663
+ this.verboseLog(`Error during purchase validation: ${error}`);
196
664
  return false;
197
665
  }
198
666
  }
199
667
 
200
668
  static async fetchAndConditionallyOpenUrl(affiliateLink: string, offerCodeUrlId: string): Promise<void> {
669
+ this.verboseLog('Fetching offer code and opening URL...');
201
670
  const encoded = encodeURIComponent(affiliateLink);
671
+ this.verboseLog(`Encoded affiliate link: ${encoded}`);
202
672
 
203
673
  try {
204
- const res = await fetch(`https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${encoded}`);
674
+ // Get company code
675
+ const companyCode = this.companyCode || await getValue('companyCode');
676
+ if (!companyCode) {
677
+ this.verboseLog('Cannot fetch offer code: no company code available');
678
+ return;
679
+ }
680
+
681
+ this.verboseLog('Making API call to fetch offer code...');
682
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}`;
683
+ this.verboseLog(`API URL: ${url}`);
684
+
685
+ const res = await fetch(url);
686
+ this.verboseLog(`API response status: ${res.status}`);
687
+
205
688
  const offerCode = (await res.text()).replace(/[^a-zA-Z0-9]/g, '');
689
+ this.verboseLog(`Received offer code: ${offerCode}`);
206
690
 
207
691
  const errorCodes = [
208
692
  'errorofferCodeNotFound',
@@ -212,37 +696,59 @@ export class InsertAffiliate {
212
696
  ];
213
697
 
214
698
  if (errorCodes.includes(offerCode)) {
215
- console.warn('[Insert Affiliate] Offer Code Not Found');
699
+ this.verboseLog('Offer code not found or invalid');
216
700
  return;
217
701
  }
218
702
 
219
703
  const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
704
+ this.verboseLog(`Opening redeem URL: ${redeemUrl}`);
220
705
  window.open(redeemUrl, '_blank');
706
+ this.verboseLog('Redeem URL opened successfully');
221
707
  } catch (err) {
222
- console.error('[Insert Affiliate] Error fetching/opening offer code:', err);
708
+ this.verboseLog(`Error fetching/opening offer code: ${err}`);
223
709
  }
224
710
  }
225
711
 
226
712
  private static async getOrCreateUserID(): Promise<string> {
713
+ this.verboseLog('Getting or creating user ID...');
227
714
  let id = await getValue('userId');
715
+ this.verboseLog(`Existing user ID: ${id || 'none'}`);
716
+
228
717
  if (!id) {
718
+ this.verboseLog('Generating new user ID...');
229
719
  id = generateShortDeviceID();
230
720
  await saveValue('userId', id);
721
+ this.verboseLog(`Generated and saved new user ID: ${id}`);
231
722
  }
723
+
232
724
  return id;
233
725
  }
234
726
 
235
727
  private static async fetchShortLink(link: string): Promise<string | null> {
236
728
  try {
729
+ this.verboseLog('Converting deep link to short link...');
237
730
  const encoded = encodeURIComponent(link);
238
731
  const companyCode = this.companyCode || await getValue('companyCode');
239
- if (!companyCode) return null;
732
+ this.verboseLog(`Company code: ${companyCode || 'empty'}`);
733
+
734
+ if (!companyCode) {
735
+ this.verboseLog('No company code available, cannot convert link');
736
+ return null;
737
+ }
738
+
739
+ const url = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encoded}`;
740
+ this.verboseLog(`Making API call to: ${url}`);
741
+
742
+ const res = await fetch(url);
743
+ this.verboseLog(`API response status: ${res.status}`);
240
744
 
241
- const res = await fetch(`https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encoded}`);
242
745
  const data = await res.json();
243
- return data?.shortLink || null;
746
+ const shortLink = data?.shortLink || null;
747
+ this.verboseLog(`Short link received: ${shortLink || 'none'}`);
748
+
749
+ return shortLink;
244
750
  } catch (err: any) {
245
- console.error('[Insert Affiliate] Failed to fetch short link:', err?.message || err);
751
+ this.verboseLog(`Error fetching short link: ${err?.message || err}`);
246
752
  return null;
247
753
  }
248
754
  }