react-native-iap 9.0.4 → 10.0.2

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 (70) hide show
  1. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +2 -1
  2. package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +2 -1
  3. package/ios/RNIapIos.swift +848 -848
  4. package/lib/commonjs/eventEmitter.js +54 -0
  5. package/lib/commonjs/eventEmitter.js.map +1 -0
  6. package/lib/commonjs/hooks/useIAP.js +33 -15
  7. package/lib/commonjs/hooks/useIAP.js.map +1 -1
  8. package/lib/commonjs/hooks/withIAPContext.js +9 -7
  9. package/lib/commonjs/hooks/withIAPContext.js.map +1 -1
  10. package/lib/commonjs/iap.js +127 -197
  11. package/lib/commonjs/iap.js.map +1 -1
  12. package/lib/commonjs/index.js +28 -15
  13. package/lib/commonjs/index.js.map +1 -1
  14. package/lib/commonjs/internal/enhancedFetch.js +30 -0
  15. package/lib/commonjs/internal/enhancedFetch.js.map +1 -0
  16. package/lib/commonjs/internal/fillProductsWithAdditionalData.js +47 -0
  17. package/lib/commonjs/internal/fillProductsWithAdditionalData.js.map +1 -0
  18. package/lib/commonjs/internal/index.js +45 -0
  19. package/lib/commonjs/internal/index.js.map +1 -0
  20. package/lib/commonjs/internal/platform.js +19 -0
  21. package/lib/commonjs/internal/platform.js.map +1 -0
  22. package/lib/commonjs/purchaseError.js +47 -0
  23. package/lib/commonjs/purchaseError.js.map +1 -0
  24. package/lib/commonjs/types/index.js +1 -23
  25. package/lib/commonjs/types/index.js.map +1 -1
  26. package/lib/module/eventEmitter.js +36 -0
  27. package/lib/module/eventEmitter.js.map +1 -0
  28. package/lib/module/hooks/useIAP.js +34 -16
  29. package/lib/module/hooks/useIAP.js.map +1 -1
  30. package/lib/module/hooks/withIAPContext.js +6 -5
  31. package/lib/module/hooks/withIAPContext.js.map +1 -1
  32. package/lib/module/iap.js +118 -185
  33. package/lib/module/iap.js.map +1 -1
  34. package/lib/module/index.js +2 -2
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/internal/enhancedFetch.js +21 -0
  37. package/lib/module/internal/enhancedFetch.js.map +1 -0
  38. package/lib/module/internal/fillProductsWithAdditionalData.js +37 -0
  39. package/lib/module/internal/fillProductsWithAdditionalData.js.map +1 -0
  40. package/lib/module/internal/index.js +4 -0
  41. package/lib/module/internal/index.js.map +1 -0
  42. package/lib/module/internal/platform.js +8 -0
  43. package/lib/module/internal/platform.js.map +1 -0
  44. package/lib/module/purchaseError.js +38 -0
  45. package/lib/module/purchaseError.js.map +1 -0
  46. package/lib/module/types/index.js +0 -21
  47. package/lib/module/types/index.js.map +1 -1
  48. package/lib/typescript/eventEmitter.d.ts +17 -0
  49. package/lib/typescript/hooks/useIAP.d.ts +15 -9
  50. package/lib/typescript/hooks/withIAPContext.d.ts +4 -3
  51. package/lib/typescript/iap.d.ts +80 -41
  52. package/lib/typescript/index.d.ts +2 -2
  53. package/lib/typescript/internal/enhancedFetch.d.ts +6 -0
  54. package/lib/typescript/internal/fillProductsWithAdditionalData.d.ts +5 -0
  55. package/lib/typescript/internal/index.d.ts +3 -0
  56. package/lib/typescript/internal/platform.d.ts +3 -0
  57. package/lib/typescript/purchaseError.d.ts +26 -0
  58. package/lib/typescript/types/index.d.ts +1 -48
  59. package/package.json +1 -1
  60. package/src/eventEmitter.ts +49 -0
  61. package/src/hooks/useIAP.ts +38 -35
  62. package/src/hooks/withIAPContext.tsx +12 -13
  63. package/src/iap.ts +151 -232
  64. package/src/index.ts +2 -4
  65. package/src/internal/enhancedFetch.ts +25 -0
  66. package/src/internal/fillProductsWithAdditionalData.ts +43 -0
  67. package/src/internal/index.ts +3 -0
  68. package/src/internal/platform.ts +7 -0
  69. package/src/purchaseError.ts +35 -0
  70. package/src/types/index.ts +1 -59
package/src/iap.ts CHANGED
@@ -1,47 +1,31 @@
1
- import {
2
- EmitterSubscription,
3
- Linking,
4
- NativeEventEmitter,
5
- NativeModules,
6
- Platform,
7
- } from 'react-native';
1
+ import {Linking, NativeModules, Platform} from 'react-native';
8
2
 
9
3
  import type * as Amazon from './types/amazon';
10
4
  import type * as Android from './types/android';
11
5
  import type * as Apple from './types/apple';
12
6
  import {ReceiptValidationStatus} from './types/apple';
7
+ import {
8
+ enhancedFetch,
9
+ fillProductsWithAdditionalData,
10
+ isAmazon,
11
+ isAndroid,
12
+ } from './internal';
13
13
  import type {
14
- InAppPurchase,
15
14
  Product,
16
- ProductCommon,
17
15
  ProductPurchase,
18
- PurchaseError,
16
+ ProrationModesAndroid,
19
17
  PurchaseResult,
20
- RequestPurchase,
21
- RequestSubscription,
18
+ Sku,
22
19
  Subscription,
20
+ SubscriptionOffer,
23
21
  SubscriptionPurchase,
24
22
  } from './types';
25
- import {
26
- IAPErrorCode,
27
- InstallSourceAndroid,
28
- PurchaseStateAndroid,
29
- } from './types';
23
+ import {InstallSourceAndroid, PurchaseStateAndroid} from './types';
30
24
 
31
25
  const {RNIapIos, RNIapModule, RNIapAmazonModule} = NativeModules;
32
- const isAndroid = Platform.OS === 'android';
33
- const isAmazon = isAndroid && !!RNIapAmazonModule;
34
- const isIos = Platform.OS === 'ios';
35
26
  const ANDROID_ITEM_TYPE_SUBSCRIPTION = 'subs';
36
27
  const ANDROID_ITEM_TYPE_IAP = 'inapp';
37
28
 
38
- export class IapError implements PurchaseError {
39
- constructor(public code?: string, public message?: string) {
40
- this.code = code;
41
- this.message = message;
42
- }
43
- }
44
-
45
29
  export const getInstallSourceAndroid = (): InstallSourceAndroid => {
46
30
  return RNIapModule
47
31
  ? InstallSourceAndroid.GOOGLE_PLAY
@@ -58,11 +42,13 @@ export const setAndroidNativeModule = (
58
42
 
59
43
  const checkNativeAndroidAvailable = (): void => {
60
44
  if (!RNIapModule && !RNIapAmazonModule) {
61
- throw new Error(IAPErrorCode.E_IAP_NOT_AVAILABLE);
45
+ throw new Error('IAP_NOT_AVAILABLE');
62
46
  }
63
47
  };
64
48
 
65
- const getAndroidModule = (): typeof RNIapModule | typeof RNIapAmazonModule => {
49
+ export const getAndroidModule = ():
50
+ | typeof RNIapModule
51
+ | typeof RNIapAmazonModule => {
66
52
  checkNativeAndroidAvailable();
67
53
 
68
54
  return androidNativeModule
@@ -74,17 +60,17 @@ const getAndroidModule = (): typeof RNIapModule | typeof RNIapAmazonModule => {
74
60
 
75
61
  const checkNativeIOSAvailable = (): void => {
76
62
  if (!RNIapIos) {
77
- throw new Error(IAPErrorCode.E_IAP_NOT_AVAILABLE);
63
+ throw new Error('IAP_NOT_AVAILABLE');
78
64
  }
79
65
  };
80
66
 
81
- const getIosModule = (): typeof RNIapIos => {
67
+ export const getIosModule = (): typeof RNIapIos => {
82
68
  checkNativeIOSAvailable();
83
69
 
84
70
  return RNIapIos;
85
71
  };
86
72
 
87
- const getNativeModule = ():
73
+ export const getNativeModule = ():
88
74
  | typeof RNIapModule
89
75
  | typeof RNIapAmazonModule
90
76
  | typeof RNIapIos => {
@@ -113,52 +99,16 @@ export const flushFailedPurchasesCachedAsPendingAndroid = (): Promise<
113
99
  string[]
114
100
  > => getAndroidModule().flushFailedPurchasesCachedAsPending();
115
101
 
116
- /**
117
- * Fill products with additional data
118
- * @param {Array<ProductCommon>} products Products
119
- */
120
- const fillProductsAdditionalData = async (
121
- products: Array<ProductCommon>,
122
- ): Promise<Array<ProductCommon>> => {
123
- // Amazon
124
- if (RNIapAmazonModule) {
125
- // On amazon we must get the user marketplace to detect the currency
126
- const user = await RNIapAmazonModule.getUser();
127
-
128
- const currencies = {
129
- CA: 'CAD',
130
- ES: 'EUR',
131
- AU: 'AUD',
132
- DE: 'EUR',
133
- IN: 'INR',
134
- US: 'USD',
135
- JP: 'JPY',
136
- GB: 'GBP',
137
- IT: 'EUR',
138
- BR: 'BRL',
139
- FR: 'EUR',
140
- };
141
-
142
- const currency =
143
- currencies[user.userMarketplaceAmazon as keyof typeof currencies];
144
-
145
- // Add currency to products
146
- products.forEach((product) => {
147
- if (currency) {
148
- product.currency = currency;
149
- }
150
- });
151
- }
152
-
153
- return products;
154
- };
155
-
156
102
  /**
157
103
  * Get a list of products (consumable and non-consumable items, but not subscriptions)
158
104
  * @param {string[]} skus The item skus
159
105
  * @returns {Promise<Product[]>}
160
106
  */
161
- export const getProducts = (skus: string[]): Promise<Array<Product>> =>
107
+ export const getProducts = ({
108
+ skus,
109
+ }: {
110
+ skus: string[];
111
+ }): Promise<Array<Product>> =>
162
112
  (
163
113
  Platform.select({
164
114
  ios: async () => {
@@ -175,7 +125,7 @@ export const getProducts = (skus: string[]): Promise<Array<Product>> =>
175
125
  skus,
176
126
  );
177
127
 
178
- return fillProductsAdditionalData(products);
128
+ return fillProductsWithAdditionalData(products);
179
129
  },
180
130
  }) || Promise.resolve
181
131
  )();
@@ -185,7 +135,11 @@ export const getProducts = (skus: string[]): Promise<Array<Product>> =>
185
135
  * @param {string[]} skus The item skus
186
136
  * @returns {Promise<Subscription[]>}
187
137
  */
188
- export const getSubscriptions = (skus: string[]): Promise<Subscription[]> =>
138
+ export const getSubscriptions = ({
139
+ skus,
140
+ }: {
141
+ skus: string[];
142
+ }): Promise<Subscription[]> =>
189
143
  (
190
144
  Platform.select({
191
145
  ios: async () => {
@@ -202,17 +156,17 @@ export const getSubscriptions = (skus: string[]): Promise<Subscription[]> =>
202
156
  skus,
203
157
  );
204
158
 
205
- return fillProductsAdditionalData(subscriptions);
159
+ return fillProductsWithAdditionalData(subscriptions);
206
160
  },
207
161
  }) || Promise.resolve
208
162
  )();
209
163
 
210
164
  /**
211
165
  * Gets an inventory of purchases made by the user regardless of consumption status
212
- * @returns {Promise<(InAppPurchase | SubscriptionPurchase)[]>}
166
+ * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
213
167
  */
214
168
  export const getPurchaseHistory = (): Promise<
215
- (InAppPurchase | SubscriptionPurchase)[]
169
+ (ProductPurchase | SubscriptionPurchase)[]
216
170
  > =>
217
171
  (
218
172
  Platform.select({
@@ -239,10 +193,10 @@ export const getPurchaseHistory = (): Promise<
239
193
 
240
194
  /**
241
195
  * Get all purchases made by the user (either non-consumable, or haven't been consumed yet)
242
- * @returns {Promise<(InAppPurchase | SubscriptionPurchase)[]>}
196
+ * @returns {Promise<(ProductPurchase | SubscriptionPurchase)[]>}
243
197
  */
244
198
  export const getAvailablePurchases = (): Promise<
245
- (InAppPurchase | SubscriptionPurchase)[]
199
+ (ProductPurchase | SubscriptionPurchase)[]
246
200
  > =>
247
201
  (
248
202
  Platform.select({
@@ -276,18 +230,27 @@ export const getAvailablePurchases = (): Promise<
276
230
  * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
277
231
  * @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
278
232
  * @param {boolean} [isOfferPersonalized] Defaults to false, Only for Android V5
279
- * @returns {Promise<InAppPurchase>}
233
+ * @returns {Promise<ProductPurchase>}
280
234
  */
281
235
 
282
236
  export const requestPurchase = ({
283
237
  sku,
284
238
  andDangerouslyFinishTransactionAutomaticallyIOS = false,
239
+ applicationUsername,
285
240
  obfuscatedAccountIdAndroid,
286
241
  obfuscatedProfileIdAndroid,
287
- applicationUsername,
288
- skus, // Android Billing V5
289
- isOfferPersonalized = undefined, // Android Billing V5
290
- }: RequestPurchase): Promise<InAppPurchase> =>
242
+ skus,
243
+ isOfferPersonalized,
244
+ }: {
245
+ sku?: Sku;
246
+ andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
247
+ applicationUsername?: string;
248
+ obfuscatedAccountIdAndroid?: string;
249
+ obfuscatedProfileIdAndroid?: string;
250
+ /** For Google Play Billing Library 5 https://developer.android.com/google/play/billing/integrate#personalized-price */
251
+ skus?: Sku[];
252
+ isOfferPersonalized?: boolean;
253
+ }): Promise<ProductPurchase> =>
291
254
  (
292
255
  Platform.select({
293
256
  ios: async () => {
@@ -337,14 +300,26 @@ export const requestPurchase = ({
337
300
  export const requestSubscription = ({
338
301
  sku,
339
302
  andDangerouslyFinishTransactionAutomaticallyIOS = false,
303
+ applicationUsername,
340
304
  purchaseTokenAndroid,
341
305
  prorationModeAndroid = -1,
306
+ subscriptionOffers,
342
307
  obfuscatedAccountIdAndroid,
343
308
  obfuscatedProfileIdAndroid,
344
- subscriptionOffers = undefined, // Android Billing V5
345
- isOfferPersonalized = undefined, // Android Billing V5
346
- applicationUsername,
347
- }: RequestSubscription): Promise<SubscriptionPurchase | null> =>
309
+ isOfferPersonalized = undefined,
310
+ }: {
311
+ sku?: Sku;
312
+ andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
313
+ applicationUsername?: string;
314
+ purchaseTokenAndroid?: string;
315
+ prorationModeAndroid?: ProrationModesAndroid;
316
+ /** For Google Play Billing Library 5 */
317
+ subscriptionOffers?: SubscriptionOffer[];
318
+ obfuscatedAccountIdAndroid?: string;
319
+ obfuscatedProfileIdAndroid?: string;
320
+ /** For Google Play Billing Library 5 https://developer.android.com/google/play/billing/integrate#personalized-price */
321
+ isOfferPersonalized?: boolean;
322
+ }): Promise<SubscriptionPurchase | null> =>
348
323
  (
349
324
  Platform.select({
350
325
  ios: async () => {
@@ -390,10 +365,13 @@ export const requestSubscription = ({
390
365
  * @param {string} sku The product's sku/ID
391
366
  * @returns {Promise<void>}
392
367
  */
393
- export const requestPurchaseWithQuantityIOS = (
394
- sku: string,
395
- quantity: number,
396
- ): Promise<InAppPurchase> =>
368
+ export const requestPurchaseWithQuantityIOS = ({
369
+ sku,
370
+ quantity,
371
+ }: {
372
+ sku: Sku;
373
+ quantity: number;
374
+ }): Promise<ProductPurchase> =>
397
375
  getIosModule().buyProductWithQuantityIOS(sku, quantity);
398
376
 
399
377
  /**
@@ -408,11 +386,15 @@ export const requestPurchaseWithQuantityIOS = (
408
386
  * @param {string} developerPayloadAndroid Android developerPayload.
409
387
  * @returns {Promise<string | void> }
410
388
  */
411
- export const finishTransaction = (
412
- purchase: InAppPurchase | ProductPurchase,
413
- isConsumable?: boolean,
414
- developerPayloadAndroid?: string,
415
- ): Promise<string | void> => {
389
+ export const finishTransaction = ({
390
+ purchase,
391
+ isConsumable,
392
+ developerPayloadAndroid,
393
+ }: {
394
+ purchase: ProductPurchase | ProductPurchase;
395
+ isConsumable?: boolean;
396
+ developerPayloadAndroid?: string;
397
+ }): Promise<string | void> => {
416
398
  return (
417
399
  Platform.select({
418
400
  ios: async () => {
@@ -468,10 +450,13 @@ export const clearProductsIOS = (): Promise<void> =>
468
450
  * @param {string} token The product's token (on Android)
469
451
  * @returns {Promise<PurchaseResult | void>}
470
452
  */
471
- export const acknowledgePurchaseAndroid = (
472
- token: string,
473
- developerPayload?: string,
474
- ): Promise<PurchaseResult | void> => {
453
+ export const acknowledgePurchaseAndroid = ({
454
+ token,
455
+ developerPayload,
456
+ }: {
457
+ token: string;
458
+ developerPayload?: string;
459
+ }): Promise<PurchaseResult | void> => {
475
460
  return getAndroidModule().acknowledgePurchase(token, developerPayload);
476
461
  };
477
462
 
@@ -480,9 +465,11 @@ export const acknowledgePurchaseAndroid = (
480
465
  * @param {string} sku The product's SKU (on Android)
481
466
  * @returns {Promise<void>}
482
467
  */
483
- export const deepLinkToSubscriptionsAndroid = async (
484
- sku: string,
485
- ): Promise<void> => {
468
+ export const deepLinkToSubscriptionsAndroid = async ({
469
+ sku,
470
+ }: {
471
+ sku: Sku;
472
+ }): Promise<void> => {
486
473
  checkNativeAndroidAvailable();
487
474
 
488
475
  return Linking.openURL(
@@ -506,42 +493,26 @@ export const getPromotedProductIOS = (): Promise<Product | null> =>
506
493
  export const buyPromotedProductIOS = (): Promise<void> =>
507
494
  getIosModule().buyPromotedProduct();
508
495
 
509
- const fetchJsonOrThrow = async (
510
- url: string,
511
- receiptBody: Record<string, unknown>,
512
- ): Promise<Apple.ReceiptValidationResponse | false> => {
513
- const response = await fetch(url, {
514
- method: 'POST',
515
- headers: {
516
- Accept: 'application/json',
517
- 'Content-Type': 'application/json',
518
- },
519
- body: JSON.stringify(receiptBody),
520
- });
521
-
522
- if (!response.ok) {
523
- throw Object.assign(new Error(response.statusText), {
524
- statusCode: response.status,
525
- });
526
- }
527
-
528
- return response.json();
529
- };
530
-
531
496
  const requestAgnosticReceiptValidationIos = async (
532
497
  receiptBody: Record<string, unknown>,
533
498
  ): Promise<Apple.ReceiptValidationResponse | false> => {
534
- const response = await fetchJsonOrThrow(
499
+ const response = await enhancedFetch<Apple.ReceiptValidationResponse>(
535
500
  'https://buy.itunes.apple.com/verifyReceipt',
536
- receiptBody,
501
+ {
502
+ method: 'POST',
503
+ body: receiptBody,
504
+ },
537
505
  );
538
506
 
539
507
  // Best practice is to check for test receipt and check sandbox instead
540
508
  // https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
541
509
  if (response && response.status === ReceiptValidationStatus.TEST_RECEIPT) {
542
- const testResponse = await fetchJsonOrThrow(
510
+ const testResponse = await enhancedFetch<Apple.ReceiptValidationResponse>(
543
511
  'https://sandbox.itunes.apple.com/verifyReceipt',
544
- receiptBody,
512
+ {
513
+ method: 'POST',
514
+ body: receiptBody,
515
+ },
545
516
  );
546
517
 
547
518
  return testResponse;
@@ -565,11 +536,16 @@ const requestAgnosticReceiptValidationIos = async (
565
536
  * @param {number} withOffer.timestamp The timestamp of the signature
566
537
  * @returns {Promise<void>}
567
538
  */
568
- export const requestPurchaseWithOfferIOS = (
569
- sku: string,
570
- forUser: string,
571
- withOffer: Apple.PaymentDiscount,
572
- ): Promise<void> => getIosModule().buyProductWithOffer(sku, forUser, withOffer);
539
+ export const requestPurchaseWithOfferIOS = ({
540
+ sku,
541
+ forUser,
542
+ withOffer,
543
+ }: {
544
+ sku: Sku;
545
+ forUser: string;
546
+ withOffer: Apple.PaymentDiscount;
547
+ }): Promise<void> =>
548
+ getIosModule().buyProductWithOffer(sku, forUser, withOffer);
573
549
 
574
550
  /**
575
551
  * Validate receipt for iOS.
@@ -577,10 +553,13 @@ export const requestPurchaseWithOfferIOS = (
577
553
  * @param {boolean} isTest whether this is in test environment which is sandbox.
578
554
  * @returns {Promise<Apple.ReceiptValidationResponse | false>}
579
555
  */
580
- export const validateReceiptIos = async (
581
- receiptBody: Record<string, unknown>,
582
- isTest?: boolean,
583
- ): Promise<Apple.ReceiptValidationResponse | false> => {
556
+ export const validateReceiptIos = async ({
557
+ receiptBody,
558
+ isTest,
559
+ }: {
560
+ receiptBody: Record<string, unknown>;
561
+ isTest?: boolean;
562
+ }): Promise<Apple.ReceiptValidationResponse | false> => {
584
563
  if (isTest == null) {
585
564
  return await requestAgnosticReceiptValidationIos(receiptBody);
586
565
  }
@@ -589,9 +568,7 @@ export const validateReceiptIos = async (
589
568
  ? 'https://sandbox.itunes.apple.com/verifyReceipt'
590
569
  : 'https://buy.itunes.apple.com/verifyReceipt';
591
570
 
592
- const response = await fetchJsonOrThrow(url, receiptBody);
593
-
594
- return response;
571
+ return await enhancedFetch<Apple.ReceiptValidationResponse>(url);
595
572
  };
596
573
 
597
574
  /**
@@ -605,13 +582,19 @@ export const validateReceiptIos = async (
605
582
  * @param {boolean} isSub whether this is subscription or inapp. `true` for subscription.
606
583
  * @returns {Promise<object>}
607
584
  */
608
- export const validateReceiptAndroid = async (
609
- packageName: string,
610
- productId: string,
611
- productToken: string,
612
- accessToken: string,
613
- isSub?: boolean,
614
- ): Promise<Android.ReceiptType> => {
585
+ export const validateReceiptAndroid = async ({
586
+ packageName,
587
+ productId,
588
+ productToken,
589
+ accessToken,
590
+ isSub,
591
+ }: {
592
+ packageName: string;
593
+ productId: string;
594
+ productToken: string;
595
+ accessToken: string;
596
+ isSub?: boolean;
597
+ }): Promise<Android.ReceiptType> => {
615
598
  const type = isSub ? 'subscriptions' : 'products';
616
599
 
617
600
  const url =
@@ -619,20 +602,7 @@ export const validateReceiptAndroid = async (
619
602
  `/${packageName}/purchases/${type}/${productId}` +
620
603
  `/tokens/${productToken}?access_token=${accessToken}`;
621
604
 
622
- const response = await fetch(url, {
623
- method: 'GET',
624
- headers: {
625
- 'Content-Type': 'application/json',
626
- },
627
- });
628
-
629
- if (!response.ok) {
630
- throw Object.assign(new Error(response.statusText), {
631
- statusCode: response.status,
632
- });
633
- }
634
-
635
- return response.json();
605
+ return await enhancedFetch<Android.ReceiptType>(url);
636
606
  };
637
607
 
638
608
  /**
@@ -645,75 +615,21 @@ export const validateReceiptAndroid = async (
645
615
  * @param {boolean} useSandbox Defaults to true, use sandbox environment or production.
646
616
  * @returns {Promise<object>}
647
617
  */
648
- export const validateReceiptAmazon = async (
649
- developerSecret: string,
650
- userId: string,
651
- receiptId: string,
652
- useSandbox: boolean = true,
653
- ): Promise<Amazon.ReceiptType> => {
618
+ export const validateReceiptAmazon = async ({
619
+ developerSecret,
620
+ userId,
621
+ receiptId,
622
+ useSandbox = true,
623
+ }: {
624
+ developerSecret: string;
625
+ userId: string;
626
+ receiptId: string;
627
+ useSandbox: boolean;
628
+ }): Promise<Amazon.ReceiptType> => {
654
629
  const sandBoxUrl = useSandbox ? 'sandbox/' : '';
655
630
  const url = `https://appstore-sdk.amazon.com/${sandBoxUrl}version/1.0/verifyReceiptId/developer/${developerSecret}/user/${userId}/receiptId/${receiptId}`;
656
631
 
657
- const response = await fetch(url, {
658
- method: 'GET',
659
- headers: {
660
- 'Content-Type': 'application/json',
661
- },
662
- });
663
-
664
- if (!response.ok) {
665
- throw Object.assign(new Error(response.statusText), {
666
- statusCode: response.status,
667
- });
668
- }
669
-
670
- return response.json();
671
- };
672
-
673
- /**
674
- * Add IAP purchase event
675
- * @returns {callback(e: InAppPurchase | ProductPurchase)}
676
- */
677
- export const purchaseUpdatedListener = (
678
- listener: (event: InAppPurchase | SubscriptionPurchase) => void,
679
- ): EmitterSubscription => {
680
- const emitterSubscription = new NativeEventEmitter(
681
- getNativeModule(),
682
- ).addListener('purchase-updated', listener);
683
-
684
- if (isAndroid) {
685
- getAndroidModule().startListening();
686
- }
687
-
688
- return emitterSubscription;
689
- };
690
-
691
- /**
692
- * Add IAP purchase error event
693
- * @returns {callback(e: PurchaseError)}
694
- */
695
- export const purchaseErrorListener = (
696
- listener: (errorEvent: PurchaseError) => void,
697
- ): EmitterSubscription =>
698
- new NativeEventEmitter(getNativeModule()).addListener(
699
- 'purchase-error',
700
- listener,
701
- );
702
-
703
- /**
704
- * Add IAP promoted subscription event
705
- * Only available on iOS
706
- */
707
- export const promotedProductListener = (
708
- listener: (productId?: string) => void,
709
- ): EmitterSubscription | null => {
710
- if (isIos) {
711
- return new NativeEventEmitter(getIosModule()).addListener(
712
- 'iap-promoted-product',
713
- listener,
714
- );
715
- }
716
- return null;
632
+ return await enhancedFetch<Amazon.ReceiptType>(url);
717
633
  };
718
634
 
719
635
  /**
@@ -729,8 +645,11 @@ export const getPendingPurchasesIOS = async (): Promise<ProductPurchase[]> =>
729
645
  * @param {forceRefresh?:boolean}
730
646
  * @returns {Promise<string>}
731
647
  */
732
- export const getReceiptIOS = async (forceRefresh?: boolean): Promise<string> =>
733
- getIosModule().requestReceipt(forceRefresh ?? false);
648
+ export const getReceiptIOS = async ({
649
+ forceRefresh,
650
+ }: {
651
+ forceRefresh?: boolean;
652
+ }): Promise<string> => getIosModule().requestReceipt(forceRefresh ?? false);
734
653
 
735
654
  /**
736
655
  * Launches a modal to register the redeem offer code in IOS.
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
1
- import * as iap from './iap';
2
-
3
1
  export * from './iap';
4
2
  export * from './types';
3
+ export * from './eventEmitter';
5
4
  export * from './hooks/useIAP';
6
5
  export * from './hooks/withIAPContext';
7
-
8
- export default iap;
6
+ export * from './purchaseError';
@@ -0,0 +1,25 @@
1
+ interface OverwrittenRequestInit extends Omit<RequestInit, 'body'> {
2
+ body: Record<string, any>;
3
+ }
4
+
5
+ export const enhancedFetch = async <T = any>(
6
+ url: string,
7
+ init?: OverwrittenRequestInit,
8
+ ) => {
9
+ const response = await fetch(url, {
10
+ method: init?.method ?? 'GET',
11
+ headers: {
12
+ Accept: 'application/json',
13
+ 'Content-Type': 'application/json',
14
+ },
15
+ ...(init?.body ? {body: JSON.stringify(init.body)} : {}),
16
+ });
17
+
18
+ if (!response.ok) {
19
+ throw Object.assign(new Error(response.statusText), {
20
+ statusCode: response.status,
21
+ });
22
+ }
23
+
24
+ return response.json() as Promise<T>;
25
+ };
@@ -0,0 +1,43 @@
1
+ import {NativeModules} from 'react-native';
2
+
3
+ import type {ProductCommon} from '../types';
4
+
5
+ const {RNIapAmazonModule} = NativeModules;
6
+
7
+ /**
8
+ * Fill products with additional data
9
+ */
10
+ export const fillProductsWithAdditionalData = async <T extends ProductCommon>(
11
+ items: T[],
12
+ ) => {
13
+ if (RNIapAmazonModule) {
14
+ // On amazon we must get the user marketplace to detect the currency
15
+ const user = await RNIapAmazonModule.getUser();
16
+
17
+ const currencies = {
18
+ CA: 'CAD',
19
+ ES: 'EUR',
20
+ AU: 'AUD',
21
+ DE: 'EUR',
22
+ IN: 'INR',
23
+ US: 'USD',
24
+ JP: 'JPY',
25
+ GB: 'GBP',
26
+ IT: 'EUR',
27
+ BR: 'BRL',
28
+ FR: 'EUR',
29
+ };
30
+
31
+ const currency =
32
+ currencies[user.userMarketplaceAmazon as keyof typeof currencies];
33
+
34
+ // Add currency to items
35
+ items.forEach((item) => {
36
+ if (currency) {
37
+ item.currency = currency;
38
+ }
39
+ });
40
+ }
41
+
42
+ return items;
43
+ };
@@ -0,0 +1,3 @@
1
+ export * from './enhancedFetch';
2
+ export * from './fillProductsWithAdditionalData';
3
+ export * from './platform';
@@ -0,0 +1,7 @@
1
+ import {NativeModules, Platform} from 'react-native';
2
+
3
+ const {RNIapAmazonModule} = NativeModules;
4
+
5
+ export const isIos = Platform.OS === 'ios';
6
+ export const isAndroid = Platform.OS === 'android';
7
+ export const isAmazon = isAndroid && !!RNIapAmazonModule;