react-native-iap 10.0.7 → 10.1.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.
Files changed (73) hide show
  1. package/README.md +0 -1
  2. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +1 -2
  3. package/lib/commonjs/hooks/index.js +19 -0
  4. package/lib/commonjs/hooks/index.js.map +1 -0
  5. package/lib/commonjs/hooks/useIAP.js +8 -4
  6. package/lib/commonjs/hooks/useIAP.js.map +1 -1
  7. package/lib/commonjs/iap.js +390 -51
  8. package/lib/commonjs/iap.js.map +1 -1
  9. package/lib/commonjs/index.js +13 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/modules/amazon.js +12 -0
  12. package/lib/commonjs/modules/amazon.js.map +1 -0
  13. package/lib/commonjs/modules/android.js +12 -0
  14. package/lib/commonjs/modules/android.js.map +1 -0
  15. package/lib/commonjs/modules/common.js +2 -0
  16. package/lib/commonjs/modules/common.js.map +1 -0
  17. package/lib/commonjs/modules/index.js +58 -0
  18. package/lib/commonjs/modules/index.js.map +1 -0
  19. package/lib/commonjs/modules/ios.js +6 -0
  20. package/lib/commonjs/modules/ios.js.map +1 -0
  21. package/lib/commonjs/types/amazon.js.map +1 -1
  22. package/lib/commonjs/types/apple.js +0 -163
  23. package/lib/commonjs/types/apple.js.map +1 -1
  24. package/lib/commonjs/types/index.js +11 -1
  25. package/lib/commonjs/types/index.js.map +1 -1
  26. package/lib/module/hooks/index.js +2 -0
  27. package/lib/module/hooks/index.js.map +1 -0
  28. package/lib/module/hooks/useIAP.js +6 -4
  29. package/lib/module/hooks/useIAP.js.map +1 -1
  30. package/lib/module/iap.js +391 -50
  31. package/lib/module/iap.js.map +1 -1
  32. package/lib/module/index.js +1 -0
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/modules/amazon.js +3 -0
  35. package/lib/module/modules/amazon.js.map +1 -0
  36. package/lib/module/modules/android.js +3 -0
  37. package/lib/module/modules/android.js.map +1 -0
  38. package/lib/module/modules/common.js +2 -0
  39. package/lib/module/modules/common.js.map +1 -0
  40. package/lib/module/modules/index.js +5 -0
  41. package/lib/module/modules/index.js.map +1 -0
  42. package/lib/module/modules/ios.js +2 -0
  43. package/lib/module/modules/ios.js.map +1 -0
  44. package/lib/module/types/amazon.js.map +1 -1
  45. package/lib/module/types/apple.js +0 -151
  46. package/lib/module/types/apple.js.map +1 -1
  47. package/lib/module/types/index.js +9 -0
  48. package/lib/module/types/index.js.map +1 -1
  49. package/lib/typescript/hooks/index.d.ts +1 -0
  50. package/lib/typescript/hooks/useIAP.d.ts +6 -3
  51. package/lib/typescript/iap.d.ts +356 -65
  52. package/lib/typescript/index.d.ts +1 -0
  53. package/lib/typescript/modules/amazon.d.ts +23 -0
  54. package/lib/typescript/modules/android.d.ts +24 -0
  55. package/lib/typescript/modules/common.d.ts +13 -0
  56. package/lib/typescript/modules/index.d.ts +4 -0
  57. package/lib/typescript/modules/ios.d.ts +32 -0
  58. package/lib/typescript/types/amazon.d.ts +8 -0
  59. package/lib/typescript/types/apple.d.ts +0 -399
  60. package/lib/typescript/types/index.d.ts +51 -1
  61. package/package.json +7 -2
  62. package/src/hooks/index.ts +1 -0
  63. package/src/hooks/useIAP.ts +11 -5
  64. package/src/iap.ts +420 -91
  65. package/src/index.ts +1 -0
  66. package/src/modules/amazon.ts +40 -0
  67. package/src/modules/android.ts +65 -0
  68. package/src/modules/common.ts +16 -0
  69. package/src/modules/index.ts +4 -0
  70. package/src/modules/ios.ts +57 -0
  71. package/src/types/amazon.ts +9 -0
  72. package/src/types/apple.ts +0 -438
  73. package/src/types/index.ts +71 -4
@@ -7,8 +7,6 @@ exports.validateReceiptIos = exports.validateReceiptAndroid = exports.validateRe
7
7
 
8
8
  var _reactNative = require("react-native");
9
9
 
10
- var _apple = require("./types/apple");
11
-
12
10
  var _internal = require("./internal");
13
11
 
14
12
  var _types = require("./types");
@@ -18,8 +16,8 @@ const {
18
16
  RNIapModule,
19
17
  RNIapAmazonModule
20
18
  } = _reactNative.NativeModules;
21
- const ANDROID_ITEM_TYPE_SUBSCRIPTION = 'subs';
22
- const ANDROID_ITEM_TYPE_IAP = 'inapp';
19
+ const ANDROID_ITEM_TYPE_SUBSCRIPTION = _types.ProductType.subs;
20
+ const ANDROID_ITEM_TYPE_IAP = _types.ProductType.inapp;
23
21
 
24
22
  const getInstallSourceAndroid = () => {
25
23
  return RNIapModule ? _types.InstallSourceAndroid.GOOGLE_PLAY : _types.InstallSourceAndroid.AMAZON;
@@ -65,7 +63,21 @@ const getNativeModule = () => {
65
63
  };
66
64
  /**
67
65
  * Init module for purchase flow. Required on Android. In ios it will check whether user canMakePayment.
68
- * @returns {Promise<boolean>}
66
+ * ## Usage
67
+
68
+ ```tsx
69
+ import React, {useEffect} from 'react';
70
+ import {View} from 'react-native';
71
+ import {initConnection} from 'react-native-iap';
72
+
73
+ const App = () => {
74
+ useEffect(() => {
75
+ void initConnection();
76
+ }, []);
77
+
78
+ return <View />;
79
+ };
80
+ ```
69
81
  */
70
82
 
71
83
 
@@ -73,7 +85,23 @@ exports.getNativeModule = getNativeModule;
73
85
 
74
86
  const initConnection = () => getNativeModule().initConnection();
75
87
  /**
76
- * End module for purchase flow.
88
+ * Disconnects from native SDK
89
+ * Usage
90
+ * ```tsx
91
+ import React, {useEffect} from 'react';
92
+ import {View} from 'react-native';
93
+ import {endConnection} from 'react-native-iap';
94
+
95
+ const App = () => {
96
+ useEffect(() => {
97
+ return () => {
98
+ void endConnection();
99
+ };
100
+ }, []);
101
+
102
+ return <View />;
103
+ };
104
+ ```
77
105
  * @returns {Promise<void>}
78
106
  */
79
107
 
@@ -92,8 +120,47 @@ exports.endConnection = endConnection;
92
120
  const flushFailedPurchasesCachedAsPendingAndroid = () => getAndroidModule().flushFailedPurchasesCachedAsPending();
93
121
  /**
94
122
  * Get a list of products (consumable and non-consumable items, but not subscriptions)
95
- * @param {string[]} skus The item skus
96
- * @returns {Promise<Product[]>}
123
+ ## Usage
124
+
125
+ ```ts
126
+ import React, {useState} from 'react';
127
+ import {Platform} from 'react-native';
128
+ import {getProducts, Product} from 'react-native-iap';
129
+
130
+ const skus = Platform.select({
131
+ ios: ['com.example.consumableIos'],
132
+ android: ['com.example.consumableAndroid'],
133
+ });
134
+
135
+ const App = () => {
136
+ const [products, setProducts] = useState<Product[]>([]);
137
+
138
+ const handleProducts = async () => {
139
+ const items = await getProducts({skus});
140
+
141
+ setProducts(items);
142
+ };
143
+
144
+ useEffect(() => {
145
+ void handleProducts();
146
+ }, []);
147
+
148
+ return (
149
+ <>
150
+ {products.map((product) => (
151
+ <Text key={product.productId}>{product.productId}</Text>
152
+ ))}
153
+ </>
154
+ );
155
+ };
156
+ ```
157
+
158
+ Just a few things to keep in mind:
159
+
160
+ - You can get your products in `componentDidMount`, `useEffect` or another appropriate area of your app.
161
+ - Since a user may start your app with a bad or no internet connection, preparing/getting the items more than once may be a good idea.
162
+ - If the user has no IAPs available when the app starts first, you may want to check again when the user enters your IAP store.
163
+
97
164
  */
98
165
 
99
166
 
@@ -112,12 +179,28 @@ const getProducts = _ref => {
112
179
  const products = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_IAP, skus);
113
180
  return (0, _internal.fillProductsWithAdditionalData)(products);
114
181
  }
115
- }) || Promise.resolve)();
182
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
116
183
  };
117
184
  /**
118
185
  * Get a list of subscriptions
119
- * @param {string[]} skus The item skus
120
- * @returns {Promise<Subscription[]>}
186
+ * ## Usage
187
+
188
+ ```tsx
189
+ import React, {useCallback} from 'react';
190
+ import {View} from 'react-native';
191
+ import {getSubscriptions} from 'react-native-iap';
192
+
193
+ const App = () => {
194
+ const subscriptions = useCallback(
195
+ async () =>
196
+ await getSubscriptions(['com.example.product1', 'com.example.product2']),
197
+ [],
198
+ );
199
+
200
+ return <View />;
201
+ };
202
+ ```
203
+
121
204
  */
122
205
 
123
206
 
@@ -136,11 +219,30 @@ const getSubscriptions = _ref2 => {
136
219
  const subscriptions = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, skus);
137
220
  return (0, _internal.fillProductsWithAdditionalData)(subscriptions);
138
221
  }
139
- }) || Promise.resolve)();
222
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
140
223
  };
141
224
  /**
142
225
  * Gets an inventory of purchases made by the user regardless of consumption status
143
- * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
226
+ * ## Usage
227
+
228
+ ```tsx
229
+ import React, {useCallback} from 'react';
230
+ import {View} from 'react-native';
231
+ import {getPurchaseHistory} from 'react-native-iap';
232
+
233
+ const App = () => {
234
+ const history = useCallback(
235
+ async () =>
236
+ await getPurchaseHistory([
237
+ 'com.example.product1',
238
+ 'com.example.product2',
239
+ ]),
240
+ [],
241
+ );
242
+
243
+ return <View />;
244
+ };
245
+ ```
144
246
  */
145
247
 
146
248
 
@@ -155,14 +257,88 @@ const getPurchaseHistory = () => (_reactNative.Platform.select({
155
257
  return await RNIapAmazonModule.getAvailableItems();
156
258
  }
157
259
 
158
- const products = await getAndroidModule().getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
159
- const subscriptions = await getAndroidModule().getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
260
+ const products = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
261
+ const subscriptions = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
160
262
  return products.concat(subscriptions);
161
263
  }
162
- }) || Promise.resolve)();
264
+ }) || (() => Promise.resolve([])))();
163
265
  /**
164
266
  * Get all purchases made by the user (either non-consumable, or haven't been consumed yet)
165
- * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
267
+ * ## Usage
268
+
269
+ ```tsx
270
+ import React, {useCallback} from 'react';
271
+ import {View} from 'react-native';
272
+ import {getAvailablePurchases} from 'react-native-iap';
273
+
274
+ const App = () => {
275
+ const availablePurchases = useCallback(
276
+ async () => await getAvailablePurchases(),
277
+ [],
278
+ );
279
+
280
+ return <View />;
281
+ };
282
+ ```
283
+
284
+ ## Restoring purchases
285
+
286
+ You can use `getAvailablePurchases()` to do what's commonly understood as "restoring" purchases.
287
+
288
+ :::note
289
+ For debugging you may want to consume all items, you have then to iterate over the purchases returned by `getAvailablePurchases()`.
290
+ :::
291
+
292
+ :::warning
293
+ Beware that if you consume an item without having recorded the purchase in your database the user may have paid for something without getting it delivered and you will have no way to recover the receipt to validate and restore their purchase.
294
+ :::
295
+
296
+ ```tsx
297
+ import React from 'react';
298
+ import {Button} from 'react-native';
299
+ import {getAvailablePurchases,finishTransaction} from 'react-native-iap';
300
+
301
+ const App = () => {
302
+ handleRestore = async () => {
303
+ try {
304
+ const purchases = await getAvailablePurchases();
305
+ const newState = {premium: false, ads: true};
306
+ let titles = [];
307
+
308
+ await Promise.all(purchases.map(async purchase => {
309
+ switch (purchase.productId) {
310
+ case 'com.example.premium':
311
+ newState.premium = true;
312
+ titles.push('Premium Version');
313
+ break;
314
+
315
+ case 'com.example.no_ads':
316
+ newState.ads = false;
317
+ titles.push('No Ads');
318
+ break;
319
+
320
+ case 'com.example.coins100':
321
+ await finishTransaction(purchase.purchaseToken);
322
+ CoinStore.addCoins(100);
323
+ }
324
+ })
325
+
326
+ Alert.alert(
327
+ 'Restore Successful',
328
+ `You successfully restored the following purchases: ${titles.join(', ')}`,
329
+ );
330
+ } catch (error) {
331
+ console.warn(error);
332
+ Alert.alert(error.message);
333
+ }
334
+ };
335
+
336
+ return (
337
+ <Button title="Restore purchases" onPress={handleRestore} />
338
+ )
339
+ };
340
+ ```
341
+ *
166
342
  */
167
343
 
168
344
 
@@ -177,21 +353,77 @@ const getAvailablePurchases = () => (_reactNative.Platform.select({
177
353
  return await RNIapAmazonModule.getAvailableItems();
178
354
  }
179
355
 
180
- const products = await getAndroidModule().getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
181
- const subscriptions = await getAndroidModule().getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
356
+ const products = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
357
+ const subscriptions = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
182
358
  return products.concat(subscriptions);
183
359
  }
184
- }) || Promise.resolve)();
360
+ }) || (() => Promise.resolve([])))();
185
361
  /**
186
362
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
187
- * @param {string} sku The product's sku/ID
188
- * @param {string} [applicationUsername] The purchaser's user ID
189
- * @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
190
- * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
191
- * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
192
- * @param {string[]} [skus] Product Ids to purchase. Note that this is only for Android. iOS only uses a single SKU. If not provided, it'll default to using [sku] for backward-compatibility
193
- * @param {boolean} [isOfferPersonalized] Defaults to false, Only for Android V5
194
- * @returns {Promise<ProductPurchase>}
363
+ * Request a purchase for a product (consumables or non-consumables).
364
+
365
+ The response will be received through the `PurchaseUpdatedListener`.
366
+
367
+ :::note
368
+ `andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
369
+ always keeping at false, and verifying the transaction receipts on the server-side.
370
+ :::
371
+
372
+ ## Signature
373
+
374
+ ```ts
375
+ requestPurchase(
376
+ The product's sku/ID
377
+ sku,
378
+
379
+
380
+ * You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
381
+ * @default false
382
+
383
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
384
+
385
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
386
+ obfuscatedAccountIdAndroid,
387
+
388
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
389
+ obfuscatedProfileIdAndroid,
390
+
391
+ The purchaser's user ID
392
+ applicationUsername,
393
+ ): Promise<ProductPurchase>;
394
+ ```
395
+
396
+ ## Usage
397
+
398
+ ```tsx
399
+ import React, {useCallback} from 'react';
400
+ import {Button} from 'react-native';
401
+ import {requestPurchase, Product, Sku, getProducts} from 'react-native-iap';
402
+
403
+ const App = () => {
404
+ const products = useCallback(
405
+ async () => getProducts(['com.example.product']),
406
+ [],
407
+ );
408
+
409
+ const handlePurchase = async (sku: Sku) => {
410
+ await requestPurchase({sku});
411
+ };
412
+
413
+ return (
414
+ <>
415
+ {products.map((product) => (
416
+ <Button
417
+ key={product.productId}
418
+ title="Buy product"
419
+ onPress={() => handlePurchase(product.productId)}
420
+ />
421
+ ))}
422
+ </>
423
+ );
424
+ };
425
+ ```
426
+
195
427
  */
196
428
 
197
429
 
@@ -209,6 +441,10 @@ const requestPurchase = _ref3 => {
209
441
  } = _ref3;
210
442
  return (_reactNative.Platform.select({
211
443
  ios: async () => {
444
+ if (!sku) {
445
+ return Promise.reject(new Error('sku is required for iOS purchase'));
446
+ }
447
+
212
448
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
213
449
  console.warn('You are dangerously allowing react-native-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.');
214
450
  }
@@ -217,24 +453,97 @@ const requestPurchase = _ref3 => {
217
453
  },
218
454
  android: async () => {
219
455
  if (_internal.isAmazon) {
456
+ if (!sku) {
457
+ return Promise.reject(new Error('sku is required for Amazon purchase'));
458
+ }
459
+
220
460
  return RNIapAmazonModule.buyItemByType(sku);
221
461
  } else {
222
- return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], null, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
462
+ if (!(sku !== null && sku !== void 0 && sku.length) && !sku) {
463
+ return Promise.reject(new Error('skus is required for Android purchase'));
464
+ }
465
+
466
+ return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], undefined, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
223
467
  }
224
468
  }
225
469
  }) || Promise.resolve)();
226
470
  };
227
471
  /**
228
472
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
229
- * @param {string} [sku] The product's sku/ID
230
- * @param {string} [applicationUsername] The purchaser's user ID
231
- * @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
232
- * @param {string} [purchaseTokenAndroid] purchaseToken that the user is upgrading or downgrading from (Android).
233
- * @param {ProrationModesAndroid} [prorationModeAndroid] UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
234
- * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
235
- * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
236
- * @param {SubscriptionOffers[]} [subscriptionOffers] Array of SubscriptionOffers. Every sku must be paired with a corresponding offerToken
237
- * @returns {Promise<SubscriptionPurchase | null>} Promise resolves to null when using proratioModesAndroid=DEFERRED, and to a SubscriptionPurchase otherwise
473
+ * Request a purchase for a subscription.
474
+
475
+ The response will be received through the `PurchaseUpdatedListener`.
476
+
477
+ :::note
478
+ `andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
479
+ always keeping at false, and verifying the transaction receipts on the server-side.
480
+ :::
481
+
482
+ ## Signature
483
+
484
+ ```ts
485
+ requestSubscription(
486
+ The product's sku/ID
487
+ sku,
488
+
489
+
490
+ * You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
491
+ * @default false
492
+
493
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
494
+
495
+ purchaseToken that the user is upgrading or downgrading from (Android).
496
+ purchaseTokenAndroid,
497
+
498
+ UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
499
+ prorationModeAndroid = -1,
500
+
501
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
502
+ obfuscatedAccountIdAndroid,
503
+
504
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
505
+ obfuscatedProfileIdAndroid,
506
+
507
+ The purchaser's user ID
508
+ applicationUsername,
509
+ ): Promise<SubscriptionPurchase>
510
+ ```
511
+
512
+ ## Usage
513
+
514
+ ```tsx
515
+ import React, {useCallback} from 'react';
516
+ import {Button} from 'react-native';
517
+ import {
518
+ requestSubscription,
519
+ Product,
520
+ Sku,
521
+ getSubscriptions,
522
+ } from 'react-native-iap';
523
+
524
+ const App = () => {
525
+ const subscriptions = useCallback(
526
+ async () => getSubscriptions(['com.example.subscription']),
527
+ [],
528
+ );
529
+
530
+ const handlePurchase = async (sku: Sku) => {
531
+ await requestSubscription({sku});
532
+ };
533
+
534
+ return (
535
+ <>
536
+ {subscriptions.map((subscription) => (
537
+ <Button
538
+ key={subscription.productId}
539
+ title="Buy subscription"
540
+ onPress={() => handlePurchase(subscription.productId)}
541
+ />
542
+ ))}
543
+ </>
544
+ );
545
+ };
546
+ ```
238
547
  */
239
548
 
240
549
 
@@ -254,6 +563,10 @@ const requestSubscription = _ref4 => {
254
563
  } = _ref4;
255
564
  return (_reactNative.Platform.select({
256
565
  ios: async () => {
566
+ if (!sku) {
567
+ return Promise.reject(new Error('sku is required for iOS subscription'));
568
+ }
569
+
257
570
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
258
571
  console.warn('You are dangerously allowing react-native-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.');
259
572
  }
@@ -262,6 +575,10 @@ const requestSubscription = _ref4 => {
262
575
  },
263
576
  android: async () => {
264
577
  if (_internal.isAmazon) {
578
+ if (!sku) {
579
+ return Promise.reject(new Error('sku is required for Amazon purchase'));
580
+ }
581
+
265
582
  return RNIapAmazonModule.buyItemByType(sku);
266
583
  } else {
267
584
  if (!(subscriptionOffers !== null && subscriptionOffers !== void 0 && subscriptionOffers.length)) {
@@ -271,7 +588,7 @@ const requestSubscription = _ref4 => {
271
588
  return RNIapModule.buyItemByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.sku), purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.offerToken), isOfferPersonalized ?? false);
272
589
  }
273
590
  }
274
- }) || Promise.resolve)();
591
+ }) || (() => Promise.resolve(null)))();
275
592
  };
276
593
  /**
277
594
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
@@ -296,10 +613,22 @@ const requestPurchaseWithQuantityIOS = _ref5 => {
296
613
  * Call this after you have persisted the purchased state to your server or local data in your app.
297
614
  * `react-native-iap` will continue to deliver the purchase updated events with the successful purchase until you finish the transaction. **Even after the app has relaunched.**
298
615
  * Android: it will consume purchase for consumables and acknowledge purchase for non-consumables.
299
- * @param {object} purchase The purchase that you would like to finish.
300
- * @param {boolean} isConsumable Checks if purchase is consumable. Has effect on `android`.
301
- * @param {string} developerPayloadAndroid Android developerPayload.
302
- * @returns {Promise<string | void> }
616
+ *
617
+ ```tsx
618
+ import React from 'react';
619
+ import {Button} from 'react-native';
620
+ import {finishTransaction} from 'react-native-iap';
621
+
622
+ const App = () => {
623
+ const handlePurchase = async () => {
624
+ // ... handle the purchase request
625
+
626
+ const result = finishTransaction(purchase);
627
+ };
628
+
629
+ return <Button title="Buy product" onPress={handlePurchase} />;
630
+ };
631
+ ```
303
632
  */
304
633
 
305
634
 
@@ -313,22 +642,28 @@ const finishTransaction = _ref6 => {
313
642
  } = _ref6;
314
643
  return (_reactNative.Platform.select({
315
644
  ios: async () => {
316
- return getIosModule().finishTransaction(purchase.transactionId);
645
+ const transactionId = purchase.transactionId;
646
+
647
+ if (!transactionId) {
648
+ return Promise.reject(new Error('transactionId required to finish iOS transaction'));
649
+ }
650
+
651
+ return getIosModule().finishTransaction(transactionId);
317
652
  },
318
653
  android: async () => {
319
- if (purchase) {
654
+ if (purchase !== null && purchase !== void 0 && purchase.purchaseToken) {
320
655
  if (isConsumable) {
321
656
  return getAndroidModule().consumeProduct(purchase.purchaseToken, developerPayloadAndroid);
322
657
  } else if (purchase.userIdAmazon || !purchase.isAcknowledgedAndroid && purchase.purchaseStateAndroid === _types.PurchaseStateAndroid.PURCHASED) {
323
658
  return getAndroidModule().acknowledgePurchase(purchase.purchaseToken, developerPayloadAndroid);
324
659
  } else {
325
- throw new Error('purchase is not suitable to be purchased');
660
+ return Promise.reject(new Error('purchase is not suitable to be purchased'));
326
661
  }
327
- } else {
328
- throw new Error('purchase is not assigned');
329
662
  }
663
+
664
+ return Promise.reject(new Error('purchase is not suitable to be purchased'));
330
665
  }
331
- }) || Promise.resolve)();
666
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
332
667
  };
333
668
  /**
334
669
  * Clear Transaction (iOS only)
@@ -406,6 +741,7 @@ exports.getPromotedProductIOS = getPromotedProductIOS;
406
741
  const buyPromotedProductIOS = () => getIosModule().buyPromotedProduct();
407
742
 
408
743
  exports.buyPromotedProductIOS = buyPromotedProductIOS;
744
+ const TEST_RECEIPT = 21007;
409
745
 
410
746
  const requestAgnosticReceiptValidationIos = async receiptBody => {
411
747
  const response = await (0, _internal.enhancedFetch)('https://buy.itunes.apple.com/verifyReceipt', {
@@ -414,7 +750,7 @@ const requestAgnosticReceiptValidationIos = async receiptBody => {
414
750
  }); // Best practice is to check for test receipt and check sandbox instead
415
751
  // https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
416
752
 
417
- if (response && response.status === _apple.ReceiptValidationStatus.TEST_RECEIPT) {
753
+ if (response && response.status === TEST_RECEIPT) {
418
754
  const testResponse = await (0, _internal.enhancedFetch)('https://sandbox.itunes.apple.com/verifyReceipt', {
419
755
  method: 'POST',
420
756
  body: receiptBody
@@ -470,7 +806,10 @@ const validateReceiptIos = async _ref10 => {
470
806
  }
471
807
 
472
808
  const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
473
- return await (0, _internal.enhancedFetch)(url);
809
+ return await (0, _internal.enhancedFetch)(url, {
810
+ method: 'POST',
811
+ body: receiptBody
812
+ });
474
813
  };
475
814
  /**
476
815
  * Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including