@encorekit/web-sdk 2.0.0 → 2.0.2
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 +104 -95
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/encore.min.js +1 -1
- package/dist/umd/encore.min.js.map +1 -1
- package/package.json +1 -1
- package/types/analytics/events.d.ts +7 -28
- package/types/analytics/events.d.ts.map +1 -1
- package/types/api/APIClient.d.ts +10 -6
- package/types/api/APIClient.d.ts.map +1 -1
- package/types/api/models.d.ts +8 -3
- package/types/api/models.d.ts.map +1 -1
- package/types/core/Configuration.d.ts +0 -4
- package/types/core/Configuration.d.ts.map +1 -1
- package/types/core/Encore.d.ts +8 -33
- package/types/core/Encore.d.ts.map +1 -1
- package/types/core/PlacementBuilder.d.ts +7 -31
- package/types/core/PlacementBuilder.d.ts.map +1 -1
- package/types/core/useCase.d.ts +54 -13
- package/types/core/useCase.d.ts.map +1 -1
- package/types/core/version.d.ts +1 -1
- package/types/types.d.ts +85 -272
- package/types/types.d.ts.map +1 -1
- package/types/ui/OfferCarousel.d.ts +59 -1
- package/types/ui/OfferCarousel.d.ts.map +1 -1
- package/types/ui/RewardActions.d.ts +60 -0
- package/types/ui/RewardActions.d.ts.map +1 -0
- package/types/ui/RewardCard.d.ts +49 -0
- package/types/ui/RewardCard.d.ts.map +1 -0
- package/types/ui/RewardConfetti.d.ts +46 -0
- package/types/ui/RewardConfetti.d.ts.map +1 -0
- package/types/ui/RewardLayout.d.ts +55 -0
- package/types/ui/RewardLayout.d.ts.map +1 -0
- package/types/ui/brandTint.d.ts +61 -0
- package/types/ui/brandTint.d.ts.map +1 -0
- package/types/ui/styles.d.ts +96 -0
- package/types/ui/styles.d.ts.map +1 -1
- package/types/utils/text.d.ts +24 -0
- package/types/utils/text.d.ts.map +1 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A JavaScript/TypeScript SDK that enables web applications to display targeted of
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- 🎯 **Targeted Offer Presentation** - Display offers at critical moments (cancellation flows, paywalls, onboarding)
|
|
8
|
-
- 🎁 **Use Cases** -
|
|
8
|
+
- 🎁 **Use Cases** - `reduceChurn` (the default) or `rewardUsers` — see [Use Cases](#use-cases)
|
|
9
9
|
- 🔐 **Entitlement Management** - Two-tier system (provisional + verified) for instant UX and reliable billing
|
|
10
10
|
- 📱 **Framework Agnostic** - Works with React, Vue, Angular, Svelte, vanilla JavaScript, or any web framework
|
|
11
11
|
- 🎨 **Responsive UI** - Beautiful, accessible modal interface that works on desktop and mobile
|
|
@@ -16,7 +16,7 @@ A JavaScript/TypeScript SDK that enables web applications to display targeted of
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install @encorekit/web-sdk
|
|
19
|
+
npm install @encorekit/web-sdk@3.0.0
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Or via CDN:
|
|
@@ -25,6 +25,24 @@ Or via CDN:
|
|
|
25
25
|
<script src="https://encorekit.com/encore.min.js"></script>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Upgrading to 3.0.0
|
|
29
|
+
|
|
30
|
+
3.0.0 removes the deferred redemption flow and the layout system. Claiming an
|
|
31
|
+
offer now always redirects to the advertiser immediately, and the composition is
|
|
32
|
+
derived from the placement's use case.
|
|
33
|
+
|
|
34
|
+
| Removed | Replacement |
|
|
35
|
+
|:--------|:------------|
|
|
36
|
+
| `redemptionMode` — on `configure()` and on placement options | None. Redirect-on-claim is the only flow. |
|
|
37
|
+
| `Encore.redeem()`, `placement(id).redeem()`, `getPendingTransaction()`, `RedeemOptions`, `RedeemResult` | None. There is no pending transaction to redeem. |
|
|
38
|
+
| `layout: 'list' \| 'thankYou'` | `useCase` — `'reduceChurn'` renders the offer carousel, `'rewardUsers'` the reward composition. |
|
|
39
|
+
| `header.title`, `header.subtitle` | `headline` / `subheadline` on `PlacementOptions`. |
|
|
40
|
+
| `header.icon` | `statusIcon` on `PlacementOptions` — **the default flips from `'success'` to `'none'`**, so the check no longer renders unless you ask for it. |
|
|
41
|
+
| `offerContext`, `footer`, `receipt` / `ShowOptions`, `Receipt` | None. Render any other copy yourself. |
|
|
42
|
+
| `display` / `HostDisplay` | None. It only ever fed the deferred-flow screens; on the immediate path it resolved to nothing. |
|
|
43
|
+
| `.headline()` / `.subheadline()` builder steps | The flat `headline` / `subheadline` options. `.useCase()` stays. |
|
|
44
|
+
| `'churn-intervention'` / `'post-action-reward'` | `'reduceChurn'` / `'rewardUsers'` — a rename of the TypeScript symbols only; the values sent over HTTP are unchanged. |
|
|
45
|
+
|
|
28
46
|
## Quick Start
|
|
29
47
|
|
|
30
48
|
```typescript
|
|
@@ -43,93 +61,97 @@ Encore.identify('user-123', {
|
|
|
43
61
|
subscriptionTier: 'free'
|
|
44
62
|
});
|
|
45
63
|
|
|
46
|
-
// Present
|
|
47
|
-
const result = await Encore.
|
|
48
|
-
if (result.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
64
|
+
// Present a placement — defaults to the 'reduceChurn' use case
|
|
65
|
+
const result = await Encore.show();
|
|
66
|
+
if (result.status === 'claimed') {
|
|
67
|
+
// The user claimed an offer and the advertiser opened in a new tab.
|
|
68
|
+
// Keep result.transactionId — it is how the conversion is traced back to
|
|
69
|
+
// this user and app, and what waitForVerification() / didGrant() take.
|
|
70
|
+
// The SDK renders nothing after a claim — show your own confirmation here.
|
|
52
71
|
} else {
|
|
53
|
-
|
|
72
|
+
// 'dismissed' (with an optional reason) or 'unavailable' — nothing was claimed.
|
|
73
|
+
console.log('Nothing claimed:', result);
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
// Check entitlement on YOUR server (not client-side)
|
|
57
77
|
// See: https://docs.encorekit.com/server-side-validation
|
|
58
78
|
```
|
|
59
79
|
|
|
60
|
-
##
|
|
80
|
+
## Placements
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
your payment copy, the offer carousel, and an optional trust footer.
|
|
82
|
+
A **placement** is one presentation of offers. There are two call forms, and they
|
|
83
|
+
take the same options:
|
|
65
84
|
|
|
66
85
|
```typescript
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
// Builder form
|
|
87
|
+
const result = await Encore.placement('milestone_reached', {
|
|
88
|
+
headline: 'Nice work, Sam!',
|
|
89
|
+
subheadline: 'That is your fifth week running',
|
|
69
90
|
appearance: { accentColor: '#0A6' },
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
await
|
|
91
|
+
})
|
|
92
|
+
.useCase('rewardUsers')
|
|
93
|
+
.onNotGranted((reason) => console.log('closed without a claim', reason))
|
|
94
|
+
.onLoadingStateChange((isLoading) => setSpinner(isLoading))
|
|
95
|
+
.show(); // takes no arguments
|
|
96
|
+
|
|
97
|
+
// Flat form — callbacks first, then the same options, then the placement id
|
|
98
|
+
await Encore.show(
|
|
99
|
+
{ onGranted, onNotGranted, onError },
|
|
100
|
+
{ useCase: 'rewardUsers', headline: 'Nice work, Sam!' },
|
|
101
|
+
'milestone_reached'
|
|
102
|
+
);
|
|
78
103
|
```
|
|
79
104
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
- `header` is the sheet's top heading block in EVERY layout — the modal
|
|
83
|
-
title/subtitle in `'list'` (a local override of the remote copy) and the
|
|
84
|
-
payment-status block in `'thankYou'`. One name, one meaning.
|
|
85
|
-
- `receipt` values are rendered verbatim as plain text (pre-format currency yourself),
|
|
86
|
-
never sent to Encore servers, and override `header.subtitle` when provided.
|
|
87
|
-
- `offerContext` is `'thankYou'`-only priming copy ABOVE the offers (why they're
|
|
88
|
-
being shown), not any offer's own copy — `'list'` ignores it. When the active
|
|
89
|
-
offer's campaign carries a `perk` line (e.g. "Enjoy 30 days of Apple Music free")
|
|
90
|
-
the perk takes the subtitle slot and follows the carousel as it pages;
|
|
91
|
-
`offerContext.subtitle` is the static fallback.
|
|
92
|
-
- Need a custom confirmation line? Skip `receipt` and pass your own pre-built string as
|
|
93
|
-
`header.subtitle` — placement options are per-call.
|
|
94
|
-
- Unknown `layout` values warn and fall back to `'list'`, so older SDK versions degrade
|
|
95
|
-
gracefully when layout selection later moves to remote config.
|
|
96
|
-
- In `'thankYou'` the carousel pages one card per view at every breakpoint, with round
|
|
97
|
-
chevron navigation bubbles when there is more than one offer (hidden for a single offer).
|
|
98
|
-
- Pair with `redemptionMode: 'immediate'` for post-purchase: Claim opens the advertiser
|
|
99
|
-
in a new tab within the tap gesture and `show()` resolves `{ status: 'claimed' }` —
|
|
100
|
-
the SDK renders nothing after a claim. The host owns that moment: render your own
|
|
101
|
-
confirmation if you want one, keyed off the result. The result deliberately makes no
|
|
102
|
-
claim about advertiser conversion (it is asynchronous and server-authoritative —
|
|
103
|
-
verify entitlements/conversions server-side by user, never from the client).
|
|
104
|
-
`show()` never rejects: failures resolve as `{ status: 'dismissed', reason }`.
|
|
105
|
+
`Encore.show()`'s first argument is **callbacks only** — `onGranted`,
|
|
106
|
+
`onNotGranted`, `onError`. Every other option is a placement option.
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
### `PlacementOptions`
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
| Option | Type | Meaning |
|
|
111
|
+
|:-------|:-----|:--------|
|
|
112
|
+
| `useCase` | `'reduceChurn' \| 'rewardUsers'` | Why the placement presents, and which composition it renders. Defaults to `'reduceChurn'`. |
|
|
113
|
+
| `headline` | `string` | Per-call override of the sheet's primary heading. |
|
|
114
|
+
| `subheadline` | `string` | Per-call override of the line under it. |
|
|
115
|
+
| `appearance` | `{ backgroundColor?, textColor?, accentColor? }` | Colour overrides for the sheet. |
|
|
116
|
+
| `statusIcon` | `'success' \| 'none'` | Status icon above the headline. Defaults to `'none'` — nothing renders unless you ask for `'success'`, which draws a green-circle check. Only `'rewardUsers'` has a status block; `'reduceChurn'` ignores it. The default matches iOS and Android, whose reward variant renders no checkmark. |
|
|
111
117
|
|
|
112
|
-
|
|
113
|
-
// Reward a user for something they just did. No entitlement is granted.
|
|
114
|
-
const result = await Encore.placement('milestone_reached')
|
|
115
|
-
.useCase('post-action-reward')
|
|
116
|
-
.show();
|
|
118
|
+
### The result
|
|
117
119
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
`show()` **never rejects**. It resolves one of three statuses:
|
|
121
|
+
|
|
122
|
+
| Status | Meaning |
|
|
123
|
+
|:-------|:--------|
|
|
124
|
+
| `{ status: 'claimed', offerId, campaignId, advertiserName, transactionId? }` | The user claimed an offer. The advertiser opened in a new tab inside the tap gesture and the SDK renders nothing afterwards — the host owns that moment, so render your own confirmation if you want one. The payload mirrors iOS's and Android's `ClaimedOffer` field-for-field. **`transactionId` carries the attribution** — it is how a completion that lands days later is traced back to this user and app, and it is the argument `didGrant()` and `waitForVerification()` take. It is optional because the transaction write can fail while the claim genuinely happened; the SDK logs a warning when that occurs. |
|
|
125
|
+
| `{ status: 'dismissed', reason? }` | The sheet closed without a claim. Failures land here too, carrying `reason.error`. |
|
|
126
|
+
| `{ status: 'unavailable' }` | Nothing was presented — no eligible offers, or the surface did not resolve. |
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
`claimed` deliberately makes no assertion about advertiser conversion: that is
|
|
129
|
+
asynchronous and server-authoritative. Verify entitlements and conversions
|
|
130
|
+
server-side, by user, never from the client.
|
|
131
|
+
|
|
132
|
+
## Use Cases
|
|
133
|
+
|
|
134
|
+
A placement's **use case** says *why* it presents — and it is what selects the
|
|
135
|
+
composition. It defaults to `'reduceChurn'`.
|
|
136
|
+
|
|
137
|
+
| Value | Composition | Meaning |
|
|
138
|
+
|:------|:------------|:--------|
|
|
139
|
+
| `'reduceChurn'` | Offer carousel | The default. A user is cancelling or downgrading; the offer is an alternative to leaving. |
|
|
140
|
+
| `'rewardUsers'` | Reward composition | The user completed something worth celebrating — a purchase, a milestone, a streak, a level. The offer is a gift. |
|
|
141
|
+
|
|
142
|
+
It is both a plain placement option and a builder step — use whichever reads
|
|
143
|
+
better:
|
|
124
144
|
|
|
125
145
|
```typescript
|
|
126
|
-
Encore.placement('milestone_reached', { useCase: '
|
|
146
|
+
Encore.placement('milestone_reached', { useCase: 'rewardUsers' });
|
|
147
|
+
Encore.placement('milestone_reached').useCase('rewardUsers');
|
|
127
148
|
```
|
|
128
149
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
> **The wire values are unchanged.** `reduceChurn` / `rewardUsers` are the
|
|
151
|
+
> TypeScript symbols. Over HTTP the SDK still sends `churn-intervention` /
|
|
152
|
+
> `post-action-reward` (`GET /config?useCase=`, `POST /transactions`, and the
|
|
153
|
+
> `use_case` analytics property), so existing dashboards and attribution are
|
|
154
|
+
> unaffected by the rename.
|
|
133
155
|
|
|
134
156
|
### Supplying your own headline and subheadline
|
|
135
157
|
|
|
@@ -137,57 +159,44 @@ Pass your copy at presentation time. This is the intended way to set it — you
|
|
|
137
159
|
know what the user just did, so you can name it:
|
|
138
160
|
|
|
139
161
|
```typescript
|
|
140
|
-
await Encore.placement('milestone_reached'
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
162
|
+
await Encore.placement('milestone_reached', {
|
|
163
|
+
useCase: 'rewardUsers',
|
|
164
|
+
headline: 'Nice work, Sam!',
|
|
165
|
+
subheadline: 'That is your fifth week running',
|
|
166
|
+
}).show();
|
|
145
167
|
```
|
|
146
168
|
|
|
147
169
|
Both are **per-call overrides**: they beat whatever copy Encore resolved for the
|
|
148
|
-
surface, and they work on either use case.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
They are also available as a plain option, if you are already passing others:
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
await Encore.show(undefined, {
|
|
155
|
-
useCase: 'post-action-reward',
|
|
156
|
-
header: { title: 'Nice work, Sam!', subtitle: 'That is your fifth week running' },
|
|
157
|
-
});
|
|
158
|
-
```
|
|
170
|
+
surface, and they work on either use case. `headline` fills the sheet's primary
|
|
171
|
+
heading and `subheadline` the line under it.
|
|
159
172
|
|
|
160
173
|
> **Blank falls through — it does not clear the copy.** A blank or
|
|
161
174
|
> whitespace-only value is treated as "not supplied" and falls through to the
|
|
162
175
|
> copy Encore resolved, so passing `''` is never a way to blank the sheet. Omit
|
|
163
|
-
> the
|
|
164
|
-
> came back empty
|
|
165
|
-
> rendering a headless sheet.
|
|
176
|
+
> the option instead. This keeps a value that is empty because an interpolation
|
|
177
|
+
> came back empty — a `headline` built from a template literal whose `name` was
|
|
178
|
+
> undefined — from rendering a headless sheet.
|
|
166
179
|
|
|
167
180
|
If you supply neither, the sheet falls back to Encore's resolved copy for the
|
|
168
181
|
surface, and finally to a built-in string, so the heading is never empty.
|
|
169
182
|
|
|
170
|
-
### `
|
|
183
|
+
### `rewardUsers` grants no entitlement
|
|
171
184
|
|
|
172
185
|
A reward claim is a gift the brand funds directly, not a subscription unlock. So
|
|
173
186
|
`show()` resolves `{ status: 'claimed' }` and **no entitlement is granted** — on
|
|
174
187
|
this path the SDK skips the automatic provisional grant it performs for
|
|
175
|
-
`'
|
|
176
|
-
|
|
188
|
+
`'reduceChurn'`, so no grant signal reaches Encore. Nothing was promised, so
|
|
189
|
+
nothing is unlocked. (Reward activation is confirmed server-side via the
|
|
177
190
|
offer-completed webhook, an entirely separate mechanism.)
|
|
178
191
|
|
|
179
192
|
This is why `claimed` is its own result and not folded into `dismissed`: a user
|
|
180
193
|
who successfully claimed a reward must never run your "user declined" branch.
|
|
181
194
|
|
|
182
|
-
> That guarantee is on `show()`'s `ShowResult`. The deprecated `presentOffer()`
|
|
183
|
-
> predates it and still reports a **deferred** claim as `granted: false`, so use
|
|
184
|
-
> `show()` for any reward placement.
|
|
185
|
-
|
|
186
195
|
### Unrecognised values fail closed
|
|
187
196
|
|
|
188
197
|
Anything that is not one of the two values above is rejected before anything is
|
|
189
|
-
presented, rather than quietly falling back to the churn
|
|
190
|
-
|
|
198
|
+
presented, rather than quietly falling back to the churn surface. `show()` still
|
|
199
|
+
never rejects — it resolves:
|
|
191
200
|
|
|
192
201
|
```typescript
|
|
193
202
|
{ status: 'dismissed', reason: { type: 'error', error: { code: 'INVALID_REQUEST', … } } }
|