insert-affiliate-js-sdk 1.0.2 → 1.1.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,463 @@ 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();
35
74
  }
36
75
 
37
- static async returnInsertAffiliateIdentifier(): Promise<string | null> {
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
+ }
100
+ }
101
+
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;
61
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;
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
+ static async getOfferCode(): Promise<string | null> {
330
+ this.verboseLog('Getting offer code...');
331
+
332
+ // Return cached offer code if available
333
+ if (this.offerCode) {
334
+ this.verboseLog(`Returning cached offer code: ${this.offerCode}`);
335
+ return this.offerCode;
336
+ }
337
+
338
+ // Try to get from storage
339
+ const storedOfferCode = await getValue('offerCode');
340
+ if (storedOfferCode) {
341
+ this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
342
+ this.offerCode = storedOfferCode;
343
+ return storedOfferCode;
344
+ }
345
+
346
+ this.verboseLog('No offer code found');
347
+ return null;
348
+ }
349
+
350
+ private static async fetchAndStoreOfferCode(shortCode: string): Promise<void> {
351
+ this.verboseLog(`Fetching offer code for short code: ${shortCode}`);
352
+
353
+ try {
354
+ // Get company code
355
+ const companyCode = this.companyCode || await getValue('companyCode');
356
+ if (!companyCode) {
357
+ this.verboseLog('Cannot fetch offer code: no company code available');
358
+ return;
359
+ }
360
+
361
+ // Use the more efficient endpoint with company code and just the short code
362
+ const encoded = encodeURIComponent(shortCode);
363
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}`;
364
+ this.verboseLog(`Making API call to: ${url}`);
365
+
366
+ const response = await fetch(url);
367
+ this.verboseLog(`API response status: ${response.status}`);
368
+
369
+ if (!response.ok) {
370
+ this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
371
+ return;
372
+ }
373
+
374
+ const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, '');
375
+ this.verboseLog(`Received offer code: ${offerCode}`);
376
+
377
+ // Check for error codes
378
+ const errorCodes = [
379
+ 'errorofferCodeNotFound',
380
+ 'errorAffiliateoffercodenotfoundinanycompany',
381
+ 'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas',
382
+ 'Routenotfound'
383
+ ];
384
+
385
+ if (errorCodes.includes(offerCode)) {
386
+ this.verboseLog('Offer code not found or invalid');
387
+ return;
388
+ }
389
+
390
+ // Store offer code
391
+ this.offerCode = offerCode;
392
+ await saveValue('offerCode', offerCode);
393
+ this.verboseLog(`Offer code stored successfully: ${offerCode}`);
394
+ } catch (error) {
395
+ this.verboseLog(`Error fetching offer code: ${error}`);
396
+ }
63
397
  }
64
398
 
65
399
  static async trackEvent(eventName: string): Promise<void> {
400
+ this.verboseLog(`Tracking event: ${eventName}`);
401
+
66
402
  const id = await this.returnInsertAffiliateIdentifier();
67
403
 
68
404
  if (!id) {
69
- console.warn('[Insert Affiliate] No affiliate identifier found.');
405
+ this.verboseLog('Cannot track event: no affiliate identifier available');
70
406
  return;
71
407
  }
72
408
 
73
409
  const companyCode = this.companyCode || await getValue('companyCode');
410
+ this.verboseLog(`Company code: ${companyCode || 'empty'}`);
74
411
 
75
- if (!companyCode) return;
412
+ if (!companyCode) {
413
+ this.verboseLog('Cannot track event: no company code available');
414
+ return;
415
+ }
416
+
417
+ const payload = {
418
+ eventName,
419
+ deepLinkParam: id,
420
+ companyId: companyCode,
421
+ };
422
+
423
+ this.verboseLog(`Track event payload: ${JSON.stringify(payload)}`);
424
+ this.verboseLog('Making API call to track event...');
76
425
 
77
426
  try {
78
- await fetch('https://api.insertaffiliate.com/v1/trackEvent', {
427
+ const response = await fetch('https://api.insertaffiliate.com/v1/trackEvent', {
79
428
  method: 'POST',
80
429
  headers: { 'Content-Type': 'application/json' },
81
- body: JSON.stringify(
82
- {
83
- eventName,
84
- deepLinkParam: id,
85
- companyId: companyCode,
86
- }),
430
+ body: JSON.stringify(payload),
87
431
  });
88
- console.log('[Insert Affiliate] Event tracked:', eventName);
432
+
433
+ this.verboseLog(`Track event API response status: ${response.status}`);
434
+
435
+ if (response.ok) {
436
+ this.verboseLog(`Event tracked successfully: ${eventName}`);
437
+ } else {
438
+ this.verboseLog(`Failed to track event with status code: ${response.status}`);
439
+ }
89
440
  } catch (err) {
90
- console.error('[Insert Affiliate] Failed to track event:', err);
441
+ this.verboseLog(`Network error tracking event: ${err}`);
91
442
  }
92
443
  }
93
444
 
94
445
  static async returnUserAccountTokenAndStoreExpectedTransaction(): Promise<string | null> {
446
+ this.verboseLog('Getting user account token and storing expected transaction...');
447
+
95
448
  const shortCode = await this.returnInsertAffiliateIdentifier();
96
- if (!shortCode) return null;
449
+ if (!shortCode) {
450
+ this.verboseLog('No affiliate identifier found, not saving expected transaction');
451
+ return null;
452
+ }
97
453
 
98
454
  let token = await getValue('userAccountToken');
455
+ this.verboseLog(`Existing token: ${token || 'none'}`);
456
+
99
457
  if (!token) {
458
+ this.verboseLog('Generating new user account token...');
100
459
  token = generateUUID();
101
460
  await saveValue('userAccountToken', token);
461
+ this.verboseLog(`Generated and saved new token: ${token}`);
102
462
  }
103
463
 
464
+ this.verboseLog(`User account token: ${token}`);
104
465
  await this.storeExpectedStoreTransaction(token);
105
466
  return token;
106
467
  }
107
468
 
108
469
  static async storeExpectedStoreTransaction(userAccountToken: string): Promise<void> {
470
+ this.verboseLog(`Storing expected store transaction with token: ${userAccountToken}`);
471
+
109
472
  const companyCode = this.companyCode || await getValue('companyCode');
110
473
  const shortCode = await this.returnInsertAffiliateIdentifier();
111
474
 
475
+ this.verboseLog(`Company code: ${companyCode || 'empty'}, Short code: ${shortCode || 'empty'}`);
476
+
112
477
  if (!companyCode || !shortCode) {
113
- console.error('[Insert Affiliate] Missing company code or identifier.');
478
+ this.verboseLog('Cannot store transaction: missing company code or identifier');
114
479
  return;
115
480
  }
116
481
 
@@ -121,6 +486,9 @@ export class InsertAffiliate {
121
486
  storedDate: new Date().toISOString(),
122
487
  };
123
488
 
489
+ this.verboseLog(`Payload: ${JSON.stringify(payload)}`);
490
+ this.verboseLog('Making API call to store expected transaction...');
491
+
124
492
  try {
125
493
  const res = await fetch('https://api.insertaffiliate.com/v1/api/app-store-webhook/create-expected-transaction', {
126
494
  method: 'POST',
@@ -128,13 +496,15 @@ export class InsertAffiliate {
128
496
  body: JSON.stringify(payload),
129
497
  });
130
498
 
499
+ this.verboseLog(`API response status: ${res.status}`);
500
+
131
501
  if (res.status === 200) {
132
- console.log('[Insert Affiliate] Stored expected transaction');
502
+ this.verboseLog('Expected transaction stored successfully on server');
133
503
  } else {
134
- console.warn('[Insert Affiliate] Failed storing transaction:', res.status);
504
+ this.verboseLog(`Failed to store transaction with status: ${res.status}`);
135
505
  }
136
506
  } catch (error) {
137
- console.error('[Insert Affiliate] Error storing transaction:', error);
507
+ this.verboseLog(`Network error storing transaction: ${error}`);
138
508
  }
139
509
  }
140
510
 
@@ -145,7 +515,9 @@ export class InsertAffiliate {
145
515
  iapticPublicKey: string
146
516
  ): Promise<boolean> {
147
517
  try {
518
+ this.verboseLog('Starting Iaptic purchase validation...');
148
519
  const isIOS = typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
520
+ this.verboseLog(`Platform detected: ${isIOS ? 'iOS' : 'Android'}`);
149
521
 
150
522
  const baseRequest = {
151
523
  id: iapticAppId,
@@ -155,12 +527,14 @@ export class InsertAffiliate {
155
527
  let transaction: any;
156
528
 
157
529
  if (isIOS) {
530
+ this.verboseLog('Creating iOS transaction payload');
158
531
  transaction = {
159
532
  id: iapticAppId,
160
533
  type: 'ios-appstore',
161
534
  appStoreReceipt: jsonIapPurchase.transactionReceipt,
162
535
  };
163
536
  } else {
537
+ this.verboseLog('Creating Android transaction payload');
164
538
  const receiptJson: IapticAndroidReceipt = JSON.parse(atob(jsonIapPurchase.transactionReceipt));
165
539
  transaction = {
166
540
  id: receiptJson.orderId,
@@ -172,6 +546,7 @@ export class InsertAffiliate {
172
546
  }
173
547
 
174
548
  const insertAffiliateIdentifier = await this.returnInsertAffiliateIdentifier();
549
+ this.verboseLog(`Affiliate identifier: ${insertAffiliateIdentifier || 'none'}`);
175
550
 
176
551
  const payload = {
177
552
  ...baseRequest,
@@ -181,6 +556,7 @@ export class InsertAffiliate {
181
556
  : undefined,
182
557
  };
183
558
 
559
+ this.verboseLog('Making API call to Iaptic validator...');
184
560
  const response = await fetch('https://validator.iaptic.com/v1/validate', {
185
561
  method: 'POST',
186
562
  headers: {
@@ -190,19 +566,43 @@ export class InsertAffiliate {
190
566
  body: JSON.stringify(payload),
191
567
  });
192
568
 
193
- return response.status === 200;
569
+ this.verboseLog(`Iaptic validation response status: ${response.status}`);
570
+
571
+ if (response.status === 200) {
572
+ this.verboseLog('Purchase validated successfully');
573
+ return true;
574
+ } else {
575
+ this.verboseLog(`Validation failed with status: ${response.status}`);
576
+ return false;
577
+ }
194
578
  } catch (error) {
195
- console.error('[Insert Affiliate] Purchase validation failed:', error);
579
+ this.verboseLog(`Error during purchase validation: ${error}`);
196
580
  return false;
197
581
  }
198
582
  }
199
583
 
200
584
  static async fetchAndConditionallyOpenUrl(affiliateLink: string, offerCodeUrlId: string): Promise<void> {
585
+ this.verboseLog('Fetching offer code and opening URL...');
201
586
  const encoded = encodeURIComponent(affiliateLink);
587
+ this.verboseLog(`Encoded affiliate link: ${encoded}`);
202
588
 
203
589
  try {
204
- const res = await fetch(`https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${encoded}`);
590
+ // Get company code
591
+ const companyCode = this.companyCode || await getValue('companyCode');
592
+ if (!companyCode) {
593
+ this.verboseLog('Cannot fetch offer code: no company code available');
594
+ return;
595
+ }
596
+
597
+ this.verboseLog('Making API call to fetch offer code...');
598
+ const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}`;
599
+ this.verboseLog(`API URL: ${url}`);
600
+
601
+ const res = await fetch(url);
602
+ this.verboseLog(`API response status: ${res.status}`);
603
+
205
604
  const offerCode = (await res.text()).replace(/[^a-zA-Z0-9]/g, '');
605
+ this.verboseLog(`Received offer code: ${offerCode}`);
206
606
 
207
607
  const errorCodes = [
208
608
  'errorofferCodeNotFound',
@@ -212,37 +612,59 @@ export class InsertAffiliate {
212
612
  ];
213
613
 
214
614
  if (errorCodes.includes(offerCode)) {
215
- console.warn('[Insert Affiliate] Offer Code Not Found');
615
+ this.verboseLog('Offer code not found or invalid');
216
616
  return;
217
617
  }
218
618
 
219
619
  const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
620
+ this.verboseLog(`Opening redeem URL: ${redeemUrl}`);
220
621
  window.open(redeemUrl, '_blank');
622
+ this.verboseLog('Redeem URL opened successfully');
221
623
  } catch (err) {
222
- console.error('[Insert Affiliate] Error fetching/opening offer code:', err);
624
+ this.verboseLog(`Error fetching/opening offer code: ${err}`);
223
625
  }
224
626
  }
225
627
 
226
628
  private static async getOrCreateUserID(): Promise<string> {
629
+ this.verboseLog('Getting or creating user ID...');
227
630
  let id = await getValue('userId');
631
+ this.verboseLog(`Existing user ID: ${id || 'none'}`);
632
+
228
633
  if (!id) {
634
+ this.verboseLog('Generating new user ID...');
229
635
  id = generateShortDeviceID();
230
636
  await saveValue('userId', id);
637
+ this.verboseLog(`Generated and saved new user ID: ${id}`);
231
638
  }
639
+
232
640
  return id;
233
641
  }
234
642
 
235
643
  private static async fetchShortLink(link: string): Promise<string | null> {
236
644
  try {
645
+ this.verboseLog('Converting deep link to short link...');
237
646
  const encoded = encodeURIComponent(link);
238
647
  const companyCode = this.companyCode || await getValue('companyCode');
239
- if (!companyCode) return null;
648
+ this.verboseLog(`Company code: ${companyCode || 'empty'}`);
649
+
650
+ if (!companyCode) {
651
+ this.verboseLog('No company code available, cannot convert link');
652
+ return null;
653
+ }
654
+
655
+ const url = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encoded}`;
656
+ this.verboseLog(`Making API call to: ${url}`);
657
+
658
+ const res = await fetch(url);
659
+ this.verboseLog(`API response status: ${res.status}`);
240
660
 
241
- const res = await fetch(`https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encoded}`);
242
661
  const data = await res.json();
243
- return data?.shortLink || null;
662
+ const shortLink = data?.shortLink || null;
663
+ this.verboseLog(`Short link received: ${shortLink || 'none'}`);
664
+
665
+ return shortLink;
244
666
  } catch (err: any) {
245
- console.error('[Insert Affiliate] Failed to fetch short link:', err?.message || err);
667
+ this.verboseLog(`Error fetching short link: ${err?.message || err}`);
246
668
  return null;
247
669
  }
248
670
  }