expo-iap 2.8.7 → 2.9.0-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 (47) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/CLAUDE.md +7 -0
  3. package/CONTRIBUTING.md +3 -4
  4. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +7 -7
  5. package/android/src/main/java/expo/modules/iap/Types.kt +1 -1
  6. package/build/ExpoIap.types.d.ts +4 -4
  7. package/build/ExpoIap.types.d.ts.map +1 -1
  8. package/build/ExpoIap.types.js +3 -0
  9. package/build/ExpoIap.types.js.map +1 -1
  10. package/build/helpers/subscription.d.ts.map +1 -1
  11. package/build/helpers/subscription.js +3 -6
  12. package/build/helpers/subscription.js.map +1 -1
  13. package/build/index.d.ts +1 -1
  14. package/build/index.d.ts.map +1 -1
  15. package/build/index.js +14 -12
  16. package/build/index.js.map +1 -1
  17. package/build/modules/android.d.ts.map +1 -1
  18. package/build/modules/android.js.map +1 -1
  19. package/build/modules/ios.d.ts.map +1 -1
  20. package/build/modules/ios.js.map +1 -1
  21. package/build/types/ExpoIapAndroid.types.d.ts +2 -2
  22. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
  23. package/build/types/ExpoIapAndroid.types.js.map +1 -1
  24. package/build/types/ExpoIapIOS.types.d.ts +3 -3
  25. package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
  26. package/build/types/ExpoIapIOS.types.js.map +1 -1
  27. package/build/useIAP.d.ts.map +1 -1
  28. package/build/useIAP.js.map +1 -1
  29. package/ios/ExpoIap.podspec +1 -0
  30. package/ios/ExpoIapModule.swift +353 -1180
  31. package/jest.config.js +14 -17
  32. package/package.json +5 -3
  33. package/plugin/build/withIAP.d.ts +7 -1
  34. package/plugin/build/withIAP.js +16 -2
  35. package/plugin/build/withLocalOpenIAP.d.ts +9 -0
  36. package/plugin/build/withLocalOpenIAP.js +85 -0
  37. package/plugin/src/withIAP.ts +21 -2
  38. package/plugin/src/withLocalOpenIAP.ts +66 -0
  39. package/plugin/tsconfig.tsbuildinfo +1 -1
  40. package/src/ExpoIap.types.ts +5 -11
  41. package/src/helpers/subscription.ts +21 -28
  42. package/src/index.ts +27 -25
  43. package/src/modules/android.ts +7 -7
  44. package/src/modules/ios.ts +11 -5
  45. package/src/types/ExpoIapAndroid.types.ts +3 -4
  46. package/src/types/ExpoIapIOS.types.ts +4 -3
  47. package/src/useIAP.ts +10 -4
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.7",
3
+ "version": "2.9.0-rc.2",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -25,7 +25,8 @@
25
25
  "docs:start": "cd docs && bun run start",
26
26
  "docs:build": "cd docs && bun run build",
27
27
  "docs:serve": "cd docs && bun run serve",
28
- "docs:install": "cd docs && bun install"
28
+ "docs:install": "cd docs && bun install",
29
+ "generate:icon": "npx sharp-cli resize 32 32 -i docs/static/img/icon.png -o docs/static/img/favicon-32x32.png && npx sharp-cli resize 16 16 -i docs/static/img/icon.png -o docs/static/img/favicon-16x16.png && npx sharp-cli resize 180 180 -i docs/static/img/icon.png -o docs/static/img/apple-touch-icon.png && npx sharp-cli resize 192 192 -i docs/static/img/icon.png -o docs/static/img/android-chrome-192x192.png && npx sharp-cli resize 512 512 -i docs/static/img/icon.png -o docs/static/img/android-chrome-512x512.png && npx sharp-cli resize 150 150 -i docs/static/img/icon.png -o docs/static/img/mstile-150x150.png && npx sharp-cli resize 1200 630 -i docs/static/img/icon.png -o docs/static/img/og-image.png && npx sharp-cli resize 1200 600 -i docs/static/img/icon.png -o docs/static/img/twitter-card.png && npx sharp-cli resize 16 16 -i docs/static/img/icon.png -o docs/static/img/favicon.png && cp docs/static/img/favicon-16x16.png docs/static/img/favicon.ico"
29
30
  },
30
31
  "keywords": [
31
32
  "react-native",
@@ -60,5 +61,6 @@
60
61
  },
61
62
  "expo": {
62
63
  "plugin": "./app.plugin.js"
63
- }
64
+ },
65
+ "packageManager": "yarn@3.6.1+sha512.de524adec81a6c3d7a26d936d439d2832e351cdfc5728f9d91f3fc85dd20b04391c038e9b4ecab11cae2b0dd9f0d55fd355af766bc5c1a7f8d25d96bb2a0b2ca"
64
66
  }
@@ -1,3 +1,9 @@
1
1
  import { ConfigPlugin } from 'expo/config-plugins';
2
- declare const _default: ConfigPlugin<void>;
2
+ export interface ExpoIapPluginOptions {
3
+ /** Local development path for OpenIAP library */
4
+ localPath?: string;
5
+ /** Enable local development mode */
6
+ enableLocalDev?: boolean;
7
+ }
8
+ declare const _default: ConfigPlugin<void | ExpoIapPluginOptions>;
3
9
  export default _default;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const config_plugins_1 = require("expo/config-plugins");
7
+ const withLocalOpenIAP_1 = __importDefault(require("./withLocalOpenIAP"));
4
8
  const pkg = require('../../package.json');
5
9
  // Global flag to prevent duplicate logs
6
10
  let hasLoggedPluginExecution = false;
@@ -65,9 +69,19 @@ const withIapAndroid = (config) => {
65
69
  });
66
70
  return config;
67
71
  };
68
- const withIap = (config, _props) => {
72
+ const withIap = (config, options) => {
69
73
  try {
70
- const result = withIapAndroid(config);
74
+ // Apply Android modifications
75
+ let result = withIapAndroid(config);
76
+ // Apply iOS local development if enabled
77
+ if (options?.enableLocalDev || options?.localPath) {
78
+ const localPath = options.localPath || '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
79
+ console.log(`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`);
80
+ result = (0, withLocalOpenIAP_1.default)(result, { localPath });
81
+ }
82
+ else {
83
+ console.log('📦 [expo-iap] Using OpenIAP from CocoaPods');
84
+ }
71
85
  // Set flag after first execution to prevent duplicate logs
72
86
  hasLoggedPluginExecution = true;
73
87
  return result;
@@ -0,0 +1,9 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ /**
3
+ * Plugin to add local OpenIAP pod dependency for development
4
+ * This is only for local development with openiap-apple library
5
+ */
6
+ declare const withLocalOpenIAP: ConfigPlugin<{
7
+ localPath?: string;
8
+ } | void>;
9
+ export default withLocalOpenIAP;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const config_plugins_1 = require("@expo/config-plugins");
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * Plugin to add local OpenIAP pod dependency for development
41
+ * This is only for local development with openiap-apple library
42
+ */
43
+ const withLocalOpenIAP = (config, props) => {
44
+ return (0, config_plugins_1.withDangerousMod)(config, [
45
+ 'ios',
46
+ async (config) => {
47
+ const { platformProjectRoot } = config.modRequest;
48
+ const podfilePath = path.join(platformProjectRoot, 'Podfile');
49
+ // Default local path or use provided one
50
+ const localOpenIapPath = props?.localPath ||
51
+ '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
52
+ // Check if local path exists
53
+ if (!fs.existsSync(localOpenIapPath)) {
54
+ console.warn(`⚠️ Local openiap-apple path not found: ${localOpenIapPath}`);
55
+ console.warn(' Skipping local pod injection. Using default pod resolution.');
56
+ return config;
57
+ }
58
+ // Read Podfile
59
+ let podfileContent = fs.readFileSync(podfilePath, 'utf8');
60
+ // Check if already has the local pod reference
61
+ if (podfileContent.includes("pod 'openiap',")) {
62
+ console.log('✅ Local OpenIAP pod already configured');
63
+ return config;
64
+ }
65
+ // Find the target block and inject the local pod
66
+ const targetRegex = /target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
67
+ if (targetRegex.test(podfileContent)) {
68
+ podfileContent = podfileContent.replace(targetRegex, (match) => {
69
+ return `${match}
70
+
71
+ # Local OpenIAP pod for development (added by expo-iap plugin)
72
+ pod 'openiap', :path => '${localOpenIapPath}'`;
73
+ });
74
+ // Write back to Podfile
75
+ fs.writeFileSync(podfilePath, podfileContent);
76
+ console.log(`✅ Added local OpenIAP pod at: ${localOpenIapPath}`);
77
+ }
78
+ else {
79
+ console.warn('⚠️ Could not find target block in Podfile');
80
+ }
81
+ return config;
82
+ },
83
+ ]);
84
+ };
85
+ exports.default = withLocalOpenIAP;
@@ -5,6 +5,7 @@ import {
5
5
  withAndroidManifest,
6
6
  withAppBuildGradle,
7
7
  } from 'expo/config-plugins';
8
+ import withLocalOpenIAP from './withLocalOpenIAP';
8
9
 
9
10
  const pkg = require('../../package.json');
10
11
 
@@ -98,9 +99,27 @@ const withIapAndroid: ConfigPlugin = (config) => {
98
99
  return config;
99
100
  };
100
101
 
101
- const withIap: ConfigPlugin = (config, _props) => {
102
+ export interface ExpoIapPluginOptions {
103
+ /** Local development path for OpenIAP library */
104
+ localPath?: string;
105
+ /** Enable local development mode */
106
+ enableLocalDev?: boolean;
107
+ }
108
+
109
+ const withIap: ConfigPlugin<ExpoIapPluginOptions | void> = (config, options) => {
102
110
  try {
103
- const result = withIapAndroid(config);
111
+ // Apply Android modifications
112
+ let result = withIapAndroid(config);
113
+
114
+ // Apply iOS local development if enabled
115
+ if (options?.enableLocalDev || options?.localPath) {
116
+ const localPath = options.localPath || '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
117
+ console.log(`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`);
118
+ result = withLocalOpenIAP(result, { localPath });
119
+ } else {
120
+ console.log('📦 [expo-iap] Using OpenIAP from CocoaPods');
121
+ }
122
+
104
123
  // Set flag after first execution to prevent duplicate logs
105
124
  hasLoggedPluginExecution = true;
106
125
  return result;
@@ -0,0 +1,66 @@
1
+ import {ConfigPlugin, withDangerousMod} from '@expo/config-plugins';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+
5
+ /**
6
+ * Plugin to add local OpenIAP pod dependency for development
7
+ * This is only for local development with openiap-apple library
8
+ */
9
+ const withLocalOpenIAP: ConfigPlugin<{localPath?: string} | void> = (
10
+ config,
11
+ props,
12
+ ) => {
13
+ return withDangerousMod(config, [
14
+ 'ios',
15
+ async (config) => {
16
+ const {platformProjectRoot} = config.modRequest;
17
+ const podfilePath = path.join(platformProjectRoot, 'Podfile');
18
+
19
+ // Default local path or use provided one
20
+ const localOpenIapPath =
21
+ props?.localPath ||
22
+ '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
23
+
24
+ // Check if local path exists
25
+ if (!fs.existsSync(localOpenIapPath)) {
26
+ console.warn(`⚠️ Local openiap-apple path not found: ${localOpenIapPath}`);
27
+ console.warn(
28
+ ' Skipping local pod injection. Using default pod resolution.',
29
+ );
30
+ return config;
31
+ }
32
+
33
+ // Read Podfile
34
+ let podfileContent = fs.readFileSync(podfilePath, 'utf8');
35
+
36
+ // Check if already has the local pod reference
37
+ if (podfileContent.includes("pod 'openiap',")) {
38
+ console.log('✅ Local OpenIAP pod already configured');
39
+ return config;
40
+ }
41
+
42
+ // Find the target block and inject the local pod
43
+ const targetRegex =
44
+ /target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
45
+
46
+ if (targetRegex.test(podfileContent)) {
47
+ podfileContent = podfileContent.replace(targetRegex, (match) => {
48
+ return `${match}
49
+
50
+ # Local OpenIAP pod for development (added by expo-iap plugin)
51
+ pod 'openiap', :path => '${localOpenIapPath}'`;
52
+ });
53
+
54
+ // Write back to Podfile
55
+ fs.writeFileSync(podfilePath, podfileContent);
56
+ console.log(`✅ Added local OpenIAP pod at: ${localOpenIapPath}`);
57
+ } else {
58
+ console.warn('⚠️ Could not find target block in Podfile');
59
+ }
60
+
61
+ return config;
62
+ },
63
+ ]);
64
+ };
65
+
66
+ export default withLocalOpenIAP;
@@ -1 +1 @@
1
- {"root":["./src/withIAP.ts"],"version":"5.9.2"}
1
+ {"root":["./src/withiap.ts","./src/withlocalopeniap.ts"],"version":"5.9.2"}
@@ -1,9 +1,9 @@
1
- import {
1
+ import type {
2
2
  ProductAndroid,
3
3
  PurchaseAndroid,
4
4
  ProductSubscriptionAndroid,
5
5
  } from './types/ExpoIapAndroid.types';
6
- import {
6
+ import type {
7
7
  ProductIOS,
8
8
  PurchaseIOS,
9
9
  ProductSubscriptionIOS,
@@ -61,15 +61,9 @@ export type SubscriptionProduct =
61
61
  | (ProductSubscriptionAndroid & AndroidPlatform)
62
62
  | (ProductSubscriptionIOS & IosPlatform);
63
63
 
64
- // Re-export platform-specific types
65
- export type {
66
- PurchaseAndroid,
67
- ProductSubscriptionAndroid,
68
- } from './types/ExpoIapAndroid.types';
69
- export type {
70
- PurchaseIOS,
71
- ProductSubscriptionIOS,
72
- } from './types/ExpoIapIOS.types';
64
+ // Re-export all platform-specific types to avoid deep imports
65
+ export * from './types/ExpoIapAndroid.types';
66
+ export * from './types/ExpoIapIOS.types';
73
67
 
74
68
  // Unified purchase type for both products and subscriptions
75
69
  export type Purchase =
@@ -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
@@ -45,7 +45,7 @@ export {
45
45
  // Get the native constant value
46
46
  export const PI = ExpoIapModule.PI;
47
47
 
48
- export enum IapEvent {
48
+ export enum OpenIapEvent {
49
49
  PurchaseUpdated = 'purchase-updated',
50
50
  PurchaseError = 'purchase-error',
51
51
  /** @deprecated Use PurchaseUpdated instead. This will be removed in a future version. */
@@ -73,7 +73,7 @@ export const purchaseUpdatedListener = (
73
73
  listener: (event: Purchase) => void,
74
74
  ) => {
75
75
  const emitterSubscription = emitter.addListener(
76
- IapEvent.PurchaseUpdated,
76
+ OpenIapEvent.PurchaseUpdated,
77
77
  listener,
78
78
  );
79
79
  return emitterSubscription;
@@ -82,7 +82,7 @@ export const purchaseUpdatedListener = (
82
82
  export const purchaseErrorListener = (
83
83
  listener: (error: PurchaseError) => void,
84
84
  ) => {
85
- return emitter.addListener(IapEvent.PurchaseError, listener);
85
+ return emitter.addListener(OpenIapEvent.PurchaseError, listener);
86
86
  };
87
87
 
88
88
  /**
@@ -114,7 +114,7 @@ export const promotedProductListenerIOS = (
114
114
  );
115
115
  return {remove: () => {}};
116
116
  }
117
- return emitter.addListener(IapEvent.PromotedProductIOS, listener);
117
+ return emitter.addListener(OpenIapEvent.PromotedProductIOS, listener);
118
118
  };
119
119
 
120
120
  export function initConnection(): Promise<boolean> {
@@ -273,11 +273,11 @@ export const fetchProducts = async ({
273
273
 
274
274
  /**
275
275
  * @deprecated Use `fetchProducts` instead. This method will be removed in version 3.0.0.
276
- *
276
+ *
277
277
  * The 'request' prefix should only be used for event-based operations that trigger
278
278
  * purchase flows. Since this function simply fetches product information, it has been
279
279
  * renamed to `fetchProducts` to follow OpenIAP terminology guidelines.
280
- *
280
+ *
281
281
  * @example
282
282
  * ```typescript
283
283
  * // Old way (deprecated)
@@ -285,7 +285,7 @@ export const fetchProducts = async ({
285
285
  * skus: ['com.example.product1'],
286
286
  * type: 'inapp'
287
287
  * });
288
- *
288
+ *
289
289
  * // New way (recommended)
290
290
  * const products = await fetchProducts({
291
291
  * skus: ['com.example.product1'],
@@ -301,9 +301,9 @@ export const requestProducts = async ({
301
301
  type?: 'inapp' | 'subs';
302
302
  }): Promise<Product[] | SubscriptionProduct[]> => {
303
303
  console.warn(
304
- "`requestProducts` is deprecated. Use `fetchProducts` instead. The 'request' prefix should only be used for event-based operations. This method will be removed in version 3.0.0.",
304
+ "`requestProducts` is deprecated. Use `fetchProducts` instead. The 'request' prefix should only be used for event-based operations. This method will be removed in version 3.0.0."
305
305
  );
306
- return fetchProducts({skus, type});
306
+ return fetchProducts({ skus, type });
307
307
  };
308
308
 
309
309
  /**
@@ -326,10 +326,8 @@ export const getPurchaseHistory = ({
326
326
  '`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.',
327
327
  );
328
328
  return getPurchaseHistories({
329
- alsoPublishToEventListenerIOS:
330
- alsoPublishToEventListenerIOS ?? alsoPublishToEventListener,
331
- onlyIncludeActiveItemsIOS:
332
- onlyIncludeActiveItemsIOS ?? onlyIncludeActiveItems,
329
+ alsoPublishToEventListenerIOS: alsoPublishToEventListenerIOS ?? alsoPublishToEventListener,
330
+ onlyIncludeActiveItemsIOS: onlyIncludeActiveItemsIOS ?? onlyIncludeActiveItems,
333
331
  });
334
332
  };
335
333
 
@@ -392,9 +390,8 @@ export const getAvailablePurchases = ({
392
390
  ),
393
391
  android: async () => {
394
392
  const products = await ExpoIapModule.getAvailableItemsByType('inapp');
395
- const subscriptions = await ExpoIapModule.getAvailableItemsByType(
396
- 'subs',
397
- );
393
+ const subscriptions =
394
+ await ExpoIapModule.getAvailableItemsByType('subs');
398
395
  return products.concat(subscriptions);
399
396
  },
400
397
  }) || (() => Promise.resolve([]))
@@ -468,7 +465,11 @@ const normalizeRequestProps = (
468
465
  */
469
466
  export const requestPurchase = (
470
467
  requestObj: PurchaseRequest,
471
- ): Promise<Purchase | Purchase[] | void> => {
468
+ ): Promise<
469
+ | Purchase
470
+ | Purchase[]
471
+ | void
472
+ > => {
472
473
  const {request, type = 'inapp'} = requestObj;
473
474
 
474
475
  if (Platform.OS === 'ios') {
@@ -498,7 +499,9 @@ export const requestPurchase = (
498
499
  offer,
499
500
  );
500
501
 
501
- return type === 'inapp' ? (purchase as Purchase) : (purchase as Purchase);
502
+ return type === 'inapp'
503
+ ? (purchase as Purchase)
504
+ : (purchase as Purchase);
502
505
  })();
503
506
  }
504
507
 
@@ -626,11 +629,10 @@ export const finishTransaction = ({
626
629
  },
627
630
  android: async () => {
628
631
  const androidPurchase = purchase as PurchaseAndroid;
629
-
632
+
630
633
  // Use purchaseToken if available, fallback to purchaseTokenAndroid for backward compatibility
631
- const token =
632
- androidPurchase.purchaseToken || androidPurchase.purchaseTokenAndroid;
633
-
634
+ const token = androidPurchase.purchaseToken || androidPurchase.purchaseTokenAndroid;
635
+
634
636
  if (!token) {
635
637
  return Promise.reject(
636
638
  new PurchaseError(
@@ -640,8 +642,8 @@ export const finishTransaction = ({
640
642
  undefined,
641
643
  'E_DEVELOPER_ERROR' as ErrorCode,
642
644
  androidPurchase.productId,
643
- 'android',
644
- ),
645
+ 'android'
646
+ )
645
647
  );
646
648
  }
647
649
 
@@ -674,7 +676,7 @@ export const getStorefrontIOS = (): Promise<string> => {
674
676
  console.warn('getStorefrontIOS: This method is only available on iOS');
675
677
  return Promise.resolve('');
676
678
  }
677
- return ExpoIapModule.getStorefront();
679
+ return ExpoIapModule.getStorefrontIOS();
678
680
  };
679
681
 
680
682
  /**
@@ -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
  };