react-native-iap 14.2.2 → 14.2.3
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/NitroIap.podspec +4 -1
- package/README.md +10 -0
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +5 -1
- package/app.plugin.js +1 -1
- package/lib/module/helpers/subscription.js.map +1 -1
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/react-test-renderer.d.js +2 -0
- package/lib/module/types/react-test-renderer.d.js.map +1 -0
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/errorMapping.js.map +1 -1
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
- package/lib/typescript/src/helpers/subscription.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/utils/errorMapping.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/package.json +1 -9
- package/plugin/src/withIAP.ts +59 -59
- package/src/helpers/subscription.ts +21 -21
- package/src/hooks/useIAP.ts +193 -193
- package/src/index.ts +346 -344
- package/src/specs/RnIap.nitro.ts +160 -157
- package/src/types/react-test-renderer.d.ts +7 -0
- package/src/types.ts +294 -294
- package/src/utils/error.ts +19 -19
- package/src/utils/errorMapping.ts +13 -13
- package/src/utils/type-bridge.ts +94 -93
package/src/hooks/useIAP.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// External dependencies
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {useCallback, useEffect, useState, useRef} from 'react';
|
|
3
|
+
import {Platform} from 'react-native';
|
|
4
4
|
|
|
5
5
|
// Internal modules
|
|
6
6
|
import {
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
getActiveSubscriptions,
|
|
17
17
|
hasActiveSubscriptions,
|
|
18
18
|
restorePurchases as restorePurchasesTopLevel,
|
|
19
|
-
} from '../'
|
|
20
|
-
import {
|
|
19
|
+
} from '../';
|
|
20
|
+
import {getPromotedProductIOS, requestPurchaseOnPromotedProductIOS} from '../';
|
|
21
21
|
|
|
22
22
|
// Types
|
|
23
23
|
import type {
|
|
@@ -29,76 +29,76 @@ import type {
|
|
|
29
29
|
RequestPurchaseProps,
|
|
30
30
|
RequestSubscriptionProps,
|
|
31
31
|
ActiveSubscription,
|
|
32
|
-
} from '../types'
|
|
32
|
+
} from '../types';
|
|
33
33
|
|
|
34
34
|
// Types for event subscriptions
|
|
35
35
|
interface EventSubscription {
|
|
36
|
-
remove(): void
|
|
36
|
+
remove(): void;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
type UseIap = {
|
|
40
|
-
connected: boolean
|
|
41
|
-
products: Product[]
|
|
42
|
-
promotedProductsIOS: Purchase[]
|
|
43
|
-
promotedProductIdIOS?: string
|
|
44
|
-
subscriptions: SubscriptionProduct[]
|
|
45
|
-
availablePurchases: Purchase[]
|
|
46
|
-
currentPurchase?: Purchase
|
|
47
|
-
currentPurchaseError?: PurchaseError
|
|
48
|
-
promotedProductIOS?: Product
|
|
49
|
-
activeSubscriptions: ActiveSubscription[]
|
|
50
|
-
clearCurrentPurchase: () => void
|
|
51
|
-
clearCurrentPurchaseError: () => void
|
|
40
|
+
connected: boolean;
|
|
41
|
+
products: Product[];
|
|
42
|
+
promotedProductsIOS: Purchase[];
|
|
43
|
+
promotedProductIdIOS?: string;
|
|
44
|
+
subscriptions: SubscriptionProduct[];
|
|
45
|
+
availablePurchases: Purchase[];
|
|
46
|
+
currentPurchase?: Purchase;
|
|
47
|
+
currentPurchaseError?: PurchaseError;
|
|
48
|
+
promotedProductIOS?: Product;
|
|
49
|
+
activeSubscriptions: ActiveSubscription[];
|
|
50
|
+
clearCurrentPurchase: () => void;
|
|
51
|
+
clearCurrentPurchaseError: () => void;
|
|
52
52
|
finishTransaction: ({
|
|
53
53
|
purchase,
|
|
54
54
|
isConsumable,
|
|
55
55
|
}: {
|
|
56
|
-
purchase: Purchase
|
|
57
|
-
isConsumable?: boolean
|
|
58
|
-
}) => Promise<PurchaseResult | boolean
|
|
59
|
-
getAvailablePurchases: (skus?: string[]) => Promise<void
|
|
56
|
+
purchase: Purchase;
|
|
57
|
+
isConsumable?: boolean;
|
|
58
|
+
}) => Promise<PurchaseResult | boolean>;
|
|
59
|
+
getAvailablePurchases: (skus?: string[]) => Promise<void>;
|
|
60
60
|
fetchProducts: (params: {
|
|
61
|
-
skus: string[]
|
|
62
|
-
type?: 'inapp' | 'subs'
|
|
63
|
-
}) => Promise<void
|
|
61
|
+
skus: string[];
|
|
62
|
+
type?: 'inapp' | 'subs';
|
|
63
|
+
}) => Promise<void>;
|
|
64
64
|
/**
|
|
65
65
|
* @deprecated Use fetchProducts({ skus, type: 'inapp' }) instead. This method will be removed in version 3.0.0.
|
|
66
66
|
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
67
67
|
*/
|
|
68
|
-
getProducts: (skus: string[]) => Promise<void
|
|
68
|
+
getProducts: (skus: string[]) => Promise<void>;
|
|
69
69
|
/**
|
|
70
70
|
* @deprecated Use fetchProducts({ skus, type: 'subs' }) instead. This method will be removed in version 3.0.0.
|
|
71
71
|
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
72
72
|
*/
|
|
73
|
-
getSubscriptions: (skus: string[]) => Promise<void
|
|
73
|
+
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
74
74
|
requestPurchase: (params: {
|
|
75
|
-
request: RequestPurchaseProps | RequestSubscriptionProps
|
|
76
|
-
type?: 'inapp' | 'subs'
|
|
77
|
-
}) => Promise<any
|
|
75
|
+
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
76
|
+
type?: 'inapp' | 'subs';
|
|
77
|
+
}) => Promise<any>;
|
|
78
78
|
validateReceipt: (
|
|
79
79
|
sku: string,
|
|
80
80
|
androidOptions?: {
|
|
81
|
-
packageName: string
|
|
82
|
-
productToken: string
|
|
83
|
-
accessToken: string
|
|
84
|
-
isSub?: boolean
|
|
85
|
-
}
|
|
86
|
-
) => Promise<any
|
|
87
|
-
restorePurchases: () => Promise<void
|
|
88
|
-
getPromotedProductIOS: () => Promise<Product | null
|
|
89
|
-
requestPurchaseOnPromotedProductIOS: () => Promise<void
|
|
81
|
+
packageName: string;
|
|
82
|
+
productToken: string;
|
|
83
|
+
accessToken: string;
|
|
84
|
+
isSub?: boolean;
|
|
85
|
+
},
|
|
86
|
+
) => Promise<any>;
|
|
87
|
+
restorePurchases: () => Promise<void>;
|
|
88
|
+
getPromotedProductIOS: () => Promise<Product | null>;
|
|
89
|
+
requestPurchaseOnPromotedProductIOS: () => Promise<void>;
|
|
90
90
|
getActiveSubscriptions: (
|
|
91
|
-
subscriptionIds?: string[]
|
|
92
|
-
) => Promise<ActiveSubscription[]
|
|
93
|
-
hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean
|
|
94
|
-
}
|
|
91
|
+
subscriptionIds?: string[],
|
|
92
|
+
) => Promise<ActiveSubscription[]>;
|
|
93
|
+
hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;
|
|
94
|
+
};
|
|
95
95
|
|
|
96
96
|
export interface UseIapOptions {
|
|
97
|
-
onPurchaseSuccess?: (purchase: Purchase) => void
|
|
98
|
-
onPurchaseError?: (error: PurchaseError) => void
|
|
99
|
-
onSyncError?: (error: Error) => void
|
|
100
|
-
shouldAutoSyncPurchases?: boolean // New option to control auto-syncing
|
|
101
|
-
onPromotedProductIOS?: (product: Product) => void
|
|
97
|
+
onPurchaseSuccess?: (purchase: Purchase) => void;
|
|
98
|
+
onPurchaseError?: (error: PurchaseError) => void;
|
|
99
|
+
onSyncError?: (error: Error) => void;
|
|
100
|
+
shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing
|
|
101
|
+
onPromotedProductIOS?: (product: Product) => void;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -106,145 +106,145 @@ export interface UseIapOptions {
|
|
|
106
106
|
* See documentation at https://react-native-iap.hyo.dev/docs/hooks/useIAP
|
|
107
107
|
*/
|
|
108
108
|
export function useIAP(options?: UseIapOptions): UseIap {
|
|
109
|
-
const [connected, setConnected] = useState<boolean>(false)
|
|
110
|
-
const [products, setProducts] = useState<Product[]>([])
|
|
111
|
-
const [promotedProductsIOS] = useState<Purchase[]>([])
|
|
112
|
-
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([])
|
|
113
|
-
const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([])
|
|
114
|
-
const [currentPurchase, setCurrentPurchase] = useState<Purchase>()
|
|
115
|
-
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>()
|
|
109
|
+
const [connected, setConnected] = useState<boolean>(false);
|
|
110
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
111
|
+
const [promotedProductsIOS] = useState<Purchase[]>([]);
|
|
112
|
+
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
|
|
113
|
+
const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
|
|
114
|
+
const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
|
|
115
|
+
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
|
|
116
116
|
const [currentPurchaseError, setCurrentPurchaseError] =
|
|
117
|
-
useState<PurchaseError>()
|
|
118
|
-
const [promotedProductIdIOS] = useState<string>()
|
|
117
|
+
useState<PurchaseError>();
|
|
118
|
+
const [promotedProductIdIOS] = useState<string>();
|
|
119
119
|
const [activeSubscriptions, setActiveSubscriptions] = useState<
|
|
120
120
|
ActiveSubscription[]
|
|
121
|
-
>([])
|
|
121
|
+
>([]);
|
|
122
122
|
|
|
123
|
-
const optionsRef = useRef<UseIapOptions | undefined>(options)
|
|
124
|
-
const connectedRef = useRef<boolean>(false)
|
|
123
|
+
const optionsRef = useRef<UseIapOptions | undefined>(options);
|
|
124
|
+
const connectedRef = useRef<boolean>(false);
|
|
125
125
|
|
|
126
126
|
// Helper function to merge arrays with duplicate checking
|
|
127
127
|
const mergeWithDuplicateCheck = useCallback(
|
|
128
128
|
<T>(
|
|
129
129
|
existingItems: T[],
|
|
130
130
|
newItems: T[],
|
|
131
|
-
getKey: (item: T) => string
|
|
131
|
+
getKey: (item: T) => string,
|
|
132
132
|
): T[] => {
|
|
133
|
-
const merged = [...existingItems]
|
|
133
|
+
const merged = [...existingItems];
|
|
134
134
|
newItems.forEach((newItem) => {
|
|
135
135
|
const isDuplicate = merged.some(
|
|
136
|
-
(existingItem) => getKey(existingItem) === getKey(newItem)
|
|
137
|
-
)
|
|
136
|
+
(existingItem) => getKey(existingItem) === getKey(newItem),
|
|
137
|
+
);
|
|
138
138
|
if (!isDuplicate) {
|
|
139
|
-
merged.push(newItem)
|
|
139
|
+
merged.push(newItem);
|
|
140
140
|
}
|
|
141
|
-
})
|
|
142
|
-
return merged
|
|
141
|
+
});
|
|
142
|
+
return merged;
|
|
143
143
|
},
|
|
144
|
-
[]
|
|
145
|
-
)
|
|
144
|
+
[],
|
|
145
|
+
);
|
|
146
146
|
|
|
147
147
|
useEffect(() => {
|
|
148
|
-
optionsRef.current = options
|
|
149
|
-
}, [options])
|
|
148
|
+
optionsRef.current = options;
|
|
149
|
+
}, [options]);
|
|
150
150
|
|
|
151
151
|
useEffect(() => {
|
|
152
|
-
connectedRef.current = connected
|
|
153
|
-
}, [connected])
|
|
152
|
+
connectedRef.current = connected;
|
|
153
|
+
}, [connected]);
|
|
154
154
|
|
|
155
155
|
const subscriptionsRef = useRef<{
|
|
156
|
-
purchaseUpdate?: EventSubscription
|
|
157
|
-
purchaseError?: EventSubscription
|
|
158
|
-
promotedProductsIOS?: EventSubscription
|
|
159
|
-
promotedProductIOS?: EventSubscription
|
|
160
|
-
}>({})
|
|
156
|
+
purchaseUpdate?: EventSubscription;
|
|
157
|
+
purchaseError?: EventSubscription;
|
|
158
|
+
promotedProductsIOS?: EventSubscription;
|
|
159
|
+
promotedProductIOS?: EventSubscription;
|
|
160
|
+
}>({});
|
|
161
161
|
|
|
162
|
-
const subscriptionsRefState = useRef<SubscriptionProduct[]>([])
|
|
162
|
+
const subscriptionsRefState = useRef<SubscriptionProduct[]>([]);
|
|
163
163
|
|
|
164
164
|
useEffect(() => {
|
|
165
|
-
subscriptionsRefState.current = subscriptions
|
|
166
|
-
}, [subscriptions])
|
|
165
|
+
subscriptionsRefState.current = subscriptions;
|
|
166
|
+
}, [subscriptions]);
|
|
167
167
|
|
|
168
168
|
const clearCurrentPurchase = useCallback(() => {
|
|
169
|
-
setCurrentPurchase(undefined)
|
|
170
|
-
}, [])
|
|
169
|
+
setCurrentPurchase(undefined);
|
|
170
|
+
}, []);
|
|
171
171
|
|
|
172
172
|
const clearCurrentPurchaseError = useCallback(() => {
|
|
173
|
-
setCurrentPurchaseError(undefined)
|
|
174
|
-
}, [])
|
|
173
|
+
setCurrentPurchaseError(undefined);
|
|
174
|
+
}, []);
|
|
175
175
|
|
|
176
176
|
const getProductsInternal = useCallback(
|
|
177
177
|
async (skus: string[]): Promise<void> => {
|
|
178
178
|
try {
|
|
179
|
-
const result = await fetchProducts({
|
|
179
|
+
const result = await fetchProducts({skus, type: 'inapp'});
|
|
180
180
|
setProducts((prevProducts: Product[]) =>
|
|
181
181
|
mergeWithDuplicateCheck(
|
|
182
182
|
prevProducts,
|
|
183
183
|
result as Product[],
|
|
184
|
-
(product: Product) => product.id
|
|
185
|
-
)
|
|
186
|
-
)
|
|
184
|
+
(product: Product) => product.id,
|
|
185
|
+
),
|
|
186
|
+
);
|
|
187
187
|
} catch (error) {
|
|
188
|
-
console.error('Error fetching products:', error)
|
|
188
|
+
console.error('Error fetching products:', error);
|
|
189
189
|
}
|
|
190
190
|
},
|
|
191
|
-
[mergeWithDuplicateCheck]
|
|
192
|
-
)
|
|
191
|
+
[mergeWithDuplicateCheck],
|
|
192
|
+
);
|
|
193
193
|
|
|
194
194
|
const getSubscriptionsInternal = useCallback(
|
|
195
195
|
async (skus: string[]): Promise<void> => {
|
|
196
196
|
try {
|
|
197
|
-
const result = await fetchProducts({
|
|
197
|
+
const result = await fetchProducts({skus, type: 'subs'});
|
|
198
198
|
setSubscriptions((prevSubscriptions: SubscriptionProduct[]) =>
|
|
199
199
|
mergeWithDuplicateCheck(
|
|
200
200
|
prevSubscriptions,
|
|
201
201
|
result as SubscriptionProduct[],
|
|
202
|
-
(subscription: SubscriptionProduct) => subscription.id
|
|
203
|
-
)
|
|
204
|
-
)
|
|
202
|
+
(subscription: SubscriptionProduct) => subscription.id,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
205
|
} catch (error) {
|
|
206
|
-
console.error('Error fetching subscriptions:', error)
|
|
206
|
+
console.error('Error fetching subscriptions:', error);
|
|
207
207
|
}
|
|
208
208
|
},
|
|
209
|
-
[mergeWithDuplicateCheck]
|
|
210
|
-
)
|
|
209
|
+
[mergeWithDuplicateCheck],
|
|
210
|
+
);
|
|
211
211
|
|
|
212
212
|
const fetchProductsInternal = useCallback(
|
|
213
213
|
async (params: {
|
|
214
|
-
skus: string[]
|
|
215
|
-
type?: 'inapp' | 'subs'
|
|
214
|
+
skus: string[];
|
|
215
|
+
type?: 'inapp' | 'subs';
|
|
216
216
|
}): Promise<void> => {
|
|
217
217
|
if (!connectedRef.current) {
|
|
218
218
|
console.warn(
|
|
219
|
-
'[useIAP] fetchProducts called before connection; skipping'
|
|
220
|
-
)
|
|
221
|
-
return
|
|
219
|
+
'[useIAP] fetchProducts called before connection; skipping',
|
|
220
|
+
);
|
|
221
|
+
return;
|
|
222
222
|
}
|
|
223
223
|
try {
|
|
224
|
-
const result = await fetchProducts(params)
|
|
224
|
+
const result = await fetchProducts(params);
|
|
225
225
|
if (params.type === 'subs') {
|
|
226
226
|
setSubscriptions((prevSubscriptions: SubscriptionProduct[]) =>
|
|
227
227
|
mergeWithDuplicateCheck(
|
|
228
228
|
prevSubscriptions,
|
|
229
229
|
result as SubscriptionProduct[],
|
|
230
|
-
(subscription: SubscriptionProduct) => subscription.id
|
|
231
|
-
)
|
|
232
|
-
)
|
|
230
|
+
(subscription: SubscriptionProduct) => subscription.id,
|
|
231
|
+
),
|
|
232
|
+
);
|
|
233
233
|
} else {
|
|
234
234
|
setProducts((prevProducts: Product[]) =>
|
|
235
235
|
mergeWithDuplicateCheck(
|
|
236
236
|
prevProducts,
|
|
237
237
|
result as Product[],
|
|
238
|
-
(product: Product) => product.id
|
|
239
|
-
)
|
|
240
|
-
)
|
|
238
|
+
(product: Product) => product.id,
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
241
|
}
|
|
242
242
|
} catch (error) {
|
|
243
|
-
console.error('Error fetching products:', error)
|
|
243
|
+
console.error('Error fetching products:', error);
|
|
244
244
|
}
|
|
245
245
|
},
|
|
246
|
-
[mergeWithDuplicateCheck]
|
|
247
|
-
)
|
|
246
|
+
[mergeWithDuplicateCheck],
|
|
247
|
+
);
|
|
248
248
|
|
|
249
249
|
const getAvailablePurchasesInternal = useCallback(
|
|
250
250
|
async (_skus?: string[]): Promise<void> => {
|
|
@@ -252,64 +252,64 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
252
252
|
const result = await getAvailablePurchases({
|
|
253
253
|
alsoPublishToEventListenerIOS: false,
|
|
254
254
|
onlyIncludeActiveItemsIOS: true,
|
|
255
|
-
})
|
|
256
|
-
setAvailablePurchases(result)
|
|
255
|
+
});
|
|
256
|
+
setAvailablePurchases(result);
|
|
257
257
|
} catch (error) {
|
|
258
|
-
console.error('Error fetching available purchases:', error)
|
|
258
|
+
console.error('Error fetching available purchases:', error);
|
|
259
259
|
}
|
|
260
260
|
},
|
|
261
|
-
[]
|
|
262
|
-
)
|
|
261
|
+
[],
|
|
262
|
+
);
|
|
263
263
|
|
|
264
264
|
const getActiveSubscriptionsInternal = useCallback(
|
|
265
265
|
async (subscriptionIds?: string[]): Promise<ActiveSubscription[]> => {
|
|
266
266
|
try {
|
|
267
|
-
const result = await getActiveSubscriptions(subscriptionIds)
|
|
268
|
-
setActiveSubscriptions(result)
|
|
269
|
-
return result
|
|
267
|
+
const result = await getActiveSubscriptions(subscriptionIds);
|
|
268
|
+
setActiveSubscriptions(result);
|
|
269
|
+
return result;
|
|
270
270
|
} catch (error) {
|
|
271
|
-
console.error('Error getting active subscriptions:', error)
|
|
271
|
+
console.error('Error getting active subscriptions:', error);
|
|
272
272
|
// Don't clear existing activeSubscriptions on error - preserve current state
|
|
273
273
|
// This prevents the UI from showing empty state when there are temporary network issues
|
|
274
|
-
return []
|
|
274
|
+
return [];
|
|
275
275
|
}
|
|
276
276
|
},
|
|
277
|
-
[]
|
|
278
|
-
)
|
|
277
|
+
[],
|
|
278
|
+
);
|
|
279
279
|
|
|
280
280
|
const hasActiveSubscriptionsInternal = useCallback(
|
|
281
281
|
async (subscriptionIds?: string[]): Promise<boolean> => {
|
|
282
282
|
try {
|
|
283
|
-
return await hasActiveSubscriptions(subscriptionIds)
|
|
283
|
+
return await hasActiveSubscriptions(subscriptionIds);
|
|
284
284
|
} catch (error) {
|
|
285
|
-
console.error('Error checking active subscriptions:', error)
|
|
286
|
-
return false
|
|
285
|
+
console.error('Error checking active subscriptions:', error);
|
|
286
|
+
return false;
|
|
287
287
|
}
|
|
288
288
|
},
|
|
289
|
-
[]
|
|
290
|
-
)
|
|
289
|
+
[],
|
|
290
|
+
);
|
|
291
291
|
|
|
292
292
|
const finishTransaction = useCallback(
|
|
293
293
|
async ({
|
|
294
294
|
purchase,
|
|
295
295
|
isConsumable,
|
|
296
296
|
}: {
|
|
297
|
-
purchase: Purchase
|
|
298
|
-
isConsumable?: boolean
|
|
297
|
+
purchase: Purchase;
|
|
298
|
+
isConsumable?: boolean;
|
|
299
299
|
}): Promise<PurchaseResult | boolean> => {
|
|
300
300
|
try {
|
|
301
301
|
return await finishTransactionInternal({
|
|
302
302
|
purchase,
|
|
303
303
|
isConsumable,
|
|
304
|
-
})
|
|
304
|
+
});
|
|
305
305
|
} catch (err) {
|
|
306
|
-
throw err
|
|
306
|
+
throw err;
|
|
307
307
|
} finally {
|
|
308
308
|
if (purchase.id === currentPurchase?.id) {
|
|
309
|
-
clearCurrentPurchase()
|
|
309
|
+
clearCurrentPurchase();
|
|
310
310
|
}
|
|
311
311
|
if (purchase.id === currentPurchaseError?.productId) {
|
|
312
|
-
clearCurrentPurchaseError()
|
|
312
|
+
clearCurrentPurchaseError();
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
},
|
|
@@ -318,22 +318,22 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
318
318
|
currentPurchaseError?.productId,
|
|
319
319
|
clearCurrentPurchase,
|
|
320
320
|
clearCurrentPurchaseError,
|
|
321
|
-
]
|
|
322
|
-
)
|
|
321
|
+
],
|
|
322
|
+
);
|
|
323
323
|
|
|
324
324
|
const requestPurchaseWithReset = useCallback(
|
|
325
|
-
async (requestObj: {
|
|
326
|
-
clearCurrentPurchase()
|
|
327
|
-
clearCurrentPurchaseError()
|
|
325
|
+
async (requestObj: {request: any; type?: 'inapp' | 'subs'}) => {
|
|
326
|
+
clearCurrentPurchase();
|
|
327
|
+
clearCurrentPurchaseError();
|
|
328
328
|
|
|
329
329
|
try {
|
|
330
|
-
return await requestPurchaseInternal(requestObj)
|
|
330
|
+
return await requestPurchaseInternal(requestObj);
|
|
331
331
|
} catch (error) {
|
|
332
|
-
throw error
|
|
332
|
+
throw error;
|
|
333
333
|
}
|
|
334
334
|
},
|
|
335
|
-
[clearCurrentPurchase, clearCurrentPurchaseError]
|
|
336
|
-
)
|
|
335
|
+
[clearCurrentPurchase, clearCurrentPurchaseError],
|
|
336
|
+
);
|
|
337
337
|
|
|
338
338
|
// No local restorePurchases; use the top-level helper via returned API
|
|
339
339
|
|
|
@@ -341,35 +341,35 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
341
341
|
async (
|
|
342
342
|
sku: string,
|
|
343
343
|
androidOptions?: {
|
|
344
|
-
packageName: string
|
|
345
|
-
productToken: string
|
|
346
|
-
accessToken: string
|
|
347
|
-
isSub?: boolean
|
|
348
|
-
}
|
|
344
|
+
packageName: string;
|
|
345
|
+
productToken: string;
|
|
346
|
+
accessToken: string;
|
|
347
|
+
isSub?: boolean;
|
|
348
|
+
},
|
|
349
349
|
) => {
|
|
350
|
-
return validateReceiptInternal(sku, androidOptions)
|
|
350
|
+
return validateReceiptInternal(sku, androidOptions);
|
|
351
351
|
},
|
|
352
|
-
[]
|
|
353
|
-
)
|
|
352
|
+
[],
|
|
353
|
+
);
|
|
354
354
|
|
|
355
355
|
const initIapWithSubscriptions = useCallback(async (): Promise<void> => {
|
|
356
356
|
// Register listeners BEFORE initConnection to avoid race condition
|
|
357
357
|
subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(
|
|
358
358
|
async (purchase: Purchase) => {
|
|
359
|
-
setCurrentPurchaseError(undefined)
|
|
360
|
-
setCurrentPurchase(purchase)
|
|
359
|
+
setCurrentPurchaseError(undefined);
|
|
360
|
+
setCurrentPurchase(purchase);
|
|
361
361
|
// Always refresh subscription state after a purchase event
|
|
362
362
|
try {
|
|
363
|
-
await getActiveSubscriptionsInternal()
|
|
364
|
-
await getAvailablePurchasesInternal()
|
|
363
|
+
await getActiveSubscriptionsInternal();
|
|
364
|
+
await getAvailablePurchasesInternal();
|
|
365
365
|
} catch (e) {
|
|
366
|
-
console.warn('[useIAP] post-purchase refresh failed:', e)
|
|
366
|
+
console.warn('[useIAP] post-purchase refresh failed:', e);
|
|
367
367
|
}
|
|
368
368
|
if (optionsRef.current?.onPurchaseSuccess) {
|
|
369
|
-
optionsRef.current.onPurchaseSuccess(purchase)
|
|
369
|
+
optionsRef.current.onPurchaseSuccess(purchase);
|
|
370
370
|
}
|
|
371
|
-
}
|
|
372
|
-
)
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
373
|
|
|
374
374
|
subscriptionsRef.current.purchaseError = purchaseErrorListener(
|
|
375
375
|
(error: PurchaseError) => {
|
|
@@ -379,52 +379,52 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
379
379
|
(error as any).code === 'E_INIT_CONNECTION' &&
|
|
380
380
|
!connectedRef.current
|
|
381
381
|
) {
|
|
382
|
-
return
|
|
382
|
+
return;
|
|
383
383
|
}
|
|
384
|
-
setCurrentPurchase(undefined)
|
|
385
|
-
setCurrentPurchaseError(error)
|
|
384
|
+
setCurrentPurchase(undefined);
|
|
385
|
+
setCurrentPurchaseError(error);
|
|
386
386
|
if (optionsRef.current?.onPurchaseError) {
|
|
387
|
-
optionsRef.current.onPurchaseError(error)
|
|
387
|
+
optionsRef.current.onPurchaseError(error);
|
|
388
388
|
}
|
|
389
|
-
}
|
|
390
|
-
)
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
391
|
|
|
392
392
|
if (Platform.OS === 'ios') {
|
|
393
393
|
subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(
|
|
394
394
|
(product: Product) => {
|
|
395
|
-
setPromotedProductIOS(product)
|
|
395
|
+
setPromotedProductIOS(product);
|
|
396
396
|
if (optionsRef.current?.onPromotedProductIOS) {
|
|
397
|
-
optionsRef.current.onPromotedProductIOS(product)
|
|
397
|
+
optionsRef.current.onPromotedProductIOS(product);
|
|
398
398
|
}
|
|
399
|
-
}
|
|
400
|
-
)
|
|
399
|
+
},
|
|
400
|
+
);
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
-
const result = await initConnection()
|
|
404
|
-
setConnected(result)
|
|
403
|
+
const result = await initConnection();
|
|
404
|
+
setConnected(result);
|
|
405
405
|
if (!result) {
|
|
406
406
|
// Clean up some listeners but leave purchaseError for potential retries
|
|
407
|
-
subscriptionsRef.current.purchaseUpdate?.remove()
|
|
408
|
-
subscriptionsRef.current.promotedProductsIOS?.remove()
|
|
409
|
-
subscriptionsRef.current.purchaseUpdate = undefined
|
|
410
|
-
subscriptionsRef.current.promotedProductsIOS = undefined
|
|
411
|
-
return
|
|
407
|
+
subscriptionsRef.current.purchaseUpdate?.remove();
|
|
408
|
+
subscriptionsRef.current.promotedProductsIOS?.remove();
|
|
409
|
+
subscriptionsRef.current.purchaseUpdate = undefined;
|
|
410
|
+
subscriptionsRef.current.promotedProductsIOS = undefined;
|
|
411
|
+
return;
|
|
412
412
|
}
|
|
413
|
-
}, [getActiveSubscriptionsInternal, getAvailablePurchasesInternal])
|
|
413
|
+
}, [getActiveSubscriptionsInternal, getAvailablePurchasesInternal]);
|
|
414
414
|
|
|
415
415
|
useEffect(() => {
|
|
416
|
-
initIapWithSubscriptions()
|
|
417
|
-
const currentSubscriptions = subscriptionsRef.current
|
|
416
|
+
initIapWithSubscriptions();
|
|
417
|
+
const currentSubscriptions = subscriptionsRef.current;
|
|
418
418
|
|
|
419
419
|
return () => {
|
|
420
|
-
currentSubscriptions.purchaseUpdate?.remove()
|
|
421
|
-
currentSubscriptions.purchaseError?.remove()
|
|
422
|
-
currentSubscriptions.promotedProductsIOS?.remove()
|
|
423
|
-
currentSubscriptions.promotedProductIOS?.remove()
|
|
420
|
+
currentSubscriptions.purchaseUpdate?.remove();
|
|
421
|
+
currentSubscriptions.purchaseError?.remove();
|
|
422
|
+
currentSubscriptions.promotedProductsIOS?.remove();
|
|
423
|
+
currentSubscriptions.promotedProductIOS?.remove();
|
|
424
424
|
// Keep connection alive across screens to avoid race conditions
|
|
425
|
-
setConnected(false)
|
|
426
|
-
}
|
|
427
|
-
}, [initIapWithSubscriptions])
|
|
425
|
+
setConnected(false);
|
|
426
|
+
};
|
|
427
|
+
}, [initIapWithSubscriptions]);
|
|
428
428
|
|
|
429
429
|
return {
|
|
430
430
|
connected,
|
|
@@ -449,10 +449,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
449
449
|
const purchases = await restorePurchasesTopLevel({
|
|
450
450
|
alsoPublishToEventListenerIOS: false,
|
|
451
451
|
onlyIncludeActiveItemsIOS: true,
|
|
452
|
-
})
|
|
453
|
-
setAvailablePurchases(purchases)
|
|
452
|
+
});
|
|
453
|
+
setAvailablePurchases(purchases);
|
|
454
454
|
} catch (e) {
|
|
455
|
-
console.warn('Failed to restore purchases:', e)
|
|
455
|
+
console.warn('Failed to restore purchases:', e);
|
|
456
456
|
}
|
|
457
457
|
},
|
|
458
458
|
getProducts: getProductsInternal,
|
|
@@ -461,5 +461,5 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
461
461
|
requestPurchaseOnPromotedProductIOS,
|
|
462
462
|
getActiveSubscriptions: getActiveSubscriptionsInternal,
|
|
463
463
|
hasActiveSubscriptions: hasActiveSubscriptionsInternal,
|
|
464
|
-
}
|
|
464
|
+
};
|
|
465
465
|
}
|