insert-affiliate-js-sdk 1.3.0 → 1.4.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/CHANGELOG.md +7 -0
- package/dist/index.d.ts +26 -4
- package/dist/index.js +51 -41
- package/package.json +1 -1
- package/src/sdk/InsertAffiliate.ts +87 -46
- package/.claude/settings.local.json +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to the Insert Affiliate JavaScript SDK will be documented in
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.1] - 2026-03-29
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Offer code sanitization** - Fixed offer codes with dashes/underscores being stripped (e.g. `pro-v3-ext` was incorrectly becoming `prov3ext`)
|
|
12
|
+
- Now checks for API error responses before cleaning the offer code
|
|
13
|
+
- Sanitization regex updated to preserve dashes and underscores
|
|
14
|
+
|
|
8
15
|
## [1.1.0] - 2025-11-23
|
|
9
16
|
|
|
10
17
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ interface AffiliateDetails {
|
|
|
6
6
|
affiliateShortCode: string;
|
|
7
7
|
deeplinkUrl: string;
|
|
8
8
|
}
|
|
9
|
+
type AffiliateLookupStatus = 'found' | 'not_found' | 'lookup_failed' | 'not_configured';
|
|
10
|
+
interface AffiliateLookupResult {
|
|
11
|
+
status: AffiliateLookupStatus;
|
|
12
|
+
details: AffiliateDetails | null;
|
|
13
|
+
}
|
|
9
14
|
type InsertAffiliateIdentifierChangeCallback = (identifier: string | null, offerCode: string | null) => void;
|
|
10
15
|
declare class InsertAffiliate {
|
|
11
16
|
private static isInitialized;
|
|
@@ -24,9 +29,13 @@ declare class InsertAffiliate {
|
|
|
24
29
|
* Validates and sets a short code for affiliate tracking
|
|
25
30
|
* Validates the short code against the API before storing
|
|
26
31
|
* @param shortCode The short code to validate and set
|
|
32
|
+
* @param options.onLookupFailed called when the lookup itself couldn't be completed
|
|
33
|
+
* (not just an invalid code) — use it to offer a retry instead of proceeding unattributed.
|
|
27
34
|
* @returns true if the code exists and was successfully validated and stored, false otherwise
|
|
28
35
|
*/
|
|
29
|
-
static setShortCode(shortCode: string
|
|
36
|
+
static setShortCode(shortCode: string, options?: {
|
|
37
|
+
onLookupFailed?: () => void;
|
|
38
|
+
}): Promise<boolean>;
|
|
30
39
|
static setInsertAffiliateIdentifierChangeCallback(callback: InsertAffiliateIdentifierChangeCallback | null): void;
|
|
31
40
|
static isAffiliateAttributionValid(): Promise<boolean>;
|
|
32
41
|
static getAffiliateStoredDate(): Promise<string | null>;
|
|
@@ -36,8 +45,21 @@ declare class InsertAffiliate {
|
|
|
36
45
|
*/
|
|
37
46
|
static getAffiliateExpiryTimestamp(): Promise<number | null>;
|
|
38
47
|
/**
|
|
39
|
-
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
40
|
-
*
|
|
48
|
+
* Retrieve detailed information about an affiliate by their short code or deep link,
|
|
49
|
+
* distinguishing "no affiliate matches this code" from "couldn't check" (backend
|
|
50
|
+
* outage, timeout, rate limit). This method queries the API and does not store or set
|
|
51
|
+
* the affiliate identifier.
|
|
52
|
+
* @param affiliateCode The short code or deep link to look up
|
|
53
|
+
* @returns an AffiliateLookupResult with a status of 'found', 'not_found', 'lookup_failed', or 'not_configured'
|
|
54
|
+
*/
|
|
55
|
+
static getAffiliateLookupResult(affiliateCode: string, options?: {
|
|
56
|
+
trackUsage?: boolean;
|
|
57
|
+
}): Promise<AffiliateLookupResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Retrieve detailed information about an affiliate by their short code or deep link.
|
|
60
|
+
* Kept for backward compatibility: collapses 'not_found' and 'lookup_failed' into the
|
|
61
|
+
* same null result, exactly as before. Use getAffiliateLookupResult if you need to tell
|
|
62
|
+
* an invalid code apart from a backend outage.
|
|
41
63
|
* @param affiliateCode The short code or deep link to look up
|
|
42
64
|
* @returns AffiliateDetails if found, null otherwise
|
|
43
65
|
*/
|
|
@@ -76,4 +98,4 @@ declare class InsertAffiliate {
|
|
|
76
98
|
private static fetchShortLink;
|
|
77
99
|
}
|
|
78
100
|
|
|
79
|
-
export { type AffiliateDetails, InsertAffiliate, type InsertAffiliateIdentifierChangeCallback };
|
|
101
|
+
export { type AffiliateDetails, type AffiliateLookupResult, type AffiliateLookupStatus, InsertAffiliate, type InsertAffiliateIdentifierChangeCallback };
|
package/dist/index.js
CHANGED
|
@@ -190,9 +190,12 @@ var InsertAffiliate = class {
|
|
|
190
190
|
* Validates and sets a short code for affiliate tracking
|
|
191
191
|
* Validates the short code against the API before storing
|
|
192
192
|
* @param shortCode The short code to validate and set
|
|
193
|
+
* @param options.onLookupFailed called when the lookup itself couldn't be completed
|
|
194
|
+
* (not just an invalid code) — use it to offer a retry instead of proceeding unattributed.
|
|
193
195
|
* @returns true if the code exists and was successfully validated and stored, false otherwise
|
|
194
196
|
*/
|
|
195
|
-
static async setShortCode(shortCode) {
|
|
197
|
+
static async setShortCode(shortCode, options) {
|
|
198
|
+
var _a;
|
|
196
199
|
this.verboseLog(`Setting short code. Input: ${shortCode}`);
|
|
197
200
|
const valid = /^[a-zA-Z0-9]{3,25}$/.test(shortCode);
|
|
198
201
|
this.verboseLog(`Short code validation: ${valid ? "Valid" : "Invalid"}`);
|
|
@@ -200,12 +203,16 @@ var InsertAffiliate = class {
|
|
|
200
203
|
this.verboseLog("Invalid short code format, aborting");
|
|
201
204
|
return false;
|
|
202
205
|
}
|
|
203
|
-
const
|
|
204
|
-
if (!
|
|
205
|
-
this.verboseLog(`Short code '${shortCode}' does not exist or validation failed`);
|
|
206
|
+
const lookup = await this.getAffiliateLookupResult(shortCode, { trackUsage: true });
|
|
207
|
+
if (lookup.status !== "found" || !lookup.details) {
|
|
208
|
+
this.verboseLog(`Short code '${shortCode}' does not exist or validation failed (status: ${lookup.status})`);
|
|
206
209
|
console.error(`[Insert Affiliate] Error: Short code '${shortCode}' does not exist or validation failed.`);
|
|
210
|
+
if (lookup.status === "lookup_failed") {
|
|
211
|
+
(_a = options == null ? void 0 : options.onLookupFailed) == null ? void 0 : _a.call(options);
|
|
212
|
+
}
|
|
207
213
|
return false;
|
|
208
214
|
}
|
|
215
|
+
const affiliateDetails = lookup.details;
|
|
209
216
|
this.verboseLog(`Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
|
|
210
217
|
console.log(`[Insert Affiliate] Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
|
|
211
218
|
this.verboseLog("Calling setInsertAffiliateIdentifier with short code");
|
|
@@ -271,17 +278,19 @@ var InsertAffiliate = class {
|
|
|
271
278
|
return expiryTimestamp;
|
|
272
279
|
}
|
|
273
280
|
/**
|
|
274
|
-
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
275
|
-
*
|
|
281
|
+
* Retrieve detailed information about an affiliate by their short code or deep link,
|
|
282
|
+
* distinguishing "no affiliate matches this code" from "couldn't check" (backend
|
|
283
|
+
* outage, timeout, rate limit). This method queries the API and does not store or set
|
|
284
|
+
* the affiliate identifier.
|
|
276
285
|
* @param affiliateCode The short code or deep link to look up
|
|
277
|
-
* @returns
|
|
286
|
+
* @returns an AffiliateLookupResult with a status of 'found', 'not_found', 'lookup_failed', or 'not_configured'
|
|
278
287
|
*/
|
|
279
|
-
static async
|
|
288
|
+
static async getAffiliateLookupResult(affiliateCode, options) {
|
|
280
289
|
this.verboseLog(`Getting affiliate details for: ${affiliateCode}`);
|
|
281
290
|
const companyCode = this.companyCode || await getValue("companyCode");
|
|
282
291
|
if (!companyCode) {
|
|
283
292
|
this.verboseLog("Cannot get affiliate details: no company code available");
|
|
284
|
-
return null;
|
|
293
|
+
return { status: "not_configured", details: null };
|
|
285
294
|
}
|
|
286
295
|
const cleanCode = affiliateCode.includes("-") ? affiliateCode.split("-")[0] : affiliateCode;
|
|
287
296
|
this.verboseLog(`Clean code: ${cleanCode}`);
|
|
@@ -304,26 +313,42 @@ var InsertAffiliate = class {
|
|
|
304
313
|
this.verboseLog(`API response status: ${response.status}`);
|
|
305
314
|
if (!response.ok) {
|
|
306
315
|
this.verboseLog(`Failed to get affiliate details, status: ${response.status}`);
|
|
307
|
-
return null;
|
|
316
|
+
return { status: "lookup_failed", details: null };
|
|
308
317
|
}
|
|
309
318
|
const data = await response.json();
|
|
310
319
|
this.verboseLog(`API response data: ${JSON.stringify(data)}`);
|
|
311
|
-
if (data.exists
|
|
320
|
+
if (data.exists) {
|
|
321
|
+
if (!data.affiliate) {
|
|
322
|
+
this.verboseLog("Affiliate exists but response is missing affiliate details");
|
|
323
|
+
return { status: "lookup_failed", details: null };
|
|
324
|
+
}
|
|
312
325
|
const details = {
|
|
313
326
|
affiliateName: data.affiliate.affiliateName,
|
|
314
327
|
affiliateShortCode: data.affiliate.affiliateShortCode,
|
|
315
328
|
deeplinkUrl: data.affiliate.deeplinkurl
|
|
316
329
|
};
|
|
317
330
|
this.verboseLog(`Successfully retrieved affiliate details for: ${details.affiliateName}`);
|
|
318
|
-
return details;
|
|
331
|
+
return { status: "found", details };
|
|
319
332
|
}
|
|
320
333
|
this.verboseLog("Affiliate does not exist");
|
|
321
|
-
return null;
|
|
334
|
+
return { status: "not_found", details: null };
|
|
322
335
|
} catch (error) {
|
|
323
336
|
this.verboseLog(`Error fetching affiliate details: ${error}`);
|
|
324
|
-
return null;
|
|
337
|
+
return { status: "lookup_failed", details: null };
|
|
325
338
|
}
|
|
326
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Retrieve detailed information about an affiliate by their short code or deep link.
|
|
342
|
+
* Kept for backward compatibility: collapses 'not_found' and 'lookup_failed' into the
|
|
343
|
+
* same null result, exactly as before. Use getAffiliateLookupResult if you need to tell
|
|
344
|
+
* an invalid code apart from a backend outage.
|
|
345
|
+
* @param affiliateCode The short code or deep link to look up
|
|
346
|
+
* @returns AffiliateDetails if found, null otherwise
|
|
347
|
+
*/
|
|
348
|
+
static async getAffiliateDetails(affiliateCode, options) {
|
|
349
|
+
const result = await this.getAffiliateLookupResult(affiliateCode, options);
|
|
350
|
+
return result.details;
|
|
351
|
+
}
|
|
327
352
|
static async returnCompanyId() {
|
|
328
353
|
this.verboseLog("Getting company ID...");
|
|
329
354
|
const companyCode = this.companyCode || await getValue("companyCode");
|
|
@@ -390,18 +415,13 @@ var InsertAffiliate = class {
|
|
|
390
415
|
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
391
416
|
return null;
|
|
392
417
|
}
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
const errorCodes = [
|
|
396
|
-
"errorofferCodeNotFound",
|
|
397
|
-
"errorAffiliateoffercodenotfoundinanycompany",
|
|
398
|
-
"errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas",
|
|
399
|
-
"Routenotfound"
|
|
400
|
-
];
|
|
401
|
-
if (errorCodes.includes(offerCode)) {
|
|
418
|
+
const rawOfferCode = await response.text();
|
|
419
|
+
if (rawOfferCode.includes("errorofferCodeNotFound") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompany") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas") || rawOfferCode.includes("Routenotfound")) {
|
|
402
420
|
this.verboseLog("Offer code not found or invalid");
|
|
403
421
|
return null;
|
|
404
422
|
}
|
|
423
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
424
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
405
425
|
const storageKey = platformType === "stripe" ? "offerCode" : `offerCode_${platformType}`;
|
|
406
426
|
await saveValue(storageKey, offerCode);
|
|
407
427
|
if (platformType === "stripe") {
|
|
@@ -431,18 +451,13 @@ var InsertAffiliate = class {
|
|
|
431
451
|
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
432
452
|
return null;
|
|
433
453
|
}
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
const errorCodes = [
|
|
437
|
-
"errorofferCodeNotFound",
|
|
438
|
-
"errorAffiliateoffercodenotfoundinanycompany",
|
|
439
|
-
"errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas",
|
|
440
|
-
"Routenotfound"
|
|
441
|
-
];
|
|
442
|
-
if (errorCodes.includes(offerCode)) {
|
|
454
|
+
const rawOfferCode = await response.text();
|
|
455
|
+
if (rawOfferCode.includes("errorofferCodeNotFound") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompany") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas") || rawOfferCode.includes("Routenotfound")) {
|
|
443
456
|
this.verboseLog("Offer code not found or invalid");
|
|
444
457
|
return null;
|
|
445
458
|
}
|
|
459
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
460
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
446
461
|
this.offerCode = offerCode;
|
|
447
462
|
await saveValue("offerCode", offerCode);
|
|
448
463
|
this.verboseLog(`Offer code stored successfully: ${offerCode}`);
|
|
@@ -611,18 +626,13 @@ var InsertAffiliate = class {
|
|
|
611
626
|
this.verboseLog(`API URL: ${url}`);
|
|
612
627
|
const res = await fetch(url);
|
|
613
628
|
this.verboseLog(`API response status: ${res.status}`);
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
const errorCodes = [
|
|
617
|
-
"errorofferCodeNotFound",
|
|
618
|
-
"errorAffiliateoffercodenotfoundinanycompany",
|
|
619
|
-
"errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas",
|
|
620
|
-
"Routenotfound"
|
|
621
|
-
];
|
|
622
|
-
if (errorCodes.includes(offerCode)) {
|
|
629
|
+
const rawOfferCode = await res.text();
|
|
630
|
+
if (rawOfferCode.includes("errorofferCodeNotFound") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompany") || rawOfferCode.includes("errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas") || rawOfferCode.includes("Routenotfound")) {
|
|
623
631
|
this.verboseLog("Offer code not found or invalid");
|
|
624
632
|
return;
|
|
625
633
|
}
|
|
634
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
635
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
626
636
|
const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
|
|
627
637
|
this.verboseLog(`Opening redeem URL: ${redeemUrl}`);
|
|
628
638
|
window.open(redeemUrl, "_blank");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/sdk/InsertAffiliate.ts
|
|
2
2
|
import { getValue, saveValue } from '../utils/asyncStorage';
|
|
3
|
-
import {
|
|
3
|
+
import { generateShortDeviceID, generateUUID } from '../utils/helpers';
|
|
4
4
|
|
|
5
5
|
interface IapticAndroidReceipt {
|
|
6
6
|
orderId: string;
|
|
@@ -25,6 +25,15 @@ export interface AffiliateDetails {
|
|
|
25
25
|
deeplinkUrl: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// 'lookup_failed' (outage, timeout) may be worth retrying. 'not_configured' (no company
|
|
29
|
+
// code set) never will be — it's always a bug in the calling app, not the code or backend.
|
|
30
|
+
export type AffiliateLookupStatus = 'found' | 'not_found' | 'lookup_failed' | 'not_configured';
|
|
31
|
+
|
|
32
|
+
export interface AffiliateLookupResult {
|
|
33
|
+
status: AffiliateLookupStatus;
|
|
34
|
+
details: AffiliateDetails | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
export type InsertAffiliateIdentifierChangeCallback = (identifier: string | null, offerCode: string | null) => void;
|
|
29
38
|
|
|
30
39
|
export class InsertAffiliate {
|
|
@@ -196,9 +205,11 @@ export class InsertAffiliate {
|
|
|
196
205
|
* Validates and sets a short code for affiliate tracking
|
|
197
206
|
* Validates the short code against the API before storing
|
|
198
207
|
* @param shortCode The short code to validate and set
|
|
208
|
+
* @param options.onLookupFailed called when the lookup itself couldn't be completed
|
|
209
|
+
* (not just an invalid code) — use it to offer a retry instead of proceeding unattributed.
|
|
199
210
|
* @returns true if the code exists and was successfully validated and stored, false otherwise
|
|
200
211
|
*/
|
|
201
|
-
static async setShortCode(shortCode: string): Promise<boolean> {
|
|
212
|
+
static async setShortCode(shortCode: string, options?: { onLookupFailed?: () => void }): Promise<boolean> {
|
|
202
213
|
this.verboseLog(`Setting short code. Input: ${shortCode}`);
|
|
203
214
|
|
|
204
215
|
const valid = /^[a-zA-Z0-9]{3,25}$/.test(shortCode);
|
|
@@ -210,12 +221,16 @@ export class InsertAffiliate {
|
|
|
210
221
|
}
|
|
211
222
|
|
|
212
223
|
// Validate that the short code exists in the system
|
|
213
|
-
const
|
|
214
|
-
if (!
|
|
215
|
-
this.verboseLog(`Short code '${shortCode}' does not exist or validation failed`);
|
|
224
|
+
const lookup = await this.getAffiliateLookupResult(shortCode, { trackUsage: true });
|
|
225
|
+
if (lookup.status !== 'found' || !lookup.details) {
|
|
226
|
+
this.verboseLog(`Short code '${shortCode}' does not exist or validation failed (status: ${lookup.status})`);
|
|
216
227
|
console.error(`[Insert Affiliate] Error: Short code '${shortCode}' does not exist or validation failed.`);
|
|
228
|
+
if (lookup.status === 'lookup_failed') {
|
|
229
|
+
options?.onLookupFailed?.();
|
|
230
|
+
}
|
|
217
231
|
return false;
|
|
218
232
|
}
|
|
233
|
+
const affiliateDetails = lookup.details;
|
|
219
234
|
|
|
220
235
|
this.verboseLog(`Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
|
|
221
236
|
console.log(`[Insert Affiliate] Short code validated successfully for affiliate: ${affiliateDetails.affiliateName}`);
|
|
@@ -304,18 +319,20 @@ export class InsertAffiliate {
|
|
|
304
319
|
}
|
|
305
320
|
|
|
306
321
|
/**
|
|
307
|
-
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
308
|
-
*
|
|
322
|
+
* Retrieve detailed information about an affiliate by their short code or deep link,
|
|
323
|
+
* distinguishing "no affiliate matches this code" from "couldn't check" (backend
|
|
324
|
+
* outage, timeout, rate limit). This method queries the API and does not store or set
|
|
325
|
+
* the affiliate identifier.
|
|
309
326
|
* @param affiliateCode The short code or deep link to look up
|
|
310
|
-
* @returns
|
|
327
|
+
* @returns an AffiliateLookupResult with a status of 'found', 'not_found', 'lookup_failed', or 'not_configured'
|
|
311
328
|
*/
|
|
312
|
-
static async
|
|
329
|
+
static async getAffiliateLookupResult(affiliateCode: string, options?: { trackUsage?: boolean }): Promise<AffiliateLookupResult> {
|
|
313
330
|
this.verboseLog(`Getting affiliate details for: ${affiliateCode}`);
|
|
314
331
|
|
|
315
332
|
const companyCode = this.companyCode || await getValue('companyCode');
|
|
316
333
|
if (!companyCode) {
|
|
317
334
|
this.verboseLog('Cannot get affiliate details: no company code available');
|
|
318
|
-
return null;
|
|
335
|
+
return { status: 'not_configured', details: null };
|
|
319
336
|
}
|
|
320
337
|
|
|
321
338
|
// Strip UUID from code if present (e.g., "ABC123-uuid" becomes "ABC123")
|
|
@@ -346,13 +363,19 @@ export class InsertAffiliate {
|
|
|
346
363
|
|
|
347
364
|
if (!response.ok) {
|
|
348
365
|
this.verboseLog(`Failed to get affiliate details, status: ${response.status}`);
|
|
349
|
-
return null;
|
|
366
|
+
return { status: 'lookup_failed', details: null };
|
|
350
367
|
}
|
|
351
368
|
|
|
352
369
|
const data = await response.json();
|
|
353
370
|
this.verboseLog(`API response data: ${JSON.stringify(data)}`);
|
|
354
371
|
|
|
355
|
-
if (data.exists
|
|
372
|
+
if (data.exists) {
|
|
373
|
+
if (!data.affiliate) {
|
|
374
|
+
// Malformed response, not a real not-found.
|
|
375
|
+
this.verboseLog('Affiliate exists but response is missing affiliate details');
|
|
376
|
+
return { status: 'lookup_failed', details: null };
|
|
377
|
+
}
|
|
378
|
+
|
|
356
379
|
const details: AffiliateDetails = {
|
|
357
380
|
affiliateName: data.affiliate.affiliateName,
|
|
358
381
|
affiliateShortCode: data.affiliate.affiliateShortCode,
|
|
@@ -360,17 +383,30 @@ export class InsertAffiliate {
|
|
|
360
383
|
};
|
|
361
384
|
|
|
362
385
|
this.verboseLog(`Successfully retrieved affiliate details for: ${details.affiliateName}`);
|
|
363
|
-
return details;
|
|
386
|
+
return { status: 'found', details };
|
|
364
387
|
}
|
|
365
388
|
|
|
366
389
|
this.verboseLog('Affiliate does not exist');
|
|
367
|
-
return null;
|
|
390
|
+
return { status: 'not_found', details: null };
|
|
368
391
|
} catch (error) {
|
|
369
392
|
this.verboseLog(`Error fetching affiliate details: ${error}`);
|
|
370
|
-
return null;
|
|
393
|
+
return { status: 'lookup_failed', details: null };
|
|
371
394
|
}
|
|
372
395
|
}
|
|
373
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Retrieve detailed information about an affiliate by their short code or deep link.
|
|
399
|
+
* Kept for backward compatibility: collapses 'not_found' and 'lookup_failed' into the
|
|
400
|
+
* same null result, exactly as before. Use getAffiliateLookupResult if you need to tell
|
|
401
|
+
* an invalid code apart from a backend outage.
|
|
402
|
+
* @param affiliateCode The short code or deep link to look up
|
|
403
|
+
* @returns AffiliateDetails if found, null otherwise
|
|
404
|
+
*/
|
|
405
|
+
static async getAffiliateDetails(affiliateCode: string, options?: { trackUsage?: boolean }): Promise<AffiliateDetails | null> {
|
|
406
|
+
const result = await this.getAffiliateLookupResult(affiliateCode, options);
|
|
407
|
+
return result.details;
|
|
408
|
+
}
|
|
409
|
+
|
|
374
410
|
static async returnCompanyId(): Promise<string | null> {
|
|
375
411
|
this.verboseLog('Getting company ID...');
|
|
376
412
|
const companyCode = this.companyCode || await getValue('companyCode');
|
|
@@ -453,21 +489,23 @@ export class InsertAffiliate {
|
|
|
453
489
|
return null;
|
|
454
490
|
}
|
|
455
491
|
|
|
456
|
-
const
|
|
457
|
-
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
458
|
-
|
|
459
|
-
const errorCodes = [
|
|
460
|
-
'errorofferCodeNotFound',
|
|
461
|
-
'errorAffiliateoffercodenotfoundinanycompany',
|
|
462
|
-
'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas',
|
|
463
|
-
'Routenotfound'
|
|
464
|
-
];
|
|
492
|
+
const rawOfferCode = await response.text();
|
|
465
493
|
|
|
466
|
-
|
|
494
|
+
// Check for specific error strings from API before cleaning
|
|
495
|
+
if (
|
|
496
|
+
rawOfferCode.includes('errorofferCodeNotFound') ||
|
|
497
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompany') ||
|
|
498
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas') ||
|
|
499
|
+
rawOfferCode.includes('Routenotfound')
|
|
500
|
+
) {
|
|
467
501
|
this.verboseLog('Offer code not found or invalid');
|
|
468
502
|
return null;
|
|
469
503
|
}
|
|
470
504
|
|
|
505
|
+
// Remove special characters, keep only alphanumeric, underscores, and dashes
|
|
506
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
507
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
508
|
+
|
|
471
509
|
// Store offer code with platform-specific key
|
|
472
510
|
const storageKey = platformType === 'stripe' ? 'offerCode' : `offerCode_${platformType}`;
|
|
473
511
|
await saveValue(storageKey, offerCode);
|
|
@@ -507,22 +545,23 @@ export class InsertAffiliate {
|
|
|
507
545
|
return null;
|
|
508
546
|
}
|
|
509
547
|
|
|
510
|
-
const
|
|
511
|
-
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
548
|
+
const rawOfferCode = await response.text();
|
|
512
549
|
|
|
513
|
-
// Check for error
|
|
514
|
-
|
|
515
|
-
'errorofferCodeNotFound'
|
|
516
|
-
'errorAffiliateoffercodenotfoundinanycompany'
|
|
517
|
-
'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas'
|
|
518
|
-
'Routenotfound'
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (errorCodes.includes(offerCode)) {
|
|
550
|
+
// Check for specific error strings from API before cleaning
|
|
551
|
+
if (
|
|
552
|
+
rawOfferCode.includes('errorofferCodeNotFound') ||
|
|
553
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompany') ||
|
|
554
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas') ||
|
|
555
|
+
rawOfferCode.includes('Routenotfound')
|
|
556
|
+
) {
|
|
522
557
|
this.verboseLog('Offer code not found or invalid');
|
|
523
558
|
return null;
|
|
524
559
|
}
|
|
525
560
|
|
|
561
|
+
// Remove special characters, keep only alphanumeric, underscores, and dashes
|
|
562
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
563
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
564
|
+
|
|
526
565
|
// Store offer code
|
|
527
566
|
this.offerCode = offerCode;
|
|
528
567
|
await saveValue('offerCode', offerCode);
|
|
@@ -739,21 +778,23 @@ export class InsertAffiliate {
|
|
|
739
778
|
const res = await fetch(url);
|
|
740
779
|
this.verboseLog(`API response status: ${res.status}`);
|
|
741
780
|
|
|
742
|
-
const
|
|
743
|
-
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
781
|
+
const rawOfferCode = await res.text();
|
|
744
782
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
'
|
|
748
|
-
'
|
|
749
|
-
'
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if (errorCodes.includes(offerCode)) {
|
|
783
|
+
// Check for specific error strings from API before cleaning
|
|
784
|
+
if (
|
|
785
|
+
rawOfferCode.includes('errorofferCodeNotFound') ||
|
|
786
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompany') ||
|
|
787
|
+
rawOfferCode.includes('errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas') ||
|
|
788
|
+
rawOfferCode.includes('Routenotfound')
|
|
789
|
+
) {
|
|
753
790
|
this.verboseLog('Offer code not found or invalid');
|
|
754
791
|
return;
|
|
755
792
|
}
|
|
756
793
|
|
|
794
|
+
// Remove special characters, keep only alphanumeric, underscores, and dashes
|
|
795
|
+
const offerCode = rawOfferCode.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
796
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
797
|
+
|
|
757
798
|
const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
|
|
758
799
|
this.verboseLog(`Opening redeem URL: ${redeemUrl}`);
|
|
759
800
|
window.open(redeemUrl, '_blank');
|