expo-iap 2.7.13 → 2.8.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 (44) hide show
  1. package/.copilot-instructions.md +5 -5
  2. package/.cursorrules +31 -13
  3. package/CHANGELOG.md +16 -1
  4. package/CLAUDE.md +5 -1
  5. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +2 -0
  6. package/build/ExpoIap.types.d.ts +24 -11
  7. package/build/ExpoIap.types.d.ts.map +1 -1
  8. package/build/ExpoIap.types.js.map +1 -1
  9. package/build/helpers/subscription.d.ts.map +1 -1
  10. package/build/helpers/subscription.js +24 -17
  11. package/build/helpers/subscription.js.map +1 -1
  12. package/build/index.d.ts +2 -2
  13. package/build/index.d.ts.map +1 -1
  14. package/build/index.js +12 -17
  15. package/build/index.js.map +1 -1
  16. package/build/modules/ios.d.ts +10 -23
  17. package/build/modules/ios.d.ts.map +1 -1
  18. package/build/modules/ios.js +9 -23
  19. package/build/modules/ios.js.map +1 -1
  20. package/build/types/ExpoIapAndroid.types.d.ts +6 -0
  21. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
  22. package/build/types/ExpoIapAndroid.types.js.map +1 -1
  23. package/build/types/{ExpoIapIos.types.d.ts → ExpoIapIOS.types.d.ts} +45 -33
  24. package/build/types/{ExpoIapIos.types.d.ts.map → ExpoIapIOS.types.d.ts.map} +1 -1
  25. package/build/types/ExpoIapIOS.types.js +2 -0
  26. package/build/types/ExpoIapIOS.types.js.map +1 -0
  27. package/build/{useIap.d.ts → useIAP.d.ts} +2 -2
  28. package/build/{useIap.d.ts.map → useIAP.d.ts.map} +1 -1
  29. package/build/{useIap.js → useIAP.js} +7 -6
  30. package/build/useIAP.js.map +1 -0
  31. package/bun.lock +677 -61
  32. package/ios/ExpoIapModule.swift +46 -45
  33. package/jest.config.js +43 -0
  34. package/package.json +8 -3
  35. package/src/ExpoIap.types.ts +34 -15
  36. package/src/helpers/subscription.ts +27 -20
  37. package/src/index.ts +16 -24
  38. package/src/modules/ios.ts +13 -40
  39. package/src/types/ExpoIapAndroid.types.ts +6 -0
  40. package/src/types/{ExpoIapIos.types.ts → ExpoIapIOS.types.ts} +46 -32
  41. package/src/{useIap.ts → useIAP.ts} +7 -6
  42. package/build/types/ExpoIapIos.types.js +0 -2
  43. package/build/types/ExpoIapIos.types.js.map +0 -1
  44. package/build/useIap.js.map +0 -1
@@ -12,9 +12,9 @@ import {
12
12
  SubscriptionPurchase,
13
13
  } from '../ExpoIap.types';
14
14
  import type {
15
- ProductStatusIos,
15
+ ProductStatusIOS,
16
16
  AppTransactionIOS,
17
- } from '../types/ExpoIapIos.types';
17
+ } from '../types/ExpoIapIOS.types';
18
18
  import {Linking} from 'react-native';
19
19
 
20
20
  export type TransactionEvent = {
@@ -36,7 +36,7 @@ export type TransactionEvent = {
36
36
  * // Use:
37
37
  * // purchaseUpdatedListener((purchase) => { ... });
38
38
  */
39
- export const transactionUpdatedIos = (
39
+ export const transactionUpdatedIOS = (
40
40
  listener: (event: TransactionEvent) => void,
41
41
  ) => {
42
42
  const isProductPurchase = (item: unknown): item is ProductPurchase => {
@@ -74,7 +74,7 @@ export const transactionUpdatedIos = (
74
74
  };
75
75
 
76
76
  // Type guards
77
- export function isProductIos<T extends {platform?: string}>(
77
+ export function isProductIOS<T extends {platform?: string}>(
78
78
  item: unknown,
79
79
  ): item is T & {platform: 'ios'} {
80
80
  return (
@@ -102,16 +102,16 @@ export const syncIOS = (): Promise<null> => {
102
102
  /**
103
103
  * Check if user is eligible for introductory offer
104
104
  *
105
- * @param groupID The subscription group ID
105
+ * @param groupId The subscription group ID
106
106
  * @returns Promise resolving to true if eligible
107
107
  * @throws Error if called on non-iOS platform
108
108
  *
109
109
  * @platform iOS
110
110
  */
111
111
  export const isEligibleForIntroOfferIOS = (
112
- groupID: string,
112
+ groupId: string,
113
113
  ): Promise<boolean> => {
114
- return ExpoIapModule.isEligibleForIntroOffer(groupID);
114
+ return ExpoIapModule.isEligibleForIntroOffer(groupId);
115
115
  };
116
116
 
117
117
  /**
@@ -125,7 +125,7 @@ export const isEligibleForIntroOfferIOS = (
125
125
  */
126
126
  export const subscriptionStatusIOS = (
127
127
  sku: string,
128
- ): Promise<ProductStatusIos[]> => {
128
+ ): Promise<ProductStatusIOS[]> => {
129
129
  return ExpoIapModule.subscriptionStatus(sku);
130
130
  };
131
131
 
@@ -176,7 +176,7 @@ export const beginRefundRequestIOS = (
176
176
  /**
177
177
  * Shows the system UI for managing subscriptions.
178
178
  * When the user changes subscription renewal status, the system will emit events to
179
- * purchaseUpdatedListener and transactionUpdatedIos listeners.
179
+ * purchaseUpdatedListener and transactionUpdatedIOS listeners.
180
180
  *
181
181
  * @returns Promise resolving to null on success
182
182
  * @throws Error if called on non-iOS platform
@@ -321,7 +321,7 @@ export const buyPromotedProductIOS = (): Promise<void> => {
321
321
  *
322
322
  * @platform iOS
323
323
  */
324
- export const deepLinkToSubscriptionsIos = (): Promise<void> =>
324
+ export const deepLinkToSubscriptionsIOS = (): Promise<void> =>
325
325
  Linking.openURL('https://apps.apple.com/account/subscriptions');
326
326
 
327
327
  // ============= DEPRECATED FUNCTIONS =============
@@ -340,11 +340,11 @@ export const sync = (): Promise<null> => {
340
340
  /**
341
341
  * @deprecated Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.
342
342
  */
343
- export const isEligibleForIntroOffer = (groupID: string): Promise<boolean> => {
343
+ export const isEligibleForIntroOffer = (groupId: string): Promise<boolean> => {
344
344
  console.warn(
345
345
  '`isEligibleForIntroOffer` is deprecated. Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.',
346
346
  );
347
- return isEligibleForIntroOfferIOS(groupID);
347
+ return isEligibleForIntroOfferIOS(groupId);
348
348
  };
349
349
 
350
350
  /**
@@ -352,7 +352,7 @@ export const isEligibleForIntroOffer = (groupID: string): Promise<boolean> => {
352
352
  */
353
353
  export const subscriptionStatus = (
354
354
  sku: string,
355
- ): Promise<ProductStatusIos[]> => {
355
+ ): Promise<ProductStatusIOS[]> => {
356
356
  console.warn(
357
357
  '`subscriptionStatus` is deprecated. Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.',
358
358
  );
@@ -401,16 +401,6 @@ export const showManageSubscriptions = (): Promise<null> => {
401
401
  return showManageSubscriptionsIOS();
402
402
  };
403
403
 
404
- /**
405
- * @deprecated Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.
406
- */
407
- export const getReceiptIos = (): Promise<string> => {
408
- console.warn(
409
- '`getReceiptIos` is deprecated. Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.',
410
- );
411
- return getReceiptIOS();
412
- };
413
-
414
404
  /**
415
405
  * @deprecated Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.
416
406
  */
@@ -431,23 +421,6 @@ export const getTransactionJws = (sku: string): Promise<string> => {
431
421
  return getTransactionJwsIOS(sku);
432
422
  };
433
423
 
434
- /**
435
- * @deprecated Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.
436
- */
437
- export const validateReceiptIos = async (
438
- sku: string,
439
- ): Promise<{
440
- isValid: boolean;
441
- receiptData: string;
442
- jwsRepresentation: string;
443
- latestTransaction?: ProductPurchase;
444
- }> => {
445
- console.warn(
446
- '`validateReceiptIos` is deprecated. Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.',
447
- );
448
- return validateReceiptIOS(sku);
449
- };
450
-
451
424
  /**
452
425
  * @deprecated Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.
453
426
  */
@@ -68,6 +68,9 @@ type SubscriptionOffer = {
68
68
  };
69
69
 
70
70
  export type RequestSubscriptionAndroidProps = RequestPurchaseAndroidProps & {
71
+ /**
72
+ * @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
73
+ */
71
74
  purchaseTokenAndroid?: string;
72
75
  replacementModeAndroid?: ReplacementModesAndroid;
73
76
  subscriptionOffers: SubscriptionOffer[];
@@ -115,6 +118,9 @@ export enum PurchaseStateAndroid {
115
118
 
116
119
  export type ProductPurchaseAndroid = PurchaseBase & {
117
120
  ids?: string[];
121
+ /**
122
+ * @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
123
+ */
118
124
  purchaseTokenAndroid?: string;
119
125
  dataAndroid?: string;
120
126
  signatureAndroid?: string;
@@ -19,14 +19,14 @@ type SubscriptionOffer = {
19
19
  type SubscriptionInfo = {
20
20
  introductoryOffer?: SubscriptionOffer;
21
21
  promotionalOffers?: SubscriptionOffer[];
22
- subscriptionGroupID: string;
22
+ subscriptionGroupId: string;
23
23
  subscriptionPeriod: {
24
24
  unit: SubscriptionIosPeriod;
25
25
  value: number;
26
26
  };
27
27
  };
28
28
 
29
- export type ProductIos = ProductBase & {
29
+ export type ProductIOS = ProductBase & {
30
30
  displayName: string;
31
31
  isFamilyShareable: boolean;
32
32
  jsonRepresentation: string;
@@ -45,7 +45,7 @@ export type Discount = {
45
45
  subscriptionPeriod: string;
46
46
  };
47
47
 
48
- export type SubscriptionProductIos = ProductIos & {
48
+ export type SubscriptionProductIOS = ProductIOS & {
49
49
  discounts?: Discount[];
50
50
  introductoryPrice?: string;
51
51
  introductoryPriceAsAmountIOS?: string;
@@ -79,7 +79,7 @@ export type PaymentDiscount = {
79
79
  timestamp: number;
80
80
  };
81
81
 
82
- export type RequestPurchaseIosProps = {
82
+ export type RequestPurchaseIOSProps = {
83
83
  sku: string;
84
84
  andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
85
85
  /**
@@ -90,7 +90,17 @@ export type RequestPurchaseIosProps = {
90
90
  withOffer?: PaymentDiscount;
91
91
  };
92
92
 
93
- export type RequestSubscriptionIosProps = RequestPurchaseIosProps;
93
+ export type RequestSubscriptionIOSProps = RequestPurchaseIOSProps;
94
+
95
+ /**
96
+ * @deprecated Use RequestPurchaseIOSProps instead. This alias will be removed in v3.0.0.
97
+ */
98
+ export type RequestPurchaseIosProps = RequestPurchaseIOSProps;
99
+
100
+ /**
101
+ * @deprecated Use RequestSubscriptionIOSProps instead. This alias will be removed in v3.0.0.
102
+ */
103
+ export type RequestSubscriptionIosProps = RequestSubscriptionIOSProps;
94
104
 
95
105
  type SubscriptionStatus =
96
106
  | 'expired'
@@ -105,46 +115,50 @@ type RenewalInfo = {
105
115
  autoRenewPreference?: string;
106
116
  };
107
117
 
108
- export type ProductStatusIos = {
118
+ export type ProductStatusIOS = {
109
119
  state: SubscriptionStatus;
110
120
  renewalInfo?: RenewalInfo;
111
121
  };
112
122
 
113
- export type ProductPurchaseIos = PurchaseBase & {
123
+ export type ProductPurchaseIOS = PurchaseBase & {
114
124
  // iOS basic fields
115
- quantityIos?: number;
116
- originalTransactionDateIos?: number;
117
- originalTransactionIdentifierIos?: string;
125
+ quantityIOS?: number;
126
+ originalTransactionDateIOS?: number;
127
+ originalTransactionIdentifierIOS?: string;
118
128
  appAccountToken?: string;
119
129
  // iOS additional fields from StoreKit 2
120
- expirationDateIos?: number;
121
- webOrderLineItemIdIos?: number;
122
- environmentIos?: string;
123
- storefrontCountryCodeIos?: string;
124
- appBundleIdIos?: string;
125
- productTypeIos?: string;
126
- subscriptionGroupIdIos?: string;
127
- isUpgradedIos?: boolean;
128
- ownershipTypeIos?: string;
129
- reasonIos?: string;
130
- reasonStringRepresentationIos?: string;
131
- transactionReasonIos?: 'PURCHASE' | 'RENEWAL' | string;
132
- revocationDateIos?: number;
133
- revocationReasonIos?: string;
134
- offerIos?: {
130
+ expirationDateIOS?: number;
131
+ webOrderLineItemIdIOS?: number;
132
+ environmentIOS?: string;
133
+ storefrontCountryCodeIOS?: string;
134
+ appBundleIdIOS?: string;
135
+ productTypeIOS?: string;
136
+ subscriptionGroupIdIOS?: string;
137
+ isUpgradedIOS?: boolean;
138
+ ownershipTypeIOS?: string;
139
+ reasonIOS?: string;
140
+ reasonStringRepresentationIOS?: string;
141
+ transactionReasonIOS?: 'PURCHASE' | 'RENEWAL' | string;
142
+ revocationDateIOS?: number;
143
+ revocationReasonIOS?: string;
144
+ offerIOS?: {
135
145
  id: string;
136
146
  type: string;
137
147
  paymentMode: string;
138
148
  };
139
- priceIos?: number;
140
- currencyIos?: string;
141
- jwsRepresentationIos?: string;
149
+ priceIOS?: number;
150
+ currencyIOS?: string;
151
+ /**
152
+ * @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
153
+ * iOS 15+ JWS representation is now available through the `purchaseToken` field.
154
+ */
155
+ jwsRepresentationIOS?: string;
142
156
  };
143
157
 
144
158
  export type AppTransactionIOS = {
145
- appTransactionID?: string; // Only available in iOS 18.4+
159
+ appTransactionId?: string; // Only available in iOS 18.4+
146
160
  originalPlatform?: string; // Only available in iOS 18.4+
147
- bundleID: string;
161
+ bundleId: string;
148
162
  appVersion: string;
149
163
  originalAppVersion: string;
150
164
  originalPurchaseDate: number;
@@ -152,7 +166,7 @@ export type AppTransactionIOS = {
152
166
  deviceVerificationNonce: string;
153
167
  environment: string;
154
168
  signedDate: number;
155
- appID?: number;
156
- appVersionID?: number;
169
+ appId?: number;
170
+ appVersionId?: number;
157
171
  preorderDate?: number;
158
172
  };
@@ -19,7 +19,7 @@ import {
19
19
  getActiveSubscriptions,
20
20
  hasActiveSubscriptions,
21
21
  type ActiveSubscription,
22
- } from './';
22
+ } from '.';
23
23
  import {
24
24
  syncIOS,
25
25
  getPromotedProductIOS,
@@ -158,7 +158,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
158
158
  const subscriptionsRef = useRef<{
159
159
  purchaseUpdate?: EventSubscription;
160
160
  purchaseError?: EventSubscription;
161
- promotedProductsIos?: EventSubscription;
161
+ promotedProductsIOS?: EventSubscription;
162
162
  promotedProductIOS?: EventSubscription;
163
163
  }>({});
164
164
 
@@ -260,7 +260,8 @@ export function useIAP(options?: UseIAPOptions): UseIap {
260
260
  return result;
261
261
  } catch (error) {
262
262
  console.error('Error getting active subscriptions:', error);
263
- setActiveSubscriptions([]);
263
+ // Don't clear existing activeSubscriptions on error - preserve current state
264
+ // This prevents the UI from showing empty state when there are temporary network issues
264
265
  return [];
265
266
  }
266
267
  },
@@ -385,7 +386,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
385
386
  setCurrentPurchaseError(undefined);
386
387
  setCurrentPurchase(purchase);
387
388
 
388
- if ('expirationDateIos' in purchase) {
389
+ if ('expirationDateIOS' in purchase) {
389
390
  await refreshSubscriptionStatus(purchase.id);
390
391
  }
391
392
 
@@ -408,7 +409,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
408
409
 
409
410
  if (Platform.OS === 'ios') {
410
411
  // iOS promoted products listener
411
- subscriptionsRef.current.promotedProductsIos =
412
+ subscriptionsRef.current.promotedProductsIOS =
412
413
  promotedProductListenerIOS((product: Product) => {
413
414
  setPromotedProductIOS(product);
414
415
 
@@ -427,7 +428,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
427
428
  return () => {
428
429
  currentSubscriptions.purchaseUpdate?.remove();
429
430
  currentSubscriptions.purchaseError?.remove();
430
- currentSubscriptions.promotedProductsIos?.remove();
431
+ currentSubscriptions.promotedProductsIOS?.remove();
431
432
  currentSubscriptions.promotedProductIOS?.remove();
432
433
  endConnection();
433
434
  setConnected(false);
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=ExpoIapIos.types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ExpoIapIos.types.js","sourceRoot":"","sources":["../../src/types/ExpoIapIos.types.ts"],"names":[],"mappings":"","sourcesContent":["import {PurchaseBase, ProductBase} from '../ExpoIap.types';\n\ntype SubscriptionIosPeriod = 'DAY' | 'WEEK' | 'MONTH' | 'YEAR' | '';\ntype PaymentMode = '' | 'FREETRIAL' | 'PAYASYOUGO' | 'PAYUPFRONT';\n\ntype SubscriptionOffer = {\n displayPrice: string;\n id: string;\n paymentMode: PaymentMode;\n period: {\n unit: SubscriptionIosPeriod;\n value: number;\n };\n periodCount: number;\n price: number;\n type: 'introductory' | 'promotional';\n};\n\ntype SubscriptionInfo = {\n introductoryOffer?: SubscriptionOffer;\n promotionalOffers?: SubscriptionOffer[];\n subscriptionGroupID: string;\n subscriptionPeriod: {\n unit: SubscriptionIosPeriod;\n value: number;\n };\n};\n\nexport type ProductIos = ProductBase & {\n displayName: string;\n isFamilyShareable: boolean;\n jsonRepresentation: string;\n subscription?: SubscriptionInfo;\n introductoryPriceNumberOfPeriodsIOS?: string;\n introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;\n};\n\nexport type Discount = {\n identifier: string;\n type: string;\n numberOfPeriods: string;\n price: string;\n localizedPrice: string;\n paymentMode: PaymentMode;\n subscriptionPeriod: string;\n};\n\nexport type SubscriptionProductIos = ProductIos & {\n discounts?: Discount[];\n introductoryPrice?: string;\n introductoryPriceAsAmountIOS?: string;\n introductoryPricePaymentModeIOS?: PaymentMode;\n introductoryPriceNumberOfPeriodsIOS?: string;\n introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;\n subscriptionPeriodNumberIOS?: string;\n subscriptionPeriodUnitIOS?: SubscriptionIosPeriod;\n};\n\nexport type PaymentDiscount = {\n /**\n * A string used to uniquely identify a discount offer for a product.\n */\n identifier: string;\n /**\n * A string that identifies the key used to generate the signature.\n */\n keyIdentifier: string;\n /**\n * A universally unique ID (UUID) value that you define.\n */\n nonce: string;\n /**\n * A UTF-8 string representing the properties of a specific discount offer, cryptographically signed.\n */\n signature: string;\n /**\n * The date and time of the signature's creation in milliseconds, formatted in Unix epoch time.\n */\n timestamp: number;\n};\n\nexport type RequestPurchaseIosProps = {\n sku: string;\n andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;\n /**\n * UUID representing user account\n */\n appAccountToken?: string;\n quantity?: number;\n withOffer?: PaymentDiscount;\n};\n\nexport type RequestSubscriptionIosProps = RequestPurchaseIosProps;\n\ntype SubscriptionStatus =\n | 'expired'\n | 'inBillingRetryPeriod'\n | 'inGracePeriod'\n | 'revoked'\n | 'subscribed';\n\ntype RenewalInfo = {\n jsonRepresentation?: string;\n willAutoRenew: boolean;\n autoRenewPreference?: string;\n};\n\nexport type ProductStatusIos = {\n state: SubscriptionStatus;\n renewalInfo?: RenewalInfo;\n};\n\nexport type ProductPurchaseIos = PurchaseBase & {\n // iOS basic fields\n quantityIos?: number;\n originalTransactionDateIos?: number;\n originalTransactionIdentifierIos?: string;\n appAccountToken?: string;\n // iOS additional fields from StoreKit 2\n expirationDateIos?: number;\n webOrderLineItemIdIos?: number;\n environmentIos?: string;\n storefrontCountryCodeIos?: string;\n appBundleIdIos?: string;\n productTypeIos?: string;\n subscriptionGroupIdIos?: string;\n isUpgradedIos?: boolean;\n ownershipTypeIos?: string;\n reasonIos?: string;\n reasonStringRepresentationIos?: string;\n transactionReasonIos?: 'PURCHASE' | 'RENEWAL' | string;\n revocationDateIos?: number;\n revocationReasonIos?: string;\n offerIos?: {\n id: string;\n type: string;\n paymentMode: string;\n };\n priceIos?: number;\n currencyIos?: string;\n jwsRepresentationIos?: string;\n};\n\nexport type AppTransactionIOS = {\n appTransactionID?: string; // Only available in iOS 18.4+\n originalPlatform?: string; // Only available in iOS 18.4+\n bundleID: string;\n appVersion: string;\n originalAppVersion: string;\n originalPurchaseDate: number;\n deviceVerification: string;\n deviceVerificationNonce: string;\n environment: string;\n signedDate: number;\n appID?: number;\n appVersionID?: number;\n preorderDate?: number;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\n// Internal modules\nimport {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n promotedProductListenerIOS,\n getAvailablePurchases,\n getPurchaseHistories,\n finishTransaction as finishTransactionInternal,\n requestPurchase as requestPurchaseInternal,\n requestProducts,\n validateReceipt as validateReceiptInternal,\n getActiveSubscriptions,\n hasActiveSubscriptions,\n type ActiveSubscription,\n} from './';\nimport {\n syncIOS,\n getPromotedProductIOS,\n buyPromotedProductIOS,\n} from './modules/ios';\n\n// Types\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n RequestPurchaseProps,\n RequestSubscriptionProps,\n} from './ExpoIap.types';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n promotedProductIdIOS?: string;\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n promotedProductIOS?: Product;\n activeSubscriptions: ActiveSubscription[];\n clearCurrentPurchase: () => void;\n clearCurrentPurchaseError: () => void;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<PurchaseResult | boolean>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n requestProducts: (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }) => Promise<void>;\n /**\n * @deprecated Use requestProducts({ skus, type: 'inapp' }) instead. This method will be removed in version 3.0.0.\n * Note: This method internally uses requestProducts, so no deprecation warning is shown.\n */\n getProducts: (skus: string[]) => Promise<void>;\n /**\n * @deprecated Use requestProducts({ skus, type: 'subs' }) instead. This method will be removed in version 3.0.0.\n * Note: This method internally uses requestProducts, so no deprecation warning is shown.\n */\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: (params: {\n request: RequestPurchaseProps | RequestSubscriptionProps;\n type?: 'inapp' | 'subs';\n }) => Promise<any>;\n validateReceipt: (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => Promise<any>;\n restorePurchases: () => Promise<void>; // 구매 복원 함수 추가\n getPromotedProductIOS: () => Promise<any | null>;\n buyPromotedProductIOS: () => Promise<void>;\n getActiveSubscriptions: (\n subscriptionIds?: string[],\n ) => Promise<ActiveSubscription[]>;\n hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (\n purchase: ProductPurchase | SubscriptionPurchase,\n ) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n onPromotedProductIOS?: (product: Product) => void;\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS] = useState<ProductPurchase[]>([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n const [promotedProductIdIOS] = useState<string>();\n const [activeSubscriptions, setActiveSubscriptions] = useState<\n ActiveSubscription[]\n >([]);\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIos?: EventSubscription;\n promotedProductIOS?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<SubscriptionProduct[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await requestProducts({skus, type: 'inapp'});\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result as Product[],\n (product) => product.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await requestProducts({skus, type: 'subs'});\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result as SubscriptionProduct[],\n (subscription) => subscription.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching subscriptions:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const requestProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }): Promise<void> => {\n try {\n const result = await requestProducts(params);\n if (params.type === 'subs') {\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result as SubscriptionProduct[],\n (subscription) => subscription.id,\n ),\n );\n } else {\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result as Product[],\n (product) => product.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases();\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<ActiveSubscription[]> => {\n try {\n const result = await getActiveSubscriptions(subscriptionIds);\n setActiveSubscriptions(result);\n return result;\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n setActiveSubscriptions([]);\n return [];\n }\n },\n [],\n );\n\n const hasActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<boolean> => {\n try {\n return await hasActiveSubscriptions(subscriptionIds);\n } catch (error) {\n console.error('Error checking active subscriptions:', error);\n return false;\n }\n },\n [],\n );\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistories());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<PurchaseResult | boolean> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n clearCurrentPurchase();\n }\n if (purchase.id === currentPurchaseError?.productId) {\n clearCurrentPurchaseError();\n }\n }\n },\n [\n currentPurchase?.id,\n currentPurchaseError?.productId,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n ],\n );\n\n const requestPurchaseWithReset = useCallback(\n async (requestObj: {request: any; type?: 'inapp' | 'subs'}) => {\n clearCurrentPurchase();\n clearCurrentPurchaseError();\n\n try {\n return await requestPurchaseInternal(requestObj);\n } catch (error) {\n throw error;\n }\n },\n [clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await getSubscriptionsInternal([productId]);\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [getAvailablePurchasesInternal, getSubscriptionsInternal],\n );\n\n const restorePurchases = useCallback(async (): Promise<void> => {\n try {\n if (Platform.OS === 'ios') {\n await syncIOS().catch((error) => {\n if (optionsRef.current?.onSyncError) {\n optionsRef.current.onSyncError(error);\n } else {\n console.warn('Error restoring purchases:', error);\n }\n });\n }\n await getAvailablePurchasesInternal();\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, [getAvailablePurchasesInternal]);\n\n const validateReceipt = useCallback(\n async (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => {\n return validateReceiptInternal(sku, androidOptions);\n },\n [],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products listener\n subscriptionsRef.current.promotedProductsIos =\n promotedProductListenerIOS((product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n });\n }\n }\n }, [refreshSubscriptionStatus]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n currentSubscriptions.promotedProductIOS?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n promotedProductIdIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n promotedProductIOS,\n activeSubscriptions,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n requestProducts: requestProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n getPromotedProductIOS,\n buyPromotedProductIOS,\n getActiveSubscriptions: getActiveSubscriptionsInternal,\n hasActiveSubscriptions: hasActiveSubscriptionsInternal,\n };\n}\n"]}