react-native-iap 12.0.2 → 12.1.0

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 (42) hide show
  1. package/lib/commonjs/iap.js +143 -110
  2. package/lib/commonjs/iap.js.map +1 -1
  3. package/lib/commonjs/internal/fillProductsWithAdditionalData.js +2 -1
  4. package/lib/commonjs/internal/fillProductsWithAdditionalData.js.map +1 -1
  5. package/lib/commonjs/internal/platform.js +28 -1
  6. package/lib/commonjs/internal/platform.js.map +1 -1
  7. package/lib/commonjs/modules/android.js +14 -1
  8. package/lib/commonjs/modules/android.js.map +1 -1
  9. package/lib/commonjs/modules/ios.js +24 -12
  10. package/lib/commonjs/modules/ios.js.map +1 -1
  11. package/lib/commonjs/types/appleSk2.js +3 -0
  12. package/lib/commonjs/types/appleSk2.js.map +1 -1
  13. package/lib/commonjs/types/index.js +15 -1
  14. package/lib/commonjs/types/index.js.map +1 -1
  15. package/lib/module/iap.js +143 -111
  16. package/lib/module/iap.js.map +1 -1
  17. package/lib/module/internal/fillProductsWithAdditionalData.js +2 -1
  18. package/lib/module/internal/fillProductsWithAdditionalData.js.map +1 -1
  19. package/lib/module/internal/platform.js +24 -0
  20. package/lib/module/internal/platform.js.map +1 -1
  21. package/lib/module/modules/android.js +15 -2
  22. package/lib/module/modules/android.js.map +1 -1
  23. package/lib/module/modules/ios.js +25 -13
  24. package/lib/module/modules/ios.js.map +1 -1
  25. package/lib/module/types/appleSk2.js +2 -0
  26. package/lib/module/types/appleSk2.js.map +1 -1
  27. package/lib/module/types/index.js +12 -0
  28. package/lib/module/types/index.js.map +1 -1
  29. package/lib/typescript/iap.d.ts +4 -4
  30. package/lib/typescript/internal/fillProductsWithAdditionalData.d.ts +2 -1
  31. package/lib/typescript/internal/platform.d.ts +9 -0
  32. package/lib/typescript/modules/android.d.ts +1 -1
  33. package/lib/typescript/modules/ios.d.ts +4 -4
  34. package/lib/typescript/types/index.d.ts +54 -28
  35. package/package.json +1 -1
  36. package/src/iap.ts +130 -74
  37. package/src/internal/fillProductsWithAdditionalData.ts +3 -2
  38. package/src/internal/platform.ts +20 -0
  39. package/src/modules/android.ts +16 -7
  40. package/src/modules/ios.ts +35 -20
  41. package/src/types/appleSk2.ts +2 -0
  42. package/src/types/index.ts +70 -30
package/src/iap.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  import {
16
16
  fillProductsWithAdditionalData,
17
17
  getAndroidModule,
18
+ getAndroidModuleType,
18
19
  getIosModule,
19
20
  getNativeModule,
20
21
  isAmazon,
@@ -28,12 +29,16 @@ import {
28
29
  ProductPurchase,
29
30
  ProductType,
30
31
  PurchaseResult,
32
+ PurchaseStateAndroid,
31
33
  RequestPurchase,
32
34
  RequestSubscription,
33
35
  Subscription,
36
+ SubscriptionAmazon,
37
+ SubscriptionAndroid,
38
+ SubscriptionIOS,
39
+ SubscriptionPlatform,
34
40
  SubscriptionPurchase,
35
41
  } from './types';
36
- import {PurchaseStateAndroid} from './types';
37
42
 
38
43
  export {IapAndroid, IapAmazon, IapIos, IapIosSk2, isIosStorekit2};
39
44
 
@@ -231,32 +236,69 @@ export const getSubscriptions = ({
231
236
  }): Promise<Subscription[]> =>
232
237
  (
233
238
  Platform.select({
234
- ios: async () => {
235
- let items: Subscription[];
239
+ ios: async (): Promise<SubscriptionIOS[]> => {
240
+ let items: SubscriptionIOS[];
236
241
  if (isIosStorekit2()) {
237
242
  items = ((await RNIapIosSk2.getItems(skus)) as ProductSk2[]).map(
238
243
  subscriptionSk2Map,
239
244
  );
240
245
  } else {
241
- items = (await RNIapIos.getItems(skus)) as Subscription[];
246
+ items = (await RNIapIos.getItems(skus)) as SubscriptionIOS[];
242
247
  }
243
248
 
244
- return items.filter(
245
- (item: Subscription) =>
249
+ items = items.filter(
250
+ (item: SubscriptionIOS) =>
246
251
  skus.includes(item.productId) && item.type === 'subs',
247
252
  );
253
+
254
+ return addSubscriptionPlatform(items, SubscriptionPlatform.ios);
248
255
  },
249
- android: async () => {
250
- const subscriptions = (await getAndroidModule().getItemsByType(
256
+ android: async (): Promise<Subscription[]> => {
257
+ const androidPlatform = getAndroidModuleType();
258
+
259
+ let subscriptions = (await getAndroidModule().getItemsByType(
251
260
  ANDROID_ITEM_TYPE_SUBSCRIPTION,
252
261
  skus,
253
- )) as Subscription[];
254
-
255
- return fillProductsWithAdditionalData(subscriptions);
262
+ )) as SubscriptionAndroid[] | SubscriptionAmazon[];
263
+
264
+ switch (androidPlatform) {
265
+ case 'android': {
266
+ const castSubscriptions = subscriptions as SubscriptionAndroid[];
267
+ return addSubscriptionPlatform(
268
+ castSubscriptions,
269
+ SubscriptionPlatform.android,
270
+ );
271
+ }
272
+ case 'amazon':
273
+ let castSubscriptions = subscriptions as SubscriptionAmazon[];
274
+ castSubscriptions = await fillProductsWithAdditionalData(
275
+ castSubscriptions,
276
+ );
277
+ return addSubscriptionPlatform(
278
+ castSubscriptions,
279
+ SubscriptionPlatform.amazon,
280
+ );
281
+ case null:
282
+ default:
283
+ throw new Error(
284
+ `getSubscriptions received unknown platform ${androidPlatform}. Verify the logic in getAndroidModuleType`,
285
+ );
286
+ }
256
287
  },
257
288
  }) || (() => Promise.reject(new Error('Unsupported Platform')))
258
289
  )();
259
290
 
291
+ /**
292
+ * Adds an extra property to subscriptions so we can distinguish the platform
293
+ * we retrieved them on.
294
+ */
295
+ const addSubscriptionPlatform = <T>(
296
+ subscriptions: T[],
297
+ platform: SubscriptionPlatform,
298
+ ): T[] => {
299
+ return subscriptions.map((subscription) => ({...subscription, platform}));
300
+ };
301
+
260
302
  /**
261
303
  * Gets an inventory of purchases made by the user regardless of consumption status
262
304
  * ## Usage
@@ -281,8 +323,8 @@ const App = () => {
281
323
  ```
282
324
  @param {alsoPublishToEventListener}:boolean. (IOS Sk2 only) When `true`, every element will also be pushed to the purchaseUpdated listener.
283
325
  Note that this is only for backaward compatiblity. It won't publish to transactionUpdated (Storekit2) Defaults to `false`
284
- @param {automaticallyFinishRestoredTransactions}:boolean. (IOS Sk1 only) When `true`, all the transactions that are returned are automatically
285
- finished. This means that if you call this method again you won't get the same result on the same device. On the other hand, if `false` you'd
326
+ @param {automaticallyFinishRestoredTransactions}:boolean. (IOS Sk1 only) When `true`, all the transactions that are returned are automatically
327
+ finished. This means that if you call this method again you won't get the same result on the same device. On the other hand, if `false` you'd
286
328
  have to manually finish the returned transaction once you have delivered the content to your user.
287
329
  */
288
330
  export const getPurchaseHistory = ({
@@ -403,7 +445,7 @@ const App = () => {
403
445
  ```
404
446
  @param {alsoPublishToEventListener}:boolean When `true`, every element will also be pushed to the purchaseUpdated listener.
405
447
  Note that this is only for backaward compatiblity. It won't publish to transactionUpdated (Storekit2) Defaults to `false`
406
- *
448
+ *
407
449
  */
408
450
  export const getAvailablePurchases = ({
409
451
  alsoPublishToEventListener = false,
@@ -460,22 +502,22 @@ always keeping at false, and verifying the transaction receipts on the server-si
460
502
 
461
503
  ```ts
462
504
  requestPurchase(
463
- The product's sku/ID
505
+ The product's sku/ID
464
506
  sku,
465
507
 
466
-
508
+
467
509
  * You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
468
510
  * @default false
469
-
511
+
470
512
  andDangerouslyFinishTransactionAutomaticallyIOS = false,
471
513
 
472
- /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
514
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
473
515
  obfuscatedAccountIdAndroid,
474
516
 
475
- Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
517
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
476
518
  obfuscatedProfileIdAndroid,
477
519
 
478
- The purchaser's user ID
520
+ The purchaser's user ID
479
521
  applicationUsername,
480
522
  ): Promise<ProductPurchase>;
481
523
  ```
@@ -513,23 +555,24 @@ const App = () => {
513
555
 
514
556
  */
515
557
 
516
- export const requestPurchase = ({
517
- sku,
518
- andDangerouslyFinishTransactionAutomaticallyIOS = false,
519
- obfuscatedAccountIdAndroid,
520
- obfuscatedProfileIdAndroid,
521
- appAccountToken,
522
- skus, // Android Billing V5
523
- isOfferPersonalized = undefined, // Android Billing V5
524
- quantity,
525
- withOffer,
526
- }: RequestPurchase): Promise<ProductPurchase | void> =>
558
+ export const requestPurchase = (
559
+ request: RequestPurchase,
560
+ ): Promise<ProductPurchase | void> =>
527
561
  (
528
562
  Platform.select({
529
563
  ios: async () => {
530
- if (!sku) {
531
- return Promise.reject(new Error('sku is required for iOS purchase'));
564
+ if (!('sku' in request)) {
565
+ throw new Error('sku is required for iOS purchase');
532
566
  }
567
+
568
+ const {
569
+ sku,
570
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
571
+ appAccountToken,
572
+ quantity,
573
+ withOffer,
574
+ } = request;
575
+
533
576
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
534
577
  console.warn(
535
578
  '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.',
@@ -557,21 +600,25 @@ export const requestPurchase = ({
557
600
  },
558
601
  android: async () => {
559
602
  if (isAmazon) {
560
- if (!sku) {
561
- return Promise.reject(
562
- new Error('sku is required for Amazon purchase'),
563
- );
603
+ if (!('sku' in request)) {
604
+ throw new Error('sku is required for Amazon purchase');
564
605
  }
606
+ const {sku} = request;
565
607
  return RNIapAmazonModule.buyItemByType(sku);
566
608
  } else {
567
- if (!sku?.length && !sku) {
568
- return Promise.reject(
569
- new Error('skus is required for Android purchase'),
570
- );
609
+ if (!('skus' in request) || !request.skus.length) {
610
+ throw new Error('skus is required for Android purchase');
571
611
  }
612
+
613
+ const {
614
+ skus,
615
+ obfuscatedAccountIdAndroid,
616
+ obfuscatedProfileIdAndroid,
617
+ isOfferPersonalized,
618
+ } = request;
572
619
  return getAndroidModule().buyItemByType(
573
620
  ANDROID_ITEM_TYPE_IAP,
574
- skus?.length ? skus : [sku],
621
+ skus,
575
622
  undefined,
576
623
  -1,
577
624
  obfuscatedAccountIdAndroid,
@@ -599,7 +646,7 @@ always keeping at false, and verifying the transaction receipts on the server-si
599
646
 
600
647
  ```ts
601
648
  requestSubscription(
602
- The product's sku/ID
649
+ The product's sku/ID
603
650
  sku,
604
651
 
605
652
 
@@ -608,19 +655,19 @@ requestSubscription(
608
655
 
609
656
  andDangerouslyFinishTransactionAutomaticallyIOS = false,
610
657
 
611
- purchaseToken that the user is upgrading or downgrading from (Android).
658
+ purchaseToken that the user is upgrading or downgrading from (Android).
612
659
  purchaseTokenAndroid,
613
660
 
614
- UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
661
+ UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
615
662
  prorationModeAndroid = -1,
616
663
 
617
- /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
664
+ /** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
618
665
  obfuscatedAccountIdAndroid,
619
666
 
620
- Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
667
+ Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
621
668
  obfuscatedProfileIdAndroid,
622
669
 
623
- The purchaser's user ID
670
+ The purchaser's user ID
624
671
  applicationUsername,
625
672
  ): Promise<SubscriptionPurchase>
626
673
  ```
@@ -661,27 +708,24 @@ const App = () => {
661
708
  };
662
709
  ```
663
710
  */
664
- export const requestSubscription = ({
665
- sku,
666
- andDangerouslyFinishTransactionAutomaticallyIOS = false,
667
- purchaseTokenAndroid,
668
- prorationModeAndroid = -1,
669
- obfuscatedAccountIdAndroid,
670
- obfuscatedProfileIdAndroid,
671
- subscriptionOffers = undefined, // Android Billing V5
672
- isOfferPersonalized = undefined, // Android Billing V5
673
- appAccountToken,
674
- quantity,
675
- withOffer,
676
- }: RequestSubscription): Promise<SubscriptionPurchase | null | void> =>
711
+ export const requestSubscription = (
712
+ request: RequestSubscription,
713
+ ): Promise<SubscriptionPurchase | null | void> =>
677
714
  (
678
715
  Platform.select({
679
716
  ios: async () => {
680
- if (!sku) {
681
- return Promise.reject(
682
- new Error('sku is required for iOS subscription'),
683
- );
717
+ if (!('sku' in request)) {
718
+ throw new Error('sku is required for iOS subscriptions');
684
719
  }
720
+
721
+ const {
722
+ sku,
723
+ andDangerouslyFinishTransactionAutomaticallyIOS = false,
724
+ appAccountToken,
725
+ quantity,
726
+ withOffer,
727
+ } = request;
728
+
685
729
  if (andDangerouslyFinishTransactionAutomaticallyIOS) {
686
730
  console.warn(
687
731
  '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.',
@@ -710,18 +754,30 @@ export const requestSubscription = ({
710
754
  },
711
755
  android: async () => {
712
756
  if (isAmazon) {
713
- if (!sku) {
714
- return Promise.reject(
715
- new Error('sku is required for Amazon purchase'),
716
- );
757
+ if (!('sku' in request)) {
758
+ throw new Error('sku is required for Amazon subscriptions');
717
759
  }
760
+ const {sku} = request;
718
761
  return RNIapAmazonModule.buyItemByType(sku);
719
762
  } else {
720
- if (!subscriptionOffers?.length) {
721
- return Promise.reject(
722
- 'subscriptionOffers are required for Google Play Subscriptions',
763
+ if (
764
+ !('subscriptionOffers' in request) ||
765
+ request.subscriptionOffers.length === 0
766
+ ) {
767
+ throw new Error(
768
+ 'subscriptionOffers are required for Google Play subscriptions',
723
769
  );
724
770
  }
771
+
772
+ const {
773
+ subscriptionOffers,
774
+ purchaseTokenAndroid,
775
+ prorationModeAndroid,
776
+ obfuscatedAccountIdAndroid,
777
+ obfuscatedProfileIdAndroid,
778
+ isOfferPersonalized,
779
+ } = request;
780
+
725
781
  return RNIapModule.buyItemByType(
726
782
  ANDROID_ITEM_TYPE_SUBSCRIPTION,
727
783
  subscriptionOffers?.map((so) => so.sku),
@@ -744,7 +800,7 @@ export const requestSubscription = ({
744
800
  * Call this after you have persisted the purchased state to your server or local data in your app.
745
801
  * `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.**
746
802
  * Android: it will consume purchase for consumables and acknowledge purchase for non-consumables.
747
- *
803
+ *
748
804
  ```tsx
749
805
  import React from 'react';
750
806
  import {Button} from 'react-native';
@@ -759,7 +815,7 @@ const App = () => {
759
815
 
760
816
  return <Button title="Buy product" onPress={handlePurchase} />;
761
817
  };
762
- ```
818
+ ```
763
819
  */
764
820
  export const finishTransaction = ({
765
821
  purchase,
@@ -5,11 +5,12 @@ import type {ProductCommon} from '../types';
5
5
  const {RNIapAmazonModule} = NativeModules;
6
6
 
7
7
  /**
8
- * Fill products with additional data
8
+ * For Amazon products, we add the currency code from the user's information
9
+ * since it isn't included in the product information.
9
10
  */
10
11
  export const fillProductsWithAdditionalData = async <T extends ProductCommon>(
11
12
  items: T[],
12
- ) => {
13
+ ): Promise<T[]> => {
13
14
  if (RNIapAmazonModule) {
14
15
  // On amazon we must get the user marketplace to detect the currency
15
16
  const user = await RNIapAmazonModule.getUser();
@@ -25,6 +25,10 @@ export const checkNativeAndroidAvailable = (): void => {
25
25
  }
26
26
  };
27
27
 
28
+ /**
29
+ * If changing the typings of `getAndroidModule` to accommodate extra modules,
30
+ * make sure to update `getAndroidModuleType`.
31
+ */
28
32
  export const getAndroidModule = ():
29
33
  | typeof RNIapModule
30
34
  | typeof RNIapAmazonModule => {
@@ -37,6 +41,22 @@ export const getAndroidModule = ():
37
41
  : RNIapAmazonModule;
38
42
  };
39
43
 
44
+ /**
45
+ * Returns whether the Android in-app-purchase code is using the Android,
46
+ * Amazon, or another store.
47
+ */
48
+ export const getAndroidModuleType = (): 'android' | 'amazon' | null => {
49
+ const module = getAndroidModule();
50
+ switch (module) {
51
+ case RNIapModule:
52
+ return 'android';
53
+ case RNIapAmazonModule:
54
+ return 'amazon';
55
+ default:
56
+ return null;
57
+ }
58
+ };
59
+
40
60
  export const getNativeModule = ():
41
61
  | typeof RNIapModule
42
62
  | typeof RNIapAmazonModule
@@ -1,10 +1,6 @@
1
1
  import {Linking, NativeModules} from 'react-native';
2
2
 
3
- import {
4
- checkNativeAndroidAvailable,
5
- enhancedFetch,
6
- getAndroidModule,
7
- } from '../internal';
3
+ import {checkNativeAndroidAvailable, getAndroidModule} from '../internal';
8
4
  import {
9
5
  InstallSourceAndroid,
10
6
  Product,
@@ -39,7 +35,7 @@ export type BuyItemByType = (
39
35
  type: string,
40
36
  skus: Sku[],
41
37
  purchaseToken: string | undefined,
42
- prorationMode: ProrationModesAndroid,
38
+ prorationMode: ProrationModesAndroid | undefined,
43
39
  obfuscatedAccountId: string | undefined,
44
40
  obfuscatedProfileId: string | undefined,
45
41
  subscriptionOffers: string[],
@@ -127,7 +123,20 @@ export const validateReceiptAndroid = async ({
127
123
  `/${packageName}/purchases/${type}/${productId}` +
128
124
  `/tokens/${productToken}?access_token=${accessToken}`;
129
125
 
130
- return await enhancedFetch<Android.ReceiptType>(url);
126
+ const response = await fetch(url, {
127
+ method: 'GET',
128
+ headers: {
129
+ 'Content-Type': 'application/json',
130
+ },
131
+ });
132
+
133
+ if (!response.ok) {
134
+ throw Object.assign(new Error(response.statusText), {
135
+ statusCode: response.status,
136
+ });
137
+ }
138
+
139
+ return response.json();
131
140
  };
132
141
 
133
142
  /**
@@ -1,18 +1,18 @@
1
1
  import type {ResponseBody as ReceiptValidationResponse} from '@jeremybarbet/apple-api-types';
2
2
 
3
- import {enhancedFetch, getIosModule, isIosStorekit2} from '../internal';
3
+ import {getIosModule, isIosStorekit2} from '../internal';
4
4
  import type {
5
- Product,
5
+ ProductIOS,
6
6
  ProductPurchase,
7
7
  Purchase,
8
8
  Sku,
9
- Subscription,
9
+ SubscriptionIOS,
10
10
  } from '../types';
11
11
  import type {PaymentDiscount} from '../types/apple';
12
12
 
13
13
  import type {NativeModuleProps} from './common';
14
14
 
15
- type getItems = (skus: Sku[]) => Promise<Product[] | Subscription[]>;
15
+ type getItems = (skus: Sku[]) => Promise<ProductIOS[] | SubscriptionIOS[]>;
16
16
 
17
17
  type getAvailableItems = (
18
18
  automaticallyFinishRestoredTransactions: boolean,
@@ -28,7 +28,7 @@ export type BuyProduct = (
28
28
 
29
29
  type clearTransaction = () => Promise<void>;
30
30
  type clearProducts = () => Promise<void>;
31
- type promotedProduct = () => Promise<Product | null>;
31
+ type promotedProduct = () => Promise<ProductIOS | null>;
32
32
  type buyPromotedProduct = () => Promise<void>;
33
33
  type requestReceipt = (refresh: boolean) => Promise<string>;
34
34
 
@@ -83,7 +83,7 @@ export const presentCodeRedemptionSheetIOS = async (): Promise<null> =>
83
83
  * Indicates the the App Store purchase should continue from the app instead of the App Store.
84
84
  * @returns {Promise<Product | null>} promoted product
85
85
  */
86
- export const getPromotedProductIOS = (): Promise<Product | null> => {
86
+ export const getPromotedProductIOS = (): Promise<ProductIOS | null> => {
87
87
  if (!isIosStorekit2()) {
88
88
  return getIosModule().promotedProduct();
89
89
  } else {
@@ -99,27 +99,43 @@ export const getPromotedProductIOS = (): Promise<Product | null> => {
99
99
  export const buyPromotedProductIOS = (): Promise<void> =>
100
100
  getIosModule().buyPromotedProduct();
101
101
 
102
+ const fetchJsonOrThrow = async (
103
+ url: string,
104
+ receiptBody: Record<string, unknown>,
105
+ ): Promise<ReceiptValidationResponse | false> => {
106
+ const response = await fetch(url, {
107
+ method: 'POST',
108
+ headers: {
109
+ Accept: 'application/json',
110
+ 'Content-Type': 'application/json',
111
+ },
112
+ body: JSON.stringify(receiptBody),
113
+ });
114
+
115
+ if (!response.ok) {
116
+ throw Object.assign(new Error(response.statusText), {
117
+ statusCode: response.status,
118
+ });
119
+ }
120
+
121
+ return response.json();
122
+ };
123
+
102
124
  const TEST_RECEIPT = 21007;
103
125
  const requestAgnosticReceiptValidationIos = async (
104
126
  receiptBody: Record<string, unknown>,
105
127
  ): Promise<ReceiptValidationResponse | false> => {
106
- const response = await enhancedFetch<ReceiptValidationResponse>(
128
+ const response = await fetchJsonOrThrow(
107
129
  'https://buy.itunes.apple.com/verifyReceipt',
108
- {
109
- method: 'POST',
110
- body: receiptBody,
111
- },
130
+ receiptBody,
112
131
  );
113
132
 
114
133
  // Best practice is to check for test receipt and check sandbox instead
115
134
  // https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
116
135
  if (response && response.status === TEST_RECEIPT) {
117
- const testResponse = await enhancedFetch<ReceiptValidationResponse>(
136
+ const testResponse = await fetchJsonOrThrow(
118
137
  'https://sandbox.itunes.apple.com/verifyReceipt',
119
- {
120
- method: 'POST',
121
- body: receiptBody,
122
- },
138
+ receiptBody,
123
139
  );
124
140
 
125
141
  return testResponse;
@@ -149,10 +165,9 @@ export const validateReceiptIos = async ({
149
165
  ? 'https://sandbox.itunes.apple.com/verifyReceipt'
150
166
  : 'https://buy.itunes.apple.com/verifyReceipt';
151
167
 
152
- return await enhancedFetch<ReceiptValidationResponse>(url, {
153
- method: 'POST',
154
- body: receiptBody,
155
- });
168
+ const response = await fetchJsonOrThrow(url, receiptBody);
169
+
170
+ return response;
156
171
  };
157
172
 
158
173
  /**
@@ -7,6 +7,7 @@ import type {
7
7
  SubscriptionIosPeriod,
8
8
  } from '.';
9
9
  import type * as Apple from './apple';
10
+ import {SubscriptionPlatform} from '.';
10
11
 
11
12
  export type SubscriptionPeriod = {
12
13
  unit: 'day' | 'week' | 'month' | 'year';
@@ -71,6 +72,7 @@ export const subscriptionSk2Map = ({
71
72
  subscription,
72
73
  }: ProductSk2): SubscriptionIOS => {
73
74
  const prod: SubscriptionIOS = {
75
+ platform: SubscriptionPlatform.ios,
74
76
  title: displayName,
75
77
  productId: String(id),
76
78
  description,