@onborn/billing 0.1.0-beta.4 → 0.1.0-beta.5
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 +6 -0
- package/dist/expo-iap.d.ts +2 -0
- package/dist/expo-iap.js +45 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.5
|
|
4
|
+
|
|
5
|
+
- Wait for the native store connection before loading localized products,
|
|
6
|
+
purchasing, or restoring. This prevents a cold-open paywall from showing an
|
|
7
|
+
unavailable state that only clears after a manual retry.
|
|
8
|
+
|
|
3
9
|
## 0.1.0-beta.4
|
|
4
10
|
|
|
5
11
|
- Surface native store product-loading failures instead of silently falling
|
package/dist/expo-iap.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { OnbornBillingAdapter } from "./types";
|
|
|
3
3
|
export type ExpoIapBillingAdapterOptions = {
|
|
4
4
|
/** Additional subscription SKUs that should share the adapter product cache. */
|
|
5
5
|
productIds?: string[];
|
|
6
|
+
/** Maximum time an operation waits for the native store connection. */
|
|
7
|
+
connectionTimeoutMs?: number;
|
|
6
8
|
purchaseTimeoutMs?: number;
|
|
7
9
|
purchaseRecoveryDelayMs?: number;
|
|
8
10
|
productCacheMs?: number;
|
package/dist/expo-iap.js
CHANGED
|
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { Platform } from "react-native";
|
|
4
4
|
import { createNativeStoresBillingAdapter } from "./adapters/nativeStores";
|
|
5
5
|
const DEFAULT_PURCHASE_TIMEOUT_MS = 2 * 60 * 1000;
|
|
6
|
+
const DEFAULT_CONNECTION_TIMEOUT_MS = 15_000;
|
|
6
7
|
const DEFAULT_RECOVERY_DELAY_MS = 750;
|
|
7
8
|
const DEFAULT_PRODUCT_CACHE_MS = 10_000;
|
|
8
9
|
const DEFAULT_PRODUCT_RETRY_DELAY_MS = 500;
|
|
@@ -15,6 +16,8 @@ const DEFAULT_RESTORE_SETTLE_DELAY_MS = 300;
|
|
|
15
16
|
*/
|
|
16
17
|
export function useExpoIapBillingAdapter(options = {}) {
|
|
17
18
|
const pendingPurchaseRef = useRef(null);
|
|
19
|
+
const connectedRef = useRef(false);
|
|
20
|
+
const connectionWaitersRef = useRef(new Set());
|
|
18
21
|
const productCacheRef = useRef(null);
|
|
19
22
|
const productRequestRef = useRef(null);
|
|
20
23
|
const knownProductIdsRef = useRef(new Set(normalizeIds(options.productIds)));
|
|
@@ -59,13 +62,48 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
59
62
|
settlePendingPurchase((current) => current.reject(normalized));
|
|
60
63
|
},
|
|
61
64
|
});
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
connectedRef.current = connected;
|
|
67
|
+
if (!connected)
|
|
68
|
+
return;
|
|
69
|
+
for (const waiter of connectionWaitersRef.current) {
|
|
70
|
+
clearTimeout(waiter.timeout);
|
|
71
|
+
waiter.resolve();
|
|
72
|
+
}
|
|
73
|
+
connectionWaitersRef.current.clear();
|
|
74
|
+
}, [connected]);
|
|
75
|
+
const waitForConnection = useCallback(async () => {
|
|
76
|
+
if (connectedRef.current)
|
|
77
|
+
return;
|
|
78
|
+
await new Promise((resolve, reject) => {
|
|
79
|
+
const waiter = {
|
|
80
|
+
resolve,
|
|
81
|
+
reject,
|
|
82
|
+
timeout: setTimeout(() => {
|
|
83
|
+
connectionWaitersRef.current.delete(waiter);
|
|
84
|
+
reject(new Error("Native store connection timed out."));
|
|
85
|
+
}, options.connectionTimeoutMs ?? DEFAULT_CONNECTION_TIMEOUT_MS),
|
|
86
|
+
};
|
|
87
|
+
connectionWaitersRef.current.add(waiter);
|
|
88
|
+
// The connection may have completed between the first check and
|
|
89
|
+
// registering this waiter.
|
|
90
|
+
if (connectedRef.current) {
|
|
91
|
+
connectionWaitersRef.current.delete(waiter);
|
|
92
|
+
clearTimeout(waiter.timeout);
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}, [options.connectionTimeoutMs]);
|
|
62
97
|
useEffect(() => () => {
|
|
63
98
|
settlePendingPurchase((item) => item.reject(new Error("Purchase interrupted.")));
|
|
99
|
+
for (const waiter of connectionWaitersRef.current) {
|
|
100
|
+
clearTimeout(waiter.timeout);
|
|
101
|
+
waiter.reject(new Error("Native store connection was interrupted."));
|
|
102
|
+
}
|
|
103
|
+
connectionWaitersRef.current.clear();
|
|
64
104
|
}, [settlePendingPurchase]);
|
|
65
105
|
const loadProducts = useCallback(async (requestedProductIds, force = false) => {
|
|
66
|
-
|
|
67
|
-
throw new Error("Native store is not connected.");
|
|
68
|
-
}
|
|
106
|
+
await waitForConnection();
|
|
69
107
|
const requestedIds = normalizeIds(requestedProductIds);
|
|
70
108
|
for (const productId of requestedIds) {
|
|
71
109
|
knownProductIdsRef.current.add(productId);
|
|
@@ -111,10 +149,10 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
111
149
|
}
|
|
112
150
|
return matches;
|
|
113
151
|
}, [
|
|
114
|
-
connected,
|
|
115
152
|
options.productCacheMs,
|
|
116
153
|
options.productRetryCount,
|
|
117
154
|
options.productRetryDelayMs,
|
|
155
|
+
waitForConnection,
|
|
118
156
|
]);
|
|
119
157
|
const reloadProducts = useCallback(async (productIds = options.productIds ?? []) => {
|
|
120
158
|
const normalizedIds = normalizeIds(productIds);
|
|
@@ -141,9 +179,7 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
141
179
|
return nativeProducts.map(normalizeStoreProduct);
|
|
142
180
|
},
|
|
143
181
|
async purchaseProduct({ storeProductId }) {
|
|
144
|
-
|
|
145
|
-
throw new Error("Native store is not connected.");
|
|
146
|
-
}
|
|
182
|
+
await waitForConnection();
|
|
147
183
|
if (pendingPurchaseRef.current) {
|
|
148
184
|
throw new Error("Another purchase is already in progress.");
|
|
149
185
|
}
|
|
@@ -212,9 +248,7 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
212
248
|
return normalizeStorePurchase(purchase);
|
|
213
249
|
},
|
|
214
250
|
async restorePurchases({ products: configuredProducts }) {
|
|
215
|
-
|
|
216
|
-
throw new Error("Native store is not connected.");
|
|
217
|
-
}
|
|
251
|
+
await waitForConnection();
|
|
218
252
|
const subscriptionIds = normalizeIds([
|
|
219
253
|
...configuredProducts.map((product) => product.storeProductId),
|
|
220
254
|
...knownProductIdsRef.current,
|
|
@@ -285,7 +319,6 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
285
319
|
},
|
|
286
320
|
};
|
|
287
321
|
}, [
|
|
288
|
-
connected,
|
|
289
322
|
finishTransaction,
|
|
290
323
|
loadProducts,
|
|
291
324
|
options.purchaseTimeoutMs,
|
|
@@ -293,6 +326,7 @@ export function useExpoIapBillingAdapter(options = {}) {
|
|
|
293
326
|
recoverPurchase,
|
|
294
327
|
requestPurchase,
|
|
295
328
|
settlePendingPurchase,
|
|
329
|
+
waitForConnection,
|
|
296
330
|
]);
|
|
297
331
|
return {
|
|
298
332
|
billingAdapter,
|