insert-affiliate-js-sdk 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -571
- package/dist/index.d.ts +19 -1
- package/dist/index.js +74 -5
- 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 +90 -6
package/dist/index.d.ts
CHANGED
|
@@ -37,7 +37,25 @@ declare class InsertAffiliate {
|
|
|
37
37
|
*/
|
|
38
38
|
static getAffiliateDetails(affiliateCode: string): Promise<AffiliateDetails | null>;
|
|
39
39
|
static returnCompanyId(): Promise<string | null>;
|
|
40
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Get the offer code for the current affiliate
|
|
42
|
+
* @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
|
|
43
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
44
|
+
*/
|
|
45
|
+
static getOfferCode(platformType?: 'ios' | 'android' | 'stripe'): Promise<string | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Get the Stripe coupon/promo code for the current affiliate
|
|
48
|
+
* Convenience method that calls getOfferCode with platformType='stripe'
|
|
49
|
+
* @returns The Stripe coupon/promo code, or null if not found
|
|
50
|
+
*/
|
|
51
|
+
static getStripeCouponCode(): Promise<string | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Fetch offer code for a specific platform
|
|
54
|
+
* @param shortCode The affiliate short code
|
|
55
|
+
* @param platformType The platform type: 'ios', 'android', or 'stripe'
|
|
56
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
57
|
+
*/
|
|
58
|
+
private static fetchOfferCodeForPlatform;
|
|
41
59
|
private static fetchAndStoreOfferCode;
|
|
42
60
|
static trackEvent(eventName: string): Promise<void>;
|
|
43
61
|
static returnUserAccountTokenAndStoreExpectedTransaction(): Promise<string | null>;
|
package/dist/index.js
CHANGED
|
@@ -295,21 +295,90 @@ var InsertAffiliate = class {
|
|
|
295
295
|
this.verboseLog(`Company ID: ${companyCode || "none"}`);
|
|
296
296
|
return companyCode;
|
|
297
297
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Get the offer code for the current affiliate
|
|
300
|
+
* @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
|
|
301
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
302
|
+
*/
|
|
303
|
+
static async getOfferCode(platformType = "stripe") {
|
|
304
|
+
this.verboseLog(`Getting offer code for platform: ${platformType}...`);
|
|
305
|
+
const storageKey = platformType === "stripe" ? "offerCode" : `offerCode_${platformType}`;
|
|
306
|
+
if (platformType === "stripe" && this.offerCode) {
|
|
301
307
|
this.verboseLog(`Returning cached offer code: ${this.offerCode}`);
|
|
302
308
|
return this.offerCode;
|
|
303
309
|
}
|
|
304
|
-
const storedOfferCode = await getValue(
|
|
310
|
+
const storedOfferCode = await getValue(storageKey);
|
|
305
311
|
if (storedOfferCode) {
|
|
306
312
|
this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
|
|
307
|
-
|
|
313
|
+
if (platformType === "stripe") {
|
|
314
|
+
this.offerCode = storedOfferCode;
|
|
315
|
+
}
|
|
308
316
|
return storedOfferCode;
|
|
309
317
|
}
|
|
318
|
+
const shortCode = await getValue("referrerLink");
|
|
319
|
+
if (shortCode) {
|
|
320
|
+
this.verboseLog(`No stored offer code, fetching for short code: ${shortCode}`);
|
|
321
|
+
const fetchedCode = await this.fetchOfferCodeForPlatform(shortCode, platformType);
|
|
322
|
+
return fetchedCode;
|
|
323
|
+
}
|
|
310
324
|
this.verboseLog("No offer code found");
|
|
311
325
|
return null;
|
|
312
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Get the Stripe coupon/promo code for the current affiliate
|
|
329
|
+
* Convenience method that calls getOfferCode with platformType='stripe'
|
|
330
|
+
* @returns The Stripe coupon/promo code, or null if not found
|
|
331
|
+
*/
|
|
332
|
+
static async getStripeCouponCode() {
|
|
333
|
+
return this.getOfferCode("stripe");
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Fetch offer code for a specific platform
|
|
337
|
+
* @param shortCode The affiliate short code
|
|
338
|
+
* @param platformType The platform type: 'ios', 'android', or 'stripe'
|
|
339
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
340
|
+
*/
|
|
341
|
+
static async fetchOfferCodeForPlatform(shortCode, platformType) {
|
|
342
|
+
this.verboseLog(`Fetching offer code for platform: ${platformType}, short code: ${shortCode}`);
|
|
343
|
+
try {
|
|
344
|
+
const companyCode = this.companyCode || await getValue("companyCode");
|
|
345
|
+
if (!companyCode) {
|
|
346
|
+
this.verboseLog("Cannot fetch offer code: no company code available");
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const encoded = encodeURIComponent(shortCode);
|
|
350
|
+
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}?platformType=${platformType}`;
|
|
351
|
+
this.verboseLog(`Making API call to: ${url}`);
|
|
352
|
+
const response = await fetch(url);
|
|
353
|
+
this.verboseLog(`API response status: ${response.status}`);
|
|
354
|
+
if (!response.ok) {
|
|
355
|
+
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, "");
|
|
359
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
360
|
+
const errorCodes = [
|
|
361
|
+
"errorofferCodeNotFound",
|
|
362
|
+
"errorAffiliateoffercodenotfoundinanycompany",
|
|
363
|
+
"errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas",
|
|
364
|
+
"Routenotfound"
|
|
365
|
+
];
|
|
366
|
+
if (errorCodes.includes(offerCode)) {
|
|
367
|
+
this.verboseLog("Offer code not found or invalid");
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const storageKey = platformType === "stripe" ? "offerCode" : `offerCode_${platformType}`;
|
|
371
|
+
await saveValue(storageKey, offerCode);
|
|
372
|
+
if (platformType === "stripe") {
|
|
373
|
+
this.offerCode = offerCode;
|
|
374
|
+
}
|
|
375
|
+
this.verboseLog(`Offer code stored successfully with key ${storageKey}: ${offerCode}`);
|
|
376
|
+
return offerCode;
|
|
377
|
+
} catch (error) {
|
|
378
|
+
this.verboseLog(`Error fetching offer code: ${error}`);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
313
382
|
static async fetchAndStoreOfferCode(shortCode) {
|
|
314
383
|
this.verboseLog(`Fetching offer code for short code: ${shortCode}`);
|
|
315
384
|
try {
|
|
@@ -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)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Stripe Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide covers all Stripe-based payment integrations for affiliate tracking with Insert Affiliate.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Insert Affiliate SDK initialized in your web application
|
|
8
|
+
- Stripe account connected to Insert Affiliate (see [Connect Stripe Account](#1-connect-your-stripe-account) below)
|
|
9
|
+
|
|
10
|
+
## 1. Connect Your Stripe Account
|
|
11
|
+
|
|
12
|
+
Before integrating the SDK code, you must connect your Stripe account to Insert Affiliate:
|
|
13
|
+
|
|
14
|
+
1. Go to your [Insert Affiliate dashboard settings](https://app.insertaffiliate.com/settings)
|
|
15
|
+
2. Navigate to the payment verification settings
|
|
16
|
+
3. Select **Stripe** as your verification method
|
|
17
|
+
4. Click **Connect with Stripe** to authorize the connection via Stripe Connect
|
|
18
|
+
5. Once connected, Insert Affiliate will automatically receive your Stripe events
|
|
19
|
+
|
|
20
|
+
## 2. Direct Stripe Checkout Integration
|
|
21
|
+
|
|
22
|
+
For web-based subscriptions and payments using Stripe Checkout directly.
|
|
23
|
+
|
|
24
|
+
### Frontend Code
|
|
25
|
+
|
|
26
|
+
Retrieve the affiliate identifier and company ID before creating a checkout session:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
30
|
+
|
|
31
|
+
const affiliateId = await InsertAffiliate.returnInsertAffiliateIdentifier();
|
|
32
|
+
const companyId = await InsertAffiliate.returnCompanyId();
|
|
33
|
+
|
|
34
|
+
const response = await fetch('https://your-backend.com/create-checkout-session', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
priceId: 'price_xxxxx',
|
|
41
|
+
insertAffiliate: affiliateId,
|
|
42
|
+
insertAffiliateCompanyId: companyId,
|
|
43
|
+
successUrl: window.location.origin + '/success',
|
|
44
|
+
cancelUrl: window.location.origin + '/canceled',
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const { sessionId } = await response.json();
|
|
49
|
+
// Redirect to Stripe Checkout
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Backend Code (Node.js)
|
|
53
|
+
|
|
54
|
+
Store both the affiliate identifier and company ID in Stripe metadata:
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const stripe = require('stripe')('sk_test_xxxxx');
|
|
58
|
+
|
|
59
|
+
app.post('/create-checkout-session', async (req, res) => {
|
|
60
|
+
const { priceId, insertAffiliate, insertAffiliateCompanyId, successUrl, cancelUrl } = req.body;
|
|
61
|
+
|
|
62
|
+
const session = await stripe.checkout.sessions.create({
|
|
63
|
+
mode: 'subscription',
|
|
64
|
+
line_items: [{
|
|
65
|
+
price: priceId,
|
|
66
|
+
quantity: 1,
|
|
67
|
+
}],
|
|
68
|
+
metadata: {
|
|
69
|
+
insertAffiliate: insertAffiliate || '',
|
|
70
|
+
insertAffiliateCompanyId: insertAffiliateCompanyId || '',
|
|
71
|
+
},
|
|
72
|
+
subscription_data: {
|
|
73
|
+
metadata: {
|
|
74
|
+
insertAffiliate: insertAffiliate || '',
|
|
75
|
+
insertAffiliateCompanyId: insertAffiliateCompanyId || '',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
success_url: successUrl,
|
|
79
|
+
cancel_url: cancelUrl,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
res.json({ sessionId: session.id });
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Required Metadata Fields:**
|
|
87
|
+
- `insertAffiliate`: The affiliate's short code
|
|
88
|
+
- `insertAffiliateCompanyId`: Your Insert Affiliate company ID
|
|
89
|
+
|
|
90
|
+
Both fields are required for proper affiliate attribution and commission tracking.
|
|
91
|
+
|
|
92
|
+
## 3. Stripe Billing with RevenueCat
|
|
93
|
+
|
|
94
|
+
If you're using RevenueCat's [Stripe Billing](https://www.revenuecat.com/docs/web/integrations/stripe), you can track affiliate conversions through Insert Affiliate while using RevenueCat for subscription management.
|
|
95
|
+
|
|
96
|
+
**Prerequisites:**
|
|
97
|
+
- You must host your own web checkout page where Stripe Checkout is embedded
|
|
98
|
+
- Follow RevenueCat's [Stripe integration guide](https://www.revenuecat.com/docs/web/integrations/stripe)
|
|
99
|
+
|
|
100
|
+
**Integration:**
|
|
101
|
+
|
|
102
|
+
Follow the same steps as [Direct Stripe Checkout Integration](#2-direct-stripe-checkout-integration) above. Since you've connected your Stripe account via Stripe Connect, Insert Affiliate will automatically receive all Stripe events. RevenueCat handles subscription management, while Insert Affiliate handles affiliate attribution through the Stripe metadata.
|
|
103
|
+
|
|
104
|
+
## 4. RevenueCat Web Billing Integration
|
|
105
|
+
|
|
106
|
+
If you're using [RevenueCat's Web SDK with Web Billing](https://www.revenuecat.com/docs/web/web-billing/overview), pass UTM parameters as purchase metadata.
|
|
107
|
+
|
|
108
|
+
**Prerequisites:**
|
|
109
|
+
- RevenueCat Web SDK installed and configured
|
|
110
|
+
- RevenueCat Web Billing set up with Stripe
|
|
111
|
+
- Insert Affiliate SDK initialized on your page
|
|
112
|
+
- Stripe account connected via Stripe Connect
|
|
113
|
+
|
|
114
|
+
### Implementation
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
118
|
+
import { Purchases } from '@revenuecat/purchases-js';
|
|
119
|
+
|
|
120
|
+
// Initialize both SDKs
|
|
121
|
+
await InsertAffiliate.initialize('your_company_code');
|
|
122
|
+
const purchases = Purchases.configure('your_revenuecat_web_api_key');
|
|
123
|
+
|
|
124
|
+
// Get affiliate information
|
|
125
|
+
const affiliateId = await InsertAffiliate.returnInsertAffiliateIdentifier(true);
|
|
126
|
+
const companyId = await InsertAffiliate.returnCompanyId();
|
|
127
|
+
|
|
128
|
+
// Prepare metadata with UTM parameters
|
|
129
|
+
const metadata: Record<string, string> = {};
|
|
130
|
+
|
|
131
|
+
if (affiliateId && affiliateId !== 'none') {
|
|
132
|
+
metadata.utm_source = 'insertAffiliate';
|
|
133
|
+
metadata.utm_medium = companyId || 'none';
|
|
134
|
+
metadata.utm_campaign = affiliateId;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Get offerings and make purchase
|
|
138
|
+
const offerings = await purchases.getOfferings();
|
|
139
|
+
const selectedPackage = offerings.current?.availablePackages[0];
|
|
140
|
+
|
|
141
|
+
if (selectedPackage) {
|
|
142
|
+
const { customerInfo } = await purchases.purchase({
|
|
143
|
+
rcPackage: selectedPackage,
|
|
144
|
+
metadata: metadata,
|
|
145
|
+
});
|
|
146
|
+
console.log('Purchase successful!');
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**UTM Parameter Mapping:**
|
|
151
|
+
| Parameter | Value | Purpose |
|
|
152
|
+
|-----------|-------|---------|
|
|
153
|
+
| `utm_source` | `'insertAffiliate'` | Identifies Insert Affiliate conversions |
|
|
154
|
+
| `utm_medium` | Your company ID | Links to your Insert Affiliate account |
|
|
155
|
+
| `utm_campaign` | Affiliate identifier | Credits the specific affiliate |
|
|
156
|
+
|
|
157
|
+
## 5. RevenueCat Web Purchase Links
|
|
158
|
+
|
|
159
|
+
If you're using [RevenueCat Web Purchase Links](https://www.revenuecat.com/docs/web/web-billing/web-purchase-links) for online campaigns, append UTM parameters to the links.
|
|
160
|
+
|
|
161
|
+
### URL Format
|
|
162
|
+
|
|
163
|
+
Base URL:
|
|
164
|
+
```
|
|
165
|
+
https://pay.rev.cat/sandbox/viqxbcoudyfaeaae/
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
With affiliate parameters:
|
|
169
|
+
```
|
|
170
|
+
https://pay.rev.cat/sandbox/viqxbcoudyfaeaae/?utm_source=insertAffiliate&utm_medium={companyId}&utm_campaign={affiliateShortCode}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Full Example
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
https://pay.rev.cat/sandbox/viqxbcoudyfaeaxa/?utm_source=insertAffiliate&utm_medium=12345&utm_campaign=AFF123
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Where:
|
|
180
|
+
- `utm_source=insertAffiliate` - Identifies this as an Insert Affiliate conversion
|
|
181
|
+
- `utm_medium=12345` - Your Insert Affiliate company ID
|
|
182
|
+
- `utm_campaign=AFF123` - The affiliate's short code
|
|
183
|
+
|
|
184
|
+
The Insert Affiliate SDK automatically processes the UTM parameters when users open these URLs and attributes the resulting purchase to the correct affiliate.
|
|
185
|
+
|
|
186
|
+
## Using the Callback for Automatic Integration
|
|
187
|
+
|
|
188
|
+
The SDK provides a callback that fires whenever the affiliate identifier changes, making it easy to automatically update your checkout flow:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
192
|
+
|
|
193
|
+
let currentAffiliateId = null;
|
|
194
|
+
|
|
195
|
+
InsertAffiliate.setInsertAffiliateIdentifierChangeCallback((identifier) => {
|
|
196
|
+
if (identifier) {
|
|
197
|
+
console.log('Affiliate identifier changed:', identifier);
|
|
198
|
+
currentAffiliateId = identifier;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Later, when creating a Stripe checkout session
|
|
203
|
+
const companyId = await InsertAffiliate.returnCompanyId();
|
|
204
|
+
|
|
205
|
+
const response = await fetch('/create-checkout-session', {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: { 'Content-Type': 'application/json' },
|
|
208
|
+
body: JSON.stringify({
|
|
209
|
+
priceId: 'price_xxxxx',
|
|
210
|
+
insertAffiliate: currentAffiliateId,
|
|
211
|
+
insertAffiliateCompanyId: companyId,
|
|
212
|
+
successUrl: window.location.origin + '/success',
|
|
213
|
+
cancelUrl: window.location.origin + '/canceled',
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Troubleshooting
|
|
219
|
+
|
|
220
|
+
**Problem:** Affiliate not credited for purchase
|
|
221
|
+
- **Solution:** Verify both `insertAffiliate` and `insertAffiliateCompanyId` are included in Stripe metadata
|
|
222
|
+
- Check that your Stripe account is connected via Stripe Connect in the Insert Affiliate dashboard
|
|
223
|
+
|
|
224
|
+
**Problem:** Metadata not appearing in Stripe
|
|
225
|
+
- **Solution:** Ensure metadata is passed to both `metadata` and `subscription_data.metadata` in the checkout session
|
|
226
|
+
|
|
227
|
+
**Problem:** UTM parameters not being captured
|
|
228
|
+
- **Solution:** Initialize the Insert Affiliate SDK before users interact with purchase flows
|
|
229
|
+
- Verify the SDK is processing URL parameters on page load
|
|
230
|
+
|
|
231
|
+
## Next Steps
|
|
232
|
+
|
|
233
|
+
- Test with a test affiliate link to verify attribution
|
|
234
|
+
- Make a test purchase to confirm tracking works end-to-end
|
|
235
|
+
- Monitor conversions in your Insert Affiliate dashboard
|
|
236
|
+
|
|
237
|
+
[Back to Main README](../README.md)
|
package/package.json
CHANGED
|
@@ -326,27 +326,111 @@ export class InsertAffiliate {
|
|
|
326
326
|
return companyCode;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
/**
|
|
330
|
+
* Get the offer code for the current affiliate
|
|
331
|
+
* @param platformType Optional platform type: 'stripe' (default for web), 'ios', or 'android'
|
|
332
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
333
|
+
*/
|
|
334
|
+
static async getOfferCode(platformType: 'ios' | 'android' | 'stripe' = 'stripe'): Promise<string | null> {
|
|
335
|
+
this.verboseLog(`Getting offer code for platform: ${platformType}...`);
|
|
331
336
|
|
|
332
|
-
|
|
333
|
-
|
|
337
|
+
const storageKey = platformType === 'stripe' ? 'offerCode' : `offerCode_${platformType}`;
|
|
338
|
+
|
|
339
|
+
// Return cached offer code if available (only for default Stripe)
|
|
340
|
+
if (platformType === 'stripe' && this.offerCode) {
|
|
334
341
|
this.verboseLog(`Returning cached offer code: ${this.offerCode}`);
|
|
335
342
|
return this.offerCode;
|
|
336
343
|
}
|
|
337
344
|
|
|
338
345
|
// Try to get from storage
|
|
339
|
-
const storedOfferCode = await getValue(
|
|
346
|
+
const storedOfferCode = await getValue(storageKey);
|
|
340
347
|
if (storedOfferCode) {
|
|
341
348
|
this.verboseLog(`Returning stored offer code: ${storedOfferCode}`);
|
|
342
|
-
|
|
349
|
+
if (platformType === 'stripe') {
|
|
350
|
+
this.offerCode = storedOfferCode;
|
|
351
|
+
}
|
|
343
352
|
return storedOfferCode;
|
|
344
353
|
}
|
|
345
354
|
|
|
355
|
+
// If not in storage, try to fetch it
|
|
356
|
+
const shortCode = await getValue('referrerLink');
|
|
357
|
+
if (shortCode) {
|
|
358
|
+
this.verboseLog(`No stored offer code, fetching for short code: ${shortCode}`);
|
|
359
|
+
const fetchedCode = await this.fetchOfferCodeForPlatform(shortCode, platformType);
|
|
360
|
+
return fetchedCode;
|
|
361
|
+
}
|
|
362
|
+
|
|
346
363
|
this.verboseLog('No offer code found');
|
|
347
364
|
return null;
|
|
348
365
|
}
|
|
349
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Get the Stripe coupon/promo code for the current affiliate
|
|
369
|
+
* Convenience method that calls getOfferCode with platformType='stripe'
|
|
370
|
+
* @returns The Stripe coupon/promo code, or null if not found
|
|
371
|
+
*/
|
|
372
|
+
static async getStripeCouponCode(): Promise<string | null> {
|
|
373
|
+
return this.getOfferCode('stripe');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Fetch offer code for a specific platform
|
|
378
|
+
* @param shortCode The affiliate short code
|
|
379
|
+
* @param platformType The platform type: 'ios', 'android', or 'stripe'
|
|
380
|
+
* @returns The offer code for the specified platform, or null if not found
|
|
381
|
+
*/
|
|
382
|
+
private static async fetchOfferCodeForPlatform(shortCode: string, platformType: 'ios' | 'android' | 'stripe'): Promise<string | null> {
|
|
383
|
+
this.verboseLog(`Fetching offer code for platform: ${platformType}, short code: ${shortCode}`);
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const companyCode = this.companyCode || await getValue('companyCode');
|
|
387
|
+
if (!companyCode) {
|
|
388
|
+
this.verboseLog('Cannot fetch offer code: no company code available');
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const encoded = encodeURIComponent(shortCode);
|
|
393
|
+
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${companyCode}/${encoded}?platformType=${platformType}`;
|
|
394
|
+
this.verboseLog(`Making API call to: ${url}`);
|
|
395
|
+
|
|
396
|
+
const response = await fetch(url);
|
|
397
|
+
this.verboseLog(`API response status: ${response.status}`);
|
|
398
|
+
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
this.verboseLog(`Failed to fetch offer code, status: ${response.status}`);
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const offerCode = (await response.text()).replace(/[^a-zA-Z0-9]/g, '');
|
|
405
|
+
this.verboseLog(`Received offer code: ${offerCode}`);
|
|
406
|
+
|
|
407
|
+
const errorCodes = [
|
|
408
|
+
'errorofferCodeNotFound',
|
|
409
|
+
'errorAffiliateoffercodenotfoundinanycompany',
|
|
410
|
+
'errorAffiliateoffercodenotfoundinanycompanyAffiliatelinkwas',
|
|
411
|
+
'Routenotfound'
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
if (errorCodes.includes(offerCode)) {
|
|
415
|
+
this.verboseLog('Offer code not found or invalid');
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Store offer code with platform-specific key
|
|
420
|
+
const storageKey = platformType === 'stripe' ? 'offerCode' : `offerCode_${platformType}`;
|
|
421
|
+
await saveValue(storageKey, offerCode);
|
|
422
|
+
if (platformType === 'stripe') {
|
|
423
|
+
this.offerCode = offerCode;
|
|
424
|
+
}
|
|
425
|
+
this.verboseLog(`Offer code stored successfully with key ${storageKey}: ${offerCode}`);
|
|
426
|
+
|
|
427
|
+
return offerCode;
|
|
428
|
+
} catch (error) {
|
|
429
|
+
this.verboseLog(`Error fetching offer code: ${error}`);
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
350
434
|
private static async fetchAndStoreOfferCode(shortCode: string): Promise<void> {
|
|
351
435
|
this.verboseLog(`Fetching offer code for short code: ${shortCode}`);
|
|
352
436
|
|