insert-affiliate-js-sdk 1.1.0 → 1.3.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/README.md +362 -560
- package/dist/index.d.ts +30 -4
- package/dist/index.js +121 -14
- package/docs/deep-linking-web.md +199 -0
- package/docs/stripe-integration.md +237 -0
- package/package.json +1 -1
- package/src/sdk/InsertAffiliate.ts +156 -18
package/dist/index.d.ts
CHANGED
|
@@ -6,16 +6,17 @@ interface AffiliateDetails {
|
|
|
6
6
|
affiliateShortCode: string;
|
|
7
7
|
deeplinkUrl: string;
|
|
8
8
|
}
|
|
9
|
-
type InsertAffiliateIdentifierChangeCallback = (identifier: string | null) => void;
|
|
9
|
+
type InsertAffiliateIdentifierChangeCallback = (identifier: string | null, offerCode: string | null) => void;
|
|
10
10
|
declare class InsertAffiliate {
|
|
11
11
|
private static isInitialized;
|
|
12
12
|
private static companyCode;
|
|
13
13
|
private static verboseLogging;
|
|
14
14
|
private static insertAffiliateIdentifierChangeCallback;
|
|
15
15
|
private static affiliateAttributionActiveTime;
|
|
16
|
+
private static preventAffiliateTransfer;
|
|
16
17
|
private static offerCode;
|
|
17
18
|
private static verboseLog;
|
|
18
|
-
static initialize(code: string | null, verboseLogging?: boolean, affiliateAttributionActiveTime?: number): Promise<void>;
|
|
19
|
+
static initialize(code: string | null, verboseLogging?: boolean, affiliateAttributionActiveTime?: number, preventAffiliateTransfer?: boolean): Promise<void>;
|
|
19
20
|
private static checkForInsertAffiliateParam;
|
|
20
21
|
static returnInsertAffiliateIdentifier(ignoreTimeout?: boolean): Promise<string | null>;
|
|
21
22
|
static setInsertAffiliateIdentifier(referringLink: string): Promise<string | null>;
|
|
@@ -29,15 +30,40 @@ declare class InsertAffiliate {
|
|
|
29
30
|
static setInsertAffiliateIdentifierChangeCallback(callback: InsertAffiliateIdentifierChangeCallback | null): void;
|
|
30
31
|
static isAffiliateAttributionValid(): Promise<boolean>;
|
|
31
32
|
static getAffiliateStoredDate(): Promise<string | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Get the Unix timestamp (in milliseconds) when the affiliate attribution will expire
|
|
35
|
+
* @returns The expiry timestamp in milliseconds, or null if no attribution or no timeout configured
|
|
36
|
+
*/
|
|
37
|
+
static getAffiliateExpiryTimestamp(): Promise<number | null>;
|
|
32
38
|
/**
|
|
33
39
|
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
34
40
|
* This method queries the API and does not store or set the affiliate identifier
|
|
35
41
|
* @param affiliateCode The short code or deep link to look up
|
|
36
42
|
* @returns AffiliateDetails if found, null otherwise
|
|
37
43
|
*/
|
|
38
|
-
static getAffiliateDetails(affiliateCode: string
|
|
44
|
+
static getAffiliateDetails(affiliateCode: string, options?: {
|
|
45
|
+
trackUsage?: boolean;
|
|
46
|
+
}): Promise<AffiliateDetails | null>;
|
|
39
47
|
static returnCompanyId(): Promise<string | null>;
|
|
40
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Get the offer code for the current affiliate
|
|
50
|
+
* @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
|
|
51
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
52
|
+
*/
|
|
53
|
+
static getOfferCode(platformType?: 'ios' | 'android' | 'stripe'): Promise<string | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Get the Stripe coupon/promo code for the current affiliate
|
|
56
|
+
* Convenience method that calls getOfferCode with platformType='stripe'
|
|
57
|
+
* @returns The Stripe coupon/promo code, or null if not found
|
|
58
|
+
*/
|
|
59
|
+
static getStripeCouponCode(): Promise<string | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Fetch offer code for a specific platform
|
|
62
|
+
* @param shortCode The affiliate short code
|
|
63
|
+
* @param platformType The platform type: 'ios', 'android', or 'stripe'
|
|
64
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
65
|
+
*/
|
|
66
|
+
private static fetchOfferCodeForPlatform;
|
|
41
67
|
private static fetchAndStoreOfferCode;
|
|
42
68
|
static trackEvent(eventName: string): Promise<void>;
|
|
43
69
|
static returnUserAccountTokenAndStoreExpectedTransaction(): Promise<string | null>;
|
package/dist/index.js
CHANGED
|
@@ -80,12 +80,14 @@ var InsertAffiliate = class {
|
|
|
80
80
|
console.log(`[Insert Affiliate] [VERBOSE] ${message}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
static async initialize(code, verboseLogging = false, affiliateAttributionActiveTime) {
|
|
83
|
+
static async initialize(code, verboseLogging = false, affiliateAttributionActiveTime, preventAffiliateTransfer = false) {
|
|
84
84
|
this.verboseLogging = verboseLogging;
|
|
85
|
+
this.preventAffiliateTransfer = preventAffiliateTransfer;
|
|
85
86
|
if (verboseLogging) {
|
|
86
87
|
this.verboseLog("Starting SDK initialization...");
|
|
87
88
|
this.verboseLog(`Company code provided: ${code ? "Yes" : "No"}`);
|
|
88
89
|
this.verboseLog("Verbose logging enabled");
|
|
90
|
+
this.verboseLog(`Prevent affiliate transfer: ${preventAffiliateTransfer}`);
|
|
89
91
|
}
|
|
90
92
|
if (this.isInitialized) {
|
|
91
93
|
this.verboseLog("SDK already initialized, skipping");
|
|
@@ -165,6 +167,11 @@ var InsertAffiliate = class {
|
|
|
165
167
|
this.verboseLog(`Returning existing identifier: ${identifier2}`);
|
|
166
168
|
return identifier2;
|
|
167
169
|
}
|
|
170
|
+
if (this.preventAffiliateTransfer && existingShortCode && existingShortCode !== shortCode) {
|
|
171
|
+
this.verboseLog(`Transfer blocked: existing affiliate "${existingShortCode}" protected from being replaced by "${shortCode}"`);
|
|
172
|
+
const identifier2 = `${existingShortCode}-${userId}`;
|
|
173
|
+
return identifier2;
|
|
174
|
+
}
|
|
168
175
|
this.verboseLog(`Saving short code to storage: ${shortCode}`);
|
|
169
176
|
await saveValue("referrerLink", shortCode);
|
|
170
177
|
const storedDate = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -172,10 +179,10 @@ var InsertAffiliate = class {
|
|
|
172
179
|
this.verboseLog(`Short code saved successfully with stored date: ${storedDate}`);
|
|
173
180
|
const identifier = `${shortCode}-${userId}`;
|
|
174
181
|
this.verboseLog(`Returning identifier: ${identifier}`);
|
|
175
|
-
await this.fetchAndStoreOfferCode(shortCode);
|
|
182
|
+
const offerCode = await this.fetchAndStoreOfferCode(shortCode);
|
|
176
183
|
if (this.insertAffiliateIdentifierChangeCallback) {
|
|
177
|
-
this.verboseLog(`Triggering callback with identifier: ${identifier}`);
|
|
178
|
-
this.insertAffiliateIdentifierChangeCallback(identifier);
|
|
184
|
+
this.verboseLog(`Triggering callback with identifier: ${identifier}, offerCode: ${offerCode || "none"}`);
|
|
185
|
+
this.insertAffiliateIdentifierChangeCallback(identifier, offerCode);
|
|
179
186
|
}
|
|
180
187
|
return identifier;
|
|
181
188
|
}
|
|
@@ -193,7 +200,7 @@ var InsertAffiliate = class {
|
|
|
193
200
|
this.verboseLog("Invalid short code format, aborting");
|
|
194
201
|
return false;
|
|
195
202
|
}
|
|
196
|
-
const affiliateDetails = await this.getAffiliateDetails(shortCode);
|
|
203
|
+
const affiliateDetails = await this.getAffiliateDetails(shortCode, { trackUsage: true });
|
|
197
204
|
if (!affiliateDetails) {
|
|
198
205
|
this.verboseLog(`Short code '${shortCode}' does not exist or validation failed`);
|
|
199
206
|
console.error(`[Insert Affiliate] Error: Short code '${shortCode}' does not exist or validation failed.`);
|
|
@@ -238,13 +245,38 @@ var InsertAffiliate = class {
|
|
|
238
245
|
this.verboseLog(`Stored date: ${storedDate || "none"}`);
|
|
239
246
|
return storedDate;
|
|
240
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Get the Unix timestamp (in milliseconds) when the affiliate attribution will expire
|
|
250
|
+
* @returns The expiry timestamp in milliseconds, or null if no attribution or no timeout configured
|
|
251
|
+
*/
|
|
252
|
+
static async getAffiliateExpiryTimestamp() {
|
|
253
|
+
this.verboseLog("Getting affiliate expiry timestamp...");
|
|
254
|
+
const storedDateStr = await getValue("affiliateStoredDate");
|
|
255
|
+
if (!storedDateStr) {
|
|
256
|
+
this.verboseLog("No stored date found, returning null");
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
let timeoutMs = this.affiliateAttributionActiveTime;
|
|
260
|
+
if (timeoutMs === null) {
|
|
261
|
+
const storedTimeout = await getValue("affiliateAttributionActiveTime");
|
|
262
|
+
timeoutMs = storedTimeout ? parseInt(storedTimeout, 10) : null;
|
|
263
|
+
}
|
|
264
|
+
if (timeoutMs === null) {
|
|
265
|
+
this.verboseLog("No attribution timeout configured, returning null");
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const storedDate = new Date(storedDateStr);
|
|
269
|
+
const expiryTimestamp = storedDate.getTime() + timeoutMs;
|
|
270
|
+
this.verboseLog(`Expiry timestamp: ${expiryTimestamp} (stored: ${storedDateStr}, timeout: ${timeoutMs}ms)`);
|
|
271
|
+
return expiryTimestamp;
|
|
272
|
+
}
|
|
241
273
|
/**
|
|
242
274
|
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
243
275
|
* This method queries the API and does not store or set the affiliate identifier
|
|
244
276
|
* @param affiliateCode The short code or deep link to look up
|
|
245
277
|
* @returns AffiliateDetails if found, null otherwise
|
|
246
278
|
*/
|
|
247
|
-
static async getAffiliateDetails(affiliateCode) {
|
|
279
|
+
static async getAffiliateDetails(affiliateCode, options) {
|
|
248
280
|
this.verboseLog(`Getting affiliate details for: ${affiliateCode}`);
|
|
249
281
|
const companyCode = this.companyCode || await getValue("companyCode");
|
|
250
282
|
if (!companyCode) {
|
|
@@ -259,6 +291,9 @@ var InsertAffiliate = class {
|
|
|
259
291
|
companyId: companyCode,
|
|
260
292
|
affiliateCode: cleanCode
|
|
261
293
|
};
|
|
294
|
+
if (options == null ? void 0 : options.trackUsage) {
|
|
295
|
+
payload.trackUsage = true;
|
|
296
|
+
}
|
|
262
297
|
this.verboseLog(`Making API call to: ${url}`);
|
|
263
298
|
this.verboseLog(`Payload: ${JSON.stringify(payload)}`);
|
|
264
299
|
const response = await fetch(url, {
|
|
@@ -295,28 +330,97 @@ var InsertAffiliate = class {
|
|
|
295
330
|
this.verboseLog(`Company ID: ${companyCode || "none"}`);
|
|
296
331
|
return companyCode;
|
|
297
332
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
333
|
+
/**
|
|
334
|
+
* Get the offer code for the current affiliate
|
|
335
|
+
* @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
|
|
336
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
337
|
+
*/
|
|
338
|
+
static async getOfferCode(platformType = "stripe") {
|
|
339
|
+
this.verboseLog(`Getting offer code for platform: ${platformType}...`);
|
|
340
|
+
const storageKey = platformType === "stripe" ? "offerCode" : `offerCode_${platformType}`;
|
|
341
|
+
if (platformType === "stripe" && this.offerCode) {
|
|
301
342
|
this.verboseLog(`Returning cached offer code: ${this.offerCode}`);
|
|
302
343
|
return this.offerCode;
|
|
303
344
|
}
|
|
304
|
-
const storedOfferCode = await getValue(
|
|
345
|
+
const storedOfferCode = await getValue(storageKey);
|
|
305
346
|
if (storedOfferCode) {
|
|
306
347
|
this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
|
|
307
|
-
|
|
348
|
+
if (platformType === "stripe") {
|
|
349
|
+
this.offerCode = storedOfferCode;
|
|
350
|
+
}
|
|
308
351
|
return storedOfferCode;
|
|
309
352
|
}
|
|
353
|
+
const shortCode = await getValue("referrerLink");
|
|
354
|
+
if (shortCode) {
|
|
355
|
+
this.verboseLog(`No stored offer code, fetching for short code: ${shortCode}`);
|
|
356
|
+
const fetchedCode = await this.fetchOfferCodeForPlatform(shortCode, platformType);
|
|
357
|
+
return fetchedCode;
|
|
358
|
+
}
|
|
310
359
|
this.verboseLog("No offer code found");
|
|
311
360
|
return null;
|
|
312
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Get the Stripe coupon/promo code for the current affiliate
|
|
364
|
+
* Convenience method that calls getOfferCode with platformType='stripe'
|
|
365
|
+
* @returns The Stripe coupon/promo code, or null if not found
|
|
366
|
+
*/
|
|
367
|
+
static async getStripeCouponCode() {
|
|
368
|
+
return this.getOfferCode("stripe");
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Fetch offer code for a specific platform
|
|
372
|
+
* @param shortCode The affiliate short code
|
|
373
|
+
* @param platformType The platform type: 'ios', 'android', or 'stripe'
|
|
374
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
375
|
+
*/
|
|
376
|
+
static async fetchOfferCodeForPlatform(shortCode, platformType) {
|
|
377
|
+
this.verboseLog(`Fetching offer code for platform: ${platformType}, short code: ${shortCode}`);
|
|
378
|
+
try {
|
|
379
|
+
const companyCode = this.companyCode || await getValue("companyCode");
|
|
380
|
+
if (!companyCode) {
|
|
381
|
+
this.verboseLog("Cannot fetch offer code: no company code available");
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const encoded = encodeURIComponent(shortCode);
|
|
385
|
+
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}?platformType=${platformType}`;
|
|
386
|
+
this.verboseLog(`Making API call to: ${url}`);
|
|
387
|
+
const response = await fetch(url);
|
|
388
|
+
this.verboseLog(`API response status: ${response.status}`);
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, "");
|
|
394
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
395
|
+
const errorCodes = [
|
|
396
|
+
"errorofferCodeNotFound",
|
|
397
|
+
"errorAffiliateoffercodenotfoundinanycompany",
|
|
398
|
+
"errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas",
|
|
399
|
+
"Routenotfound"
|
|
400
|
+
];
|
|
401
|
+
if (errorCodes.includes(offerCode)) {
|
|
402
|
+
this.verboseLog("Offer code not found or invalid");
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
const storageKey = platformType === "stripe" ? "offerCode" : `offerCode_${platformType}`;
|
|
406
|
+
await saveValue(storageKey, offerCode);
|
|
407
|
+
if (platformType === "stripe") {
|
|
408
|
+
this.offerCode = offerCode;
|
|
409
|
+
}
|
|
410
|
+
this.verboseLog(`Offer code stored successfully with key ${storageKey}: ${offerCode}`);
|
|
411
|
+
return offerCode;
|
|
412
|
+
} catch (error) {
|
|
413
|
+
this.verboseLog(`Error fetching offer code: ${error}`);
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
313
417
|
static async fetchAndStoreOfferCode(shortCode) {
|
|
314
418
|
this.verboseLog(`Fetching offer code for short code: ${shortCode}`);
|
|
315
419
|
try {
|
|
316
420
|
const companyCode = this.companyCode || await getValue("companyCode");
|
|
317
421
|
if (!companyCode) {
|
|
318
422
|
this.verboseLog("Cannot fetch offer code: no company code available");
|
|
319
|
-
return;
|
|
423
|
+
return null;
|
|
320
424
|
}
|
|
321
425
|
const encoded = encodeURIComponent(shortCode);
|
|
322
426
|
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}`;
|
|
@@ -325,7 +429,7 @@ var InsertAffiliate = class {
|
|
|
325
429
|
this.verboseLog(`API response status: ${response.status}`);
|
|
326
430
|
if (!response.ok) {
|
|
327
431
|
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
328
|
-
return;
|
|
432
|
+
return null;
|
|
329
433
|
}
|
|
330
434
|
const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, "");
|
|
331
435
|
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
@@ -337,13 +441,15 @@ var InsertAffiliate = class {
|
|
|
337
441
|
];
|
|
338
442
|
if (errorCodes.includes(offerCode)) {
|
|
339
443
|
this.verboseLog("Offer code not found or invalid");
|
|
340
|
-
return;
|
|
444
|
+
return null;
|
|
341
445
|
}
|
|
342
446
|
this.offerCode = offerCode;
|
|
343
447
|
await saveValue("offerCode", offerCode);
|
|
344
448
|
this.verboseLog(`Offer code stored successfully: ${offerCode}`);
|
|
449
|
+
return offerCode;
|
|
345
450
|
} catch (error) {
|
|
346
451
|
this.verboseLog(`Error fetching offer code: ${error}`);
|
|
452
|
+
return null;
|
|
347
453
|
}
|
|
348
454
|
}
|
|
349
455
|
static async trackEvent(eventName) {
|
|
@@ -567,6 +673,7 @@ InsertAffiliate.verboseLogging = false;
|
|
|
567
673
|
InsertAffiliate.insertAffiliateIdentifierChangeCallback = null;
|
|
568
674
|
InsertAffiliate.affiliateAttributionActiveTime = null;
|
|
569
675
|
// in milliseconds
|
|
676
|
+
InsertAffiliate.preventAffiliateTransfer = false;
|
|
570
677
|
InsertAffiliate.offerCode = null;
|
|
571
678
|
// Annotate the CommonJS export names for ESM import in node:
|
|
572
679
|
0 && (module.exports = {
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Deep Linking for Web Payments
|
|
2
|
+
|
|
3
|
+
This guide covers how to configure deep links from Branch.io, AppsFlyer, or Insert Links to correctly pass affiliate parameters to your web checkout.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
When using web-based payments (Stripe), your deep linking provider needs to redirect users to your web checkout page with the `insertAffiliate` parameter. The Insert Affiliate SDK will automatically detect this parameter and attribute the payment to the correct affiliate.
|
|
8
|
+
|
|
9
|
+
## Insert Links (Automatic)
|
|
10
|
+
|
|
11
|
+
If you're using [Insert Links](https://docs.insertaffiliate.com/insert-links) (Insert Affiliate's built-in deep linking solution), no additional configuration is needed for web payments.
|
|
12
|
+
|
|
13
|
+
Insert Links automatically:
|
|
14
|
+
1. Adds the `insertAffiliate` parameter to URLs
|
|
15
|
+
2. Detects and processes attribution in your web app
|
|
16
|
+
3. Tracks the payment to the correct affiliate
|
|
17
|
+
|
|
18
|
+
Just initialize the SDK and you're done:
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
22
|
+
|
|
23
|
+
await InsertAffiliate.initialize('your_company_code');
|
|
24
|
+
// Affiliate parameters are automatically detected from the URL
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Learn more: [Insert Links Documentation](https://docs.insertaffiliate.com/insert-links)
|
|
28
|
+
|
|
29
|
+
## Branch.io Web Redirect Setup
|
|
30
|
+
|
|
31
|
+
Use these steps if you're using [Branch.io](https://docs.insertaffiliate.com/branch) Quick Links to send users to your web-hosted payment page.
|
|
32
|
+
|
|
33
|
+
### Creating a Branch.io Quick Link for Web Checkout
|
|
34
|
+
|
|
35
|
+
1. **Create a new Quick Link** in your Branch.io dashboard
|
|
36
|
+
2. Go to the **Redirects** section
|
|
37
|
+
3. **Select "Web URL"** as the redirect destination
|
|
38
|
+
4. **Configure the URL** to point to your web app:
|
|
39
|
+
- Enter your web app's URL
|
|
40
|
+
- Append the parameter: `?insertAffiliate={affiliateShortCode}`
|
|
41
|
+
- Replace `{affiliateShortCode}` with the actual short code of the affiliate
|
|
42
|
+
|
|
43
|
+
**Example URL:**
|
|
44
|
+
```
|
|
45
|
+
https://yourwebsite.com/checkout?insertAffiliate=ABC123
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Once the user arrives on your site, the Insert Affiliate SDK automatically detects `insertAffiliate` and attributes the payment.
|
|
49
|
+
|
|
50
|
+
### Branch.io with Capacitor (Hybrid Apps)
|
|
51
|
+
|
|
52
|
+
For Capacitor apps using the Branch.io plugin:
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
import { BranchDeepLinks, BranchInitEvent } from 'capacitor-branch-deep-links';
|
|
56
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
57
|
+
|
|
58
|
+
// Set up callback to capture affiliate identifier
|
|
59
|
+
InsertAffiliate.setInsertAffiliateIdentifierChangeCallback((identifier) => {
|
|
60
|
+
if (identifier) {
|
|
61
|
+
console.log('Affiliate identifier captured:', identifier);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
let branchInitialised = false;
|
|
66
|
+
|
|
67
|
+
async function setUpBranchListener() {
|
|
68
|
+
if (branchInitialised) return;
|
|
69
|
+
branchInitialised = true;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
await BranchDeepLinks.addListener('init', async (event: BranchInitEvent) => {
|
|
73
|
+
const clicked = event?.referringParams?.['+clicked_branch_link'];
|
|
74
|
+
const referringLink = event?.referringParams?.['~referring_link'];
|
|
75
|
+
|
|
76
|
+
if (clicked && referringLink) {
|
|
77
|
+
// This will automatically trigger the callback
|
|
78
|
+
await InsertAffiliate.setInsertAffiliateIdentifier(referringLink);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
BranchDeepLinks.addListener('initError', (error: any) => {
|
|
83
|
+
console.error('Branch init error:', error);
|
|
84
|
+
});
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error('Error setting up Branch listener:', err);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## AppsFlyer Web Redirect Setup
|
|
92
|
+
|
|
93
|
+
Use these steps if you're using [AppsFlyer](https://docs.insertaffiliate.com/appsflyer) OneLinks to send users to your web checkout.
|
|
94
|
+
|
|
95
|
+
### Creating an AppsFlyer OneLink for Web Checkout
|
|
96
|
+
|
|
97
|
+
1. **Create a new OneLink** in your AppsFlyer dashboard
|
|
98
|
+
2. Open the link configuration settings
|
|
99
|
+
3. Under **"When link is clicked on desktop web page"**, set the redirect URL
|
|
100
|
+
4. Enter your web app's checkout URL
|
|
101
|
+
5. Append the parameter: `?insertAffiliate={affiliateShortCode}`
|
|
102
|
+
- Replace `{affiliateShortCode}` with the actual short code of the affiliate
|
|
103
|
+
|
|
104
|
+
**Example URL:**
|
|
105
|
+
```
|
|
106
|
+
https://yourwebsite.com/checkout?insertAffiliate=ABC123
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The Insert Affiliate SDK will automatically detect the parameter and attribute the resulting payment.
|
|
110
|
+
|
|
111
|
+
## How the SDK Detects Parameters
|
|
112
|
+
|
|
113
|
+
When your web app loads, the Insert Affiliate SDK automatically:
|
|
114
|
+
|
|
115
|
+
1. Checks the URL for `insertAffiliate` parameter
|
|
116
|
+
2. Validates the affiliate code with the Insert Affiliate API
|
|
117
|
+
3. Stores the affiliate identifier for later use
|
|
118
|
+
4. Triggers any registered callbacks
|
|
119
|
+
|
|
120
|
+
**Automatic URL Detection:**
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
124
|
+
|
|
125
|
+
// Initialize the SDK - it automatically checks URL parameters
|
|
126
|
+
await InsertAffiliate.initialize('your_company_code');
|
|
127
|
+
|
|
128
|
+
// The affiliate identifier is now available
|
|
129
|
+
const affiliateId = await InsertAffiliate.returnInsertAffiliateIdentifier();
|
|
130
|
+
console.log('Detected affiliate:', affiliateId);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Using the Change Callback
|
|
134
|
+
|
|
135
|
+
For dynamic updates when an affiliate link is clicked:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
139
|
+
|
|
140
|
+
// Set up callback before initialization
|
|
141
|
+
InsertAffiliate.setInsertAffiliateIdentifierChangeCallback((identifier) => {
|
|
142
|
+
if (identifier) {
|
|
143
|
+
console.log('Affiliate identifier changed:', identifier);
|
|
144
|
+
|
|
145
|
+
// Update UI
|
|
146
|
+
const banner = document.getElementById('affiliate-banner');
|
|
147
|
+
if (banner) {
|
|
148
|
+
banner.textContent = 'You used a special affiliate link!';
|
|
149
|
+
banner.style.display = 'block';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Track in analytics
|
|
153
|
+
analytics.track('affiliate_link_clicked', { identifier });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await InsertAffiliate.initialize('your_company_code');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## URL Parameter Reference
|
|
161
|
+
|
|
162
|
+
| Parameter | Description | Example |
|
|
163
|
+
|-----------|-------------|---------|
|
|
164
|
+
| `insertAffiliate` | The affiliate's short code | `?insertAffiliate=ABC123` |
|
|
165
|
+
|
|
166
|
+
## Testing
|
|
167
|
+
|
|
168
|
+
Test your deep link setup by visiting your web app with the affiliate parameter:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
https://yourwebsite.com/checkout?insertAffiliate=TEST123
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Then check the console for:
|
|
175
|
+
```
|
|
176
|
+
[Insert Affiliate] Affiliate identifier set: TEST123
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Troubleshooting
|
|
180
|
+
|
|
181
|
+
**Problem:** Affiliate parameter not detected
|
|
182
|
+
- **Solution:** Ensure the SDK is initialized before the URL parameters are processed
|
|
183
|
+
- Check that the parameter name is exactly `insertAffiliate` (case-sensitive)
|
|
184
|
+
|
|
185
|
+
**Problem:** Deep link opens app instead of web checkout
|
|
186
|
+
- **Solution:** Configure your deep linking provider to redirect to web URL for web-only flows
|
|
187
|
+
- Check the "Desktop redirect" settings in Branch.io or AppsFlyer
|
|
188
|
+
|
|
189
|
+
**Problem:** Parameter lost after page navigation
|
|
190
|
+
- **Solution:** The SDK stores the affiliate identifier in local storage, so it persists across pages
|
|
191
|
+
- Verify local storage is not being cleared
|
|
192
|
+
|
|
193
|
+
## Next Steps
|
|
194
|
+
|
|
195
|
+
- Configure your deep linking provider to pass `insertAffiliate` parameter
|
|
196
|
+
- Test with a sample affiliate short code
|
|
197
|
+
- Integrate with your payment flow (see [Stripe Integration Guide](./stripe-integration.md))
|
|
198
|
+
|
|
199
|
+
[Back to Main README](../README.md)
|