insert-affiliate-js-sdk 1.3.0 → 1.5.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/.github/workflows/publish.yml +28 -0
- package/CHANGELOG.md +26 -0
- package/README.md +175 -0
- package/dist/index.d.ts +287 -4
- package/dist/index.js +1044 -41
- package/package.json +7 -3
- package/src/index.ts +16 -1
- package/src/referrals/referAFriendModal.ts +534 -0
- package/src/referrals/referralApi.ts +385 -0
- package/src/referrals/referralStrings.ts +85 -0
- package/src/referrals/referralTypes.ts +211 -0
- package/src/referrals/referrerTokenStore.ts +42 -0
- package/src/sdk/InsertAffiliate.ts +350 -46
- package/.claude/settings.local.json +0 -10
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Publishes to npm via OIDC trusted publishing (no stored token).
|
|
2
|
+
# Trigger: push a version tag like v1.18.0 matching package.json version.
|
|
3
|
+
# One-time setup on npmjs.com (after any account suspension lifts): package ->
|
|
4
|
+
# Settings -> Trusted Publisher -> GitHub Actions -> this repo + workflow
|
|
5
|
+
# ".github/workflows/publish.yml". Requires npm CLI >= 11.5 (installed below).
|
|
6
|
+
name: Publish to npm
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write # required for npm OIDC trusted publishing
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '22'
|
|
24
|
+
registry-url: 'https://registry.npmjs.org'
|
|
25
|
+
- run: npm install -g npm@latest # npm >= 11.5 for trusted publishing
|
|
26
|
+
- run: npm ci
|
|
27
|
+
- run: npm run build --if-present
|
|
28
|
+
- run: npm publish
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **In-app referrals** - Turn app users into affiliates and show them a "Refer a friend" screen
|
|
12
|
+
- `createAffiliateForUser()`, `verifyAffiliateCode()`, `getMyAffiliateDetails()`, `isUserAnAffiliate()`, `signOutAffiliate()`, `getReferralProgramConfig()`, `shareReferralLink()`
|
|
13
|
+
- `showReferAFriend()` drop-in web modal (no dependencies, accessible, themeable)
|
|
14
|
+
- Exported types: `AffiliateEnrolmentResult`, `MyAffiliateDetails`, `ReferralProgramConfig`, `ReferAFriendOptions`, `ReferAFriendHandle` and related
|
|
15
|
+
- **Referrer rewards** - Let the server grant rewards to referrers automatically
|
|
16
|
+
- `createAffiliateForUser()` and `verifyAffiliateCode()` take optional `{ appUserId, playPurchaseToken }` and send this browser's device id
|
|
17
|
+
- `setReferrerAccount()` saves those accounts after joining, so waiting rewards are granted
|
|
18
|
+
- `MyAffiliateDetails` adds `rewardsGranted`, `premiumUntil` and `rewardCodes`
|
|
19
|
+
- `showReferAFriend()` takes optional `appUserId` and `playPurchaseToken` and passes them on, so modal-only apps need no extra call
|
|
20
|
+
- The modal shows "Free premium until {date}" and a "Your rewards" list with a Redeem button per App Store offer code or Google Play promo code; each `ReferralRewardCode` has a `store`
|
|
21
|
+
- Exported types: `ReferrerAccountOptions`, `ReferralRewardCode`
|
|
22
|
+
- **Modal text overrides** - `showReferAFriend({ strings })` replaces any label in the modal, so a site can translate or reword it
|
|
23
|
+
- Every key is optional: a missing, blank or unknown key keeps the English default, so nothing changes for apps that do not pass `strings`
|
|
24
|
+
- `codeSentNotice` keeps `{email}` and `premiumUntil` keeps `{date}`
|
|
25
|
+
- Exported type: `ReferralStrings`
|
|
26
|
+
|
|
27
|
+
## [1.3.1] - 2026-03-29
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- **Offer code sanitization** - Fixed offer codes with dashes/underscores being stripped (e.g. `pro-v3-ext` was incorrectly becoming `prov3ext`)
|
|
31
|
+
- Now checks for API error responses before cleaning the offer code
|
|
32
|
+
- Sanitization regex updated to preserve dashes and underscores
|
|
33
|
+
|
|
8
34
|
## [1.1.0] - 2025-11-23
|
|
9
35
|
|
|
10
36
|
### Added
|
package/README.md
CHANGED
|
@@ -462,6 +462,167 @@ InsertAffiliate.setInsertAffiliateIdentifierChangeCallback(null);
|
|
|
462
462
|
|
|
463
463
|
</details>
|
|
464
464
|
|
|
465
|
+
<details>
|
|
466
|
+
<summary><h3>In-App Referrals (Refer a Friend)</h3></summary>
|
|
467
|
+
|
|
468
|
+
Turn your own users into affiliates from inside your app, show them a ready-made "Refer a friend" screen, and read their referral stats so you can reward them. Referrers are normal affiliates: they get the same dashboard, referrals tab and commission as any other affiliate.
|
|
469
|
+
|
|
470
|
+
Switch the program on in your Insert Affiliate dashboard first.
|
|
471
|
+
|
|
472
|
+
**Drop-in modal (quickest):**
|
|
473
|
+
|
|
474
|
+
```javascript
|
|
475
|
+
import { InsertAffiliate } from 'insert-affiliate-js-sdk';
|
|
476
|
+
|
|
477
|
+
document.getElementById('refer-button').addEventListener('click', () => {
|
|
478
|
+
const modal = InsertAffiliate.showReferAFriend({
|
|
479
|
+
email: currentUser.email, // prefills the form (usually your logged-in user)
|
|
480
|
+
name: currentUser.name,
|
|
481
|
+
onClose: () => console.log('Refer a friend closed'),
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// modal.close() dismisses it from code
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
The modal handles everything: the "Get my link" form, the 6-digit email code step for existing affiliates, the code and link with Copy buttons, a Share button, stats (referrals and amount earned), a "Free premium until {date}" line while a premium reward is active, a "Your rewards" list of App Store offer codes or Google Play promo codes with a Redeem button each (opens the store's redemption page in a new tab) and an "Open my dashboard" button. It injects its own scoped styles, needs no framework, closes on Escape or a backdrop click, keeps keyboard focus inside while open, and returns focus afterwards. Only one modal is shown at a time: calling `showReferAFriend` again while it is open focuses the open one.
|
|
489
|
+
|
|
490
|
+
| Option | Description |
|
|
491
|
+
|--------|-------------|
|
|
492
|
+
| `email`, `name` | Prefill the form |
|
|
493
|
+
| `shareMessage` | Share message. May use `{link}` and `{code}` placeholders |
|
|
494
|
+
| `primaryColor` | Overrides the dashboard colour (any CSS colour). Default `#6A0DAD` |
|
|
495
|
+
| `headline`, `rewardText` | Override the dashboard copy. Default headline "Refer a friend" |
|
|
496
|
+
| `fontFamily`, `cornerRadius` | Match your app's look |
|
|
497
|
+
| `appUserId`, `playPurchaseToken` | The user's own accounts, for automatic referrer rewards. Sent when the user joins, or saved once when the modal opens for a user who already joined |
|
|
498
|
+
| `strings` | Replaces any of the modal's labels, for translating or rewording it |
|
|
499
|
+
| `onClose` | Called once when the modal closes |
|
|
500
|
+
|
|
501
|
+
Headline, reward text and colour set in the dashboard are used when you do not pass them, so wording changes need no release.
|
|
502
|
+
|
|
503
|
+
**Translating the modal:** pass `strings` with the keys you want to change. Every key is optional: a missing or blank one keeps the English default, and an unknown one is ignored.
|
|
504
|
+
|
|
505
|
+
```javascript
|
|
506
|
+
InsertAffiliate.showReferAFriend({
|
|
507
|
+
headline: 'Parrainez un ami',
|
|
508
|
+
strings: {
|
|
509
|
+
emailLabel: 'E-mail',
|
|
510
|
+
nameLabel: 'Nom',
|
|
511
|
+
joinButton: 'Obtenir mon lien',
|
|
512
|
+
codeSentNotice: 'Nous avons envoye un code a 6 chiffres a {email}.',
|
|
513
|
+
verifyButton: 'Valider',
|
|
514
|
+
copyCodeButton: 'Copier le code',
|
|
515
|
+
shareButton: 'Partager',
|
|
516
|
+
referralsLabel: 'Parrainages',
|
|
517
|
+
earnedLabel: 'Gagne',
|
|
518
|
+
premiumUntil: 'Premium gratuit jusqu\'au {date}',
|
|
519
|
+
errorNetwork: 'Connexion impossible. Verifiez votre connexion.',
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Keep the `{email}` placeholder in `codeSentNotice` and `{date}` in `premiumUntil`; they are filled in for you.
|
|
525
|
+
|
|
526
|
+
| Group | Keys |
|
|
527
|
+
|-------|------|
|
|
528
|
+
| Joining | `emailLabel`, `nameLabel`, `joinButton`, `joiningButton` |
|
|
529
|
+
| Email code step | `codeLabel`, `codeSentNotice` (`{email}`), `verifyButton`, `verifyingButton`, `resendButton`, `sendingNotice`, `codeResentNotice`, `differentEmailButton`, `errorCodeLength` |
|
|
530
|
+
| Joined | `copyCodeButton`, `copyLinkButton`, `copiedNotice`, `copyFailedNotice`, `shareButton`, `shareFailedNotice`, `referralsLabel`, `earnedLabel`, `premiumUntil` (`{date}`), `rewardsHeading`, `redeemButton`, `dashboardLink` |
|
|
531
|
+
| Frame and states | `closeButton`, `loading`, `tryAgainButton` |
|
|
532
|
+
| Errors | `errorProgramDisabled`, `errorAffiliateLimitReached`, `errorInvalidCode`, `errorTooManyCodes`, `errorRateLimited`, `errorInvalidEmail`, `errorNetwork`, `errorServer` |
|
|
533
|
+
|
|
534
|
+
`headline` and `rewardText` are not in `strings`: they come from the dashboard and are overridden with their own options. The full type is exported as `ReferralStrings`.
|
|
535
|
+
|
|
536
|
+
**Build your own screen:**
|
|
537
|
+
|
|
538
|
+
You can skip the modal and use the methods directly. In the order an app calls them:
|
|
539
|
+
|
|
540
|
+
1. `getReferralProgramConfig()` to check the program is on (`enabled`) and to read the dashboard copy, colour and `companyName`.
|
|
541
|
+
2. `isUserAnAffiliate()` to see whether this browser is already connected (no network call).
|
|
542
|
+
3. `createAffiliateForUser(email, name, options?)` to join. `status` is `created` (connected, `affiliate` holds the code and link), `verificationRequired` (a 6-digit code was emailed) or `error`.
|
|
543
|
+
4. `verifyAffiliateCode(email, code, name?, options?)` for the code step. Call `createAffiliateForUser` again to send a new code.
|
|
544
|
+
5. `getMyAffiliateDetails()` for the code, link, `referralCount`, `totalEarned`, `currency`, `rewardsGranted`, `premiumUntil`, `rewardCodes` (each with `code`, `store` and `redeemUrl`) and `dashboardUrl`.
|
|
545
|
+
6. `setReferrerAccount({ appUserId, playPurchaseToken })` when the user subscribes or logs in after joining.
|
|
546
|
+
7. `shareReferralLink(message?)` from a click handler, or build your own text from `details.affiliateShortCode` and `details.deeplinkurl`.
|
|
547
|
+
8. `signOutAffiliate()` on logout.
|
|
548
|
+
|
|
549
|
+
States to handle:
|
|
550
|
+
|
|
551
|
+
| State | How you know | What to show |
|
|
552
|
+
|-------|--------------|--------------|
|
|
553
|
+
| Program off | `config.enabled === false` | Hide the entry point |
|
|
554
|
+
| Not enrolled | `isUserAnAffiliate()` is false | Your email and name form |
|
|
555
|
+
| Code needed | `status === 'verificationRequired'` | A 6-digit code field, with resend and "use a different email" |
|
|
556
|
+
| Enrolled | `getMyAffiliateDetails()` returns details | The code and link with copy and share, plus the stats |
|
|
557
|
+
| Rewards | `premiumUntil` in the future, or `rewardCodes` is not empty | The premium line, and each code with a link to `redeemUrl` |
|
|
558
|
+
| Errors | `result.status === 'error'`, then `result.errorCode` | Your own wording per code |
|
|
559
|
+
| Disconnected | `getMyAffiliateDetails()` returns `null` while `isUserAnAffiliate()` is false | Back to the join form |
|
|
560
|
+
|
|
561
|
+
`getMyAffiliateDetails()` returns `null` both when the browser is not connected and when the request failed. Check `isUserAnAffiliate()` afterwards to tell them apart: still `true` means the request failed and is worth retrying.
|
|
562
|
+
|
|
563
|
+
```javascript
|
|
564
|
+
// 1. Make the user a referrer
|
|
565
|
+
const result = await InsertAffiliate.createAffiliateForUser('jane@example.com', 'Jane');
|
|
566
|
+
|
|
567
|
+
if (result.status === 'verificationRequired') {
|
|
568
|
+
// The email is already an affiliate: we emailed a 6-digit code
|
|
569
|
+
const code = prompt('Enter the code we emailed you');
|
|
570
|
+
const verified = await InsertAffiliate.verifyAffiliateCode('jane@example.com', code);
|
|
571
|
+
if (verified.status === 'error') alert(verified.errorMessage);
|
|
572
|
+
} else if (result.status === 'error') {
|
|
573
|
+
console.log(result.errorCode, result.errorMessage); // e.g. PROGRAM_DISABLED
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// 2. Read their stats, and load the program config (the app name used in the share text)
|
|
577
|
+
const [details] = await Promise.all([
|
|
578
|
+
InsertAffiliate.getMyAffiliateDetails(),
|
|
579
|
+
InsertAffiliate.getReferralProgramConfig(),
|
|
580
|
+
]);
|
|
581
|
+
if (details) {
|
|
582
|
+
console.log(details.affiliateShortCode, details.deeplinkurl);
|
|
583
|
+
console.log(`${details.referralCount} referrals, ${details.totalEarned} ${details.currency} earned`);
|
|
584
|
+
console.log(`${details.rewardsGranted} rewards, premium until ${details.premiumUntil}`);
|
|
585
|
+
details.rewardCodes.forEach((reward) => console.log(reward.code, reward.redeemUrl));
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// 3. Share (call from a click handler, after step 2 has loaded the details)
|
|
589
|
+
await InsertAffiliate.shareReferralLink(); // 'shared' | 'copied' | 'cancelled' | 'failed'
|
|
590
|
+
|
|
591
|
+
// On logout
|
|
592
|
+
await InsertAffiliate.signOutAffiliate();
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
- `createAffiliateForUser` creates a new affiliate and connects this browser straight away. If the email is already an affiliate it never connects on the email alone: it emails a 6-digit code and returns `verificationRequired`.
|
|
596
|
+
- Connecting stores a private token in `localStorage`, one per company. `isUserAnAffiliate()` checks for it without a network call. `signOutAffiliate()` removes it; the affiliate account is kept.
|
|
597
|
+
- If the token stops working (for example the affiliate was removed), `getMyAffiliateDetails()` clears it and returns `null`.
|
|
598
|
+
- `referralCount` is the count for the trigger you chose in the dashboard (install, event or purchase). It only ever goes up.
|
|
599
|
+
- Sharing uses the browser share sheet (`navigator.share`), or copies the text to the clipboard where that is not available. Browsers only allow either shortly after the tap, so `shareReferralLink()` never waits on the network: it uses the details and config already loaded by `getMyAffiliateDetails()` and `getReferralProgramConfig()`, and returns `'failed'` if the details are not loaded yet. Default text: `Try {companyName}: {link}`, or `Use my code {code} in {companyName}` when you use Short Code Only.
|
|
600
|
+
- Error codes: `INVALID_EMAIL`, `INVALID_CODE`, `PROGRAM_DISABLED`, `AFFILIATE_LIMIT_REACHED`, `TOO_MANY_CODES`, `RATE_LIMITED`, `COMPANY_NOT_FOUND`, `NETWORK_ERROR`, `NOT_INITIALIZED`.
|
|
601
|
+
|
|
602
|
+
**Automatic referrer rewards:** when you set up referrer rewards in the dashboard (RevenueCat, Adapty, App Store offer codes or Google Play), pass the user's own accounts so the reward can be granted to them:
|
|
603
|
+
|
|
604
|
+
```javascript
|
|
605
|
+
// When they join
|
|
606
|
+
await InsertAffiliate.createAffiliateForUser('jane@example.com', 'Jane', {
|
|
607
|
+
appUserId: 'RevenueCat or Adapty app user id',
|
|
608
|
+
playPurchaseToken: 'their own Google Play purchase token', // Android apps only
|
|
609
|
+
});
|
|
610
|
+
// verifyAffiliateCode(email, code, name, options) takes the same options
|
|
611
|
+
|
|
612
|
+
// Or later, if they subscribe or log in after joining
|
|
613
|
+
const saved = await InsertAffiliate.setReferrerAccount({ appUserId: 'rc_user_123' });
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
- `setReferrerAccount` needs a connected referrer on this browser and returns `false` otherwise. Any rewards that were waiting for these accounts are granted once they are saved.
|
|
617
|
+
- The SDK also sends this browser's device id (the same one in `returnInsertAffiliateIdentifier()`), so a referrer who uses their own link is not counted as their own referral.
|
|
618
|
+
- `rewardCodes` are App Store offer codes or Google Play promo codes, newest first, each with a `redeemUrl` and a `store` (`app_store` or `google_play`). `premiumUntil` is an ISO date or `null`.
|
|
619
|
+
|
|
620
|
+
**Rewarding referrers yourself:** values read on the device are for display. A modified browser can show anything, so grant anything valuable (credits, premium time) from your server using the `referral.created` webhook or the Public API. The webhook includes a running `referral_count`, so rewarding up to that number is safe to repeat.
|
|
621
|
+
|
|
622
|
+
**Store rules:** the SDK only uses the share sheet and never asks for contacts. Never lock features behind sharing, and never reward ratings or reviews.
|
|
623
|
+
|
|
624
|
+
</details>
|
|
625
|
+
|
|
465
626
|
### Prevent Affiliate Transfer
|
|
466
627
|
|
|
467
628
|
By default, clicking a new affiliate link will overwrite any existing attribution. Enable `preventAffiliateTransfer` to lock the first affiliate:
|
|
@@ -504,6 +665,20 @@ Learn more: [Prevent Affiliate Transfer Documentation](https://docs.insertaffili
|
|
|
504
665
|
| `isAffiliateAttributionValid()` | Check if attribution is still valid | `Promise<boolean>` |
|
|
505
666
|
| `setInsertAffiliateIdentifierChangeCallback(fn)` | Set change callback | `void` |
|
|
506
667
|
|
|
668
|
+
### In-App Referral Methods
|
|
669
|
+
|
|
670
|
+
| Method | Description | Returns |
|
|
671
|
+
|--------|-------------|---------|
|
|
672
|
+
| `createAffiliateForUser(email, name, options?)` | Make the app user a referrer, or email a code if they already are one | `Promise<AffiliateEnrolmentResult>` |
|
|
673
|
+
| `verifyAffiliateCode(email, code, name?, options?)` | Finish connecting with the emailed 6-digit code | `Promise<AffiliateEnrolmentResult>` |
|
|
674
|
+
| `setReferrerAccount(options)` | Save the referrer's app user id or Play purchase token after joining | `Promise<boolean>` |
|
|
675
|
+
| `getMyAffiliateDetails()` | Connected referrer's details and stats | `Promise<MyAffiliateDetails \| null>` |
|
|
676
|
+
| `isUserAnAffiliate()` | Whether a referrer is connected on this device (no network) | `Promise<boolean>` |
|
|
677
|
+
| `signOutAffiliate()` | Disconnect the referrer from this device | `Promise<void>` |
|
|
678
|
+
| `getReferralProgramConfig()` | Program on/off plus dashboard copy and colour | `Promise<ReferralProgramConfig \| null>` |
|
|
679
|
+
| `shareReferralLink(message?)` | Share sheet, or copy to clipboard | `Promise<ReferralShareOutcome>` |
|
|
680
|
+
| `showReferAFriend(options?)` | Show the drop-in modal | `ReferAFriendHandle` |
|
|
681
|
+
|
|
507
682
|
<details>
|
|
508
683
|
<summary><strong>Detailed Method Documentation</strong></summary>
|
|
509
684
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,188 @@
|
|
|
1
|
+
/** What the company counts as a referral (set in the Insert Affiliate portal). */
|
|
2
|
+
type ReferralTrigger = 'install' | 'event' | 'purchase';
|
|
3
|
+
/** The referrer's own affiliate record, as returned by enrol and verify. */
|
|
4
|
+
interface ReferrerAffiliate {
|
|
5
|
+
affiliateName: string;
|
|
6
|
+
affiliateShortCode: string;
|
|
7
|
+
/** The link to share. For Short Code Only companies this is the short code itself; empty when no link is assigned yet. */
|
|
8
|
+
deeplinkurl: string;
|
|
9
|
+
}
|
|
10
|
+
/** The referrer's affiliate record plus referral stats. Values are for display only. */
|
|
11
|
+
interface MyAffiliateDetails extends ReferrerAffiliate {
|
|
12
|
+
referralTrigger: ReferralTrigger;
|
|
13
|
+
/** The count for the company's configured trigger. Only ever goes up. */
|
|
14
|
+
referralCount: number;
|
|
15
|
+
installCount: number;
|
|
16
|
+
eventCount: number;
|
|
17
|
+
purchaseCount: number;
|
|
18
|
+
totalEarned: number;
|
|
19
|
+
totalPaid: number;
|
|
20
|
+
totalUnpaid: number;
|
|
21
|
+
currency: string;
|
|
22
|
+
dashboardUrl: string;
|
|
23
|
+
/** How many referral rewards this user has been granted. */
|
|
24
|
+
rewardsGranted: number;
|
|
25
|
+
/** ISO date the user's free premium from referrals runs until, or null. */
|
|
26
|
+
premiumUntil: string | null;
|
|
27
|
+
/** App Store offer codes or Google Play promo codes granted as rewards, newest first. */
|
|
28
|
+
rewardCodes: ReferralRewardCode[];
|
|
29
|
+
}
|
|
30
|
+
/** A referral reward code: an App Store offer code or a Google Play promo code. */
|
|
31
|
+
interface ReferralRewardCode {
|
|
32
|
+
code: string;
|
|
33
|
+
/** Opens the store's redemption page with the code filled in. */
|
|
34
|
+
redeemUrl: string;
|
|
35
|
+
/** Which store the code is for. Older servers don't send it; those are App Store codes. */
|
|
36
|
+
store: 'app_store' | 'google_play' | string;
|
|
37
|
+
/** ISO date the code was granted. */
|
|
38
|
+
grantedAt: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The referrer's own accounts, so rewards can be granted and a referrer
|
|
42
|
+
* cannot count as their own referral. Supply whichever the app uses.
|
|
43
|
+
*/
|
|
44
|
+
interface ReferrerAccountOptions {
|
|
45
|
+
/** The user's RevenueCat app user id or Adapty customer user id. */
|
|
46
|
+
appUserId?: string;
|
|
47
|
+
/** The user's own Google Play subscription purchase token. */
|
|
48
|
+
playPurchaseToken?: string;
|
|
49
|
+
}
|
|
50
|
+
/** Program on/off plus the drop-in UI copy and colour configured in the portal. */
|
|
51
|
+
interface ReferralProgramConfig {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
companyName: string;
|
|
54
|
+
referralTrigger: ReferralTrigger;
|
|
55
|
+
headline: string;
|
|
56
|
+
rewardText: string;
|
|
57
|
+
/** `#RRGGBB`, or empty when the company has not set one. */
|
|
58
|
+
primaryColor: string;
|
|
59
|
+
}
|
|
60
|
+
type AffiliateEnrolmentStatus = 'created' | 'connected' | 'verificationRequired' | 'error';
|
|
61
|
+
/**
|
|
62
|
+
* Server error codes, plus two raised by the SDK itself:
|
|
63
|
+
* `NETWORK_ERROR` (request failed or the response was unreadable) and
|
|
64
|
+
* `NOT_INITIALIZED` (no company code; call `initialize` first).
|
|
65
|
+
*/
|
|
66
|
+
type ReferralErrorCode = 'INVALID_EMAIL' | 'INVALID_COMPANY_ID' | 'INVALID_CODE' | 'PROGRAM_DISABLED' | 'AFFILIATE_LIMIT_REACHED' | 'COMPANY_NOT_FOUND' | 'TOO_MANY_CODES' | 'RATE_LIMITED' | 'NETWORK_ERROR' | 'NOT_INITIALIZED' | (string & {});
|
|
67
|
+
interface AffiliateEnrolmentResult {
|
|
68
|
+
status: AffiliateEnrolmentStatus;
|
|
69
|
+
affiliate?: ReferrerAffiliate;
|
|
70
|
+
errorCode?: ReferralErrorCode;
|
|
71
|
+
errorMessage?: string;
|
|
72
|
+
}
|
|
73
|
+
/** How a share ended: the share sheet completed, the text was copied instead, the user dismissed the sheet, or neither was possible. */
|
|
74
|
+
type ReferralShareOutcome = 'shared' | 'copied' | 'cancelled' | 'failed';
|
|
75
|
+
/**
|
|
76
|
+
* Every label the "Refer a friend" modal shows, so an app can translate or
|
|
77
|
+
* reword it. Pass only the keys you want to change: the rest keep the English
|
|
78
|
+
* defaults, and a blank value keeps the default too. Keep the `{email}` and
|
|
79
|
+
* `{date}` placeholders in the strings that have them.
|
|
80
|
+
*/
|
|
81
|
+
interface ReferralStrings {
|
|
82
|
+
/** "Email" */
|
|
83
|
+
emailLabel: string;
|
|
84
|
+
/** "Name" */
|
|
85
|
+
nameLabel: string;
|
|
86
|
+
/** "Get my link" */
|
|
87
|
+
joinButton: string;
|
|
88
|
+
/** "Please wait..." while joining */
|
|
89
|
+
joiningButton: string;
|
|
90
|
+
/** "Code" */
|
|
91
|
+
codeLabel: string;
|
|
92
|
+
/** "We sent a 6-digit code to {email}. Enter it below to connect this device." */
|
|
93
|
+
codeSentNotice: string;
|
|
94
|
+
/** "Verify" */
|
|
95
|
+
verifyButton: string;
|
|
96
|
+
/** "Verifying..." while the code is checked */
|
|
97
|
+
verifyingButton: string;
|
|
98
|
+
/** "Send a new code" */
|
|
99
|
+
resendButton: string;
|
|
100
|
+
/** "Sending..." while a new code is sent */
|
|
101
|
+
sendingNotice: string;
|
|
102
|
+
/** "We sent a new code." */
|
|
103
|
+
codeResentNotice: string;
|
|
104
|
+
/** "Use a different email" */
|
|
105
|
+
differentEmailButton: string;
|
|
106
|
+
/** "Enter the 6-digit code from the email." */
|
|
107
|
+
errorCodeLength: string;
|
|
108
|
+
/** "Copy code" */
|
|
109
|
+
copyCodeButton: string;
|
|
110
|
+
/** "Copy link" */
|
|
111
|
+
copyLinkButton: string;
|
|
112
|
+
/** "Copied" */
|
|
113
|
+
copiedNotice: string;
|
|
114
|
+
/** "Could not copy. Select the text to copy it." */
|
|
115
|
+
copyFailedNotice: string;
|
|
116
|
+
/** "Share" */
|
|
117
|
+
shareButton: string;
|
|
118
|
+
/** "Could not share. Copy your code instead." */
|
|
119
|
+
shareFailedNotice: string;
|
|
120
|
+
/** "Referrals" */
|
|
121
|
+
referralsLabel: string;
|
|
122
|
+
/** "Earned" */
|
|
123
|
+
earnedLabel: string;
|
|
124
|
+
/** "Free premium until {date}" */
|
|
125
|
+
premiumUntil: string;
|
|
126
|
+
/** "Your rewards" */
|
|
127
|
+
rewardsHeading: string;
|
|
128
|
+
/** "Redeem" */
|
|
129
|
+
redeemButton: string;
|
|
130
|
+
/** "Open my dashboard" */
|
|
131
|
+
dashboardLink: string;
|
|
132
|
+
/** The close button's accessible name, "Close" */
|
|
133
|
+
closeButton: string;
|
|
134
|
+
/** "Loading..." */
|
|
135
|
+
loading: string;
|
|
136
|
+
/** "Try again" */
|
|
137
|
+
tryAgainButton: string;
|
|
138
|
+
/** PROGRAM_DISABLED */
|
|
139
|
+
errorProgramDisabled: string;
|
|
140
|
+
/** AFFILIATE_LIMIT_REACHED */
|
|
141
|
+
errorAffiliateLimitReached: string;
|
|
142
|
+
/** INVALID_CODE */
|
|
143
|
+
errorInvalidCode: string;
|
|
144
|
+
/** TOO_MANY_CODES */
|
|
145
|
+
errorTooManyCodes: string;
|
|
146
|
+
/** RATE_LIMITED */
|
|
147
|
+
errorRateLimited: string;
|
|
148
|
+
/** INVALID_EMAIL */
|
|
149
|
+
errorInvalidEmail: string;
|
|
150
|
+
/** NETWORK_ERROR, and any unreadable response */
|
|
151
|
+
errorNetwork: string;
|
|
152
|
+
/** Every other error, "Something went wrong. Please try again." */
|
|
153
|
+
errorServer: string;
|
|
154
|
+
}
|
|
155
|
+
interface ReferAFriendOptions {
|
|
156
|
+
/** Prefills the email field (usually the app's logged-in user). */
|
|
157
|
+
email?: string;
|
|
158
|
+
/** Prefills the name field. */
|
|
159
|
+
name?: string;
|
|
160
|
+
/** Share message. May use `{link}` and `{code}` placeholders. */
|
|
161
|
+
shareMessage?: string;
|
|
162
|
+
/** Overrides the portal colour. Any CSS colour. */
|
|
163
|
+
primaryColor?: string;
|
|
164
|
+
/** Overrides the portal headline. */
|
|
165
|
+
headline?: string;
|
|
166
|
+
/** Overrides the portal reward text. */
|
|
167
|
+
rewardText?: string;
|
|
168
|
+
/** CSS font-family for the modal. Defaults to the system font stack. */
|
|
169
|
+
fontFamily?: string;
|
|
170
|
+
/** Corner radius of the modal and its controls, in pixels. Defaults to 12. */
|
|
171
|
+
cornerRadius?: number;
|
|
172
|
+
/** The user's RevenueCat app user id or Adapty customer user id, used to grant their referral rewards. */
|
|
173
|
+
appUserId?: string;
|
|
174
|
+
/** The user's own Google Play subscription purchase token (Android). */
|
|
175
|
+
playPurchaseToken?: string;
|
|
176
|
+
/** Replaces any of the modal's labels, for translating or rewording it. */
|
|
177
|
+
strings?: Partial<ReferralStrings>;
|
|
178
|
+
/** Called once after the modal closes, however it was closed. */
|
|
179
|
+
onClose?: () => void;
|
|
180
|
+
}
|
|
181
|
+
interface ReferAFriendHandle {
|
|
182
|
+
/** Closes the modal. Safe to call more than once. */
|
|
183
|
+
close(): void;
|
|
184
|
+
}
|
|
185
|
+
|
|
1
186
|
interface IapticIOSReceipt {
|
|
2
187
|
transactionReceipt: string;
|
|
3
188
|
}
|
|
@@ -6,6 +191,11 @@ interface AffiliateDetails {
|
|
|
6
191
|
affiliateShortCode: string;
|
|
7
192
|
deeplinkUrl: string;
|
|
8
193
|
}
|
|
194
|
+
type AffiliateLookupStatus = 'found' | 'not_found' | 'lookup_failed' | 'not_configured';
|
|
195
|
+
interface AffiliateLookupResult {
|
|
196
|
+
status: AffiliateLookupStatus;
|
|
197
|
+
details: AffiliateDetails | null;
|
|
198
|
+
}
|
|
9
199
|
type InsertAffiliateIdentifierChangeCallback = (identifier: string | null, offerCode: string | null) => void;
|
|
10
200
|
declare class InsertAffiliate {
|
|
11
201
|
private static isInitialized;
|
|
@@ -15,6 +205,8 @@ declare class InsertAffiliate {
|
|
|
15
205
|
private static affiliateAttributionActiveTime;
|
|
16
206
|
private static preventAffiliateTransfer;
|
|
17
207
|
private static offerCode;
|
|
208
|
+
private static lastReferralDetails;
|
|
209
|
+
private static lastReferralConfig;
|
|
18
210
|
private static verboseLog;
|
|
19
211
|
static initialize(code: string | null, verboseLogging?: boolean, affiliateAttributionActiveTime?: number, preventAffiliateTransfer?: boolean): Promise<void>;
|
|
20
212
|
private static checkForInsertAffiliateParam;
|
|
@@ -24,9 +216,13 @@ declare class InsertAffiliate {
|
|
|
24
216
|
* Validates and sets a short code for affiliate tracking
|
|
25
217
|
* Validates the short code against the API before storing
|
|
26
218
|
* @param shortCode The short code to validate and set
|
|
219
|
+
* @param options.onLookupFailed called when the lookup itself couldn't be completed
|
|
220
|
+
* (not just an invalid code) — use it to offer a retry instead of proceeding unattributed.
|
|
27
221
|
* @returns true if the code exists and was successfully validated and stored, false otherwise
|
|
28
222
|
*/
|
|
29
|
-
static setShortCode(shortCode: string
|
|
223
|
+
static setShortCode(shortCode: string, options?: {
|
|
224
|
+
onLookupFailed?: () => void;
|
|
225
|
+
}): Promise<boolean>;
|
|
30
226
|
static setInsertAffiliateIdentifierChangeCallback(callback: InsertAffiliateIdentifierChangeCallback | null): void;
|
|
31
227
|
static isAffiliateAttributionValid(): Promise<boolean>;
|
|
32
228
|
static getAffiliateStoredDate(): Promise<string | null>;
|
|
@@ -36,8 +232,21 @@ declare class InsertAffiliate {
|
|
|
36
232
|
*/
|
|
37
233
|
static getAffiliateExpiryTimestamp(): Promise<number | null>;
|
|
38
234
|
/**
|
|
39
|
-
* Retrieve detailed information about an affiliate by their short code or deep link
|
|
40
|
-
*
|
|
235
|
+
* Retrieve detailed information about an affiliate by their short code or deep link,
|
|
236
|
+
* distinguishing "no affiliate matches this code" from "couldn't check" (backend
|
|
237
|
+
* outage, timeout, rate limit). This method queries the API and does not store or set
|
|
238
|
+
* the affiliate identifier.
|
|
239
|
+
* @param affiliateCode The short code or deep link to look up
|
|
240
|
+
* @returns an AffiliateLookupResult with a status of 'found', 'not_found', 'lookup_failed', or 'not_configured'
|
|
241
|
+
*/
|
|
242
|
+
static getAffiliateLookupResult(affiliateCode: string, options?: {
|
|
243
|
+
trackUsage?: boolean;
|
|
244
|
+
}): Promise<AffiliateLookupResult>;
|
|
245
|
+
/**
|
|
246
|
+
* Retrieve detailed information about an affiliate by their short code or deep link.
|
|
247
|
+
* Kept for backward compatibility: collapses 'not_found' and 'lookup_failed' into the
|
|
248
|
+
* same null result, exactly as before. Use getAffiliateLookupResult if you need to tell
|
|
249
|
+
* an invalid code apart from a backend outage.
|
|
41
250
|
* @param affiliateCode The short code or deep link to look up
|
|
42
251
|
* @returns AffiliateDetails if found, null otherwise
|
|
43
252
|
*/
|
|
@@ -72,8 +281,82 @@ declare class InsertAffiliate {
|
|
|
72
281
|
transactionReceipt: string;
|
|
73
282
|
}, iapticAppId: string, iapticAppName: string, iapticPublicKey: string): Promise<boolean>;
|
|
74
283
|
static fetchAndConditionallyOpenUrl(affiliateLink: string, offerCodeUrlId: string): Promise<void>;
|
|
284
|
+
private static referralCompanyId;
|
|
285
|
+
/**
|
|
286
|
+
* The device id in this browser's "{shortCode}-{deviceId}" identifier, so the
|
|
287
|
+
* server can tell a referrer apart from the friends they refer.
|
|
288
|
+
* Null when storage is unavailable.
|
|
289
|
+
*/
|
|
290
|
+
private static referralDeviceId;
|
|
291
|
+
private static notInitializedResult;
|
|
292
|
+
/**
|
|
293
|
+
* Makes the app's user an affiliate (a referrer) of this company.
|
|
294
|
+
* A new email is created straight away and this device is connected.
|
|
295
|
+
* An email that is already an affiliate is sent a 6-digit code instead:
|
|
296
|
+
* the result is `verificationRequired`; finish with verifyAffiliateCode.
|
|
297
|
+
* @param email The user's email (usually the app's logged-in user)
|
|
298
|
+
* @param name The user's display name
|
|
299
|
+
* @param options The user's own accounts (RevenueCat / Adapty app user id,
|
|
300
|
+
* Google Play purchase token), used to grant their referral rewards
|
|
301
|
+
*/
|
|
302
|
+
static createAffiliateForUser(email: string, name: string, options?: ReferrerAccountOptions): Promise<AffiliateEnrolmentResult>;
|
|
303
|
+
/**
|
|
304
|
+
* Finishes connecting this device with the 6-digit code emailed by
|
|
305
|
+
* createAffiliateForUser. On success the device is connected.
|
|
306
|
+
* @param email The same email passed to createAffiliateForUser
|
|
307
|
+
* @param code The 6-digit code from the email
|
|
308
|
+
* @param name Optional display name, used if the affiliate is created now
|
|
309
|
+
* @param options The user's own accounts, as for createAffiliateForUser
|
|
310
|
+
*/
|
|
311
|
+
static verifyAffiliateCode(email: string, code: string, name?: string, options?: ReferrerAccountOptions): Promise<AffiliateEnrolmentResult>;
|
|
312
|
+
private static referrerAccountFields;
|
|
313
|
+
/**
|
|
314
|
+
* Saves the connected referrer's own accounts, for apps whose user
|
|
315
|
+
* subscribes or logs in after joining. The server then grants any
|
|
316
|
+
* rewards that were waiting for them.
|
|
317
|
+
* @param options The RevenueCat / Adapty app user id and/or the Google Play purchase token
|
|
318
|
+
* @returns True when saved; false when no referrer is connected on this device
|
|
319
|
+
* or the request failed. A revoked connection is cleared.
|
|
320
|
+
*/
|
|
321
|
+
static setReferrerAccount(options: ReferrerAccountOptions): Promise<boolean>;
|
|
322
|
+
private static loadMyAffiliateDetails;
|
|
323
|
+
/**
|
|
324
|
+
* The connected user's affiliate details and referral stats.
|
|
325
|
+
* Values are for display: grant anything valuable from your server.
|
|
326
|
+
* @returns The details, or null when this device has no referrer connected
|
|
327
|
+
* (or the request failed). A revoked connection is cleared.
|
|
328
|
+
*/
|
|
329
|
+
static getMyAffiliateDetails(): Promise<MyAffiliateDetails | null>;
|
|
330
|
+
/**
|
|
331
|
+
* Whether this device has a referrer connected for this company.
|
|
332
|
+
* Local check only, no network.
|
|
333
|
+
*/
|
|
334
|
+
static isUserAnAffiliate(): Promise<boolean>;
|
|
335
|
+
/** Disconnects the referrer from this device (call on app logout). The affiliate account is kept. */
|
|
336
|
+
static signOutAffiliate(): Promise<void>;
|
|
337
|
+
/** The company's in-app referral settings (program on/off, copy, colour), or null if unavailable. */
|
|
338
|
+
static getReferralProgramConfig(): Promise<ReferralProgramConfig | null>;
|
|
339
|
+
/**
|
|
340
|
+
* Shares the connected user's referral link with the system share sheet,
|
|
341
|
+
* or copies it to the clipboard where sharing is unavailable.
|
|
342
|
+
* Call from a click handler. Browsers only allow sharing and copying
|
|
343
|
+
* shortly after the tap, so this never waits on the network: it uses the
|
|
344
|
+
* details from the last getMyAffiliateDetails call and the app name from
|
|
345
|
+
* the last getReferralProgramConfig call. Load both before the user taps.
|
|
346
|
+
* Without loaded details it returns 'failed' and starts loading them, so a
|
|
347
|
+
* later tap can share.
|
|
348
|
+
* @param message Optional message. May use {link} and {code} placeholders.
|
|
349
|
+
*/
|
|
350
|
+
static shareReferralLink(message?: string): Promise<ReferralShareOutcome>;
|
|
351
|
+
/**
|
|
352
|
+
* Presents the drop-in "Refer a friend" modal. Handles enrolment, the
|
|
353
|
+
* email code step, sharing and stats. Browser only. Only one modal is
|
|
354
|
+
* shown at a time: calling this while it is open focuses the open one.
|
|
355
|
+
* @returns A handle whose close() dismisses the modal
|
|
356
|
+
*/
|
|
357
|
+
static showReferAFriend(options?: ReferAFriendOptions): ReferAFriendHandle;
|
|
75
358
|
private static getOrCreateUserID;
|
|
76
359
|
private static fetchShortLink;
|
|
77
360
|
}
|
|
78
361
|
|
|
79
|
-
export { type AffiliateDetails, InsertAffiliate, type InsertAffiliateIdentifierChangeCallback };
|
|
362
|
+
export { type AffiliateDetails, type AffiliateEnrolmentResult, type AffiliateEnrolmentStatus, type AffiliateLookupResult, type AffiliateLookupStatus, InsertAffiliate, type InsertAffiliateIdentifierChangeCallback, type MyAffiliateDetails, type ReferAFriendHandle, type ReferAFriendOptions, type ReferralErrorCode, type ReferralProgramConfig, type ReferralRewardCode, type ReferralShareOutcome, type ReferralStrings, type ReferralTrigger, type ReferrerAccountOptions, type ReferrerAffiliate };
|