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/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): Promise<AffiliateDetails | null>;
44
+ static getAffiliateDetails(affiliateCode: string, options?: {
45
+ trackUsage?: boolean;
46
+ }): Promise<AffiliateDetails | null>;
39
47
  static returnCompanyId(): Promise<string | null>;
40
- static getOfferCode(): Promise<string | null>;
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
- static async getOfferCode() {
299
- this.verboseLog("Getting offer code...");
300
- if (this.offerCode) {
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("offerCode");
345
+ const storedOfferCode = await getValue(storageKey);
305
346
  if (storedOfferCode) {
306
347
  this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
307
- this.offerCode = storedOfferCode;
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)