expo-iap 2.6.3 → 2.7.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 +30 -0
- package/android/build.gradle +9 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +45 -44
- package/build/ExpoIap.types.d.ts +84 -2
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js +11 -2
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +84 -7
- package/build/index.d.ts.map +1 -1
- package/build/index.js +176 -13
- package/build/index.js.map +1 -1
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +0 -43
- package/build/modules/ios.js.map +1 -1
- package/build/useIap.d.ts +6 -2
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js +16 -1
- package/build/useIap.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoIap.types.ts +125 -4
- package/src/index.ts +217 -32
- package/src/modules/ios.ts +0 -65
- package/src/useIap.ts +40 -1
package/src/index.ts
CHANGED
|
@@ -14,21 +14,17 @@ import {
|
|
|
14
14
|
Purchase,
|
|
15
15
|
PurchaseError,
|
|
16
16
|
PurchaseResult,
|
|
17
|
-
|
|
17
|
+
RequestSubscriptionPropsWithLegacy,
|
|
18
|
+
RequestPurchasePropsWithLegacy,
|
|
18
19
|
SubscriptionProduct,
|
|
19
20
|
SubscriptionPurchase,
|
|
21
|
+
isPlatformRequestProps,
|
|
22
|
+
isUnifiedRequestProps,
|
|
20
23
|
} from './ExpoIap.types';
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
RequestPurchaseAndroidProps,
|
|
24
|
-
RequestSubscriptionAndroidProps,
|
|
25
|
-
} from './types/ExpoIapAndroid.types';
|
|
26
|
-
import {
|
|
27
|
-
PaymentDiscount,
|
|
28
|
-
RequestPurchaseIosProps,
|
|
29
|
-
RequestSubscriptionIosProps,
|
|
30
|
-
} from './types/ExpoIapIos.types';
|
|
24
|
+
import {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
|
|
25
|
+
import {PaymentDiscount} from './types/ExpoIapIos.types';
|
|
31
26
|
|
|
27
|
+
// Export all types
|
|
32
28
|
export * from './ExpoIap.types';
|
|
33
29
|
export * from './modules/android';
|
|
34
30
|
export * from './modules/ios';
|
|
@@ -81,6 +77,9 @@ export function initConnection() {
|
|
|
81
77
|
}
|
|
82
78
|
|
|
83
79
|
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
80
|
+
console.warn(
|
|
81
|
+
"`getProducts` is deprecated. Use `requestProducts({ skus, type: 'inapp' })` instead. This function will be removed in version 3.0.0.",
|
|
82
|
+
);
|
|
84
83
|
if (!skus?.length) {
|
|
85
84
|
return Promise.reject(new Error('"skus" is required'));
|
|
86
85
|
}
|
|
@@ -112,6 +111,9 @@ export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
|
112
111
|
export const getSubscriptions = async (
|
|
113
112
|
skus: string[],
|
|
114
113
|
): Promise<SubscriptionProduct[]> => {
|
|
114
|
+
console.warn(
|
|
115
|
+
"`getSubscriptions` is deprecated. Use `requestProducts({ skus, type: 'subs' })` instead. This function will be removed in version 3.0.0.",
|
|
116
|
+
);
|
|
115
117
|
if (!skus?.length) {
|
|
116
118
|
return Promise.reject(new Error('"skus" is required'));
|
|
117
119
|
}
|
|
@@ -151,6 +153,78 @@ export async function endConnection(): Promise<boolean> {
|
|
|
151
153
|
return ExpoIapModule.endConnection();
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Request products with unified API (v2.7.0+)
|
|
158
|
+
*
|
|
159
|
+
* @param params - Product request configuration
|
|
160
|
+
* @param params.skus - Array of product SKUs to fetch
|
|
161
|
+
* @param params.type - Type of products: 'inapp' for regular products (default) or 'subs' for subscriptions
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* // Regular products
|
|
166
|
+
* const products = await requestProducts({
|
|
167
|
+
* skus: ['product1', 'product2'],
|
|
168
|
+
* type: 'inapp'
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* // Subscriptions
|
|
172
|
+
* const subscriptions = await requestProducts({
|
|
173
|
+
* skus: ['sub1', 'sub2'],
|
|
174
|
+
* type: 'subs'
|
|
175
|
+
* });
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
export const requestProducts = async ({
|
|
179
|
+
skus,
|
|
180
|
+
type = 'inapp',
|
|
181
|
+
}: {
|
|
182
|
+
skus: string[];
|
|
183
|
+
type?: 'inapp' | 'subs';
|
|
184
|
+
}): Promise<Product[] | SubscriptionProduct[]> => {
|
|
185
|
+
if (!skus?.length) {
|
|
186
|
+
throw new Error('No SKUs provided');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (Platform.OS === 'ios') {
|
|
190
|
+
const rawItems = await ExpoIapModule.getItems(skus);
|
|
191
|
+
const filteredItems = rawItems.filter((item: unknown) => {
|
|
192
|
+
if (!isProductIos(item)) return false;
|
|
193
|
+
return (
|
|
194
|
+
typeof item === 'object' &&
|
|
195
|
+
item !== null &&
|
|
196
|
+
'id' in item &&
|
|
197
|
+
typeof item.id === 'string' &&
|
|
198
|
+
skus.includes(item.id)
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return type === 'inapp'
|
|
203
|
+
? (filteredItems as Product[])
|
|
204
|
+
: (filteredItems as SubscriptionProduct[]);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (Platform.OS === 'android') {
|
|
208
|
+
const items = await ExpoIapModule.getItemsByType(type, skus);
|
|
209
|
+
const filteredItems = items.filter((item: unknown) => {
|
|
210
|
+
if (!isProductAndroid(item)) return false;
|
|
211
|
+
return (
|
|
212
|
+
typeof item === 'object' &&
|
|
213
|
+
item !== null &&
|
|
214
|
+
'id' in item &&
|
|
215
|
+
typeof item.id === 'string' &&
|
|
216
|
+
skus.includes(item.id)
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return type === 'inapp'
|
|
221
|
+
? (filteredItems as Product[])
|
|
222
|
+
: (filteredItems as SubscriptionProduct[]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
throw new Error('Unsupported platform');
|
|
226
|
+
};
|
|
227
|
+
|
|
154
228
|
/**
|
|
155
229
|
* @deprecated Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.
|
|
156
230
|
*/
|
|
@@ -186,10 +260,12 @@ export const getPurchaseHistories = ({
|
|
|
186
260
|
);
|
|
187
261
|
},
|
|
188
262
|
android: async () => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
263
|
+
// getPurchaseHistoryByType was removed in Google Play Billing Library v8
|
|
264
|
+
// Android doesn't provide purchase history anymore, only active purchases
|
|
265
|
+
console.warn(
|
|
266
|
+
'getPurchaseHistories is not supported on Android with Google Play Billing Library v8. Use getAvailablePurchases instead to get active purchases.',
|
|
267
|
+
);
|
|
268
|
+
return [];
|
|
193
269
|
},
|
|
194
270
|
}) || (() => Promise.resolve([]))
|
|
195
271
|
)();
|
|
@@ -231,22 +307,98 @@ const offerToRecordIos = (
|
|
|
231
307
|
};
|
|
232
308
|
|
|
233
309
|
// Define discriminated union with explicit type parameter
|
|
310
|
+
// Using legacy types internally for backward compatibility
|
|
234
311
|
type PurchaseRequest =
|
|
235
312
|
| {
|
|
236
|
-
request:
|
|
313
|
+
request: RequestPurchasePropsWithLegacy;
|
|
237
314
|
type?: 'inapp';
|
|
238
315
|
}
|
|
239
316
|
| {
|
|
240
|
-
request:
|
|
317
|
+
request: RequestSubscriptionPropsWithLegacy;
|
|
241
318
|
type: 'subs';
|
|
242
319
|
};
|
|
243
320
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Helper to normalize request props to platform-specific format
|
|
323
|
+
*/
|
|
324
|
+
const normalizeRequestProps = (
|
|
325
|
+
request: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
|
|
326
|
+
platform: 'ios' | 'android',
|
|
327
|
+
): any => {
|
|
328
|
+
// If it's already platform-specific format
|
|
329
|
+
if (isPlatformRequestProps(request)) {
|
|
330
|
+
return platform === 'ios' ? request.ios : request.android;
|
|
331
|
+
}
|
|
249
332
|
|
|
333
|
+
// If it's unified format, convert to platform-specific
|
|
334
|
+
if (isUnifiedRequestProps(request)) {
|
|
335
|
+
if (platform === 'ios') {
|
|
336
|
+
return {
|
|
337
|
+
sku: request.sku || (request.skus?.[0] ?? ''),
|
|
338
|
+
andDangerouslyFinishTransactionAutomaticallyIOS:
|
|
339
|
+
request.andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
340
|
+
appAccountToken: request.appAccountToken,
|
|
341
|
+
quantity: request.quantity,
|
|
342
|
+
withOffer: request.withOffer,
|
|
343
|
+
};
|
|
344
|
+
} else {
|
|
345
|
+
const androidRequest: any = {
|
|
346
|
+
skus: request.skus || (request.sku ? [request.sku] : []),
|
|
347
|
+
obfuscatedAccountIdAndroid: request.obfuscatedAccountIdAndroid,
|
|
348
|
+
obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
|
|
349
|
+
isOfferPersonalized: request.isOfferPersonalized,
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// Add subscription-specific fields if present
|
|
353
|
+
if ('subscriptionOffers' in request && request.subscriptionOffers) {
|
|
354
|
+
androidRequest.subscriptionOffers = request.subscriptionOffers;
|
|
355
|
+
}
|
|
356
|
+
if ('purchaseTokenAndroid' in request) {
|
|
357
|
+
androidRequest.purchaseTokenAndroid = request.purchaseTokenAndroid;
|
|
358
|
+
}
|
|
359
|
+
if ('replacementModeAndroid' in request) {
|
|
360
|
+
androidRequest.replacementModeAndroid = request.replacementModeAndroid;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return androidRequest;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Legacy format handling
|
|
368
|
+
return request;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Request a purchase for products or subscriptions.
|
|
373
|
+
*
|
|
374
|
+
* @param requestObj - Purchase request configuration
|
|
375
|
+
* @param requestObj.request - Platform-specific purchase parameters
|
|
376
|
+
* @param requestObj.type - Type of purchase: 'inapp' for products (default) or 'subs' for subscriptions
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* // Product purchase
|
|
381
|
+
* await requestPurchase({
|
|
382
|
+
* request: {
|
|
383
|
+
* ios: { sku: productId },
|
|
384
|
+
* android: { skus: [productId] }
|
|
385
|
+
* },
|
|
386
|
+
* type: 'inapp'
|
|
387
|
+
* });
|
|
388
|
+
*
|
|
389
|
+
* // Subscription purchase
|
|
390
|
+
* await requestPurchase({
|
|
391
|
+
* request: {
|
|
392
|
+
* ios: { sku: subscriptionId },
|
|
393
|
+
* android: {
|
|
394
|
+
* skus: [subscriptionId],
|
|
395
|
+
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
396
|
+
* }
|
|
397
|
+
* },
|
|
398
|
+
* type: 'subs'
|
|
399
|
+
* });
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
250
402
|
export const requestPurchase = (
|
|
251
403
|
requestObj: PurchaseRequest,
|
|
252
404
|
): Promise<
|
|
@@ -259,7 +411,9 @@ export const requestPurchase = (
|
|
|
259
411
|
const {request, type = 'inapp'} = requestObj;
|
|
260
412
|
|
|
261
413
|
if (Platform.OS === 'ios') {
|
|
262
|
-
|
|
414
|
+
const normalizedRequest = normalizeRequestProps(request, 'ios');
|
|
415
|
+
|
|
416
|
+
if (!normalizedRequest?.sku) {
|
|
263
417
|
throw new Error(
|
|
264
418
|
'Invalid request for iOS. The `sku` property is required and must be a string.',
|
|
265
419
|
);
|
|
@@ -271,7 +425,7 @@ export const requestPurchase = (
|
|
|
271
425
|
appAccountToken,
|
|
272
426
|
quantity,
|
|
273
427
|
withOffer,
|
|
274
|
-
} =
|
|
428
|
+
} = normalizedRequest;
|
|
275
429
|
|
|
276
430
|
return (async () => {
|
|
277
431
|
const offer = offerToRecordIos(withOffer);
|
|
@@ -290,13 +444,21 @@ export const requestPurchase = (
|
|
|
290
444
|
}
|
|
291
445
|
|
|
292
446
|
if (Platform.OS === 'android') {
|
|
447
|
+
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
448
|
+
|
|
449
|
+
if (!normalizedRequest?.skus?.length) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
293
455
|
if (type === 'inapp') {
|
|
294
456
|
const {
|
|
295
457
|
skus,
|
|
296
458
|
obfuscatedAccountIdAndroid,
|
|
297
459
|
obfuscatedProfileIdAndroid,
|
|
298
460
|
isOfferPersonalized,
|
|
299
|
-
} =
|
|
461
|
+
} = normalizedRequest;
|
|
300
462
|
|
|
301
463
|
return (async () => {
|
|
302
464
|
return ExpoIapModule.buyItemByType({
|
|
@@ -321,7 +483,7 @@ export const requestPurchase = (
|
|
|
321
483
|
subscriptionOffers = [],
|
|
322
484
|
replacementModeAndroid = -1,
|
|
323
485
|
purchaseTokenAndroid,
|
|
324
|
-
} =
|
|
486
|
+
} = normalizedRequest;
|
|
325
487
|
|
|
326
488
|
return (async () => {
|
|
327
489
|
return ExpoIapModule.buyItemByType({
|
|
@@ -331,14 +493,14 @@ export const requestPurchase = (
|
|
|
331
493
|
replacementMode: replacementModeAndroid,
|
|
332
494
|
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
333
495
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
334
|
-
offerTokenArr: subscriptionOffers.map((so) => so.offerToken),
|
|
496
|
+
offerTokenArr: subscriptionOffers.map((so: any) => so.offerToken),
|
|
335
497
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
336
498
|
}) as Promise<SubscriptionPurchase[]>;
|
|
337
499
|
})();
|
|
338
500
|
}
|
|
339
501
|
|
|
340
502
|
throw new Error(
|
|
341
|
-
"Invalid request for Android: Expected a
|
|
503
|
+
"Invalid request for Android: Expected a valid request object with 'skus' array.",
|
|
342
504
|
);
|
|
343
505
|
}
|
|
344
506
|
|
|
@@ -346,13 +508,35 @@ export const requestPurchase = (
|
|
|
346
508
|
};
|
|
347
509
|
|
|
348
510
|
/**
|
|
349
|
-
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0
|
|
511
|
+
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0.
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
* ```typescript
|
|
515
|
+
* // Old way (deprecated)
|
|
516
|
+
* await requestSubscription({
|
|
517
|
+
* sku: subscriptionId,
|
|
518
|
+
* // or for Android
|
|
519
|
+
* skus: [subscriptionId],
|
|
520
|
+
* });
|
|
521
|
+
*
|
|
522
|
+
* // New way (recommended)
|
|
523
|
+
* await requestPurchase({
|
|
524
|
+
* request: {
|
|
525
|
+
* ios: { sku: subscriptionId },
|
|
526
|
+
* android: {
|
|
527
|
+
* skus: [subscriptionId],
|
|
528
|
+
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
529
|
+
* }
|
|
530
|
+
* },
|
|
531
|
+
* type: 'subs'
|
|
532
|
+
* });
|
|
533
|
+
* ```
|
|
350
534
|
*/
|
|
351
535
|
export const requestSubscription = async (
|
|
352
|
-
request:
|
|
536
|
+
request: RequestSubscriptionPropsWithLegacy,
|
|
353
537
|
): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> => {
|
|
354
538
|
console.warn(
|
|
355
|
-
"`requestSubscription` is deprecated. Use `requestPurchase({ request, type: 'subs' })` instead.
|
|
539
|
+
"`requestSubscription` is deprecated and will be removed in version 3.0.0. Use `requestPurchase({ request, type: 'subs' })` instead.",
|
|
356
540
|
);
|
|
357
541
|
return (await requestPurchase({request, type: 'subs'})) as
|
|
358
542
|
| SubscriptionPurchase
|
|
@@ -418,7 +602,8 @@ export const finishTransaction = ({
|
|
|
418
602
|
*/
|
|
419
603
|
export const getStorefrontIOS = (): Promise<string> => {
|
|
420
604
|
if (Platform.OS !== 'ios') {
|
|
421
|
-
|
|
605
|
+
console.warn('getStorefrontIOS: This method is only available on iOS');
|
|
606
|
+
return Promise.resolve('');
|
|
422
607
|
}
|
|
423
608
|
return ExpoIapModule.getStorefront();
|
|
424
609
|
};
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// External dependencies
|
|
2
|
-
import {Platform} from 'react-native';
|
|
3
2
|
|
|
4
3
|
// Internal modules
|
|
5
4
|
import {purchaseUpdatedListener} from '..';
|
|
@@ -39,10 +38,6 @@ export type TransactionEvent = {
|
|
|
39
38
|
export const transactionUpdatedIos = (
|
|
40
39
|
listener: (event: TransactionEvent) => void,
|
|
41
40
|
) => {
|
|
42
|
-
if (Platform.OS !== 'ios') {
|
|
43
|
-
throw new Error('This method is only available on iOS');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
41
|
const isProductPurchase = (item: unknown): item is ProductPurchase => {
|
|
47
42
|
return (
|
|
48
43
|
item != null &&
|
|
@@ -100,9 +95,6 @@ export function isProductIos<T extends {platform?: string}>(
|
|
|
100
95
|
* @platform iOS
|
|
101
96
|
*/
|
|
102
97
|
export const syncIOS = (): Promise<null> => {
|
|
103
|
-
if (Platform.OS !== 'ios') {
|
|
104
|
-
throw new Error('syncIOS: This method is only available on iOS');
|
|
105
|
-
}
|
|
106
98
|
return ExpoIapModule.sync();
|
|
107
99
|
};
|
|
108
100
|
|
|
@@ -118,11 +110,6 @@ export const syncIOS = (): Promise<null> => {
|
|
|
118
110
|
export const isEligibleForIntroOfferIOS = (
|
|
119
111
|
groupID: string,
|
|
120
112
|
): Promise<boolean> => {
|
|
121
|
-
if (Platform.OS !== 'ios') {
|
|
122
|
-
throw new Error(
|
|
123
|
-
'isEligibleForIntroOfferIOS: This method is only available on iOS',
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
113
|
return ExpoIapModule.isEligibleForIntroOffer(groupID);
|
|
127
114
|
};
|
|
128
115
|
|
|
@@ -138,11 +125,6 @@ export const isEligibleForIntroOfferIOS = (
|
|
|
138
125
|
export const subscriptionStatusIOS = (
|
|
139
126
|
sku: string,
|
|
140
127
|
): Promise<ProductStatusIos[]> => {
|
|
141
|
-
if (Platform.OS !== 'ios') {
|
|
142
|
-
throw new Error(
|
|
143
|
-
'subscriptionStatusIOS: This method is only available on iOS',
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
128
|
return ExpoIapModule.subscriptionStatus(sku);
|
|
147
129
|
};
|
|
148
130
|
|
|
@@ -158,11 +140,6 @@ export const subscriptionStatusIOS = (
|
|
|
158
140
|
export const currentEntitlementIOS = (
|
|
159
141
|
sku: string,
|
|
160
142
|
): Promise<ProductPurchase> => {
|
|
161
|
-
if (Platform.OS !== 'ios') {
|
|
162
|
-
throw new Error(
|
|
163
|
-
'currentEntitlementIOS: This method is only available on iOS',
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
143
|
return ExpoIapModule.currentEntitlement(sku);
|
|
167
144
|
};
|
|
168
145
|
|
|
@@ -176,11 +153,6 @@ export const currentEntitlementIOS = (
|
|
|
176
153
|
* @platform iOS
|
|
177
154
|
*/
|
|
178
155
|
export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
|
|
179
|
-
if (Platform.OS !== 'ios') {
|
|
180
|
-
throw new Error(
|
|
181
|
-
'latestTransactionIOS: This method is only available on iOS',
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
156
|
return ExpoIapModule.latestTransaction(sku);
|
|
185
157
|
};
|
|
186
158
|
|
|
@@ -197,11 +169,6 @@ type RefundRequestStatus = 'success' | 'userCancelled';
|
|
|
197
169
|
export const beginRefundRequestIOS = (
|
|
198
170
|
sku: string,
|
|
199
171
|
): Promise<RefundRequestStatus> => {
|
|
200
|
-
if (Platform.OS !== 'ios') {
|
|
201
|
-
throw new Error(
|
|
202
|
-
'beginRefundRequestIOS: This method is only available on iOS',
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
172
|
return ExpoIapModule.beginRefundRequest(sku);
|
|
206
173
|
};
|
|
207
174
|
|
|
@@ -216,11 +183,6 @@ export const beginRefundRequestIOS = (
|
|
|
216
183
|
* @platform iOS
|
|
217
184
|
*/
|
|
218
185
|
export const showManageSubscriptionsIOS = (): Promise<null> => {
|
|
219
|
-
if (Platform.OS !== 'ios') {
|
|
220
|
-
throw new Error(
|
|
221
|
-
'showManageSubscriptionsIOS: This method is only available on iOS',
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
186
|
return ExpoIapModule.showManageSubscriptions();
|
|
225
187
|
};
|
|
226
188
|
|
|
@@ -235,9 +197,6 @@ export const showManageSubscriptionsIOS = (): Promise<null> => {
|
|
|
235
197
|
* @returns {Promise<string>} Base64 encoded receipt data
|
|
236
198
|
*/
|
|
237
199
|
export const getReceiptIOS = (): Promise<string> => {
|
|
238
|
-
if (Platform.OS !== 'ios') {
|
|
239
|
-
throw new Error('This method is only available on iOS');
|
|
240
|
-
}
|
|
241
200
|
return ExpoIapModule.getReceiptData();
|
|
242
201
|
};
|
|
243
202
|
|
|
@@ -252,11 +211,6 @@ export const getReceiptIOS = (): Promise<string> => {
|
|
|
252
211
|
* @platform iOS
|
|
253
212
|
*/
|
|
254
213
|
export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
|
|
255
|
-
if (Platform.OS !== 'ios') {
|
|
256
|
-
throw new Error(
|
|
257
|
-
'isTransactionVerifiedIOS: This method is only available on iOS',
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
214
|
return ExpoIapModule.isTransactionVerified(sku);
|
|
261
215
|
};
|
|
262
216
|
|
|
@@ -271,11 +225,6 @@ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
|
|
|
271
225
|
* @platform iOS
|
|
272
226
|
*/
|
|
273
227
|
export const getTransactionJwsIOS = (sku: string): Promise<string> => {
|
|
274
|
-
if (Platform.OS !== 'ios') {
|
|
275
|
-
throw new Error(
|
|
276
|
-
'getTransactionJwsIOS: This method is only available on iOS',
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
228
|
return ExpoIapModule.getTransactionJws(sku);
|
|
280
229
|
};
|
|
281
230
|
|
|
@@ -302,10 +251,6 @@ export const validateReceiptIOS = async (
|
|
|
302
251
|
jwsRepresentation: string;
|
|
303
252
|
latestTransaction?: ProductPurchase;
|
|
304
253
|
}> => {
|
|
305
|
-
if (Platform.OS !== 'ios') {
|
|
306
|
-
throw new Error('This method is only available on iOS');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
254
|
const result = await ExpoIapModule.validateReceiptIOS(sku);
|
|
310
255
|
return result;
|
|
311
256
|
};
|
|
@@ -322,11 +267,6 @@ export const validateReceiptIOS = async (
|
|
|
322
267
|
* @platform iOS
|
|
323
268
|
*/
|
|
324
269
|
export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
|
|
325
|
-
if (Platform.OS !== 'ios') {
|
|
326
|
-
throw new Error(
|
|
327
|
-
'presentCodeRedemptionSheetIOS: This method is only available on iOS',
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
270
|
return ExpoIapModule.presentCodeRedemptionSheet();
|
|
331
271
|
};
|
|
332
272
|
|
|
@@ -340,11 +280,6 @@ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
|
|
|
340
280
|
* @platform iOS
|
|
341
281
|
*/
|
|
342
282
|
export const getAppTransactionIOS = (): Promise<AppTransactionIOS | null> => {
|
|
343
|
-
if (Platform.OS !== 'ios') {
|
|
344
|
-
throw new Error(
|
|
345
|
-
'getAppTransactionIOS: This method is only available on iOS',
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
283
|
return ExpoIapModule.getAppTransaction();
|
|
349
284
|
};
|
|
350
285
|
|
package/src/useIap.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
finishTransaction as finishTransactionInternal,
|
|
16
16
|
getSubscriptions,
|
|
17
17
|
requestPurchase as requestPurchaseInternal,
|
|
18
|
+
requestProducts,
|
|
18
19
|
} from './';
|
|
19
20
|
import {syncIOS, validateReceiptIOS} from './modules/ios';
|
|
20
21
|
import {validateReceiptAndroid} from './modules/android';
|
|
@@ -28,6 +29,8 @@ import {
|
|
|
28
29
|
PurchaseResult,
|
|
29
30
|
SubscriptionProduct,
|
|
30
31
|
SubscriptionPurchase,
|
|
32
|
+
RequestPurchaseProps,
|
|
33
|
+
RequestSubscriptionProps,
|
|
31
34
|
} from './ExpoIap.types';
|
|
32
35
|
|
|
33
36
|
type UseIap = {
|
|
@@ -52,8 +55,12 @@ type UseIap = {
|
|
|
52
55
|
getPurchaseHistories: (skus: string[]) => Promise<void>;
|
|
53
56
|
getProducts: (skus: string[]) => Promise<void>;
|
|
54
57
|
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
58
|
+
requestProducts: (params: {
|
|
59
|
+
skus: string[];
|
|
60
|
+
type?: 'inapp' | 'subs';
|
|
61
|
+
}) => Promise<void>;
|
|
55
62
|
requestPurchase: (params: {
|
|
56
|
-
request:
|
|
63
|
+
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
57
64
|
type?: 'inapp' | 'subs';
|
|
58
65
|
}) => Promise<any>;
|
|
59
66
|
validateReceipt: (
|
|
@@ -177,6 +184,37 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
177
184
|
[mergeWithDuplicateCheck],
|
|
178
185
|
);
|
|
179
186
|
|
|
187
|
+
const requestProductsInternal = useCallback(
|
|
188
|
+
async (params: {
|
|
189
|
+
skus: string[];
|
|
190
|
+
type?: 'inapp' | 'subs';
|
|
191
|
+
}): Promise<void> => {
|
|
192
|
+
try {
|
|
193
|
+
const result = await requestProducts(params);
|
|
194
|
+
if (params.type === 'subs') {
|
|
195
|
+
setSubscriptions((prevSubscriptions) =>
|
|
196
|
+
mergeWithDuplicateCheck(
|
|
197
|
+
prevSubscriptions,
|
|
198
|
+
result as SubscriptionProduct[],
|
|
199
|
+
(subscription) => subscription.id,
|
|
200
|
+
),
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
setProducts((prevProducts) =>
|
|
204
|
+
mergeWithDuplicateCheck(
|
|
205
|
+
prevProducts,
|
|
206
|
+
result as Product[],
|
|
207
|
+
(product) => product.id,
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error('Error fetching products:', error);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
[mergeWithDuplicateCheck],
|
|
216
|
+
);
|
|
217
|
+
|
|
180
218
|
const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
|
|
181
219
|
try {
|
|
182
220
|
const result = await getAvailablePurchases();
|
|
@@ -387,6 +425,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
387
425
|
getPurchaseHistories: getPurchaseHistoriesInternal,
|
|
388
426
|
getProducts: getProductsInternal,
|
|
389
427
|
getSubscriptions: getSubscriptionsInternal,
|
|
428
|
+
requestProducts: requestProductsInternal,
|
|
390
429
|
requestPurchase: requestPurchaseWithReset,
|
|
391
430
|
validateReceipt,
|
|
392
431
|
restorePurchases,
|