expo-iap 2.8.6 → 2.9.0-rc.1
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/CLAUDE.md +7 -0
- package/CONTRIBUTING.md +3 -4
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +120 -7
- package/android/src/main/java/expo/modules/iap/Types.kt +1 -1
- package/build/helpers/subscription.d.ts.map +1 -1
- package/build/helpers/subscription.js +3 -6
- package/build/helpers/subscription.js.map +1 -1
- package/build/index.d.ts +31 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +53 -25
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapAndroid.types.d.ts +2 -2
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
- package/build/types/ExpoIapAndroid.types.js.map +1 -1
- package/build/types/ExpoIapIOS.types.d.ts +3 -3
- package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
- package/build/types/ExpoIapIOS.types.js.map +1 -1
- package/build/useIAP.d.ts +12 -4
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +10 -5
- package/build/useIAP.js.map +1 -1
- package/ios/ExpoIap.podspec +1 -0
- package/ios/ExpoIapModule.swift +354 -1159
- package/jest.config.js +14 -17
- package/package.json +5 -3
- package/plugin/build/withIAP.d.ts +7 -1
- package/plugin/build/withIAP.js +16 -2
- package/plugin/build/withLocalOpenIAP.d.ts +9 -0
- package/plugin/build/withLocalOpenIAP.js +85 -0
- package/plugin/src/withIAP.ts +21 -2
- package/plugin/src/withLocalOpenIAP.ts +66 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/helpers/subscription.ts +21 -28
- package/src/index.ts +70 -33
- package/src/modules/android.ts +7 -7
- package/src/modules/ios.ts +11 -5
- package/src/types/ExpoIapAndroid.types.ts +3 -4
- package/src/types/ExpoIapIOS.types.ts +4 -3
- package/src/useIAP.ts +40 -12
package/src/index.ts
CHANGED
|
@@ -45,7 +45,7 @@ export {
|
|
|
45
45
|
// Get the native constant value
|
|
46
46
|
export const PI = ExpoIapModule.PI;
|
|
47
47
|
|
|
48
|
-
export enum
|
|
48
|
+
export enum OpenIapEvent {
|
|
49
49
|
PurchaseUpdated = 'purchase-updated',
|
|
50
50
|
PurchaseError = 'purchase-error',
|
|
51
51
|
/** @deprecated Use PurchaseUpdated instead. This will be removed in a future version. */
|
|
@@ -73,7 +73,7 @@ export const purchaseUpdatedListener = (
|
|
|
73
73
|
listener: (event: Purchase) => void,
|
|
74
74
|
) => {
|
|
75
75
|
const emitterSubscription = emitter.addListener(
|
|
76
|
-
|
|
76
|
+
OpenIapEvent.PurchaseUpdated,
|
|
77
77
|
listener,
|
|
78
78
|
);
|
|
79
79
|
return emitterSubscription;
|
|
@@ -82,7 +82,7 @@ export const purchaseUpdatedListener = (
|
|
|
82
82
|
export const purchaseErrorListener = (
|
|
83
83
|
listener: (error: PurchaseError) => void,
|
|
84
84
|
) => {
|
|
85
|
-
return emitter.addListener(
|
|
85
|
+
return emitter.addListener(OpenIapEvent.PurchaseError, listener);
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
/**
|
|
@@ -114,7 +114,7 @@ export const promotedProductListenerIOS = (
|
|
|
114
114
|
);
|
|
115
115
|
return {remove: () => {}};
|
|
116
116
|
}
|
|
117
|
-
return emitter.addListener(
|
|
117
|
+
return emitter.addListener(OpenIapEvent.PromotedProductIOS, listener);
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
export function initConnection(): Promise<boolean> {
|
|
@@ -124,7 +124,7 @@ export function initConnection(): Promise<boolean> {
|
|
|
124
124
|
|
|
125
125
|
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
126
126
|
console.warn(
|
|
127
|
-
"`getProducts` is deprecated. Use `
|
|
127
|
+
"`getProducts` is deprecated. Use `fetchProducts({ skus, type: 'inapp' })` instead. This function will be removed in version 3.0.0.",
|
|
128
128
|
);
|
|
129
129
|
if (!skus?.length) {
|
|
130
130
|
return Promise.reject(new Error('"skus" is required'));
|
|
@@ -132,7 +132,7 @@ export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
|
132
132
|
|
|
133
133
|
return Platform.select({
|
|
134
134
|
ios: async () => {
|
|
135
|
-
const rawItems = await ExpoIapModule.
|
|
135
|
+
const rawItems = await ExpoIapModule.fetchProducts(skus);
|
|
136
136
|
return rawItems.filter((item: unknown) => {
|
|
137
137
|
if (!isProductIOS(item)) return false;
|
|
138
138
|
return (
|
|
@@ -145,7 +145,7 @@ export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
|
145
145
|
}) as Product[];
|
|
146
146
|
},
|
|
147
147
|
android: async () => {
|
|
148
|
-
const products = await ExpoIapModule.
|
|
148
|
+
const products = await ExpoIapModule.fetchProducts('inapp', skus);
|
|
149
149
|
return products.filter((product: unknown) =>
|
|
150
150
|
isProductAndroid<Product>(product),
|
|
151
151
|
);
|
|
@@ -158,7 +158,7 @@ export const getSubscriptions = async (
|
|
|
158
158
|
skus: string[],
|
|
159
159
|
): Promise<SubscriptionProduct[]> => {
|
|
160
160
|
console.warn(
|
|
161
|
-
"`getSubscriptions` is deprecated. Use `
|
|
161
|
+
"`getSubscriptions` is deprecated. Use `fetchProducts({ skus, type: 'subs' })` instead. This function will be removed in version 3.0.0.",
|
|
162
162
|
);
|
|
163
163
|
if (!skus?.length) {
|
|
164
164
|
return Promise.reject(new Error('"skus" is required'));
|
|
@@ -166,7 +166,7 @@ export const getSubscriptions = async (
|
|
|
166
166
|
|
|
167
167
|
return Platform.select({
|
|
168
168
|
ios: async () => {
|
|
169
|
-
const rawItems = await ExpoIapModule.
|
|
169
|
+
const rawItems = await ExpoIapModule.fetchProducts(skus);
|
|
170
170
|
return rawItems.filter((item: unknown) => {
|
|
171
171
|
if (!isProductIOS(item)) return false;
|
|
172
172
|
return (
|
|
@@ -179,7 +179,7 @@ export const getSubscriptions = async (
|
|
|
179
179
|
}) as SubscriptionProduct[];
|
|
180
180
|
},
|
|
181
181
|
android: async () => {
|
|
182
|
-
const rawItems = await ExpoIapModule.
|
|
182
|
+
const rawItems = await ExpoIapModule.fetchProducts('subs', skus);
|
|
183
183
|
return rawItems.filter((item: unknown) => {
|
|
184
184
|
if (!isProductAndroid(item)) return false;
|
|
185
185
|
return (
|
|
@@ -200,28 +200,28 @@ export async function endConnection(): Promise<boolean> {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
|
-
*
|
|
203
|
+
* Fetch products with unified API (v2.7.0+)
|
|
204
204
|
*
|
|
205
|
-
* @param params - Product
|
|
205
|
+
* @param params - Product fetch configuration
|
|
206
206
|
* @param params.skus - Array of product SKUs to fetch
|
|
207
207
|
* @param params.type - Type of products: 'inapp' for regular products (default) or 'subs' for subscriptions
|
|
208
208
|
*
|
|
209
209
|
* @example
|
|
210
210
|
* ```typescript
|
|
211
211
|
* // Regular products
|
|
212
|
-
* const products = await
|
|
212
|
+
* const products = await fetchProducts({
|
|
213
213
|
* skus: ['product1', 'product2'],
|
|
214
214
|
* type: 'inapp'
|
|
215
215
|
* });
|
|
216
216
|
*
|
|
217
217
|
* // Subscriptions
|
|
218
|
-
* const subscriptions = await
|
|
218
|
+
* const subscriptions = await fetchProducts({
|
|
219
219
|
* skus: ['sub1', 'sub2'],
|
|
220
220
|
* type: 'subs'
|
|
221
221
|
* });
|
|
222
222
|
* ```
|
|
223
223
|
*/
|
|
224
|
-
export const
|
|
224
|
+
export const fetchProducts = async ({
|
|
225
225
|
skus,
|
|
226
226
|
type = 'inapp',
|
|
227
227
|
}: {
|
|
@@ -233,7 +233,7 @@ export const requestProducts = async ({
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
if (Platform.OS === 'ios') {
|
|
236
|
-
const rawItems = await ExpoIapModule.
|
|
236
|
+
const rawItems = await ExpoIapModule.fetchProducts(skus);
|
|
237
237
|
const filteredItems = rawItems.filter((item: unknown) => {
|
|
238
238
|
if (!isProductIOS(item)) return false;
|
|
239
239
|
return (
|
|
@@ -251,7 +251,7 @@ export const requestProducts = async ({
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
if (Platform.OS === 'android') {
|
|
254
|
-
const items = await ExpoIapModule.
|
|
254
|
+
const items = await ExpoIapModule.fetchProducts(type, skus);
|
|
255
255
|
const filteredItems = items.filter((item: unknown) => {
|
|
256
256
|
if (!isProductAndroid(item)) return false;
|
|
257
257
|
return (
|
|
@@ -271,6 +271,41 @@ export const requestProducts = async ({
|
|
|
271
271
|
throw new Error('Unsupported platform');
|
|
272
272
|
};
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* @deprecated Use `fetchProducts` instead. This method will be removed in version 3.0.0.
|
|
276
|
+
*
|
|
277
|
+
* The 'request' prefix should only be used for event-based operations that trigger
|
|
278
|
+
* purchase flows. Since this function simply fetches product information, it has been
|
|
279
|
+
* renamed to `fetchProducts` to follow OpenIAP terminology guidelines.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // Old way (deprecated)
|
|
284
|
+
* const products = await requestProducts({
|
|
285
|
+
* skus: ['com.example.product1'],
|
|
286
|
+
* type: 'inapp'
|
|
287
|
+
* });
|
|
288
|
+
*
|
|
289
|
+
* // New way (recommended)
|
|
290
|
+
* const products = await fetchProducts({
|
|
291
|
+
* skus: ['com.example.product1'],
|
|
292
|
+
* type: 'inapp'
|
|
293
|
+
* });
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
export const requestProducts = async ({
|
|
297
|
+
skus,
|
|
298
|
+
type = 'inapp',
|
|
299
|
+
}: {
|
|
300
|
+
skus: string[];
|
|
301
|
+
type?: 'inapp' | 'subs';
|
|
302
|
+
}): Promise<Product[] | SubscriptionProduct[]> => {
|
|
303
|
+
console.warn(
|
|
304
|
+
"`requestProducts` is deprecated. Use `fetchProducts` instead. The 'request' prefix should only be used for event-based operations. This method will be removed in version 3.0.0."
|
|
305
|
+
);
|
|
306
|
+
return fetchProducts({ skus, type });
|
|
307
|
+
};
|
|
308
|
+
|
|
274
309
|
/**
|
|
275
310
|
* @deprecated Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.
|
|
276
311
|
*/
|
|
@@ -291,10 +326,8 @@ export const getPurchaseHistory = ({
|
|
|
291
326
|
'`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.',
|
|
292
327
|
);
|
|
293
328
|
return getPurchaseHistories({
|
|
294
|
-
alsoPublishToEventListenerIOS:
|
|
295
|
-
|
|
296
|
-
onlyIncludeActiveItemsIOS:
|
|
297
|
-
onlyIncludeActiveItemsIOS ?? onlyIncludeActiveItems,
|
|
329
|
+
alsoPublishToEventListenerIOS: alsoPublishToEventListenerIOS ?? alsoPublishToEventListener,
|
|
330
|
+
onlyIncludeActiveItemsIOS: onlyIncludeActiveItemsIOS ?? onlyIncludeActiveItems,
|
|
298
331
|
});
|
|
299
332
|
};
|
|
300
333
|
|
|
@@ -357,9 +390,8 @@ export const getAvailablePurchases = ({
|
|
|
357
390
|
),
|
|
358
391
|
android: async () => {
|
|
359
392
|
const products = await ExpoIapModule.getAvailableItemsByType('inapp');
|
|
360
|
-
const subscriptions =
|
|
361
|
-
'subs'
|
|
362
|
-
);
|
|
393
|
+
const subscriptions =
|
|
394
|
+
await ExpoIapModule.getAvailableItemsByType('subs');
|
|
363
395
|
return products.concat(subscriptions);
|
|
364
396
|
},
|
|
365
397
|
}) || (() => Promise.resolve([]))
|
|
@@ -433,7 +465,11 @@ const normalizeRequestProps = (
|
|
|
433
465
|
*/
|
|
434
466
|
export const requestPurchase = (
|
|
435
467
|
requestObj: PurchaseRequest,
|
|
436
|
-
): Promise<
|
|
468
|
+
): Promise<
|
|
469
|
+
| Purchase
|
|
470
|
+
| Purchase[]
|
|
471
|
+
| void
|
|
472
|
+
> => {
|
|
437
473
|
const {request, type = 'inapp'} = requestObj;
|
|
438
474
|
|
|
439
475
|
if (Platform.OS === 'ios') {
|
|
@@ -463,7 +499,9 @@ export const requestPurchase = (
|
|
|
463
499
|
offer,
|
|
464
500
|
);
|
|
465
501
|
|
|
466
|
-
return type === 'inapp'
|
|
502
|
+
return type === 'inapp'
|
|
503
|
+
? (purchase as Purchase)
|
|
504
|
+
: (purchase as Purchase);
|
|
467
505
|
})();
|
|
468
506
|
}
|
|
469
507
|
|
|
@@ -591,11 +629,10 @@ export const finishTransaction = ({
|
|
|
591
629
|
},
|
|
592
630
|
android: async () => {
|
|
593
631
|
const androidPurchase = purchase as PurchaseAndroid;
|
|
594
|
-
|
|
632
|
+
|
|
595
633
|
// Use purchaseToken if available, fallback to purchaseTokenAndroid for backward compatibility
|
|
596
|
-
const token =
|
|
597
|
-
|
|
598
|
-
|
|
634
|
+
const token = androidPurchase.purchaseToken || androidPurchase.purchaseTokenAndroid;
|
|
635
|
+
|
|
599
636
|
if (!token) {
|
|
600
637
|
return Promise.reject(
|
|
601
638
|
new PurchaseError(
|
|
@@ -605,8 +642,8 @@ export const finishTransaction = ({
|
|
|
605
642
|
undefined,
|
|
606
643
|
'E_DEVELOPER_ERROR' as ErrorCode,
|
|
607
644
|
androidPurchase.productId,
|
|
608
|
-
'android'
|
|
609
|
-
)
|
|
645
|
+
'android'
|
|
646
|
+
)
|
|
610
647
|
);
|
|
611
648
|
}
|
|
612
649
|
|
|
@@ -639,7 +676,7 @@ export const getStorefrontIOS = (): Promise<string> => {
|
|
|
639
676
|
console.warn('getStorefrontIOS: This method is only available on iOS');
|
|
640
677
|
return Promise.resolve('');
|
|
641
678
|
}
|
|
642
|
-
return ExpoIapModule.
|
|
679
|
+
return ExpoIapModule.getStorefrontIOS();
|
|
643
680
|
};
|
|
644
681
|
|
|
645
682
|
/**
|
package/src/modules/android.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function isProductAndroid<T extends {platform?: string}>(
|
|
|
26
26
|
* @param {string} params.sku - The product's SKU (on Android)
|
|
27
27
|
* @param {string} params.packageName - The package name of your Android app (e.g., 'com.example.app')
|
|
28
28
|
* @returns {Promise<void>}
|
|
29
|
-
*
|
|
29
|
+
*
|
|
30
30
|
* @example
|
|
31
31
|
* ```typescript
|
|
32
32
|
* await deepLinkToSubscriptionsAndroid({
|
|
@@ -43,11 +43,9 @@ export const deepLinkToSubscriptionsAndroid = async ({
|
|
|
43
43
|
packageName: string;
|
|
44
44
|
}): Promise<void> => {
|
|
45
45
|
if (!packageName) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
'packageName is required for deepLinkToSubscriptionsAndroid',
|
|
48
|
-
);
|
|
46
|
+
throw new Error('packageName is required for deepLinkToSubscriptionsAndroid');
|
|
49
47
|
}
|
|
50
|
-
|
|
48
|
+
|
|
51
49
|
return Linking.openURL(
|
|
52
50
|
`https://play.google.com/store/account/subscriptions?package=${packageName}&sku=${sku}`,
|
|
53
51
|
);
|
|
@@ -120,9 +118,11 @@ export const acknowledgePurchaseAndroid = ({
|
|
|
120
118
|
* Open the Google Play Store to redeem offer codes (Android only).
|
|
121
119
|
* Note: Google Play does not provide a direct API to redeem codes within the app.
|
|
122
120
|
* This function opens the Play Store where users can manually enter their codes.
|
|
123
|
-
*
|
|
121
|
+
*
|
|
124
122
|
* @returns {Promise<void>}
|
|
125
123
|
*/
|
|
126
124
|
export const openRedeemOfferCodeAndroid = async (): Promise<void> => {
|
|
127
|
-
return Linking.openURL(
|
|
125
|
+
return Linking.openURL(
|
|
126
|
+
`https://play.google.com/redeem?code=`
|
|
127
|
+
);
|
|
128
128
|
};
|
package/src/modules/ios.ts
CHANGED
|
@@ -5,7 +5,11 @@ import {purchaseUpdatedListener} from '..';
|
|
|
5
5
|
import ExpoIapModule from '../ExpoIapModule';
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Product,
|
|
10
|
+
Purchase,
|
|
11
|
+
PurchaseError,
|
|
12
|
+
} from '../ExpoIap.types';
|
|
9
13
|
import type {
|
|
10
14
|
ProductStatusIOS,
|
|
11
15
|
AppTransactionIOS,
|
|
@@ -133,7 +137,9 @@ export const subscriptionStatusIOS = (
|
|
|
133
137
|
*
|
|
134
138
|
* @platform iOS
|
|
135
139
|
*/
|
|
136
|
-
export const currentEntitlementIOS = (
|
|
140
|
+
export const currentEntitlementIOS = (
|
|
141
|
+
sku: string,
|
|
142
|
+
): Promise<Purchase> => {
|
|
137
143
|
return ExpoIapModule.currentEntitlementIOS(sku);
|
|
138
144
|
};
|
|
139
145
|
|
|
@@ -318,7 +324,7 @@ export const buyPromotedProductIOS = (): Promise<void> => {
|
|
|
318
324
|
|
|
319
325
|
/**
|
|
320
326
|
* Get pending transactions that haven't been finished yet (iOS only).
|
|
321
|
-
*
|
|
327
|
+
*
|
|
322
328
|
* @returns Promise resolving to array of pending transactions
|
|
323
329
|
* @platform iOS
|
|
324
330
|
*/
|
|
@@ -328,9 +334,9 @@ export const getPendingTransactionsIOS = (): Promise<any[]> => {
|
|
|
328
334
|
|
|
329
335
|
/**
|
|
330
336
|
* Clear a specific transaction (iOS only).
|
|
331
|
-
*
|
|
337
|
+
*
|
|
332
338
|
* @returns Promise resolving when transaction is cleared
|
|
333
|
-
* @platform iOS
|
|
339
|
+
* @platform iOS
|
|
334
340
|
*/
|
|
335
341
|
export const clearTransactionIOS = (): Promise<void> => {
|
|
336
342
|
return ExpoIapModule.clearTransactionIOS();
|
|
@@ -31,7 +31,7 @@ type ProductSubscriptionAndroidOfferDetail = {
|
|
|
31
31
|
export type ProductAndroid = ProductCommon & {
|
|
32
32
|
nameAndroid: string;
|
|
33
33
|
oneTimePurchaseOfferDetailsAndroid?: ProductAndroidOneTimePurchaseOfferDetail;
|
|
34
|
-
platform:
|
|
34
|
+
platform: "android";
|
|
35
35
|
subscriptionOfferDetailsAndroid?: ProductSubscriptionAndroidOfferDetail[];
|
|
36
36
|
/**
|
|
37
37
|
* @deprecated Use `nameAndroid` instead. This field will be removed in v2.9.0.
|
|
@@ -144,7 +144,7 @@ export const PurchaseStateAndroid = PurchaseAndroidState;
|
|
|
144
144
|
|
|
145
145
|
// Legacy naming for backward compatibility
|
|
146
146
|
export type ProductPurchaseAndroid = PurchaseCommon & {
|
|
147
|
-
platform:
|
|
147
|
+
platform: "android";
|
|
148
148
|
/**
|
|
149
149
|
* @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
|
|
150
150
|
*/
|
|
@@ -167,8 +167,7 @@ export type PurchaseAndroid = ProductPurchaseAndroid;
|
|
|
167
167
|
/**
|
|
168
168
|
* @deprecated Use `ProductAndroidOneTimePurchaseOfferDetail` instead. This type will be removed in v2.9.0.
|
|
169
169
|
*/
|
|
170
|
-
export type OneTimePurchaseOfferDetails =
|
|
171
|
-
ProductAndroidOneTimePurchaseOfferDetail;
|
|
170
|
+
export type OneTimePurchaseOfferDetails = ProductAndroidOneTimePurchaseOfferDetail;
|
|
172
171
|
|
|
173
172
|
/**
|
|
174
173
|
* @deprecated Use `ProductSubscriptionAndroidOfferDetail` instead. This type will be removed in v2.9.0.
|
|
@@ -30,7 +30,7 @@ export type ProductIOS = ProductCommon & {
|
|
|
30
30
|
displayNameIOS: string;
|
|
31
31
|
isFamilyShareableIOS: boolean;
|
|
32
32
|
jsonRepresentationIOS: string;
|
|
33
|
-
platform:
|
|
33
|
+
platform: "ios";
|
|
34
34
|
subscriptionInfoIOS?: SubscriptionInfo;
|
|
35
35
|
/**
|
|
36
36
|
* @deprecated Use `displayNameIOS` instead. This field will be removed in v2.9.0.
|
|
@@ -69,7 +69,7 @@ export type ProductSubscriptionIOS = ProductIOS & {
|
|
|
69
69
|
introductoryPricePaymentModeIOS?: PaymentMode;
|
|
70
70
|
introductoryPriceNumberOfPeriodsIOS?: string;
|
|
71
71
|
introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;
|
|
72
|
-
platform:
|
|
72
|
+
platform: "ios";
|
|
73
73
|
subscriptionPeriodNumberIOS?: string;
|
|
74
74
|
subscriptionPeriodUnitIOS?: SubscriptionIosPeriod;
|
|
75
75
|
/**
|
|
@@ -140,7 +140,7 @@ export type ProductStatusIOS = {
|
|
|
140
140
|
// Legacy naming for backward compatibility
|
|
141
141
|
export type ProductPurchaseIOS = PurchaseCommon & {
|
|
142
142
|
// iOS basic fields
|
|
143
|
-
platform:
|
|
143
|
+
platform: "ios";
|
|
144
144
|
quantityIOS?: number;
|
|
145
145
|
originalTransactionDateIOS?: number;
|
|
146
146
|
originalTransactionIdentifierIOS?: string;
|
|
@@ -179,6 +179,7 @@ export type ProductPurchaseIOS = PurchaseCommon & {
|
|
|
179
179
|
// Preferred naming
|
|
180
180
|
export type PurchaseIOS = ProductPurchaseIOS;
|
|
181
181
|
|
|
182
|
+
|
|
182
183
|
export type AppTransactionIOS = {
|
|
183
184
|
appTransactionId?: string; // Only available in iOS 18.4+
|
|
184
185
|
originalPlatform?: string; // Only available in iOS 18.4+
|
package/src/useIAP.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getPurchaseHistories,
|
|
15
15
|
finishTransaction as finishTransactionInternal,
|
|
16
16
|
requestPurchase as requestPurchaseInternal,
|
|
17
|
-
|
|
17
|
+
fetchProducts,
|
|
18
18
|
validateReceipt as validateReceiptInternal,
|
|
19
19
|
getActiveSubscriptions,
|
|
20
20
|
hasActiveSubscriptions,
|
|
@@ -60,18 +60,26 @@ type UseIap = {
|
|
|
60
60
|
}) => Promise<PurchaseResult | boolean>;
|
|
61
61
|
getAvailablePurchases: (skus: string[]) => Promise<void>;
|
|
62
62
|
getPurchaseHistories: (skus: string[]) => Promise<void>;
|
|
63
|
+
fetchProducts: (params: {
|
|
64
|
+
skus: string[];
|
|
65
|
+
type?: 'inapp' | 'subs';
|
|
66
|
+
}) => Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated Use fetchProducts({ skus, type: 'inapp' | 'subs' }) instead. This method will be removed in version 3.0.0.
|
|
69
|
+
* The 'request' prefix should only be used for event-based operations.
|
|
70
|
+
*/
|
|
63
71
|
requestProducts: (params: {
|
|
64
72
|
skus: string[];
|
|
65
73
|
type?: 'inapp' | 'subs';
|
|
66
74
|
}) => Promise<void>;
|
|
67
75
|
/**
|
|
68
|
-
* @deprecated Use
|
|
69
|
-
* Note: This method internally uses
|
|
76
|
+
* @deprecated Use fetchProducts({ skus, type: 'inapp' }) instead. This method will be removed in version 3.0.0.
|
|
77
|
+
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
70
78
|
*/
|
|
71
79
|
getProducts: (skus: string[]) => Promise<void>;
|
|
72
80
|
/**
|
|
73
|
-
* @deprecated Use
|
|
74
|
-
* Note: This method internally uses
|
|
81
|
+
* @deprecated Use fetchProducts({ skus, type: 'subs' }) instead. This method will be removed in version 3.0.0.
|
|
82
|
+
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
75
83
|
*/
|
|
76
84
|
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
77
85
|
requestPurchase: (params: {
|
|
@@ -99,7 +107,9 @@ type UseIap = {
|
|
|
99
107
|
};
|
|
100
108
|
|
|
101
109
|
export interface UseIAPOptions {
|
|
102
|
-
onPurchaseSuccess?: (
|
|
110
|
+
onPurchaseSuccess?: (
|
|
111
|
+
purchase: Purchase,
|
|
112
|
+
) => void;
|
|
103
113
|
onPurchaseError?: (error: PurchaseError) => void;
|
|
104
114
|
onSyncError?: (error: Error) => void;
|
|
105
115
|
shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing
|
|
@@ -115,8 +125,12 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
115
125
|
const [products, setProducts] = useState<Product[]>([]);
|
|
116
126
|
const [promotedProductsIOS] = useState<Purchase[]>([]);
|
|
117
127
|
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
|
|
118
|
-
const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>(
|
|
119
|
-
|
|
128
|
+
const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>(
|
|
129
|
+
[],
|
|
130
|
+
);
|
|
131
|
+
const [availablePurchases, setAvailablePurchases] = useState<
|
|
132
|
+
Purchase[]
|
|
133
|
+
>([]);
|
|
120
134
|
const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
|
|
121
135
|
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
|
|
122
136
|
const [currentPurchaseError, setCurrentPurchaseError] =
|
|
@@ -177,7 +191,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
177
191
|
const getProductsInternal = useCallback(
|
|
178
192
|
async (skus: string[]): Promise<void> => {
|
|
179
193
|
try {
|
|
180
|
-
const result = await
|
|
194
|
+
const result = await fetchProducts({skus, type: 'inapp'});
|
|
181
195
|
setProducts((prevProducts) =>
|
|
182
196
|
mergeWithDuplicateCheck(
|
|
183
197
|
prevProducts,
|
|
@@ -195,7 +209,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
195
209
|
const getSubscriptionsInternal = useCallback(
|
|
196
210
|
async (skus: string[]): Promise<void> => {
|
|
197
211
|
try {
|
|
198
|
-
const result = await
|
|
212
|
+
const result = await fetchProducts({skus, type: 'subs'});
|
|
199
213
|
setSubscriptions((prevSubscriptions) =>
|
|
200
214
|
mergeWithDuplicateCheck(
|
|
201
215
|
prevSubscriptions,
|
|
@@ -210,13 +224,13 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
210
224
|
[mergeWithDuplicateCheck],
|
|
211
225
|
);
|
|
212
226
|
|
|
213
|
-
const
|
|
227
|
+
const fetchProductsInternal = useCallback(
|
|
214
228
|
async (params: {
|
|
215
229
|
skus: string[];
|
|
216
230
|
type?: 'inapp' | 'subs';
|
|
217
231
|
}): Promise<void> => {
|
|
218
232
|
try {
|
|
219
|
-
const result = await
|
|
233
|
+
const result = await fetchProducts(params);
|
|
220
234
|
if (params.type === 'subs') {
|
|
221
235
|
setSubscriptions((prevSubscriptions) =>
|
|
222
236
|
mergeWithDuplicateCheck(
|
|
@@ -241,6 +255,19 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
241
255
|
[mergeWithDuplicateCheck],
|
|
242
256
|
);
|
|
243
257
|
|
|
258
|
+
const requestProductsInternal = useCallback(
|
|
259
|
+
async (params: {
|
|
260
|
+
skus: string[];
|
|
261
|
+
type?: 'inapp' | 'subs';
|
|
262
|
+
}): Promise<void> => {
|
|
263
|
+
console.warn(
|
|
264
|
+
"`requestProducts` is deprecated in useIAP hook. Use the new `fetchProducts` method instead. The 'request' prefix should only be used for event-based operations."
|
|
265
|
+
);
|
|
266
|
+
return fetchProductsInternal(params);
|
|
267
|
+
},
|
|
268
|
+
[fetchProductsInternal],
|
|
269
|
+
);
|
|
270
|
+
|
|
244
271
|
const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
|
|
245
272
|
try {
|
|
246
273
|
const result = await getAvailablePurchases();
|
|
@@ -454,6 +481,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
454
481
|
clearCurrentPurchaseError,
|
|
455
482
|
getAvailablePurchases: getAvailablePurchasesInternal,
|
|
456
483
|
getPurchaseHistories: getPurchaseHistoriesInternal,
|
|
484
|
+
fetchProducts: fetchProductsInternal,
|
|
457
485
|
requestProducts: requestProductsInternal,
|
|
458
486
|
requestPurchase: requestPurchaseWithReset,
|
|
459
487
|
validateReceipt,
|