expo-iap 2.8.1 → 2.8.3-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +15 -6
  2. package/CLAUDE.md +0 -5
  3. package/CONTRIBUTING.md +3 -4
  4. package/build/ExpoIap.types.d.ts +13 -9
  5. package/build/ExpoIap.types.d.ts.map +1 -1
  6. package/build/ExpoIap.types.js.map +1 -1
  7. package/build/helpers/subscription.d.ts.map +1 -1
  8. package/build/helpers/subscription.js +3 -6
  9. package/build/helpers/subscription.js.map +1 -1
  10. package/build/index.d.ts +6 -6
  11. package/build/index.d.ts.map +1 -1
  12. package/build/index.js.map +1 -1
  13. package/build/modules/android.d.ts.map +1 -1
  14. package/build/modules/android.js.map +1 -1
  15. package/build/modules/ios.d.ts +8 -8
  16. package/build/modules/ios.d.ts.map +1 -1
  17. package/build/modules/ios.js +3 -3
  18. package/build/modules/ios.js.map +1 -1
  19. package/build/types/ExpoIapAndroid.types.d.ts +2 -2
  20. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
  21. package/build/types/ExpoIapAndroid.types.js.map +1 -1
  22. package/build/types/ExpoIapIOS.types.d.ts +3 -3
  23. package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
  24. package/build/types/ExpoIapIOS.types.js.map +1 -1
  25. package/build/useIAP.d.ts +6 -6
  26. package/build/useIAP.d.ts.map +1 -1
  27. package/build/useIAP.js.map +1 -1
  28. package/bun.lock +122 -456
  29. package/coverage/clover.xml +601 -0
  30. package/coverage/coverage-final.json +9 -0
  31. package/coverage/lcov-report/base.css +224 -0
  32. package/coverage/lcov-report/block-navigation.js +87 -0
  33. package/coverage/lcov-report/favicon.png +0 -0
  34. package/coverage/lcov-report/index.html +176 -0
  35. package/coverage/lcov-report/prettify.css +1 -0
  36. package/coverage/lcov-report/prettify.js +2 -0
  37. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  38. package/coverage/lcov-report/sorter.js +210 -0
  39. package/coverage/lcov-report/src/ExpoIap.types.ts.html +1243 -0
  40. package/coverage/lcov-report/src/helpers/index.html +116 -0
  41. package/coverage/lcov-report/src/helpers/subscription.ts.html +430 -0
  42. package/coverage/lcov-report/src/index.html +146 -0
  43. package/coverage/lcov-report/src/index.ts.html +2227 -0
  44. package/coverage/lcov-report/src/modules/android.ts.html +469 -0
  45. package/coverage/lcov-report/src/modules/index.html +131 -0
  46. package/coverage/lcov-report/src/modules/ios.ts.html +1411 -0
  47. package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +487 -0
  48. package/coverage/lcov-report/src/types/index.html +116 -0
  49. package/coverage/lcov-report/src/useIap.ts.html +1483 -0
  50. package/coverage/lcov-report/src/utils/errorMapping.ts.html +349 -0
  51. package/coverage/lcov-report/src/utils/index.html +116 -0
  52. package/coverage/lcov.info +1115 -0
  53. package/ios/expoiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  54. package/ios/expoiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  55. package/jest.config.js +14 -17
  56. package/package.json +1 -1
  57. package/plugin/build/withIAP.js +26 -3
  58. package/plugin/src/withIAP.ts +35 -3
  59. package/plugin/tsconfig.tsbuildinfo +1 -1
  60. package/src/ExpoIap.types.ts +15 -16
  61. package/src/helpers/subscription.ts +21 -28
  62. package/src/index.ts +16 -21
  63. package/src/modules/android.ts +7 -7
  64. package/src/modules/ios.ts +13 -15
  65. package/src/types/ExpoIapAndroid.types.ts +3 -4
  66. package/src/types/ExpoIapIOS.types.ts +4 -3
  67. package/src/useIAP.ts +11 -13
@@ -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.1",
3
+ "version": "2.8.3-rc.1",
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,28 @@ 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
+ // Add StoreKit configuration for development (optional)
49
+ config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
50
+ // This is optional - adds StoreKit configuration for testing
51
+ // Users can override this with their own configuration file
52
+ if (!config.modResults.SKStoreProductParameterITunesItemIdentifier) {
53
+ // This is just a placeholder, users should set their actual app ID
54
+ config.modResults.SKStoreProductParameterITunesItemIdentifier = '';
55
+ }
56
+ return config;
57
+ });
58
+ return config;
59
+ };
60
+ const withIapAndroid = (config) => {
40
61
  // Add IAP dependencies to app build.gradle
41
62
  config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
42
63
  config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
@@ -67,10 +88,12 @@ const withIAPAndroid = (config) => {
67
88
  };
68
89
  const withIAP = (config, _props) => {
69
90
  try {
70
- const result = withIAPAndroid(config);
91
+ // Apply both iOS and Android configurations
92
+ config = withIapIOS(config);
93
+ config = withIapAndroid(config);
71
94
  // Set flag after first execution to prevent duplicate logs
72
95
  hasLoggedPluginExecution = true;
73
- return result;
96
+ return config;
74
97
  }
75
98
  catch (error) {
76
99
  config_plugins_1.WarningAggregator.addWarningAndroid('expo-iap', `expo-iap plugin encountered an error: ${error}`);
@@ -4,6 +4,8 @@ import {
4
4
  WarningAggregator,
5
5
  withAndroidManifest,
6
6
  withAppBuildGradle,
7
+ withEntitlementsPlist,
8
+ withInfoPlist,
7
9
  } from 'expo/config-plugins';
8
10
 
9
11
  const pkg = require('../../package.json');
@@ -56,7 +58,34 @@ const modifyAppBuildGradle = (gradle: string): string => {
56
58
  return modified;
57
59
  };
58
60
 
59
- const withIAPAndroid: ConfigPlugin = (config) => {
61
+ const withIapIOS: ConfigPlugin = (config) => {
62
+ // Add In-App Purchase capability to entitlements
63
+ config = withEntitlementsPlist(config, (config) => {
64
+ config.modResults['com.apple.developer.in-app-payments'] = ['Default'];
65
+
66
+ if (!hasLoggedPluginExecution) {
67
+ console.log('✅ Added In-App Purchase capability to iOS entitlements');
68
+ }
69
+
70
+ return config;
71
+ });
72
+
73
+ // Add StoreKit configuration for development (optional)
74
+ config = withInfoPlist(config, (config) => {
75
+ // This is optional - adds StoreKit configuration for testing
76
+ // Users can override this with their own configuration file
77
+ if (!config.modResults.SKStoreProductParameterITunesItemIdentifier) {
78
+ // This is just a placeholder, users should set their actual app ID
79
+ config.modResults.SKStoreProductParameterITunesItemIdentifier = '';
80
+ }
81
+
82
+ return config;
83
+ });
84
+
85
+ return config;
86
+ };
87
+
88
+ const withIapAndroid: ConfigPlugin = (config) => {
60
89
  // Add IAP dependencies to app build.gradle
61
90
  config = withAppBuildGradle(config, (config) => {
62
91
  config.modResults.contents = modifyAppBuildGradle(
@@ -100,10 +129,13 @@ const withIAPAndroid: ConfigPlugin = (config) => {
100
129
 
101
130
  const withIAP: ConfigPlugin = (config, _props) => {
102
131
  try {
103
- const result = withIAPAndroid(config);
132
+ // Apply both iOS and Android configurations
133
+ config = withIapIOS(config);
134
+ config = withIapAndroid(config);
135
+
104
136
  // Set flag after first execution to prevent duplicate logs
105
137
  hasLoggedPluginExecution = true;
106
- return result;
138
+ return config;
107
139
  } catch (error) {
108
140
  WarningAggregator.addWarningAndroid(
109
141
  'expo-iap',
@@ -1 +1 @@
1
- {"root":["./src/withIAP.ts"],"version":"5.9.2"}
1
+ {"root":["./src/withiap.ts"],"version":"5.8.3"}
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ProductAndroid,
3
- ProductPurchaseAndroid,
3
+ PurchaseAndroid,
4
4
  ProductSubscriptionAndroid,
5
5
  } from './types/ExpoIapAndroid.types';
6
6
  import {
7
7
  ProductIOS,
8
- ProductPurchaseIOS,
8
+ PurchaseIOS,
9
9
  ProductSubscriptionIOS,
10
10
  } from './types/ExpoIapIOS.types';
11
11
  import {NATIVE_ERROR_CODES} from './ExpoIapModule';
@@ -63,29 +63,28 @@ export type SubscriptionProduct =
63
63
 
64
64
  // Re-export platform-specific types
65
65
  export type {
66
- ProductPurchaseAndroid,
67
66
  PurchaseAndroid,
68
67
  ProductSubscriptionAndroid,
69
- SubscriptionProductAndroid, // Legacy
70
68
  } from './types/ExpoIapAndroid.types';
71
69
  export type {
72
- ProductPurchaseIOS,
73
70
  PurchaseIOS,
74
71
  ProductSubscriptionIOS,
75
- SubscriptionProductIOS, // Legacy
76
72
  } from './types/ExpoIapIOS.types';
77
73
 
78
- // Union type for platform-specific purchase types
79
- export type ProductPurchase =
80
- | (ProductPurchaseAndroid & AndroidPlatform)
81
- | (ProductPurchaseIOS & IosPlatform);
74
+ // Unified purchase type for both products and subscriptions
75
+ export type Purchase =
76
+ | (PurchaseAndroid & AndroidPlatform)
77
+ | (PurchaseIOS & IosPlatform);
82
78
 
83
- // Union type for platform-specific subscription purchase types
84
- export type SubscriptionPurchase =
85
- | (ProductPurchaseAndroid & AndroidPlatform & {autoRenewingAndroid: boolean})
86
- | (ProductPurchaseIOS & IosPlatform);
87
-
88
- export type Purchase = ProductPurchase | SubscriptionPurchase;
79
+ // Legacy type aliases - deprecated, use Purchase instead
80
+ /**
81
+ * @deprecated Use `Purchase` instead. This type alias will be removed in v2.9.0.
82
+ */
83
+ export type ProductPurchase = Purchase;
84
+ /**
85
+ * @deprecated Use `Purchase` instead. This type alias will be removed in v2.9.0.
86
+ */
87
+ export type SubscriptionPurchase = Purchase;
89
88
 
90
89
  export type PurchaseResult = {
91
90
  responseCode?: number;
@@ -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
@@ -18,16 +18,14 @@ import {
18
18
  // Types
19
19
  import {
20
20
  Product,
21
- ProductPurchase,
22
21
  Purchase,
23
22
  PurchaseError,
24
23
  PurchaseResult,
25
24
  RequestSubscriptionProps,
26
25
  RequestPurchaseProps,
27
26
  SubscriptionProduct,
28
- SubscriptionPurchase,
29
27
  } from './ExpoIap.types';
30
- import {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
28
+ import {PurchaseAndroid} from './types/ExpoIapAndroid.types';
31
29
  import {PaymentDiscount} from './types/ExpoIapIOS.types';
32
30
 
33
31
  // Export all types
@@ -281,7 +279,7 @@ export const getPurchaseHistory = ({
281
279
  }: {
282
280
  alsoPublishToEventListener?: boolean;
283
281
  onlyIncludeActiveItems?: boolean;
284
- } = {}): Promise<ProductPurchase[]> => {
282
+ } = {}): Promise<Purchase[]> => {
285
283
  console.warn(
286
284
  '`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.',
287
285
  );
@@ -297,7 +295,7 @@ export const getPurchaseHistories = ({
297
295
  }: {
298
296
  alsoPublishToEventListener?: boolean;
299
297
  onlyIncludeActiveItems?: boolean;
300
- } = {}): Promise<ProductPurchase[]> =>
298
+ } = {}): Promise<Purchase[]> =>
301
299
  (
302
300
  Platform.select({
303
301
  ios: async () => {
@@ -323,7 +321,7 @@ export const getAvailablePurchases = ({
323
321
  }: {
324
322
  alsoPublishToEventListener?: boolean;
325
323
  onlyIncludeActiveItems?: boolean;
326
- } = {}): Promise<ProductPurchase[]> =>
324
+ } = {}): Promise<Purchase[]> =>
327
325
  (
328
326
  Platform.select({
329
327
  ios: () =>
@@ -333,9 +331,8 @@ export const getAvailablePurchases = ({
333
331
  ),
334
332
  android: async () => {
335
333
  const products = await ExpoIapModule.getAvailableItemsByType('inapp');
336
- const subscriptions = await ExpoIapModule.getAvailableItemsByType(
337
- 'subs',
338
- );
334
+ const subscriptions =
335
+ await ExpoIapModule.getAvailableItemsByType('subs');
339
336
  return products.concat(subscriptions);
340
337
  },
341
338
  }) || (() => Promise.resolve([]))
@@ -410,10 +407,8 @@ const normalizeRequestProps = (
410
407
  export const requestPurchase = (
411
408
  requestObj: PurchaseRequest,
412
409
  ): Promise<
413
- | ProductPurchase
414
- | SubscriptionPurchase
415
- | ProductPurchase[]
416
- | SubscriptionPurchase[]
410
+ | Purchase
411
+ | Purchase[]
417
412
  | void
418
413
  > => {
419
414
  const {request, type = 'inapp'} = requestObj;
@@ -446,8 +441,8 @@ export const requestPurchase = (
446
441
  );
447
442
 
448
443
  return type === 'inapp'
449
- ? (purchase as ProductPurchase)
450
- : (purchase as SubscriptionPurchase);
444
+ ? (purchase as Purchase)
445
+ : (purchase as Purchase);
451
446
  })();
452
447
  }
453
448
 
@@ -478,7 +473,7 @@ export const requestPurchase = (
478
473
  obfuscatedProfileId: obfuscatedProfileIdAndroid,
479
474
  offerTokenArr: [],
480
475
  isOfferPersonalized: isOfferPersonalized ?? false,
481
- }) as Promise<ProductPurchase[]>;
476
+ }) as Promise<Purchase[]>;
482
477
  })();
483
478
  }
484
479
 
@@ -504,7 +499,7 @@ export const requestPurchase = (
504
499
  obfuscatedProfileId: obfuscatedProfileIdAndroid,
505
500
  offerTokenArr: subscriptionOffers.map((so: any) => so.offerToken),
506
501
  isOfferPersonalized: isOfferPersonalized ?? false,
507
- }) as Promise<SubscriptionPurchase[]>;
502
+ }) as Promise<Purchase[]>;
508
503
  })();
509
504
  }
510
505
 
@@ -543,13 +538,13 @@ export const requestPurchase = (
543
538
  */
544
539
  export const requestSubscription = async (
545
540
  request: RequestSubscriptionProps,
546
- ): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> => {
541
+ ): Promise<Purchase | Purchase[] | null | void> => {
547
542
  console.warn(
548
543
  "`requestSubscription` is deprecated and will be removed in version 3.0.0. Use `requestPurchase({ request, type: 'subs' })` instead.",
549
544
  );
550
545
  return (await requestPurchase({request, type: 'subs'})) as
551
- | SubscriptionPurchase
552
- | SubscriptionPurchase[]
546
+ | Purchase
547
+ | Purchase[]
553
548
  | null
554
549
  | void;
555
550
  };
@@ -574,7 +569,7 @@ export const finishTransaction = ({
574
569
  return Promise.resolve(true);
575
570
  },
576
571
  android: async () => {
577
- const androidPurchase = purchase as ProductPurchaseAndroid;
572
+ const androidPurchase = purchase as PurchaseAndroid;
578
573
 
579
574
  if (isConsumable) {
580
575
  return ExpoIapModule.consumeProduct(androidPurchase.purchaseToken);
@@ -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
  };
@@ -6,10 +6,8 @@ import ExpoIapModule from '../ExpoIapModule';
6
6
 
7
7
  // Types
8
8
  import {
9
- ProductPurchase,
10
- PurchaseError,
11
9
  Purchase,
12
- SubscriptionPurchase,
10
+ PurchaseError,
13
11
  } from '../ExpoIap.types';
14
12
  import type {
15
13
  ProductStatusIOS,
@@ -18,7 +16,7 @@ import type {
18
16
  import {Linking} from 'react-native';
19
17
 
20
18
  export type TransactionEvent = {
21
- transaction?: ProductPurchase;
19
+ transaction?: Purchase;
22
20
  error?: PurchaseError;
23
21
  };
24
22
 
@@ -39,7 +37,7 @@ export type TransactionEvent = {
39
37
  export const transactionUpdatedIOS = (
40
38
  listener: (event: TransactionEvent) => void,
41
39
  ) => {
42
- const isProductPurchase = (item: unknown): item is ProductPurchase => {
40
+ const isPurchase = (item: unknown): item is Purchase => {
43
41
  return (
44
42
  item != null &&
45
43
  typeof item === 'object' &&
@@ -51,18 +49,18 @@ export const transactionUpdatedIOS = (
51
49
 
52
50
  // Helper function to safely convert Purchase to TransactionEvent
53
51
  const mapPurchaseToTransactionEvent = (
54
- purchase: Purchase | SubscriptionPurchase,
52
+ purchase: Purchase,
55
53
  ): TransactionEvent => {
56
54
  // Validate the purchase object before casting
57
- if (isProductPurchase(purchase)) {
55
+ if (isPurchase(purchase)) {
58
56
  return {
59
- transaction: purchase as ProductPurchase,
57
+ transaction: purchase as Purchase,
60
58
  };
61
59
  }
62
60
 
63
61
  // Fallback: create a basic TransactionEvent structure
64
62
  return {
65
- transaction: purchase as ProductPurchase,
63
+ transaction: purchase as Purchase,
66
64
  };
67
65
  };
68
66
 
@@ -140,7 +138,7 @@ export const subscriptionStatusIOS = (
140
138
  */
141
139
  export const currentEntitlementIOS = (
142
140
  sku: string,
143
- ): Promise<ProductPurchase> => {
141
+ ): Promise<Purchase> => {
144
142
  return ExpoIapModule.currentEntitlement(sku);
145
143
  };
146
144
 
@@ -153,7 +151,7 @@ export const currentEntitlementIOS = (
153
151
  *
154
152
  * @platform iOS
155
153
  */
156
- export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
154
+ export const latestTransactionIOS = (sku: string): Promise<Purchase> => {
157
155
  return ExpoIapModule.latestTransaction(sku);
158
156
  };
159
157
 
@@ -241,7 +239,7 @@ export const getTransactionJwsIOS = (sku: string): Promise<string> => {
241
239
  * isValid: boolean;
242
240
  * receiptData: string;
243
241
  * jwsRepresentation: string;
244
- * latestTransaction?: ProductPurchase;
242
+ * latestTransaction?: Purchase;
245
243
  * }>}
246
244
  */
247
245
  export const validateReceiptIOS = async (
@@ -250,7 +248,7 @@ export const validateReceiptIOS = async (
250
248
  isValid: boolean;
251
249
  receiptData: string;
252
250
  jwsRepresentation: string;
253
- latestTransaction?: ProductPurchase;
251
+ latestTransaction?: Purchase;
254
252
  }> => {
255
253
  const result = await ExpoIapModule.validateReceiptIOS(sku);
256
254
  return result;
@@ -362,7 +360,7 @@ export const subscriptionStatus = (
362
360
  /**
363
361
  * @deprecated Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.
364
362
  */
365
- export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
363
+ export const currentEntitlement = (sku: string): Promise<Purchase> => {
366
364
  console.warn(
367
365
  '`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.',
368
366
  );
@@ -372,7 +370,7 @@ export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
372
370
  /**
373
371
  * @deprecated Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.
374
372
  */
375
- export const latestTransaction = (sku: string): Promise<ProductPurchase> => {
373
+ export const latestTransaction = (sku: string): Promise<Purchase> => {
376
374
  console.warn(
377
375
  '`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.',
378
376
  );
@@ -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.