expo-iap 3.1.20 → 3.1.22

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "apple": "1.2.30",
3
- "google": "1.3.2",
3
+ "google": "1.3.4",
4
4
  "gql": "1.2.3"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "3.1.20",
3
+ "version": "3.1.22",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -72,19 +72,23 @@ const modifyAppBuildGradle = (gradle, language, isHorizonEnabled) => {
72
72
  let modified = gradle;
73
73
  // Determine which flavor to use based on isHorizonEnabled
74
74
  const flavor = isHorizonEnabled ? 'horizon' : 'play';
75
+ // Use openiap-google-horizon artifact when horizon is enabled
76
+ const artifactId = isHorizonEnabled
77
+ ? 'openiap-google-horizon'
78
+ : 'openiap-google';
75
79
  // Ensure OpenIAP dependency exists at desired version in app-level build.gradle(.kts)
76
80
  const impl = (ga, v) => language === 'kotlin'
77
81
  ? ` implementation("${ga}:${v}")`
78
82
  : ` implementation "${ga}:${v}"`;
79
- const openiapDep = impl('io.github.hyochan.openiap:openiap-google', OPENIAP_ANDROID_VERSION);
80
- // Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
81
- const openiapAnyLine = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
83
+ const openiapDep = impl(`io.github.hyochan.openiap:${artifactId}`, OPENIAP_ANDROID_VERSION);
84
+ // Remove any existing openiap-google or openiap-google-horizon lines (any version, groovy/kotlin, implementation/api)
85
+ const openiapAnyLine = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google(?:-horizon)?:[^"']+["']\s*\)?\s*$/gm;
82
86
  const hadExisting = openiapAnyLine.test(modified);
83
87
  if (hadExisting) {
84
88
  modified = modified.replace(openiapAnyLine, '').replace(/\n{3,}/g, '\n\n');
85
89
  }
86
90
  // Ensure the desired dependency line is present
87
- if (!new RegExp(String.raw `io\.github\.hyochan\.openiap:openiap-google:${OPENIAP_ANDROID_VERSION}`).test(modified)) {
91
+ if (!new RegExp(String.raw `io\.github\.hyochan\.openiap:${artifactId}:${OPENIAP_ANDROID_VERSION}`).test(modified)) {
88
92
  // Insert just after the opening `dependencies {` line
89
93
  modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 1);
90
94
  logOnce(hadExisting
@@ -124,6 +128,20 @@ const withIapAndroid = (config, props) => {
124
128
  return config;
125
129
  });
126
130
  }
131
+ // Set horizonEnabled property in gradle.properties so expo-iap module can pick it up
132
+ config = (0, config_plugins_1.withGradleProperties)(config, (config) => {
133
+ const horizonValue = props?.isHorizonEnabled ?? false;
134
+ // Remove any existing horizonEnabled entries
135
+ config.modResults = config.modResults.filter((item) => item.type !== 'property' || item.key !== 'horizonEnabled');
136
+ // Add the horizonEnabled property
137
+ config.modResults.push({
138
+ type: 'property',
139
+ key: 'horizonEnabled',
140
+ value: String(horizonValue),
141
+ });
142
+ logOnce(`✅ Set horizonEnabled=${horizonValue} in gradle.properties`);
143
+ return config;
144
+ });
127
145
  // Note: missingDimensionStrategy for local dev is handled in withLocalOpenIAP
128
146
  config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
129
147
  const manifest = config.modResults;
@@ -153,20 +171,22 @@ const withIapAndroid = (config, props) => {
153
171
  application['meta-data'] = [];
154
172
  }
155
173
  const metaData = application['meta-data'];
174
+ // Use the correct meta-data name for Horizon Platform SDK
175
+ const horizonMetaDataName = 'com.meta.horizon.platform.ovr.OCULUS_APP_ID';
156
176
  const horizonAppIdMeta = {
157
177
  $: {
158
- 'android:name': 'com.oculus.vr.APP_ID',
178
+ 'android:name': horizonMetaDataName,
159
179
  'android:value': props.horizonAppId,
160
180
  },
161
181
  };
162
- const existingIndex = metaData.findIndex((m) => m.$['android:name'] === 'com.oculus.vr.APP_ID');
182
+ const existingIndex = metaData.findIndex((m) => m.$['android:name'] === horizonMetaDataName);
163
183
  if (existingIndex !== -1) {
164
184
  metaData[existingIndex] = horizonAppIdMeta;
165
- logOnce(`✅ Updated com.oculus.vr.APP_ID to ${props.horizonAppId} in AndroidManifest.xml`);
185
+ logOnce(`✅ Updated ${horizonMetaDataName} to ${props.horizonAppId} in AndroidManifest.xml`);
166
186
  }
167
187
  else {
168
188
  metaData.push(horizonAppIdMeta);
169
- logOnce(`✅ Added com.oculus.vr.APP_ID: ${props.horizonAppId} to AndroidManifest.xml`);
189
+ logOnce(`✅ Added ${horizonMetaDataName}: ${props.horizonAppId} to AndroidManifest.xml`);
170
190
  }
171
191
  }
172
192
  return config;
@@ -179,31 +199,23 @@ const withIapIOS = (config, options) => {
179
199
  if (options) {
180
200
  config = (0, withIosAlternativeBilling_1.withIosAlternativeBilling)(config, options);
181
201
  }
182
- return (0, config_plugins_1.withDangerousMod)(config, [
183
- 'ios',
184
- async (config) => {
185
- const { platformProjectRoot } = config.modRequest;
186
- const podfilePath = path.join(platformProjectRoot, 'Podfile');
187
- if (!fs.existsSync(podfilePath)) {
188
- return config;
189
- }
190
- let content = fs.readFileSync(podfilePath, 'utf8');
191
- // 1) Ensure CocoaPods CDN source is present at the very top
192
- const cdnLine = `source 'https://cdn.cocoapods.org/'`;
193
- if (!content.includes(cdnLine)) {
194
- content = `${cdnLine}\n\n${content}`;
195
- logOnce('📦 expo-iap: Added CocoaPods CDN source to Podfile');
196
- }
197
- // 2) Remove any lingering local OpenIAP pod injection
198
- const localPodRegex = /^\s*pod\s+'openiap'\s*,\s*:path\s*=>\s*['"][^'"]+['"][^\n]*$/gm;
199
- if (localPodRegex.test(content)) {
200
- content = content.replace(localPodRegex, '').replace(/\n{3,}/g, '\n\n');
201
- logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
202
- }
203
- fs.writeFileSync(podfilePath, content);
204
- return config;
205
- },
206
- ]);
202
+ return (0, config_plugins_1.withPodfile)(config, (config) => {
203
+ let content = config.modResults.contents;
204
+ // 1) Ensure CocoaPods CDN source is present at the very top
205
+ const cdnLine = `source 'https://cdn.cocoapods.org/'`;
206
+ if (!content.includes(cdnLine)) {
207
+ content = `${cdnLine}\n\n${content}`;
208
+ logOnce('📦 expo-iap: Added CocoaPods CDN source to Podfile');
209
+ }
210
+ // 2) Remove any lingering local OpenIAP pod injection
211
+ const localPodRegex = /^\s*pod\s+'openiap'\s*,\s*:path\s*=>\s*['"][^'"]+['"][^\n]*$/gm;
212
+ if (localPodRegex.test(content)) {
213
+ content = content.replace(localPodRegex, '').replace(/\n{3,}/g, '\n\n');
214
+ logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
215
+ }
216
+ config.modResults.contents = content;
217
+ return config;
218
+ });
207
219
  };
208
220
  const withIap = (config, options) => {
209
221
  try {
@@ -70,7 +70,7 @@ const withLocalOpenIAP = (config, props) => {
70
70
  }
71
71
  return null;
72
72
  };
73
- // iOS: inject local pod path
73
+ // iOS: inject local pod path with wrapper podspec
74
74
  config = (0, config_plugins_1.withDangerousMod)(config, [
75
75
  'ios',
76
76
  async (config) => {
@@ -88,18 +88,23 @@ const withLocalOpenIAP = (config, props) => {
88
88
  console.warn(`⚠️ Podfile not found at ${podfilePath}. Skipping.`);
89
89
  return config;
90
90
  }
91
+ logOnce(`✅ Using local OpenIAP from: ${iosPath}`);
91
92
  let podfileContent = fs.readFileSync(podfilePath, 'utf8');
93
+ // Check if local OpenIAP pod is already configured
92
94
  if (podfileContent.includes("pod 'openiap',")) {
93
95
  logOnce('✅ Local OpenIAP pod already configured');
94
96
  return config;
95
97
  }
96
98
  const targetRegex = /target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
99
+ const relativePath = path
100
+ .relative(platformProjectRoot, iosPath)
101
+ .replace(/\\/g, '/');
97
102
  if (targetRegex.test(podfileContent)) {
98
103
  podfileContent = podfileContent.replace(targetRegex, (match) => {
99
104
  return `${match}
100
-
105
+
101
106
  # Local OpenIAP pod for development (added by expo-iap plugin)
102
- pod 'openiap', :path => '${iosPath}'`;
107
+ pod 'openiap', :path => '${relativePath}'`;
103
108
  });
104
109
  fs.writeFileSync(podfilePath, podfileContent);
105
110
  logOnce(`✅ Added local OpenIAP pod at: ${iosPath}`);
@@ -195,11 +200,12 @@ const withLocalOpenIAP = (config, props) => {
195
200
  const flavor = props?.isHorizonEnabled ? 'horizon' : 'play';
196
201
  const strategyLine = ` missingDimensionStrategy "platform", "${flavor}"`;
197
202
  let contents = gradle.contents;
198
- // Remove Maven deps (avoid duplicate classes with local module)
199
- const mavenPattern = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
203
+ // Remove Maven deps (both openiap-google and openiap-google-horizon)
204
+ // to avoid duplicate classes with local module
205
+ const mavenPattern = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google(?:-horizon)?:[^"']+["']\s*\)?\s*$/gm;
200
206
  if (mavenPattern.test(contents)) {
201
207
  contents = contents.replace(mavenPattern, '\n');
202
- logOnce('🧹 Removed Maven openiap-google (using local module)');
208
+ logOnce('🧹 Removed Maven openiap-google* dependencies (using local module)');
203
209
  }
204
210
  // Add missingDimensionStrategy (required for flavored module)
205
211
  // Remove any existing platform strategies first to avoid duplicates
@@ -4,7 +4,8 @@ import {
4
4
  WarningAggregator,
5
5
  withAndroidManifest,
6
6
  withAppBuildGradle,
7
- withDangerousMod,
7
+ withGradleProperties,
8
+ withPodfile,
8
9
  } from 'expo/config-plugins';
9
10
  import * as fs from 'fs';
10
11
  import * as path from 'path';
@@ -64,19 +65,24 @@ const modifyAppBuildGradle = (
64
65
  // Determine which flavor to use based on isHorizonEnabled
65
66
  const flavor = isHorizonEnabled ? 'horizon' : 'play';
66
67
 
68
+ // Use openiap-google-horizon artifact when horizon is enabled
69
+ const artifactId = isHorizonEnabled
70
+ ? 'openiap-google-horizon'
71
+ : 'openiap-google';
72
+
67
73
  // Ensure OpenIAP dependency exists at desired version in app-level build.gradle(.kts)
68
74
  const impl = (ga: string, v: string) =>
69
75
  language === 'kotlin'
70
76
  ? ` implementation("${ga}:${v}")`
71
77
  : ` implementation "${ga}:${v}"`;
72
78
  const openiapDep = impl(
73
- 'io.github.hyochan.openiap:openiap-google',
79
+ `io.github.hyochan.openiap:${artifactId}`,
74
80
  OPENIAP_ANDROID_VERSION,
75
81
  );
76
82
 
77
- // Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
83
+ // Remove any existing openiap-google or openiap-google-horizon lines (any version, groovy/kotlin, implementation/api)
78
84
  const openiapAnyLine =
79
- /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
85
+ /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google(?:-horizon)?:[^"']+["']\s*\)?\s*$/gm;
80
86
  const hadExisting = openiapAnyLine.test(modified);
81
87
  if (hadExisting) {
82
88
  modified = modified.replace(openiapAnyLine, '').replace(/\n{3,}/g, '\n\n');
@@ -85,7 +91,7 @@ const modifyAppBuildGradle = (
85
91
  // Ensure the desired dependency line is present
86
92
  if (
87
93
  !new RegExp(
88
- String.raw`io\.github\.hyochan\.openiap:openiap-google:${OPENIAP_ANDROID_VERSION}`,
94
+ String.raw`io\.github\.hyochan\.openiap:${artifactId}:${OPENIAP_ANDROID_VERSION}`,
89
95
  ).test(modified)
90
96
  ) {
91
97
  // Insert just after the opening `dependencies {` line
@@ -155,6 +161,27 @@ const withIapAndroid: ConfigPlugin<
155
161
  });
156
162
  }
157
163
 
164
+ // Set horizonEnabled property in gradle.properties so expo-iap module can pick it up
165
+ config = withGradleProperties(config, (config) => {
166
+ const horizonValue = props?.isHorizonEnabled ?? false;
167
+
168
+ // Remove any existing horizonEnabled entries
169
+ config.modResults = config.modResults.filter(
170
+ (item) => item.type !== 'property' || item.key !== 'horizonEnabled',
171
+ );
172
+
173
+ // Add the horizonEnabled property
174
+ config.modResults.push({
175
+ type: 'property',
176
+ key: 'horizonEnabled',
177
+ value: String(horizonValue),
178
+ });
179
+
180
+ logOnce(`✅ Set horizonEnabled=${horizonValue} in gradle.properties`);
181
+
182
+ return config;
183
+ });
184
+
158
185
  // Note: missingDimensionStrategy for local dev is handled in withLocalOpenIAP
159
186
 
160
187
  config = withAndroidManifest(config, (config) => {
@@ -195,26 +222,29 @@ const withIapAndroid: ConfigPlugin<
195
222
  }
196
223
 
197
224
  const metaData = application['meta-data'];
225
+
226
+ // Use the correct meta-data name for Horizon Platform SDK
227
+ const horizonMetaDataName = 'com.meta.horizon.platform.ovr.OCULUS_APP_ID';
198
228
  const horizonAppIdMeta = {
199
229
  $: {
200
- 'android:name': 'com.oculus.vr.APP_ID',
230
+ 'android:name': horizonMetaDataName,
201
231
  'android:value': props.horizonAppId,
202
232
  },
203
233
  };
204
234
 
205
235
  const existingIndex = metaData.findIndex(
206
- (m) => m.$['android:name'] === 'com.oculus.vr.APP_ID',
236
+ (m) => m.$['android:name'] === horizonMetaDataName,
207
237
  );
208
238
 
209
239
  if (existingIndex !== -1) {
210
240
  metaData[existingIndex] = horizonAppIdMeta;
211
241
  logOnce(
212
- `✅ Updated com.oculus.vr.APP_ID to ${props.horizonAppId} in AndroidManifest.xml`,
242
+ `✅ Updated ${horizonMetaDataName} to ${props.horizonAppId} in AndroidManifest.xml`,
213
243
  );
214
244
  } else {
215
245
  metaData.push(horizonAppIdMeta);
216
246
  logOnce(
217
- `✅ Added com.oculus.vr.APP_ID: ${props.horizonAppId} to AndroidManifest.xml`,
247
+ `✅ Added ${horizonMetaDataName}: ${props.horizonAppId} to AndroidManifest.xml`,
218
248
  );
219
249
  }
220
250
  }
@@ -235,37 +265,27 @@ const withIapIOS: ConfigPlugin<IOSAlternativeBillingConfig | undefined> = (
235
265
  config = withIosAlternativeBilling(config, options);
236
266
  }
237
267
 
238
- return withDangerousMod(config, [
239
- 'ios',
240
- async (config) => {
241
- const {platformProjectRoot} = config.modRequest;
242
- const podfilePath = path.join(platformProjectRoot, 'Podfile');
268
+ return withPodfile(config, (config) => {
269
+ let content = config.modResults.contents;
243
270
 
244
- if (!fs.existsSync(podfilePath)) {
245
- return config;
246
- }
247
-
248
- let content = fs.readFileSync(podfilePath, 'utf8');
249
-
250
- // 1) Ensure CocoaPods CDN source is present at the very top
251
- const cdnLine = `source 'https://cdn.cocoapods.org/'`;
252
- if (!content.includes(cdnLine)) {
253
- content = `${cdnLine}\n\n${content}`;
254
- logOnce('📦 expo-iap: Added CocoaPods CDN source to Podfile');
255
- }
271
+ // 1) Ensure CocoaPods CDN source is present at the very top
272
+ const cdnLine = `source 'https://cdn.cocoapods.org/'`;
273
+ if (!content.includes(cdnLine)) {
274
+ content = `${cdnLine}\n\n${content}`;
275
+ logOnce('📦 expo-iap: Added CocoaPods CDN source to Podfile');
276
+ }
256
277
 
257
- // 2) Remove any lingering local OpenIAP pod injection
258
- const localPodRegex =
259
- /^\s*pod\s+'openiap'\s*,\s*:path\s*=>\s*['"][^'"]+['"][^\n]*$/gm;
260
- if (localPodRegex.test(content)) {
261
- content = content.replace(localPodRegex, '').replace(/\n{3,}/g, '\n\n');
262
- logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
263
- }
278
+ // 2) Remove any lingering local OpenIAP pod injection
279
+ const localPodRegex =
280
+ /^\s*pod\s+'openiap'\s*,\s*:path\s*=>\s*['"][^'"]+['"][^\n]*$/gm;
281
+ if (localPodRegex.test(content)) {
282
+ content = content.replace(localPodRegex, '').replace(/\n{3,}/g, '\n\n');
283
+ logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
284
+ }
264
285
 
265
- fs.writeFileSync(podfilePath, content);
266
- return config;
267
- },
268
- ]);
286
+ config.modResults.contents = content;
287
+ return config;
288
+ });
269
289
  };
270
290
 
271
291
  export interface ExpoIapPluginOptions {
@@ -61,7 +61,7 @@ const withLocalOpenIAP: ConfigPlugin<
61
61
  return null;
62
62
  };
63
63
 
64
- // iOS: inject local pod path
64
+ // iOS: inject local pod path with wrapper podspec
65
65
  config = withDangerousMod(config, [
66
66
  'ios',
67
67
  async (config) => {
@@ -82,8 +82,12 @@ const withLocalOpenIAP: ConfigPlugin<
82
82
  console.warn(`⚠️ Podfile not found at ${podfilePath}. Skipping.`);
83
83
  return config;
84
84
  }
85
+
86
+ logOnce(`✅ Using local OpenIAP from: ${iosPath}`);
87
+
85
88
  let podfileContent = fs.readFileSync(podfilePath, 'utf8');
86
89
 
90
+ // Check if local OpenIAP pod is already configured
87
91
  if (podfileContent.includes("pod 'openiap',")) {
88
92
  logOnce('✅ Local OpenIAP pod already configured');
89
93
  return config;
@@ -91,13 +95,16 @@ const withLocalOpenIAP: ConfigPlugin<
91
95
 
92
96
  const targetRegex =
93
97
  /target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
98
+ const relativePath = path
99
+ .relative(platformProjectRoot, iosPath)
100
+ .replace(/\\/g, '/');
94
101
 
95
102
  if (targetRegex.test(podfileContent)) {
96
103
  podfileContent = podfileContent.replace(targetRegex, (match) => {
97
104
  return `${match}
98
-
105
+
99
106
  # Local OpenIAP pod for development (added by expo-iap plugin)
100
- pod 'openiap', :path => '${iosPath}'`;
107
+ pod 'openiap', :path => '${relativePath}'`;
101
108
  });
102
109
  fs.writeFileSync(podfilePath, podfileContent);
103
110
  logOnce(`✅ Added local OpenIAP pod at: ${iosPath}`);
@@ -224,12 +231,15 @@ const withLocalOpenIAP: ConfigPlugin<
224
231
 
225
232
  let contents = gradle.contents;
226
233
 
227
- // Remove Maven deps (avoid duplicate classes with local module)
234
+ // Remove Maven deps (both openiap-google and openiap-google-horizon)
235
+ // to avoid duplicate classes with local module
228
236
  const mavenPattern =
229
- /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
237
+ /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google(?:-horizon)?:[^"']+["']\s*\)?\s*$/gm;
230
238
  if (mavenPattern.test(contents)) {
231
239
  contents = contents.replace(mavenPattern, '\n');
232
- logOnce('🧹 Removed Maven openiap-google (using local module)');
240
+ logOnce(
241
+ '🧹 Removed Maven openiap-google* dependencies (using local module)',
242
+ );
233
243
  }
234
244
 
235
245
  // Add missingDimensionStrategy (required for flavored module)
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // External dependencies
2
- import {NativeModulesProxy} from 'expo-modules-core';
2
+ import {requireNativeModule} from 'expo-modules-core';
3
3
  import {Platform} from 'react-native';
4
4
 
5
5
  // Internal modules
@@ -80,7 +80,7 @@ type ExpoIapEmitter = {
80
80
 
81
81
  // Ensure the emitter has proper EventEmitter interface
82
82
  export const emitter = (ExpoIapModule ||
83
- NativeModulesProxy.ExpoIap) as ExpoIapEmitter;
83
+ requireNativeModule('ExpoIap')) as ExpoIapEmitter;
84
84
 
85
85
  /**
86
86
  * TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.