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
package/lib/module/iap.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { Linking, NativeModules, Platform } from 'react-native';
2
- import { ReceiptValidationStatus } from './types/apple';
3
2
  import { enhancedFetch, fillProductsWithAdditionalData, isAmazon, isAndroid } from './internal';
3
+ import { ProductType } from './types';
4
4
  import { InstallSourceAndroid, PurchaseStateAndroid } from './types';
5
5
  const {
6
6
  RNIapIos,
7
7
  RNIapModule,
8
8
  RNIapAmazonModule
9
9
  } = NativeModules;
10
- const ANDROID_ITEM_TYPE_SUBSCRIPTION = 'subs';
11
- const ANDROID_ITEM_TYPE_IAP = 'inapp';
10
+ const ANDROID_ITEM_TYPE_SUBSCRIPTION = ProductType.subs;
11
+ const ANDROID_ITEM_TYPE_IAP = ProductType.inapp;
12
12
  export const getInstallSourceAndroid = () => {
13
13
  return RNIapModule ? InstallSourceAndroid.GOOGLE_PLAY : InstallSourceAndroid.AMAZON;
14
14
  };
@@ -43,12 +43,42 @@ export const getNativeModule = () => {
43
43
  };
44
44
  /**
45
45
  * Init module for purchase flow. Required on Android. In ios it will check whether user canMakePayment.
46
- * @returns {Promise<boolean>}
46
+ * ## Usage
47
+
48
+ ```tsx
49
+ import React, {useEffect} from 'react';
50
+ import {View} from 'react-native';
51
+ import {initConnection} from 'react-native-iap';
52
+
53
+ const App = () => {
54
+ useEffect(() => {
55
+ void initConnection();
56
+ }, []);
57
+
58
+ return <View />;
59
+ };
60
+ ```
47
61
  */
48
62
 
49
63
  export const initConnection = () => getNativeModule().initConnection();
50
64
  /**
51
- * End module for purchase flow.
65
+ * Disconnects from native SDK
66
+ * Usage
67
+ * ```tsx
68
+ import React, {useEffect} from 'react';
69
+ import {View} from 'react-native';
70
+ import {endConnection} from 'react-native-iap';
71
+
72
+ const App = () => {
73
+ useEffect(() => {
74
+ return () => {
75
+ void endConnection();
76
+ };
77
+ }, []);
78
+
79
+ return <View />;
80
+ };
81
+ ```
52
82
  * @returns {Promise<void>}
53
83
  */
54
84
 
@@ -61,8 +91,47 @@ export const endConnection = () => getNativeModule().endConnection();
61
91
  export const flushFailedPurchasesCachedAsPendingAndroid = () => getAndroidModule().flushFailedPurchasesCachedAsPending();
62
92
  /**
63
93
  * Get a list of products (consumable and non-consumable items, but not subscriptions)
64
- * @param {string[]} skus The item skus
65
- * @returns {Promise<Product[]>}
94
+ ## Usage
95
+
96
+ ```ts
97
+ import React, {useState} from 'react';
98
+ import {Platform} from 'react-native';
99
+ import {getProducts, Product} from 'react-native-iap';
100
+
101
+ const skus = Platform.select({
102
+ ios: ['com.example.consumableIos'],
103
+ android: ['com.example.consumableAndroid'],
104
+ });
105
+
106
+ const App = () => {
107
+ const [products, setProducts] = useState<Product[]>([]);
108
+
109
+ const handleProducts = async () => {
110
+ const items = await getProducts({skus});
111
+
112
+ setProducts(items);
113
+ };
114
+
115
+ useEffect(() => {
116
+ void handleProducts();
117
+ }, []);
118
+
119
+ return (
120
+ <>
121
+ {products.map((product) => (
122
+ <Text key={product.productId}>{product.productId}</Text>
123
+ ))}
124
+ </>
125
+ );
126
+ };
127
+ ```
128
+
129
+ Just a few things to keep in mind:
130
+
131
+ - You can get your products in `componentDidMount`, `useEffect` or another appropriate area of your app.
132
+ - 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.
133
+ - 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.
134
+
66
135
  */
67
136
 
68
137
  export const getProducts = _ref => {
@@ -78,12 +147,28 @@ export const getProducts = _ref => {
78
147
  const products = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_IAP, skus);
79
148
  return fillProductsWithAdditionalData(products);
80
149
  }
81
- }) || Promise.resolve)();
150
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
82
151
  };
83
152
  /**
84
153
  * Get a list of subscriptions
85
- * @param {string[]} skus The item skus
86
- * @returns {Promise<Subscription[]>}
154
+ * ## Usage
155
+
156
+ ```tsx
157
+ import React, {useCallback} from 'react';
158
+ import {View} from 'react-native';
159
+ import {getSubscriptions} from 'react-native-iap';
160
+
161
+ const App = () => {
162
+ const subscriptions = useCallback(
163
+ async () =>
164
+ await getSubscriptions(['com.example.product1', 'com.example.product2']),
165
+ [],
166
+ );
167
+
168
+ return <View />;
169
+ };
170
+ ```
171
+
87
172
  */
88
173
 
89
174
  export const getSubscriptions = _ref2 => {
@@ -99,11 +184,30 @@ export const getSubscriptions = _ref2 => {
99
184
  const subscriptions = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, skus);
100
185
  return fillProductsWithAdditionalData(subscriptions);
101
186
  }
102
- }) || Promise.resolve)();
187
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
103
188
  };
104
189
  /**
105
190
  * Gets an inventory of purchases made by the user regardless of consumption status
106
- * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
191
+ * ## Usage
192
+
193
+ ```tsx
194
+ import React, {useCallback} from 'react';
195
+ import {View} from 'react-native';
196
+ import {getPurchaseHistory} from 'react-native-iap';
197
+
198
+ const App = () => {
199
+ const history = useCallback(
200
+ async () =>
201
+ await getPurchaseHistory([
202
+ 'com.example.product1',
203
+ 'com.example.product2',
204
+ ]),
205
+ [],
206
+ );
207
+
208
+ return <View />;
209
+ };
210
+ ```
107
211
  */
108
212
 
109
213
  export const getPurchaseHistory = () => (Platform.select({
@@ -115,14 +219,88 @@ export const getPurchaseHistory = () => (Platform.select({
115
219
  return await RNIapAmazonModule.getAvailableItems();
116
220
  }
117
221
 
118
- const products = await getAndroidModule().getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
119
- const subscriptions = await getAndroidModule().getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
222
+ const products = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
223
+ const subscriptions = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
120
224
  return products.concat(subscriptions);
121
225
  }
122
- }) || Promise.resolve)();
226
+ }) || (() => Promise.resolve([])))();
123
227
  /**
124
228
  * Get all purchases made by the user (either non-consumable, or haven't been consumed yet)
125
- * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
229
+ * ## Usage
230
+
231
+ ```tsx
232
+ import React, {useCallback} from 'react';
233
+ import {View} from 'react-native';
234
+ import {getAvailablePurchases} from 'react-native-iap';
235
+
236
+ const App = () => {
237
+ const availablePurchases = useCallback(
238
+ async () => await getAvailablePurchases(),
239
+ [],
240
+ );
241
+
242
+ return <View />;
243
+ };
244
+ ```
245
+
246
+ ## Restoring purchases
247
+
248
+ You can use `getAvailablePurchases()` to do what's commonly understood as "restoring" purchases.
249
+
250
+ :::note
251
+ For debugging you may want to consume all items, you have then to iterate over the purchases returned by `getAvailablePurchases()`.
252
+ :::
253
+
254
+ :::warning
255
+ 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.
256
+ :::
257
+
258
+ ```tsx
259
+ import React from 'react';
260
+ import {Button} from 'react-native';
261
+ import {getAvailablePurchases,finishTransaction} from 'react-native-iap';
262
+
263
+ const App = () => {
264
+ handleRestore = async () => {
265
+ try {
266
+ const purchases = await getAvailablePurchases();
267
+ const newState = {premium: false, ads: true};
268
+ let titles = [];
269
+
270
+ await Promise.all(purchases.map(async purchase => {
271
+ switch (purchase.productId) {
272
+ case 'com.example.premium':
273
+ newState.premium = true;
274
+ titles.push('Premium Version');
275
+ break;
276
+
277
+ case 'com.example.no_ads':
278
+ newState.ads = false;
279
+ titles.push('No Ads');
280
+ break;
281
+
282
+ case 'com.example.coins100':
283
+ await finishTransaction(purchase.purchaseToken);
284
+ CoinStore.addCoins(100);
285
+ }
286
+ })
287
+
288
+ Alert.alert(
289
+ 'Restore Successful',
290
+ `You successfully restored the following purchases: ${titles.join(', ')}`,
291
+ );
292
+ } catch (error) {
293
+ console.warn(error);
294
+ Alert.alert(error.message);
295
+ }
296
+ };
297
+
298
+ return (
299
+ <Button title="Restore purchases" onPress={handleRestore} />
300
+ )
301
+ };
302
+ ```
303
+ *
126
304
  */
127
305
 
128
306
  export const getAvailablePurchases = () => (Platform.select({
@@ -134,21 +312,77 @@ export const getAvailablePurchases = () => (Platform.select({
134
312
  return await RNIapAmazonModule.getAvailableItems();
135
313
  }
136
314
 
137
- const products = await getAndroidModule().getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
138
- const subscriptions = await getAndroidModule().getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
315
+ const products = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
316
+ const subscriptions = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
139
317
  return products.concat(subscriptions);
140
318
  }
141
- }) || Promise.resolve)();
319
+ }) || (() => Promise.resolve([])))();
142
320
  /**
143
321
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
144
- * @param {string} sku The product's sku/ID
145
- * @param {string} [applicationUsername] The purchaser's user ID
146
- * @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.
147
- * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
148
- * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
149
- * @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
150
- * @param {boolean} [isOfferPersonalized] Defaults to false, Only for Android V5
151
- * @returns {Promise<ProductPurchase>}
322
+ * Request a purchase for a product (consumables or non-consumables).
323
+
324
+ The response will be received through the `PurchaseUpdatedListener`.
325
+
326
+ :::note
327
+ `andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
328
+ always keeping at false, and verifying the transaction receipts on the server-side.
329
+ :::
330
+
331
+ ## Signature
332
+
333
+ ```ts
334
+ requestPurchase(
335
+ The product's sku/ID
336
+ sku,
337
+
338
+
339
+ * You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
340
+ * @default false
341
+
342
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
343
+
344
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
345
+ obfuscatedAccountIdAndroid,
346
+
347
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
348
+ obfuscatedProfileIdAndroid,
349
+
350
+ The purchaser's user ID
351
+ applicationUsername,
352
+ ): Promise<ProductPurchase>;
353
+ ```
354
+
355
+ ## Usage
356
+
357
+ ```tsx
358
+ import React, {useCallback} from 'react';
359
+ import {Button} from 'react-native';
360
+ import {requestPurchase, Product, Sku, getProducts} from 'react-native-iap';
361
+
362
+ const App = () => {
363
+ const products = useCallback(
364
+ async () => getProducts(['com.example.product']),
365
+ [],
366
+ );
367
+
368
+ const handlePurchase = async (sku: Sku) => {
369
+ await requestPurchase({sku});
370
+ };
371
+
372
+ return (
373
+ <>
374
+ {products.map((product) => (
375
+ <Button
376
+ key={product.productId}
377
+ title="Buy product"
378
+ onPress={() => handlePurchase(product.productId)}
379
+ />
380
+ ))}
381
+ </>
382
+ );
383
+ };
384
+ ```
385
+
152
386
  */
153
387
 
154
388
  export const requestPurchase = _ref3 => {
@@ -163,6 +397,10 @@ export const requestPurchase = _ref3 => {
163
397
  } = _ref3;
164
398
  return (Platform.select({
165
399
  ios: async () => {
400
+ if (!sku) {
401
+ return Promise.reject(new Error('sku is required for iOS purchase'));
402
+ }
403
+
166
404
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
167
405
  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.');
168
406
  }
@@ -171,24 +409,97 @@ export const requestPurchase = _ref3 => {
171
409
  },
172
410
  android: async () => {
173
411
  if (isAmazon) {
412
+ if (!sku) {
413
+ return Promise.reject(new Error('sku is required for Amazon purchase'));
414
+ }
415
+
174
416
  return RNIapAmazonModule.buyItemByType(sku);
175
417
  } else {
176
- return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], null, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
418
+ if (!(sku !== null && sku !== void 0 && sku.length) && !sku) {
419
+ return Promise.reject(new Error('skus is required for Android purchase'));
420
+ }
421
+
422
+ return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], undefined, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
177
423
  }
178
424
  }
179
425
  }) || Promise.resolve)();
180
426
  };
181
427
  /**
182
428
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
183
- * @param {string} [sku] The product's sku/ID
184
- * @param {string} [applicationUsername] The purchaser's user ID
185
- * @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.
186
- * @param {string} [purchaseTokenAndroid] purchaseToken that the user is upgrading or downgrading from (Android).
187
- * @param {ProrationModesAndroid} [prorationModeAndroid] UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
188
- * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
189
- * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
190
- * @param {SubscriptionOffers[]} [subscriptionOffers] Array of SubscriptionOffers. Every sku must be paired with a corresponding offerToken
191
- * @returns {Promise<SubscriptionPurchase | null>} Promise resolves to null when using proratioModesAndroid=DEFERRED, and to a SubscriptionPurchase otherwise
429
+ * Request a purchase for a subscription.
430
+
431
+ The response will be received through the `PurchaseUpdatedListener`.
432
+
433
+ :::note
434
+ `andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
435
+ always keeping at false, and verifying the transaction receipts on the server-side.
436
+ :::
437
+
438
+ ## Signature
439
+
440
+ ```ts
441
+ requestSubscription(
442
+ The product's sku/ID
443
+ sku,
444
+
445
+
446
+ * You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
447
+ * @default false
448
+
449
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
450
+
451
+ purchaseToken that the user is upgrading or downgrading from (Android).
452
+ purchaseTokenAndroid,
453
+
454
+ UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
455
+ prorationModeAndroid = -1,
456
+
457
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
458
+ obfuscatedAccountIdAndroid,
459
+
460
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
461
+ obfuscatedProfileIdAndroid,
462
+
463
+ The purchaser's user ID
464
+ applicationUsername,
465
+ ): Promise<SubscriptionPurchase>
466
+ ```
467
+
468
+ ## Usage
469
+
470
+ ```tsx
471
+ import React, {useCallback} from 'react';
472
+ import {Button} from 'react-native';
473
+ import {
474
+ requestSubscription,
475
+ Product,
476
+ Sku,
477
+ getSubscriptions,
478
+ } from 'react-native-iap';
479
+
480
+ const App = () => {
481
+ const subscriptions = useCallback(
482
+ async () => getSubscriptions(['com.example.subscription']),
483
+ [],
484
+ );
485
+
486
+ const handlePurchase = async (sku: Sku) => {
487
+ await requestSubscription({sku});
488
+ };
489
+
490
+ return (
491
+ <>
492
+ {subscriptions.map((subscription) => (
493
+ <Button
494
+ key={subscription.productId}
495
+ title="Buy subscription"
496
+ onPress={() => handlePurchase(subscription.productId)}
497
+ />
498
+ ))}
499
+ </>
500
+ );
501
+ };
502
+ ```
192
503
  */
193
504
 
194
505
  export const requestSubscription = _ref4 => {
@@ -205,6 +516,10 @@ export const requestSubscription = _ref4 => {
205
516
  } = _ref4;
206
517
  return (Platform.select({
207
518
  ios: async () => {
519
+ if (!sku) {
520
+ return Promise.reject(new Error('sku is required for iOS subscription'));
521
+ }
522
+
208
523
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
209
524
  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.');
210
525
  }
@@ -213,6 +528,10 @@ export const requestSubscription = _ref4 => {
213
528
  },
214
529
  android: async () => {
215
530
  if (isAmazon) {
531
+ if (!sku) {
532
+ return Promise.reject(new Error('sku is required for Amazon purchase'));
533
+ }
534
+
216
535
  return RNIapAmazonModule.buyItemByType(sku);
217
536
  } else {
218
537
  if (!(subscriptionOffers !== null && subscriptionOffers !== void 0 && subscriptionOffers.length)) {
@@ -222,7 +541,7 @@ export const requestSubscription = _ref4 => {
222
541
  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);
223
542
  }
224
543
  }
225
- }) || Promise.resolve)();
544
+ }) || (() => Promise.resolve(null)))();
226
545
  };
227
546
  /**
228
547
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
@@ -244,10 +563,22 @@ export const requestPurchaseWithQuantityIOS = _ref5 => {
244
563
  * Call this after you have persisted the purchased state to your server or local data in your app.
245
564
  * `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.**
246
565
  * Android: it will consume purchase for consumables and acknowledge purchase for non-consumables.
247
- * @param {object} purchase The purchase that you would like to finish.
248
- * @param {boolean} isConsumable Checks if purchase is consumable. Has effect on `android`.
249
- * @param {string} developerPayloadAndroid Android developerPayload.
250
- * @returns {Promise<string | void> }
566
+ *
567
+ ```tsx
568
+ import React from 'react';
569
+ import {Button} from 'react-native';
570
+ import {finishTransaction} from 'react-native-iap';
571
+
572
+ const App = () => {
573
+ const handlePurchase = async () => {
574
+ // ... handle the purchase request
575
+
576
+ const result = finishTransaction(purchase);
577
+ };
578
+
579
+ return <Button title="Buy product" onPress={handlePurchase} />;
580
+ };
581
+ ```
251
582
  */
252
583
 
253
584
  export const finishTransaction = _ref6 => {
@@ -258,22 +589,28 @@ export const finishTransaction = _ref6 => {
258
589
  } = _ref6;
259
590
  return (Platform.select({
260
591
  ios: async () => {
261
- return getIosModule().finishTransaction(purchase.transactionId);
592
+ const transactionId = purchase.transactionId;
593
+
594
+ if (!transactionId) {
595
+ return Promise.reject(new Error('transactionId required to finish iOS transaction'));
596
+ }
597
+
598
+ return getIosModule().finishTransaction(transactionId);
262
599
  },
263
600
  android: async () => {
264
- if (purchase) {
601
+ if (purchase !== null && purchase !== void 0 && purchase.purchaseToken) {
265
602
  if (isConsumable) {
266
603
  return getAndroidModule().consumeProduct(purchase.purchaseToken, developerPayloadAndroid);
267
604
  } else if (purchase.userIdAmazon || !purchase.isAcknowledgedAndroid && purchase.purchaseStateAndroid === PurchaseStateAndroid.PURCHASED) {
268
605
  return getAndroidModule().acknowledgePurchase(purchase.purchaseToken, developerPayloadAndroid);
269
606
  } else {
270
- throw new Error('purchase is not suitable to be purchased');
607
+ return Promise.reject(new Error('purchase is not suitable to be purchased'));
271
608
  }
272
- } else {
273
- throw new Error('purchase is not assigned');
274
609
  }
610
+
611
+ return Promise.reject(new Error('purchase is not suitable to be purchased'));
275
612
  }
276
- }) || Promise.resolve)();
613
+ }) || (() => Promise.reject(new Error('Unsupported Platform'))))();
277
614
  };
278
615
  /**
279
616
  * Clear Transaction (iOS only)
@@ -331,6 +668,7 @@ export const getPromotedProductIOS = () => getIosModule().promotedProduct();
331
668
  */
332
669
 
333
670
  export const buyPromotedProductIOS = () => getIosModule().buyPromotedProduct();
671
+ const TEST_RECEIPT = 21007;
334
672
 
335
673
  const requestAgnosticReceiptValidationIos = async receiptBody => {
336
674
  const response = await enhancedFetch('https://buy.itunes.apple.com/verifyReceipt', {
@@ -339,7 +677,7 @@ const requestAgnosticReceiptValidationIos = async receiptBody => {
339
677
  }); // Best practice is to check for test receipt and check sandbox instead
340
678
  // https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
341
679
 
342
- if (response && response.status === ReceiptValidationStatus.TEST_RECEIPT) {
680
+ if (response && response.status === TEST_RECEIPT) {
343
681
  const testResponse = await enhancedFetch('https://sandbox.itunes.apple.com/verifyReceipt', {
344
682
  method: 'POST',
345
683
  body: receiptBody
@@ -392,7 +730,10 @@ export const validateReceiptIos = async _ref10 => {
392
730
  }
393
731
 
394
732
  const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
395
- return await enhancedFetch(url);
733
+ return await enhancedFetch(url, {
734
+ method: 'POST',
735
+ body: receiptBody
736
+ });
396
737
  };
397
738
  /**
398
739
  * Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including