@entitlehub/react-native 0.1.8 → 0.1.9
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 +30 -0
- package/dist/index.cjs +10 -3
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +9 -3
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.9 (2026-09-20)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`restorePurchases()` swallowed every per-purchase failure.** Each purchase was reported with
|
|
7
|
+
`.catch(() => {})`, so a `409 receipt_revoked` (the signal that a purchase was REFUNDED) died
|
|
8
|
+
inside the SDK and no amount of catching in the app could see it. A restore is the main place a
|
|
9
|
+
refund surfaces, so this was the path that mattered most. Failures now reach an optional
|
|
10
|
+
`onReportError`, and `console.warn` otherwise.
|
|
11
|
+
|
|
12
|
+
### Read this before upgrading
|
|
13
|
+
- **If you were catching around `restorePurchases()` to detect a refund, that never worked and
|
|
14
|
+
still will not.** The loop deliberately continues past a bad receipt so one stale purchase can't
|
|
15
|
+
cost someone the rest of theirs, which means `restorePurchases()` **resolves** rather than
|
|
16
|
+
rejecting. Pass `onReportError` - it is the only way to see these:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { restorePurchases, isReceiptRevoked } from "@entitlehub/react-native";
|
|
20
|
+
|
|
21
|
+
const info = await restorePurchases({
|
|
22
|
+
onReportError: (err) => { if (isReceiptRevoked(err)) showRefundNotice(); },
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Passing the options object to **0.1.7/0.1.8 is harmless** (JS drops the extra argument), so it is
|
|
27
|
+
safe to add before you upgrade.
|
|
28
|
+
- `isReceiptRevoked` is re-exported here, so you don't need to import `@entitlehub/sdk` for it.
|
|
29
|
+
It is a **0.3.1** symbol, so the peer range moves to `>=0.3.1`. On an older core SDK the
|
|
30
|
+
re-export resolves to `undefined` rather than failing loudly, which is the whole reason the
|
|
31
|
+
range had to move: `>=0.2.0` would have permitted a version that cannot satisfy it.
|
|
32
|
+
|
|
3
33
|
## 0.1.8 (2026-09-19)
|
|
4
34
|
|
|
5
35
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -37,12 +37,14 @@ __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
|
+
var import_sdk2 = require("@entitlehub/sdk");
|
|
46
48
|
var _iap;
|
|
47
49
|
async function loadIap() {
|
|
48
50
|
if (_iap) return _iap;
|
|
@@ -232,12 +234,16 @@ async function purchaseProduct(productId, opts = {}) {
|
|
|
232
234
|
armTimer(sheetTimeoutMs);
|
|
233
235
|
});
|
|
234
236
|
}
|
|
235
|
-
async function restorePurchases() {
|
|
237
|
+
async function restorePurchases(options = {}) {
|
|
236
238
|
const I = iap();
|
|
237
239
|
const purchases = await I.getAvailablePurchases() || [];
|
|
238
240
|
for (const p of purchases) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
try {
|
|
242
|
+
await reportToEntitleHub(p, p.productId ?? p.id ?? p.sku, true);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
if (options.onReportError) options.onReportError(err, p);
|
|
245
|
+
else console.warn("[entitlehub] restore: could not report", p?.productId ?? p?.id ?? p?.sku, err);
|
|
246
|
+
}
|
|
241
247
|
}
|
|
242
248
|
return eh().getCustomerInfo({ fetchPolicy: "network-only" });
|
|
243
249
|
}
|
|
@@ -267,6 +273,7 @@ async function reportToEntitleHub(purchase, productId, isSubscription) {
|
|
|
267
273
|
getOfferings,
|
|
268
274
|
getProducts,
|
|
269
275
|
isEntitled,
|
|
276
|
+
isReceiptRevoked,
|
|
270
277
|
logIn,
|
|
271
278
|
purchaseProduct,
|
|
272
279
|
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,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { EntitleHub } from "@entitlehub/sdk";
|
|
3
|
+
import { isReceiptRevoked } from "@entitlehub/sdk";
|
|
3
4
|
var _iap;
|
|
4
5
|
async function loadIap() {
|
|
5
6
|
if (_iap) return _iap;
|
|
@@ -189,12 +190,16 @@ async function purchaseProduct(productId, opts = {}) {
|
|
|
189
190
|
armTimer(sheetTimeoutMs);
|
|
190
191
|
});
|
|
191
192
|
}
|
|
192
|
-
async function restorePurchases() {
|
|
193
|
+
async function restorePurchases(options = {}) {
|
|
193
194
|
const I = iap();
|
|
194
195
|
const purchases = await I.getAvailablePurchases() || [];
|
|
195
196
|
for (const p of purchases) {
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
try {
|
|
198
|
+
await reportToEntitleHub(p, p.productId ?? p.id ?? p.sku, true);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
if (options.onReportError) options.onReportError(err, p);
|
|
201
|
+
else console.warn("[entitlehub] restore: could not report", p?.productId ?? p?.id ?? p?.sku, err);
|
|
202
|
+
}
|
|
198
203
|
}
|
|
199
204
|
return eh().getCustomerInfo({ fetchPolicy: "network-only" });
|
|
200
205
|
}
|
|
@@ -223,6 +228,7 @@ export {
|
|
|
223
228
|
getOfferings,
|
|
224
229
|
getProducts,
|
|
225
230
|
isEntitled,
|
|
231
|
+
isReceiptRevoked,
|
|
226
232
|
logIn,
|
|
227
233
|
purchaseProduct,
|
|
228
234
|
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.9",
|
|
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",
|
|
@@ -47,13 +47,14 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "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
|
},
|