expo-iap 2.8.2 → 2.8.3-rc.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 (60) hide show
  1. package/CHANGELOG.md +0 -10
  2. package/CLAUDE.md +11 -0
  3. package/CONTRIBUTING.md +3 -4
  4. package/build/helpers/subscription.d.ts.map +1 -1
  5. package/build/helpers/subscription.js +3 -6
  6. package/build/helpers/subscription.js.map +1 -1
  7. package/build/index.d.ts.map +1 -1
  8. package/build/index.js +3 -1
  9. package/build/index.js.map +1 -1
  10. package/build/modules/android.d.ts.map +1 -1
  11. package/build/modules/android.js.map +1 -1
  12. package/build/modules/ios.d.ts.map +1 -1
  13. package/build/modules/ios.js.map +1 -1
  14. package/build/types/ExpoIapAndroid.types.d.ts +2 -2
  15. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
  16. package/build/types/ExpoIapAndroid.types.js.map +1 -1
  17. package/build/types/ExpoIapIOS.types.d.ts +3 -3
  18. package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
  19. package/build/types/ExpoIapIOS.types.js.map +1 -1
  20. package/build/useIAP.d.ts.map +1 -1
  21. package/build/useIAP.js.map +1 -1
  22. package/coverage/clover.xml +601 -0
  23. package/coverage/coverage-final.json +9 -0
  24. package/coverage/lcov-report/base.css +224 -0
  25. package/coverage/lcov-report/block-navigation.js +87 -0
  26. package/coverage/lcov-report/favicon.png +0 -0
  27. package/coverage/lcov-report/index.html +176 -0
  28. package/coverage/lcov-report/prettify.css +1 -0
  29. package/coverage/lcov-report/prettify.js +2 -0
  30. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  31. package/coverage/lcov-report/sorter.js +210 -0
  32. package/coverage/lcov-report/src/ExpoIap.types.ts.html +1243 -0
  33. package/coverage/lcov-report/src/helpers/index.html +116 -0
  34. package/coverage/lcov-report/src/helpers/subscription.ts.html +430 -0
  35. package/coverage/lcov-report/src/index.html +146 -0
  36. package/coverage/lcov-report/src/index.ts.html +2227 -0
  37. package/coverage/lcov-report/src/modules/android.ts.html +469 -0
  38. package/coverage/lcov-report/src/modules/index.html +131 -0
  39. package/coverage/lcov-report/src/modules/ios.ts.html +1411 -0
  40. package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +487 -0
  41. package/coverage/lcov-report/src/types/index.html +116 -0
  42. package/coverage/lcov-report/src/useIap.ts.html +1483 -0
  43. package/coverage/lcov-report/src/utils/errorMapping.ts.html +349 -0
  44. package/coverage/lcov-report/src/utils/index.html +116 -0
  45. package/coverage/lcov.info +1115 -0
  46. package/ios/ExpoIap.podspec +6 -1
  47. package/ios/expoiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  48. package/ios/expoiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  49. package/jest.config.js +14 -17
  50. package/package.json +1 -1
  51. package/plugin/build/withIAP.js +16 -3
  52. package/plugin/src/withIAP.ts +22 -3
  53. package/plugin/tsconfig.tsbuildinfo +1 -1
  54. package/src/helpers/subscription.ts +21 -28
  55. package/src/index.ts +10 -5
  56. package/src/modules/android.ts +7 -7
  57. package/src/modules/ios.ts +7 -2
  58. package/src/types/ExpoIapAndroid.types.ts +3 -4
  59. package/src/types/ExpoIapIOS.types.ts +4 -3
  60. package/src/useIAP.ts +9 -3
@@ -10,7 +10,12 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platforms = { :ios => '15.0', :tvos => '15.0' }
13
+ # WARNING: DO NOT MODIFY iOS platform version from 13.4
14
+ # Changing this to 15.0 causes expo prebuild to exclude the module in older Expo versions (known bug)
15
+ # See: https://github.com/hyochan/expo-iap/issues/168
16
+ # Even though the code requires iOS 15.0+ for StoreKit 2, keep this at 13.4 for compatibility across all Expo versions
17
+ # The actual iOS 15.0+ requirement is enforced at build time
18
+ s.platforms = { :ios => '13.4', :tvos => '15.0' }
14
19
  s.swift_version = '5.4'
15
20
  s.source = { git: 'https://github.com/hyochan/expo-iap' }
16
21
  s.static_framework = true
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>IDEDidComputeMac32BitWarning</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>
package/jest.config.js CHANGED
@@ -4,21 +4,18 @@ module.exports = {
4
4
  roots: ['<rootDir>/src'],
5
5
  testMatch: [
6
6
  '**/__tests__/**/*.+(ts|tsx|js)',
7
- '**/?(*.)+(spec|test).+(ts|tsx|js)',
7
+ '**/?(*.)+(spec|test).+(ts|tsx|js)'
8
8
  ],
9
9
  transform: {
10
- '^.+\\.(ts|tsx)$': [
11
- 'ts-jest',
12
- {
13
- tsconfig: {
14
- jsx: 'react',
15
- esModuleInterop: true,
16
- allowSyntheticDefaultImports: true,
17
- moduleResolution: 'node',
18
- skipLibCheck: true,
19
- },
20
- },
21
- ],
10
+ '^.+\\.(ts|tsx)$': ['ts-jest', {
11
+ tsconfig: {
12
+ jsx: 'react',
13
+ esModuleInterop: true,
14
+ allowSyntheticDefaultImports: true,
15
+ moduleResolution: 'node',
16
+ skipLibCheck: true,
17
+ }
18
+ }]
22
19
  },
23
20
  moduleNameMapper: {
24
21
  '^react-native$': '<rootDir>/src/__mocks__/react-native.js',
@@ -37,7 +34,7 @@ module.exports = {
37
34
  branches: 15,
38
35
  functions: 15,
39
36
  lines: 15,
40
- statements: 15,
41
- },
42
- },
43
- };
37
+ statements: 15
38
+ }
39
+ }
40
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.8.2",
3
+ "version": "2.8.3-rc.2",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -36,7 +36,18 @@ const modifyAppBuildGradle = (gradle) => {
36
36
  }
37
37
  return modified;
38
38
  };
39
- const withIAPAndroid = (config) => {
39
+ const withIapIOS = (config) => {
40
+ // Add In-App Purchase capability to entitlements
41
+ config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
42
+ config.modResults['com.apple.developer.in-app-payments'] = ['Default'];
43
+ if (!hasLoggedPluginExecution) {
44
+ console.log('✅ Added In-App Purchase capability to iOS entitlements');
45
+ }
46
+ return config;
47
+ });
48
+ return config;
49
+ };
50
+ const withIapAndroid = (config) => {
40
51
  // Add IAP dependencies to app build.gradle
41
52
  config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
42
53
  config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
@@ -67,10 +78,12 @@ const withIAPAndroid = (config) => {
67
78
  };
68
79
  const withIAP = (config, _props) => {
69
80
  try {
70
- const result = withIAPAndroid(config);
81
+ // Apply both iOS and Android configurations
82
+ config = withIapIOS(config);
83
+ config = withIapAndroid(config);
71
84
  // Set flag after first execution to prevent duplicate logs
72
85
  hasLoggedPluginExecution = true;
73
- return result;
86
+ return config;
74
87
  }
75
88
  catch (error) {
76
89
  config_plugins_1.WarningAggregator.addWarningAndroid('expo-iap', `expo-iap plugin encountered an error: ${error}`);
@@ -4,6 +4,7 @@ import {
4
4
  WarningAggregator,
5
5
  withAndroidManifest,
6
6
  withAppBuildGradle,
7
+ withEntitlementsPlist,
7
8
  } from 'expo/config-plugins';
8
9
 
9
10
  const pkg = require('../../package.json');
@@ -56,7 +57,22 @@ const modifyAppBuildGradle = (gradle: string): string => {
56
57
  return modified;
57
58
  };
58
59
 
59
- const withIAPAndroid: ConfigPlugin = (config) => {
60
+ const withIapIOS: ConfigPlugin = (config) => {
61
+ // Add In-App Purchase capability to entitlements
62
+ config = withEntitlementsPlist(config, (config) => {
63
+ config.modResults['com.apple.developer.in-app-payments'] = ['Default'];
64
+
65
+ if (!hasLoggedPluginExecution) {
66
+ console.log('✅ Added In-App Purchase capability to iOS entitlements');
67
+ }
68
+
69
+ return config;
70
+ });
71
+
72
+ return config;
73
+ };
74
+
75
+ const withIapAndroid: ConfigPlugin = (config) => {
60
76
  // Add IAP dependencies to app build.gradle
61
77
  config = withAppBuildGradle(config, (config) => {
62
78
  config.modResults.contents = modifyAppBuildGradle(
@@ -100,10 +116,13 @@ const withIAPAndroid: ConfigPlugin = (config) => {
100
116
 
101
117
  const withIAP: ConfigPlugin = (config, _props) => {
102
118
  try {
103
- const result = withIAPAndroid(config);
119
+ // Apply both iOS and Android configurations
120
+ config = withIapIOS(config);
121
+ config = withIapAndroid(config);
122
+
104
123
  // Set flag after first execution to prevent duplicate logs
105
124
  hasLoggedPluginExecution = true;
106
- return result;
125
+ return config;
107
126
  } catch (error) {
108
127
  WarningAggregator.addWarningAndroid(
109
128
  'expo-iap',
@@ -1 +1 @@
1
- {"root":["./src/withIAP.ts"],"version":"5.9.2"}
1
+ {"root":["./src/withiap.ts"],"version":"5.8.3"}
@@ -1,5 +1,5 @@
1
- import {Platform} from 'react-native';
2
- import {getAvailablePurchases} from '../index';
1
+ import { Platform } from 'react-native';
2
+ import { getAvailablePurchases } from '../index';
3
3
 
4
4
  export interface ActiveSubscription {
5
5
  productId: string;
@@ -17,13 +17,13 @@ export interface ActiveSubscription {
17
17
  * @returns Promise<ActiveSubscription[]> array of active subscriptions with details
18
18
  */
19
19
  export const getActiveSubscriptions = async (
20
- subscriptionIds?: string[],
20
+ subscriptionIds?: string[]
21
21
  ): Promise<ActiveSubscription[]> => {
22
22
  try {
23
23
  const purchases = await getAvailablePurchases();
24
24
  const currentTime = Date.now();
25
25
  const activeSubscriptions: ActiveSubscription[] = [];
26
-
26
+
27
27
  // Filter purchases to find active subscriptions
28
28
  const filteredPurchases = purchases.filter((purchase) => {
29
29
  // If specific IDs provided, filter by them
@@ -32,17 +32,17 @@ export const getActiveSubscriptions = async (
32
32
  return false;
33
33
  }
34
34
  }
35
-
35
+
36
36
  // Check if this purchase has subscription-specific fields
37
- const hasSubscriptionFields =
37
+ const hasSubscriptionFields =
38
38
  ('expirationDateIOS' in purchase && purchase.expirationDateIOS) ||
39
- 'autoRenewingAndroid' in purchase ||
39
+ ('autoRenewingAndroid' in purchase) ||
40
40
  ('environmentIOS' in purchase && purchase.environmentIOS === 'Sandbox');
41
-
41
+
42
42
  if (!hasSubscriptionFields) {
43
43
  return false;
44
44
  }
45
-
45
+
46
46
  // Check if it's actually active
47
47
  if (Platform.OS === 'ios') {
48
48
  if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {
@@ -53,15 +53,8 @@ export const getActiveSubscriptions = async (
53
53
  if ('environmentIOS' in purchase && purchase.environmentIOS) {
54
54
  const dayInMs = 24 * 60 * 60 * 1000;
55
55
  // If no expiration date, consider active if transaction is recent (within 24 hours for Sandbox)
56
- if (
57
- !('expirationDateIOS' in purchase) ||
58
- !purchase.expirationDateIOS
59
- ) {
60
- if (
61
- purchase.environmentIOS === 'Sandbox' &&
62
- purchase.transactionDate &&
63
- currentTime - purchase.transactionDate < dayInMs
64
- ) {
56
+ if (!('expirationDateIOS' in purchase) || !purchase.expirationDateIOS) {
57
+ if (purchase.environmentIOS === 'Sandbox' && purchase.transactionDate && (currentTime - purchase.transactionDate) < dayInMs) {
65
58
  return true;
66
59
  }
67
60
  }
@@ -70,31 +63,31 @@ export const getActiveSubscriptions = async (
70
63
  // For Android, if it's in the purchases list, it's active
71
64
  return true;
72
65
  }
73
-
66
+
74
67
  return false;
75
68
  });
76
-
69
+
77
70
  // Convert to ActiveSubscription format
78
71
  for (const purchase of filteredPurchases) {
79
72
  const subscription: ActiveSubscription = {
80
73
  productId: purchase.productId,
81
74
  isActive: true,
82
75
  };
83
-
76
+
84
77
  // Add platform-specific details
85
78
  if (Platform.OS === 'ios') {
86
79
  if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {
87
80
  const expirationDate = new Date(purchase.expirationDateIOS);
88
81
  subscription.expirationDateIOS = expirationDate;
89
-
82
+
90
83
  // Calculate days until expiration (round to nearest day)
91
84
  const daysUntilExpiration = Math.round(
92
- (purchase.expirationDateIOS - currentTime) / (1000 * 60 * 60 * 24),
85
+ (purchase.expirationDateIOS - currentTime) / (1000 * 60 * 60 * 24)
93
86
  );
94
87
  subscription.daysUntilExpirationIOS = daysUntilExpiration;
95
88
  subscription.willExpireSoon = daysUntilExpiration <= 7;
96
89
  }
97
-
90
+
98
91
  if ('environmentIOS' in purchase) {
99
92
  subscription.environmentIOS = purchase.environmentIOS;
100
93
  }
@@ -105,10 +98,10 @@ export const getActiveSubscriptions = async (
105
98
  subscription.willExpireSoon = !purchase.autoRenewingAndroid;
106
99
  }
107
100
  }
108
-
101
+
109
102
  activeSubscriptions.push(subscription);
110
103
  }
111
-
104
+
112
105
  return activeSubscriptions;
113
106
  } catch (error) {
114
107
  console.error('Error getting active subscriptions:', error);
@@ -122,8 +115,8 @@ export const getActiveSubscriptions = async (
122
115
  * @returns Promise<boolean> true if user has at least one active subscription
123
116
  */
124
117
  export const hasActiveSubscriptions = async (
125
- subscriptionIds?: string[],
118
+ subscriptionIds?: string[]
126
119
  ): Promise<boolean> => {
127
120
  const subscriptions = await getActiveSubscriptions(subscriptionIds);
128
121
  return subscriptions.length > 0;
129
- };
122
+ };
package/src/index.ts CHANGED
@@ -331,9 +331,8 @@ export const getAvailablePurchases = ({
331
331
  ),
332
332
  android: async () => {
333
333
  const products = await ExpoIapModule.getAvailableItemsByType('inapp');
334
- const subscriptions = await ExpoIapModule.getAvailableItemsByType(
335
- 'subs',
336
- );
334
+ const subscriptions =
335
+ await ExpoIapModule.getAvailableItemsByType('subs');
337
336
  return products.concat(subscriptions);
338
337
  },
339
338
  }) || (() => Promise.resolve([]))
@@ -407,7 +406,11 @@ const normalizeRequestProps = (
407
406
  */
408
407
  export const requestPurchase = (
409
408
  requestObj: PurchaseRequest,
410
- ): Promise<Purchase | Purchase[] | void> => {
409
+ ): Promise<
410
+ | Purchase
411
+ | Purchase[]
412
+ | void
413
+ > => {
411
414
  const {request, type = 'inapp'} = requestObj;
412
415
 
413
416
  if (Platform.OS === 'ios') {
@@ -437,7 +440,9 @@ export const requestPurchase = (
437
440
  offer,
438
441
  );
439
442
 
440
- return type === 'inapp' ? (purchase as Purchase) : (purchase as Purchase);
443
+ return type === 'inapp'
444
+ ? (purchase as Purchase)
445
+ : (purchase as Purchase);
441
446
  })();
442
447
  }
443
448
 
@@ -26,7 +26,7 @@ export function isProductAndroid<T extends {platform?: string}>(
26
26
  * @param {string} params.sku - The product's SKU (on Android)
27
27
  * @param {string} params.packageName - The package name of your Android app (e.g., 'com.example.app')
28
28
  * @returns {Promise<void>}
29
- *
29
+ *
30
30
  * @example
31
31
  * ```typescript
32
32
  * await deepLinkToSubscriptionsAndroid({
@@ -43,11 +43,9 @@ export const deepLinkToSubscriptionsAndroid = async ({
43
43
  packageName: string;
44
44
  }): Promise<void> => {
45
45
  if (!packageName) {
46
- throw new Error(
47
- 'packageName is required for deepLinkToSubscriptionsAndroid',
48
- );
46
+ throw new Error('packageName is required for deepLinkToSubscriptionsAndroid');
49
47
  }
50
-
48
+
51
49
  return Linking.openURL(
52
50
  `https://play.google.com/store/account/subscriptions?package=${packageName}&sku=${sku}`,
53
51
  );
@@ -120,9 +118,11 @@ export const acknowledgePurchaseAndroid = ({
120
118
  * Open the Google Play Store to redeem offer codes (Android only).
121
119
  * Note: Google Play does not provide a direct API to redeem codes within the app.
122
120
  * This function opens the Play Store where users can manually enter their codes.
123
- *
121
+ *
124
122
  * @returns {Promise<void>}
125
123
  */
126
124
  export const openRedeemOfferCodeAndroid = async (): Promise<void> => {
127
- return Linking.openURL(`https://play.google.com/redeem?code=`);
125
+ return Linking.openURL(
126
+ `https://play.google.com/redeem?code=`
127
+ );
128
128
  };
@@ -5,7 +5,10 @@ import {purchaseUpdatedListener} from '..';
5
5
  import ExpoIapModule from '../ExpoIapModule';
6
6
 
7
7
  // Types
8
- import {Purchase, PurchaseError} from '../ExpoIap.types';
8
+ import {
9
+ Purchase,
10
+ PurchaseError,
11
+ } from '../ExpoIap.types';
9
12
  import type {
10
13
  ProductStatusIOS,
11
14
  AppTransactionIOS,
@@ -133,7 +136,9 @@ export const subscriptionStatusIOS = (
133
136
  *
134
137
  * @platform iOS
135
138
  */
136
- export const currentEntitlementIOS = (sku: string): Promise<Purchase> => {
139
+ export const currentEntitlementIOS = (
140
+ sku: string,
141
+ ): Promise<Purchase> => {
137
142
  return ExpoIapModule.currentEntitlement(sku);
138
143
  };
139
144
 
@@ -31,7 +31,7 @@ type ProductSubscriptionAndroidOfferDetail = {
31
31
  export type ProductAndroid = ProductCommon & {
32
32
  nameAndroid: string;
33
33
  oneTimePurchaseOfferDetailsAndroid?: ProductAndroidOneTimePurchaseOfferDetail;
34
- platform: 'android';
34
+ platform: "android";
35
35
  subscriptionOfferDetailsAndroid?: ProductSubscriptionAndroidOfferDetail[];
36
36
  /**
37
37
  * @deprecated Use `nameAndroid` instead. This field will be removed in v2.9.0.
@@ -144,7 +144,7 @@ export const PurchaseStateAndroid = PurchaseAndroidState;
144
144
 
145
145
  // Legacy naming for backward compatibility
146
146
  export type ProductPurchaseAndroid = PurchaseCommon & {
147
- platform: 'android';
147
+ platform: "android";
148
148
  /**
149
149
  * @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
150
150
  */
@@ -167,8 +167,7 @@ export type PurchaseAndroid = ProductPurchaseAndroid;
167
167
  /**
168
168
  * @deprecated Use `ProductAndroidOneTimePurchaseOfferDetail` instead. This type will be removed in v2.9.0.
169
169
  */
170
- export type OneTimePurchaseOfferDetails =
171
- ProductAndroidOneTimePurchaseOfferDetail;
170
+ export type OneTimePurchaseOfferDetails = ProductAndroidOneTimePurchaseOfferDetail;
172
171
 
173
172
  /**
174
173
  * @deprecated Use `ProductSubscriptionAndroidOfferDetail` instead. This type will be removed in v2.9.0.
@@ -30,7 +30,7 @@ export type ProductIOS = ProductCommon & {
30
30
  displayNameIOS: string;
31
31
  isFamilyShareableIOS: boolean;
32
32
  jsonRepresentationIOS: string;
33
- platform: 'ios';
33
+ platform: "ios";
34
34
  subscriptionInfoIOS?: SubscriptionInfo;
35
35
  /**
36
36
  * @deprecated Use `displayNameIOS` instead. This field will be removed in v2.9.0.
@@ -69,7 +69,7 @@ export type ProductSubscriptionIOS = ProductIOS & {
69
69
  introductoryPricePaymentModeIOS?: PaymentMode;
70
70
  introductoryPriceNumberOfPeriodsIOS?: string;
71
71
  introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;
72
- platform: 'ios';
72
+ platform: "ios";
73
73
  subscriptionPeriodNumberIOS?: string;
74
74
  subscriptionPeriodUnitIOS?: SubscriptionIosPeriod;
75
75
  /**
@@ -140,7 +140,7 @@ export type ProductStatusIOS = {
140
140
  // Legacy naming for backward compatibility
141
141
  export type ProductPurchaseIOS = PurchaseCommon & {
142
142
  // iOS basic fields
143
- platform: 'ios';
143
+ platform: "ios";
144
144
  quantityIOS?: number;
145
145
  originalTransactionDateIOS?: number;
146
146
  originalTransactionIdentifierIOS?: string;
@@ -179,6 +179,7 @@ export type ProductPurchaseIOS = PurchaseCommon & {
179
179
  // Preferred naming
180
180
  export type PurchaseIOS = ProductPurchaseIOS;
181
181
 
182
+
182
183
  export type AppTransactionIOS = {
183
184
  appTransactionId?: string; // Only available in iOS 18.4+
184
185
  originalPlatform?: string; // Only available in iOS 18.4+
package/src/useIAP.ts CHANGED
@@ -97,7 +97,9 @@ type UseIap = {
97
97
  };
98
98
 
99
99
  export interface UseIAPOptions {
100
- onPurchaseSuccess?: (purchase: Purchase) => void;
100
+ onPurchaseSuccess?: (
101
+ purchase: Purchase,
102
+ ) => void;
101
103
  onPurchaseError?: (error: PurchaseError) => void;
102
104
  onSyncError?: (error: Error) => void;
103
105
  shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing
@@ -109,8 +111,12 @@ export function useIAP(options?: UseIAPOptions): UseIap {
109
111
  const [products, setProducts] = useState<Product[]>([]);
110
112
  const [promotedProductsIOS] = useState<Purchase[]>([]);
111
113
  const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
112
- const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>([]);
113
- const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
114
+ const [purchaseHistories, setPurchaseHistories] = useState<Purchase[]>(
115
+ [],
116
+ );
117
+ const [availablePurchases, setAvailablePurchases] = useState<
118
+ Purchase[]
119
+ >([]);
114
120
  const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
115
121
  const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
116
122
  const [currentPurchaseError, setCurrentPurchaseError] =