expo-iap 3.1.21 → 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.
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +12 -4
- package/bun.lockb +0 -0
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.ts.html +1 -1
- package/coverage/lcov-report/src/modules/android.ts.html +1 -1
- package/coverage/lcov-report/src/modules/index.html +1 -1
- package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
- package/coverage/lcov-report/src/utils/debug.ts.html +1 -1
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +1 -1
- package/coverage/lcov-report/src/utils/index.html +1 -1
- package/openiap-versions.json +1 -1
- package/package.json +1 -1
- package/plugin/build/withIAP.js +45 -33
- package/plugin/build/withLocalOpenIAP.js +12 -6
- package/plugin/src/withIAP.ts +57 -37
- package/plugin/src/withLocalOpenIAP.ts +16 -6
|
@@ -69,9 +69,16 @@ class ExpoIapModule : Module() {
|
|
|
69
69
|
scope.launch {
|
|
70
70
|
connectionMutex.withLock {
|
|
71
71
|
try {
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
// CRITICAL: Set Activity BEFORE calling initConnection
|
|
73
|
+
// Horizon SDK needs Activity to initialize OVRPlatform with proper returnComponent
|
|
74
|
+
// https://github.com/meta-quest/Meta-Spatial-SDK-Samples/issues/82#issuecomment-3452577530
|
|
75
|
+
runCatching { currentActivity }
|
|
76
|
+
.onSuccess {
|
|
77
|
+
ExpoIapLog.debug("Activity available: ${it.javaClass.name}")
|
|
78
|
+
openIap.setActivity(it)
|
|
79
|
+
}.onFailure {
|
|
80
|
+
ExpoIapLog.warning("Activity not available during initConnection - OpenIAP will use Context")
|
|
81
|
+
}
|
|
75
82
|
|
|
76
83
|
// If already connected, short-circuit
|
|
77
84
|
if (connectionReady.get()) {
|
|
@@ -284,7 +291,8 @@ class ExpoIapModule : Module() {
|
|
|
284
291
|
ExpoIapHelper.addPurchasePromise(promise)
|
|
285
292
|
scope.launch {
|
|
286
293
|
try {
|
|
287
|
-
|
|
294
|
+
val activity = currentActivity
|
|
295
|
+
openIap.setActivity(activity)
|
|
288
296
|
val result = openIap.requestPurchase(requestProps)
|
|
289
297
|
val purchases =
|
|
290
298
|
when (result) {
|
package/bun.lockb
CHANGED
|
Binary file
|
package/coverage/clover.xml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<coverage generated="
|
|
3
|
-
<project timestamp="
|
|
2
|
+
<coverage generated="1761624577860" clover="3.2.0">
|
|
3
|
+
<project timestamp="1761624577861" name="All files">
|
|
4
4
|
<metrics statements="457" coveredstatements="429" conditionals="251" coveredconditionals="217" methods="95" coveredmethods="75" elements="803" coveredelements="721" complexity="0" loc="457" ncloc="457" packages="3" files="5" classes="5"/>
|
|
5
5
|
<package name="src">
|
|
6
6
|
<metrics statements="196" coveredstatements="190" conditionals="99" coveredconditionals="89" methods="41" coveredmethods="32"/>
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
132
132
|
Code coverage generated by
|
|
133
133
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
134
|
-
at 2025-10-
|
|
134
|
+
at 2025-10-28T04:09:37.843Z
|
|
135
135
|
</div>
|
|
136
136
|
<script src="prettify.js"></script>
|
|
137
137
|
<script>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
102
102
|
Code coverage generated by
|
|
103
103
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
104
|
-
at 2025-10-
|
|
104
|
+
at 2025-10-28T04:09:37.843Z
|
|
105
105
|
</div>
|
|
106
106
|
<script src="../prettify.js"></script>
|
|
107
107
|
<script>
|
|
@@ -2239,7 +2239,7 @@ export {<span class="fstat-no" title="function not covered" >ExpoIapConsole}</sp
|
|
|
2239
2239
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
2240
2240
|
Code coverage generated by
|
|
2241
2241
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
2242
|
-
at 2025-10-
|
|
2242
|
+
at 2025-10-28T04:09:37.843Z
|
|
2243
2243
|
</div>
|
|
2244
2244
|
<script src="../prettify.js"></script>
|
|
2245
2245
|
<script>
|
|
@@ -814,7 +814,7 @@ export const createAlternativeBillingTokenAndroid: MutationField<
|
|
|
814
814
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
815
815
|
Code coverage generated by
|
|
816
816
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
817
|
-
at 2025-10-
|
|
817
|
+
at 2025-10-28T04:09:37.843Z
|
|
818
818
|
</div>
|
|
819
819
|
<script src="../../prettify.js"></script>
|
|
820
820
|
<script>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
117
117
|
Code coverage generated by
|
|
118
118
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
119
|
-
at 2025-10-
|
|
119
|
+
at 2025-10-28T04:09:37.843Z
|
|
120
120
|
</div>
|
|
121
121
|
<script src="../../prettify.js"></script>
|
|
122
122
|
<script>
|
|
@@ -1267,7 +1267,7 @@ export const presentExternalPurchaseLinkIOS: MutationField<
|
|
|
1267
1267
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
1268
1268
|
Code coverage generated by
|
|
1269
1269
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1270
|
-
at 2025-10-
|
|
1270
|
+
at 2025-10-28T04:09:37.843Z
|
|
1271
1271
|
</div>
|
|
1272
1272
|
<script src="../../prettify.js"></script>
|
|
1273
1273
|
<script>
|
|
@@ -268,7 +268,7 @@ export const ExpoIapConsole = createConsole();
|
|
|
268
268
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
269
269
|
Code coverage generated by
|
|
270
270
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
271
|
-
at 2025-10-
|
|
271
|
+
at 2025-10-28T04:09:37.843Z
|
|
272
272
|
</div>
|
|
273
273
|
<script src="../../prettify.js"></script>
|
|
274
274
|
<script>
|
|
@@ -1111,7 +1111,7 @@ export function getUserFriendlyErrorMessage(error: ErrorLike): string {
|
|
|
1111
1111
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
1112
1112
|
Code coverage generated by
|
|
1113
1113
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1114
|
-
at 2025-10-
|
|
1114
|
+
at 2025-10-28T04:09:37.843Z
|
|
1115
1115
|
</div>
|
|
1116
1116
|
<script src="../../prettify.js"></script>
|
|
1117
1117
|
<script>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
<div class='footer quiet pad2 space-top1 center small'>
|
|
117
117
|
Code coverage generated by
|
|
118
118
|
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
119
|
-
at 2025-10-
|
|
119
|
+
at 2025-10-28T04:09:37.843Z
|
|
120
120
|
</div>
|
|
121
121
|
<script src="../../prettify.js"></script>
|
|
122
122
|
<script>
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
package/plugin/build/withIAP.js
CHANGED
|
@@ -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(
|
|
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
|
|
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
|
|
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':
|
|
178
|
+
'android:name': horizonMetaDataName,
|
|
159
179
|
'android:value': props.horizonAppId,
|
|
160
180
|
},
|
|
161
181
|
};
|
|
162
|
-
const existingIndex = metaData.findIndex((m) => m.$['android:name'] ===
|
|
182
|
+
const existingIndex = metaData.findIndex((m) => m.$['android:name'] === horizonMetaDataName);
|
|
163
183
|
if (existingIndex !== -1) {
|
|
164
184
|
metaData[existingIndex] = horizonAppIdMeta;
|
|
165
|
-
logOnce(`✅ Updated
|
|
185
|
+
logOnce(`✅ Updated ${horizonMetaDataName} to ${props.horizonAppId} in AndroidManifest.xml`);
|
|
166
186
|
}
|
|
167
187
|
else {
|
|
168
188
|
metaData.push(horizonAppIdMeta);
|
|
169
|
-
logOnce(`✅ Added
|
|
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.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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 => '${
|
|
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 (
|
|
199
|
-
|
|
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
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
WarningAggregator,
|
|
5
5
|
withAndroidManifest,
|
|
6
6
|
withAppBuildGradle,
|
|
7
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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':
|
|
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'] ===
|
|
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
|
|
242
|
+
`✅ Updated ${horizonMetaDataName} to ${props.horizonAppId} in AndroidManifest.xml`,
|
|
213
243
|
);
|
|
214
244
|
} else {
|
|
215
245
|
metaData.push(horizonAppIdMeta);
|
|
216
246
|
logOnce(
|
|
217
|
-
`✅ Added
|
|
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
|
|
239
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
266
|
-
|
|
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 => '${
|
|
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 (
|
|
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
|
|
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(
|
|
240
|
+
logOnce(
|
|
241
|
+
'🧹 Removed Maven openiap-google* dependencies (using local module)',
|
|
242
|
+
);
|
|
233
243
|
}
|
|
234
244
|
|
|
235
245
|
// Add missingDimensionStrategy (required for flavored module)
|