@entitlehub/react-native 0.1.8 → 0.1.10
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/CHANGELOG.md +41 -0
- package/dist/index.cjs +15 -4
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +14 -4
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.10 (2026-09-21)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **0.1.9 reported itself as 0.1.8.** The bundled `RN_SDK_VERSION` was not bumped alongside
|
|
7
|
+
`package.json`, so the `X-EntitleHub-Client` header sent on every request attributed all 0.1.9
|
|
8
|
+
traffic to 0.1.8. Nothing in your app behaves differently, but it under-reported adoption of the
|
|
9
|
+
release that fixes the refund path, which is exactly the one worth tracking. Found by the
|
|
10
|
+
StatSnap integration after upgrading.
|
|
11
|
+
- The constant now comes from `package.json` at build time, and a test asserts the built bundle
|
|
12
|
+
carries the published version.
|
|
13
|
+
|
|
14
|
+
## 0.1.9 (2026-09-20)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **`restorePurchases()` swallowed every per-purchase failure.** Each purchase was reported with
|
|
18
|
+
`.catch(() => {})`, so a `409 receipt_revoked` (the signal that a purchase was REFUNDED) died
|
|
19
|
+
inside the SDK and no amount of catching in the app could see it. A restore is the main place a
|
|
20
|
+
refund surfaces, so this was the path that mattered most. Failures now reach an optional
|
|
21
|
+
`onReportError`, and `console.warn` otherwise.
|
|
22
|
+
|
|
23
|
+
### Read this before upgrading
|
|
24
|
+
- **If you were catching around `restorePurchases()` to detect a refund, that never worked and
|
|
25
|
+
still will not.** The loop deliberately continues past a bad receipt so one stale purchase can't
|
|
26
|
+
cost someone the rest of theirs, which means `restorePurchases()` **resolves** rather than
|
|
27
|
+
rejecting. Pass `onReportError` - it is the only way to see these:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { restorePurchases, isReceiptRevoked } from "@entitlehub/react-native";
|
|
31
|
+
|
|
32
|
+
const info = await restorePurchases({
|
|
33
|
+
onReportError: (err) => { if (isReceiptRevoked(err)) showRefundNotice(); },
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Passing the options object to **0.1.7/0.1.8 is harmless** (JS drops the extra argument), so it is
|
|
38
|
+
safe to add before you upgrade.
|
|
39
|
+
- `isReceiptRevoked` is re-exported here, so you don't need to import `@entitlehub/sdk` for it.
|
|
40
|
+
It is a **0.3.1** symbol, so the peer range moves to `>=0.3.1`. On an older core SDK the
|
|
41
|
+
re-export resolves to `undefined` rather than failing loudly, which is the whole reason the
|
|
42
|
+
range had to move: `>=0.2.0` would have permitted a version that cannot satisfy it.
|
|
43
|
+
|
|
3
44
|
## 0.1.8 (2026-09-19)
|
|
4
45
|
|
|
5
46
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -37,12 +37,19 @@ __export(index_exports, {
|
|
|
37
37
|
getOfferings: () => getOfferings,
|
|
38
38
|
getProducts: () => getProducts,
|
|
39
39
|
isEntitled: () => isEntitled,
|
|
40
|
+
isReceiptRevoked: () => import_sdk2.isReceiptRevoked,
|
|
40
41
|
logIn: () => logIn,
|
|
41
42
|
purchaseProduct: () => purchaseProduct,
|
|
42
43
|
restorePurchases: () => restorePurchases
|
|
43
44
|
});
|
|
44
45
|
module.exports = __toCommonJS(index_exports);
|
|
45
46
|
var import_sdk = require("@entitlehub/sdk");
|
|
47
|
+
|
|
48
|
+
// src/version.ts
|
|
49
|
+
var RN_SDK_VERSION = "0.1.10";
|
|
50
|
+
|
|
51
|
+
// src/index.ts
|
|
52
|
+
var import_sdk2 = require("@entitlehub/sdk");
|
|
46
53
|
var _iap;
|
|
47
54
|
async function loadIap() {
|
|
48
55
|
if (_iap) return _iap;
|
|
@@ -62,7 +69,6 @@ function iap() {
|
|
|
62
69
|
if (!_iap) throw new Error("Call configureEntitleHub({ apiKey, appUserId }) before using EntitleHub purchases.");
|
|
63
70
|
return _iap;
|
|
64
71
|
}
|
|
65
|
-
var RN_SDK_VERSION = "0.1.8";
|
|
66
72
|
var client;
|
|
67
73
|
function eh() {
|
|
68
74
|
if (!client) throw new Error("Call configureEntitleHub({ apiKey, appUserId }) before using EntitleHub.");
|
|
@@ -232,12 +238,16 @@ async function purchaseProduct(productId, opts = {}) {
|
|
|
232
238
|
armTimer(sheetTimeoutMs);
|
|
233
239
|
});
|
|
234
240
|
}
|
|
235
|
-
async function restorePurchases() {
|
|
241
|
+
async function restorePurchases(options = {}) {
|
|
236
242
|
const I = iap();
|
|
237
243
|
const purchases = await I.getAvailablePurchases() || [];
|
|
238
244
|
for (const p of purchases) {
|
|
239
|
-
|
|
240
|
-
|
|
245
|
+
try {
|
|
246
|
+
await reportToEntitleHub(p, p.productId ?? p.id ?? p.sku, true);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (options.onReportError) options.onReportError(err, p);
|
|
249
|
+
else console.warn("[entitlehub] restore: could not report", p?.productId ?? p?.id ?? p?.sku, err);
|
|
250
|
+
}
|
|
241
251
|
}
|
|
242
252
|
return eh().getCustomerInfo({ fetchPolicy: "network-only" });
|
|
243
253
|
}
|
|
@@ -267,6 +277,7 @@ async function reportToEntitleHub(purchase, productId, isSubscription) {
|
|
|
267
277
|
getOfferings,
|
|
268
278
|
getProducts,
|
|
269
279
|
isEntitled,
|
|
280
|
+
isReceiptRevoked,
|
|
270
281
|
logIn,
|
|
271
282
|
purchaseProduct,
|
|
272
283
|
restorePurchases
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CustomerInfo, Offerings } from '@entitlehub/sdk';
|
|
2
|
-
export { CustomerInfo, Offerings } from '@entitlehub/sdk';
|
|
2
|
+
export { CustomerInfo, Offerings, isReceiptRevoked } from '@entitlehub/sdk';
|
|
3
3
|
|
|
4
4
|
interface ConfigureOptions {
|
|
5
5
|
/** Your EntitleHub publishable key (pk_live_… / pk_test_…). Client-safe. */
|
|
@@ -76,9 +76,22 @@ declare function purchaseProduct(productId: string, opts?: {
|
|
|
76
76
|
/** How long to poll for confirmation before reporting pending (default 30s). */
|
|
77
77
|
reconcileMs?: number;
|
|
78
78
|
}): Promise<CustomerInfo>;
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
|
|
80
|
+
interface RestoreOptions {
|
|
81
|
+
/**
|
|
82
|
+
* Called for each purchase that could not be reported. The restore continues either way, so one
|
|
83
|
+
* stale receipt can't cost the user the rest of their purchases.
|
|
84
|
+
*
|
|
85
|
+
* Worth handling: a restore is where a REFUND surfaces. Apple stamps a refunded transaction with
|
|
86
|
+
* revocationDate permanently and StoreKit keeps handing it back, so reporting it answers
|
|
87
|
+
* 409 `receipt_revoked` (EntitleHub revokes the grant server-side at the same time). That 409 is
|
|
88
|
+
* the only signal telling you the entitlement went away because the money was returned, rather
|
|
89
|
+
* than for some unexplained reason.
|
|
90
|
+
*/
|
|
91
|
+
onReportError?: (error: unknown, purchase: unknown) => void;
|
|
92
|
+
}
|
|
93
|
+
declare function restorePurchases(options?: RestoreOptions): Promise<CustomerInfo>;
|
|
81
94
|
/** Subscribe to entitlement changes (e.g. after a purchase). Returns an unsubscribe function. */
|
|
82
95
|
declare function addCustomerInfoUpdateListener(fn: (info: CustomerInfo) => void): () => void;
|
|
83
96
|
|
|
84
|
-
export { type ConfigureOptions, PurchaseError, type PurchaseErrorCode, addCustomerInfoUpdateListener, configureEntitleHub, getCustomerInfo, getOfferings, getProducts, isEntitled, logIn, purchaseProduct, restorePurchases };
|
|
97
|
+
export { type ConfigureOptions, PurchaseError, type PurchaseErrorCode, type RestoreOptions, addCustomerInfoUpdateListener, configureEntitleHub, getCustomerInfo, getOfferings, getProducts, isEntitled, logIn, purchaseProduct, restorePurchases };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CustomerInfo, Offerings } from '@entitlehub/sdk';
|
|
2
|
-
export { CustomerInfo, Offerings } from '@entitlehub/sdk';
|
|
2
|
+
export { CustomerInfo, Offerings, isReceiptRevoked } from '@entitlehub/sdk';
|
|
3
3
|
|
|
4
4
|
interface ConfigureOptions {
|
|
5
5
|
/** Your EntitleHub publishable key (pk_live_… / pk_test_…). Client-safe. */
|
|
@@ -76,9 +76,22 @@ declare function purchaseProduct(productId: string, opts?: {
|
|
|
76
76
|
/** How long to poll for confirmation before reporting pending (default 30s). */
|
|
77
77
|
reconcileMs?: number;
|
|
78
78
|
}): Promise<CustomerInfo>;
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
|
|
80
|
+
interface RestoreOptions {
|
|
81
|
+
/**
|
|
82
|
+
* Called for each purchase that could not be reported. The restore continues either way, so one
|
|
83
|
+
* stale receipt can't cost the user the rest of their purchases.
|
|
84
|
+
*
|
|
85
|
+
* Worth handling: a restore is where a REFUND surfaces. Apple stamps a refunded transaction with
|
|
86
|
+
* revocationDate permanently and StoreKit keeps handing it back, so reporting it answers
|
|
87
|
+
* 409 `receipt_revoked` (EntitleHub revokes the grant server-side at the same time). That 409 is
|
|
88
|
+
* the only signal telling you the entitlement went away because the money was returned, rather
|
|
89
|
+
* than for some unexplained reason.
|
|
90
|
+
*/
|
|
91
|
+
onReportError?: (error: unknown, purchase: unknown) => void;
|
|
92
|
+
}
|
|
93
|
+
declare function restorePurchases(options?: RestoreOptions): Promise<CustomerInfo>;
|
|
81
94
|
/** Subscribe to entitlement changes (e.g. after a purchase). Returns an unsubscribe function. */
|
|
82
95
|
declare function addCustomerInfoUpdateListener(fn: (info: CustomerInfo) => void): () => void;
|
|
83
96
|
|
|
84
|
-
export { type ConfigureOptions, PurchaseError, type PurchaseErrorCode, addCustomerInfoUpdateListener, configureEntitleHub, getCustomerInfo, getOfferings, getProducts, isEntitled, logIn, purchaseProduct, restorePurchases };
|
|
97
|
+
export { type ConfigureOptions, PurchaseError, type PurchaseErrorCode, type RestoreOptions, addCustomerInfoUpdateListener, configureEntitleHub, getCustomerInfo, getOfferings, getProducts, isEntitled, logIn, purchaseProduct, restorePurchases };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { EntitleHub } from "@entitlehub/sdk";
|
|
3
|
+
|
|
4
|
+
// src/version.ts
|
|
5
|
+
var RN_SDK_VERSION = "0.1.10";
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import { isReceiptRevoked } from "@entitlehub/sdk";
|
|
3
9
|
var _iap;
|
|
4
10
|
async function loadIap() {
|
|
5
11
|
if (_iap) return _iap;
|
|
@@ -19,7 +25,6 @@ function iap() {
|
|
|
19
25
|
if (!_iap) throw new Error("Call configureEntitleHub({ apiKey, appUserId }) before using EntitleHub purchases.");
|
|
20
26
|
return _iap;
|
|
21
27
|
}
|
|
22
|
-
var RN_SDK_VERSION = "0.1.8";
|
|
23
28
|
var client;
|
|
24
29
|
function eh() {
|
|
25
30
|
if (!client) throw new Error("Call configureEntitleHub({ apiKey, appUserId }) before using EntitleHub.");
|
|
@@ -189,12 +194,16 @@ async function purchaseProduct(productId, opts = {}) {
|
|
|
189
194
|
armTimer(sheetTimeoutMs);
|
|
190
195
|
});
|
|
191
196
|
}
|
|
192
|
-
async function restorePurchases() {
|
|
197
|
+
async function restorePurchases(options = {}) {
|
|
193
198
|
const I = iap();
|
|
194
199
|
const purchases = await I.getAvailablePurchases() || [];
|
|
195
200
|
for (const p of purchases) {
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
try {
|
|
202
|
+
await reportToEntitleHub(p, p.productId ?? p.id ?? p.sku, true);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
if (options.onReportError) options.onReportError(err, p);
|
|
205
|
+
else console.warn("[entitlehub] restore: could not report", p?.productId ?? p?.id ?? p?.sku, err);
|
|
206
|
+
}
|
|
198
207
|
}
|
|
199
208
|
return eh().getCustomerInfo({ fetchPolicy: "network-only" });
|
|
200
209
|
}
|
|
@@ -223,6 +232,7 @@ export {
|
|
|
223
232
|
getOfferings,
|
|
224
233
|
getProducts,
|
|
225
234
|
isEntitled,
|
|
235
|
+
isReceiptRevoked,
|
|
226
236
|
logIn,
|
|
227
237
|
purchaseProduct,
|
|
228
238
|
restorePurchases
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entitlehub/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "EntitleHub for React Native & Expo, one call to purchase and unlock entitlements. Wraps expo-iap for the native store sheet and validates receipts via EntitleHub.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"entitlehub",
|
|
@@ -45,15 +45,16 @@
|
|
|
45
45
|
],
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
48
|
+
"build": "node scripts/gen-version.mjs && tsup src/index.ts --format esm,cjs --dts --clean",
|
|
49
49
|
"typecheck": "tsc --noEmit",
|
|
50
|
+
"test": "npm run build && node --test \"test/*.test.mjs\"",
|
|
50
51
|
"prepublishOnly": "npm run build"
|
|
51
52
|
},
|
|
52
53
|
"engines": {
|
|
53
54
|
"node": ">=18"
|
|
54
55
|
},
|
|
55
56
|
"peerDependencies": {
|
|
56
|
-
"@entitlehub/sdk": ">=0.
|
|
57
|
+
"@entitlehub/sdk": ">=0.3.1",
|
|
57
58
|
"expo-iap": "*",
|
|
58
59
|
"react": ">=17"
|
|
59
60
|
},
|