expo-iap 5.6.3 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +10 -15
- package/android/openiap-store.gradle +365 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +2 -5
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +2 -6
- package/build/ExpoIapModule.d.ts +51 -6
- package/build/ExpoIapModule.d.ts.map +1 -1
- package/build/ExpoIapModule.js +12 -8
- package/build/ExpoIapModule.js.map +1 -1
- package/build/index.d.ts +22 -44
- package/build/index.d.ts.map +1 -1
- package/build/index.js +26 -54
- package/build/index.js.map +1 -1
- package/build/kit-api.d.ts +10 -10
- package/build/kit-api.d.ts.map +1 -1
- package/build/kit-api.js +11 -35
- package/build/kit-api.js.map +1 -1
- package/build/modules/android.d.ts +4 -6
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +9 -15
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +11 -29
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +13 -36
- package/build/modules/ios.js.map +1 -1
- package/build/onside/index.d.ts +7 -7
- package/build/onside/index.d.ts.map +1 -1
- package/build/onside/index.js +7 -8
- package/build/onside/index.js.map +1 -1
- package/build/useIAP.d.ts +3 -6
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +11 -32
- package/build/useIAP.js.map +1 -1
- package/build/utils/debug.d.ts +3 -3
- package/build/utils/debug.d.ts.map +1 -1
- package/build/utils/debug.js +3 -13
- package/build/utils/debug.js.map +1 -1
- package/build/utils/errorMapping.d.ts +4 -12
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +4 -12
- package/build/utils/errorMapping.js.map +1 -1
- package/build/utils/restorePurchases.d.ts.map +1 -1
- package/build/utils/restorePurchases.js +3 -4
- package/build/utils/restorePurchases.js.map +1 -1
- package/ios/ExpoIap.podspec +4 -7
- package/ios/ExpoIapHelper.swift +2 -3
- package/ios/ExpoIapModule.swift +2 -3
- package/package.json +2 -2
- package/plugin/build/withIAP.d.ts +16 -5
- package/plugin/build/withIAP.js +181 -162
- package/plugin/build/withLocalOpenIAP.d.ts +15 -12
- package/plugin/build/withLocalOpenIAP.js +131 -118
- package/plugin/build/withVega.d.ts +7 -0
- package/plugin/build/withVega.js +25 -5
- package/plugin/src/expoConfig.augmentation.d.ts +22 -11
- package/plugin/src/withIAP.ts +241 -222
- package/plugin/src/withLocalOpenIAP.ts +214 -163
- package/plugin/src/withVega.ts +28 -4
- package/src/ExpoIapModule.ts +136 -18
- package/src/index.ts +28 -65
- package/src/kit-api.ts +21 -46
- package/src/modules/android.ts +11 -26
- package/src/modules/ios.ts +21 -44
- package/src/onside/index.ts +7 -9
- package/src/useIAP.ts +14 -38
- package/src/utils/debug.ts +3 -14
- package/src/utils/errorMapping.ts +4 -12
- package/src/utils/restorePurchases.ts +3 -4
package/android/build.gradle
CHANGED
|
@@ -53,12 +53,9 @@ if (!(googleVersion instanceof String) || !googleVersion.trim()) {
|
|
|
53
53
|
def googleVersionString = googleVersion.trim()
|
|
54
54
|
apply from: project.file('openiap-android-sdk.gradle')
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
throw new GradleException("expo-iap: horizonEnabled and fireOsEnabled cannot both be true")
|
|
60
|
-
}
|
|
61
|
-
def openiapFlavor = fireOsEnabled ? 'amazon' : (horizonEnabled ? 'horizon' : 'play')
|
|
56
|
+
// Store selection: explicit property > task flavor > connected debug device > play.
|
|
57
|
+
apply from: project.file('openiap-store.gradle')
|
|
58
|
+
def openiapFlavor = openIapResolveStore('expo-iap').store
|
|
62
59
|
|
|
63
60
|
// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
|
|
64
61
|
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
|
|
@@ -111,17 +108,15 @@ dependencies {
|
|
|
111
108
|
testImplementation "junit:junit:4.13.2"
|
|
112
109
|
testImplementation "org.json:json:20260719"
|
|
113
110
|
|
|
114
|
-
//
|
|
111
|
+
// `api`, so an app's own native code can use dev.hyo.openiap through this module.
|
|
115
112
|
if (findProject(":openiap-google") != null) {
|
|
116
|
-
|
|
117
|
-
} else if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// Use openiap-google-horizon for Meta Quest when horizonEnabled is true
|
|
122
|
-
implementation "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"
|
|
113
|
+
api project(":openiap-google")
|
|
114
|
+
} else if (openiapFlavor == 'amazon') {
|
|
115
|
+
api "io.github.hyochan.openiap:openiap-google-amazon:${googleVersionString}"
|
|
116
|
+
} else if (openiapFlavor == 'horizon') {
|
|
117
|
+
api "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"
|
|
123
118
|
} else {
|
|
124
119
|
// Fallback to published artifact when local project isn't linked
|
|
125
|
-
|
|
120
|
+
api "io.github.hyochan.openiap:openiap-google:${googleVersionString}"
|
|
126
121
|
}
|
|
127
122
|
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// OpenIAP store resolver. Framework wrappers symlink this file into android/
|
|
2
|
+
// and publish it as a real file.
|
|
3
|
+
// Apply it, then call openIapResolveStore('<label>'), or ('<label>', [allowNone: true])
|
|
4
|
+
// where the library can build without a store SDK.
|
|
5
|
+
//
|
|
6
|
+
// First match wins:
|
|
7
|
+
// 1. explicit openiapStore=<store> (-P, ORG_GRADLE_PROJECT_openiapStore, gradle.properties).
|
|
8
|
+
// The legacy horizonEnabled, fireOsEnabled and openiapPlatform=none still work.
|
|
9
|
+
// 2. variant a requested task names a store flavor (assembleHorizonRelease).
|
|
10
|
+
// 3. device debug builds only: the Quest or Fire device that ANDROID_SERIAL
|
|
11
|
+
// names, or the only one connected.
|
|
12
|
+
// 4. play
|
|
13
|
+
// Two signals naming different stores fail the build. Only spelled-out task names
|
|
14
|
+
// are read; a task that builds another store (an abbreviation such as aHR) fails
|
|
15
|
+
// once the task graph is ready.
|
|
16
|
+
|
|
17
|
+
ext.openIapStoreAliases = [
|
|
18
|
+
play: 'play', google: 'play', gplay: 'play', googleplay: 'play', 'google-play': 'play', gms: 'play',
|
|
19
|
+
horizon: 'horizon', meta: 'horizon', quest: 'horizon',
|
|
20
|
+
amazon: 'amazon', fire: 'amazon', fireos: 'amazon', 'fire-os': 'amazon',
|
|
21
|
+
none: 'none', auto: 'auto',
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
ext.openIapNormalizeStore = { Object value, String label ->
|
|
25
|
+
def key = value?.toString()?.trim()?.toLowerCase(Locale.ROOT)
|
|
26
|
+
if (!key) {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
def store = openIapStoreAliases[key]
|
|
30
|
+
if (store == null) {
|
|
31
|
+
throw new GradleException("${label}: unknown openiapStore '${value}'; use play, horizon, amazon, auto, or none where the library allows it")
|
|
32
|
+
}
|
|
33
|
+
return store
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// assembleHorizonRelease -> [assemble, horizon, release]
|
|
37
|
+
ext.openIapTaskTokens = { String taskPath ->
|
|
38
|
+
def name = taskPath.substring(taskPath.lastIndexOf(':') + 1)
|
|
39
|
+
return name.split(/(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|[^A-Za-z0-9]+/).collect { it.toLowerCase(Locale.ROOT) }.findAll { it }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The key that set the opt-out, for error messages.
|
|
43
|
+
ext.openIapOptOutKey = {
|
|
44
|
+
def explicit = project.findProperty('openiapStore')?.toString()?.trim()?.toLowerCase(Locale.ROOT)
|
|
45
|
+
return explicit == 'none' ? 'openiapStore=none' : 'openiapPlatform=none'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ext.openIapExplicitStore = { String label, boolean allowNone ->
|
|
49
|
+
def explicit = openIapNormalizeStore(project.findProperty('openiapStore'), label)
|
|
50
|
+
def horizon = project.findProperty('horizonEnabled')?.toString()?.toBoolean() ?: false
|
|
51
|
+
def fireOs = project.findProperty('fireOsEnabled')?.toString()?.toBoolean() ?: false
|
|
52
|
+
def platform = project.findProperty('openiapPlatform')?.toString()?.trim()?.toLowerCase(Locale.ROOT)
|
|
53
|
+
if (platform != null && platform != 'none') {
|
|
54
|
+
throw new GradleException("${label}: openiapPlatform only supports the opt-out value 'none'; use openiapStore to pick a store")
|
|
55
|
+
}
|
|
56
|
+
if (horizon && fireOs) {
|
|
57
|
+
throw new GradleException("${label}: horizonEnabled and fireOsEnabled cannot both be true")
|
|
58
|
+
}
|
|
59
|
+
if (platform == 'none' && (horizon || fireOs)) {
|
|
60
|
+
throw new GradleException("${label}: openiapPlatform=none conflicts with legacy store flags")
|
|
61
|
+
}
|
|
62
|
+
def (legacyKey, legacy) = platform == 'none' ? ['openiapPlatform=none', 'none']
|
|
63
|
+
: fireOs ? ['fireOsEnabled=true', 'amazon']
|
|
64
|
+
: horizon ? ['horizonEnabled=true', 'horizon'] : [null, null]
|
|
65
|
+
if (legacy != null) {
|
|
66
|
+
logger.warn("${label}: horizonEnabled, fireOsEnabled, and openiapPlatform are deprecated; use openiapStore=${legacy}")
|
|
67
|
+
}
|
|
68
|
+
def pin = explicit == 'auto' ? null : explicit
|
|
69
|
+
if (pin != null && legacy != null && legacy != pin) {
|
|
70
|
+
throw new GradleException("${label}: openiapStore=${pin} conflicts with ${legacyKey}")
|
|
71
|
+
}
|
|
72
|
+
def store = pin ?: legacy
|
|
73
|
+
if (store == 'none' && !allowNone) {
|
|
74
|
+
throw new GradleException("${label}: ${openIapOptOutKey()} is not supported by this library")
|
|
75
|
+
}
|
|
76
|
+
return store
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Task options that take a separate value, from Gradle's `help --task` output.
|
|
80
|
+
// An unlisted option's value may be read as a task; the graph check then fails
|
|
81
|
+
// unless the value also names a task the build runs.
|
|
82
|
+
ext.openIapValueOptions = [
|
|
83
|
+
'-b', '-c', '-g', '-I', '-p',
|
|
84
|
+
'--args', '--component', '--configuration', '--console', '--dependency',
|
|
85
|
+
'--distribution-type', '--dsl', '--format',
|
|
86
|
+
'--gradle-distribution-sha256-sum', '--gradle-distribution-url',
|
|
87
|
+
'--gradle-version', '--group', '--groups', '--include-build',
|
|
88
|
+
'--insecure-protocol', '--java-version', '--jvm-vendor', '--jvm-version',
|
|
89
|
+
'--max-workers', '--network-timeout', '--package', '--project-cache-dir',
|
|
90
|
+
'--project-dir', '--project-name', '--property', '--task', '--test-framework',
|
|
91
|
+
'--tests', '--type', '--variant',
|
|
92
|
+
] as Set
|
|
93
|
+
|
|
94
|
+
// Gradle flattens an invocation into one list, options included, so drop each
|
|
95
|
+
// option and its value: `--tests *AmazonTest*` is not an Amazon build.
|
|
96
|
+
ext.openIapRequestedTasks = {
|
|
97
|
+
def names = []
|
|
98
|
+
def previous = null
|
|
99
|
+
def requested = gradle.startParameter.taskNames
|
|
100
|
+
if (requested.isEmpty()) {
|
|
101
|
+
requested = rootProject.defaultTasks ?: []
|
|
102
|
+
}
|
|
103
|
+
requested.each { String token ->
|
|
104
|
+
if (!token) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
if (token.startsWith('-')) {
|
|
108
|
+
previous = token
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
if (previous != null && openIapValueOptions.contains(previous)) {
|
|
112
|
+
previous = token
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
names << token
|
|
116
|
+
previous = token
|
|
117
|
+
}
|
|
118
|
+
return names
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Variant detection reads every requested task: keep whole-token
|
|
122
|
+
// play/horizon/amazon out of custom task names.
|
|
123
|
+
ext.openIapTaskStore = { String label ->
|
|
124
|
+
def named = openIapRequestedTasks().collectMany { openIapTaskTokens(it) }
|
|
125
|
+
.findAll { it in ['play', 'horizon', 'amazon'] } as LinkedHashSet
|
|
126
|
+
// Two stores in one build: refuse rather than guess.
|
|
127
|
+
if (named.size() > 1) {
|
|
128
|
+
throw new GradleException("${label}: cannot tell which store the requested tasks '${openIapRequestedTasks().join(' ')}' select (${named.join(', ')}); build one store per invocation, spell the tasks out, or set openiapStore")
|
|
129
|
+
}
|
|
130
|
+
return named ? named.first() : null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// `clean assembleDebug` is a debug build; any release task makes it a release
|
|
134
|
+
// build, which never depends on the attached device.
|
|
135
|
+
ext.openIapBuildTypes = { String taskPath ->
|
|
136
|
+
def types = [] as LinkedHashSet
|
|
137
|
+
openIapTaskTokens(taskPath).each { token ->
|
|
138
|
+
if (token in ['debug', 'release']) {
|
|
139
|
+
types << token
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (types) {
|
|
143
|
+
return types
|
|
144
|
+
}
|
|
145
|
+
// `aD` is assembleDebug, so the device rule has to see it as one.
|
|
146
|
+
def name = taskPath.substring(taskPath.lastIndexOf(':') + 1)
|
|
147
|
+
def segments = name.split(/(?=[A-Z])/).collect { it.toLowerCase(Locale.ROOT) }.findAll { it }
|
|
148
|
+
segments.drop(1).each { segment ->
|
|
149
|
+
['debug', 'release'].findAll { it.startsWith(segment) }.each { types << it }
|
|
150
|
+
}
|
|
151
|
+
return types
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ext.openIapDebugBuild = {
|
|
155
|
+
def names = openIapRequestedTasks()
|
|
156
|
+
if (!names) {
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
def types = names.collectMany { openIapBuildTypes(it) } as LinkedHashSet
|
|
160
|
+
return types.contains('debug') && !types.contains('release')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Gradle resolves a typed name to real tasks: an exact, case-insensitive match
|
|
164
|
+
// when one exists, otherwise a unique prefix or a camelCase abbreviation.
|
|
165
|
+
ext.openIapSelectedTasks = { graph, String request ->
|
|
166
|
+
def wanted = request.substring(request.lastIndexOf(':') + 1)
|
|
167
|
+
if (!wanted) {
|
|
168
|
+
return []
|
|
169
|
+
}
|
|
170
|
+
def lower = wanted.toLowerCase(Locale.ROOT)
|
|
171
|
+
def names = graph.allTasks.collect { it.name } as LinkedHashSet
|
|
172
|
+
def exact = names.findAll { it.toLowerCase(Locale.ROOT) == lower }
|
|
173
|
+
if (exact) {
|
|
174
|
+
return exact
|
|
175
|
+
}
|
|
176
|
+
def segments = wanted.split(/(?=[A-Z])/).findAll { it }
|
|
177
|
+
return names.findAll { String name ->
|
|
178
|
+
if (name.toLowerCase(Locale.ROOT).startsWith(lower)) {
|
|
179
|
+
return true
|
|
180
|
+
}
|
|
181
|
+
def taskSegments = name.split(/(?=[A-Z])/).findAll { it }
|
|
182
|
+
if (segments.size() < 2 || segments.size() > taskSegments.size()) {
|
|
183
|
+
return false
|
|
184
|
+
}
|
|
185
|
+
return (0..<segments.size()).every {
|
|
186
|
+
taskSegments[it].toLowerCase(Locale.ROOT).startsWith(segments[it].toLowerCase(Locale.ROOT))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Checks the store picked at configuration against the tasks that will run.
|
|
192
|
+
// Only tasks the request selected count: an anchor such as assembleDebug also
|
|
193
|
+
// builds every flavor of a library it reaches.
|
|
194
|
+
ext.openIapVerifyTaskGraph = { String label, Map resolution ->
|
|
195
|
+
def resolved = resolution.store
|
|
196
|
+
if (resolved == 'none') {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
gradle.taskGraph.whenReady { graph ->
|
|
200
|
+
def named = [] as LinkedHashSet
|
|
201
|
+
openIapRequestedTasks().each { String request ->
|
|
202
|
+
openIapSelectedTasks(graph, request).each { String taskName ->
|
|
203
|
+
openIapTaskTokens(taskName).each { token ->
|
|
204
|
+
if (token in ['play', 'horizon', 'amazon']) {
|
|
205
|
+
named << token
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (!named) {
|
|
211
|
+
// The store came from something that is not a task, such as an option's value.
|
|
212
|
+
if (resolution.source == 'variant') {
|
|
213
|
+
throw new GradleException("${label}: read ${resolved} from '${openIapRequestedTasks().join(' ')}', but no task it runs names a store; set openiapStore")
|
|
214
|
+
}
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
if (named.size() > 1) {
|
|
218
|
+
throw new GradleException("${label}: the requested tasks build more than one store (${named.join(', ')}) but a build links one; build one store per invocation or set openiapStore")
|
|
219
|
+
}
|
|
220
|
+
def actual = named.first()
|
|
221
|
+
if (actual != resolved) {
|
|
222
|
+
throw new GradleException("${label}: linked ${resolved} but the requested tasks build ${actual}; spell the task out or set openiapStore=${actual}")
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
ext.openIapEnv = { String name ->
|
|
228
|
+
// providers.environmentVariable is a tracked configuration-cache input;
|
|
229
|
+
// System.getenv is not.
|
|
230
|
+
return providers.environmentVariable(name).orNull
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
ext.openIapAdb = {
|
|
234
|
+
def roots = [openIapEnv('ANDROID_HOME'), openIapEnv('ANDROID_SDK_ROOT')]
|
|
235
|
+
// local.properties is plain I/O, not a tracked input: sdk.dir ~never
|
|
236
|
+
// changes, and staleness only loses adb discovery, never a store pick.
|
|
237
|
+
def localProperties = rootProject.file('local.properties')
|
|
238
|
+
if (localProperties.isFile()) {
|
|
239
|
+
def properties = new Properties()
|
|
240
|
+
localProperties.withInputStream { properties.load(it) }
|
|
241
|
+
roots << properties.getProperty('sdk.dir')
|
|
242
|
+
}
|
|
243
|
+
def names = ['adb', 'adb.exe']
|
|
244
|
+
def adb = roots.findResult { root ->
|
|
245
|
+
if (!root) {
|
|
246
|
+
return null
|
|
247
|
+
}
|
|
248
|
+
return names.findResult { name ->
|
|
249
|
+
def candidate = new File(root, "platform-tools/${name}")
|
|
250
|
+
return candidate.isFile() ? candidate.absolutePath : null
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (adb != null) {
|
|
254
|
+
return adb
|
|
255
|
+
}
|
|
256
|
+
// A developer with adb only on PATH still gets device selection.
|
|
257
|
+
return (openIapEnv('PATH') ?: '').split(File.pathSeparator).findResult { entry ->
|
|
258
|
+
if (!entry) {
|
|
259
|
+
return null
|
|
260
|
+
}
|
|
261
|
+
return names.findResult { name ->
|
|
262
|
+
def candidate = new File(entry, name)
|
|
263
|
+
return candidate.isFile() ? candidate.absolutePath : null
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// providers.exec keeps the probe legal under the configuration cache, and Gradle
|
|
269
|
+
// re-runs it before reusing a cached configuration, so a new device reconfigures.
|
|
270
|
+
ext.openIapAdbText = { List<String> command ->
|
|
271
|
+
try {
|
|
272
|
+
return providers.exec {
|
|
273
|
+
commandLine(command)
|
|
274
|
+
ignoreExitValue = true
|
|
275
|
+
}.standardOutput.asText.get()
|
|
276
|
+
} catch (Throwable ignored) {
|
|
277
|
+
return null
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Like every Android tool, target the device ANDROID_SERIAL names.
|
|
282
|
+
ext.openIapDeviceStore = {
|
|
283
|
+
def adb = openIapAdb()
|
|
284
|
+
if (adb == null) {
|
|
285
|
+
return null
|
|
286
|
+
}
|
|
287
|
+
def listing = openIapAdbText([adb, 'devices'])
|
|
288
|
+
if (listing == null) {
|
|
289
|
+
return null
|
|
290
|
+
}
|
|
291
|
+
// Windows adb terminates lines with CR, which the serial would otherwise keep.
|
|
292
|
+
def serials = listing.readLines()
|
|
293
|
+
.collect { it.replace('\r', '').trim() }
|
|
294
|
+
.findAll { it ==~ /^\S+\s+device$/ }
|
|
295
|
+
.collect { it.split(/\s+/)[0] }
|
|
296
|
+
def requested = openIapEnv('ANDROID_SERIAL')?.trim()
|
|
297
|
+
def serial
|
|
298
|
+
if (requested) {
|
|
299
|
+
if (!serials.contains(requested)) {
|
|
300
|
+
logger.warn("openiap: ANDROID_SERIAL=${requested} is not attached; not selecting a store from a device")
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
serial = requested
|
|
304
|
+
} else if (serials.size() == 1) {
|
|
305
|
+
serial = serials[0]
|
|
306
|
+
} else {
|
|
307
|
+
return null
|
|
308
|
+
}
|
|
309
|
+
def features = openIapAdbText([adb, '-s', serial, 'shell', 'pm', 'list', 'features'])
|
|
310
|
+
def manufacturerText = openIapAdbText([adb, '-s', serial, 'shell', 'getprop', 'ro.product.manufacturer'])
|
|
311
|
+
// A device that dropped after the listing answers with nothing; that is not Play.
|
|
312
|
+
if (!features?.trim() && !manufacturerText?.trim()) {
|
|
313
|
+
logger.warn("openiap: ${serial} stopped responding; not selecting a store from a device")
|
|
314
|
+
return null
|
|
315
|
+
}
|
|
316
|
+
features = features ?: ''
|
|
317
|
+
def manufacturer = (manufacturerText ?: '').trim()
|
|
318
|
+
def store = 'play'
|
|
319
|
+
if (features.contains('feature:horizonos.software.horizon_os') || features.contains('feature:oculus.hardware.standalone_vr')) {
|
|
320
|
+
store = 'horizon'
|
|
321
|
+
} else if (features.contains('feature:amazon.hardware.fire_tv') || manufacturer.equalsIgnoreCase('Amazon')) {
|
|
322
|
+
store = 'amazon'
|
|
323
|
+
}
|
|
324
|
+
return [store: store, reason: "device ${serial}, manufacturer ${manufacturer ?: 'unknown'}"]
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
ext.openIapResolveStore = { String label, Map options = [:] ->
|
|
328
|
+
def allowNone = options.allowNone == true
|
|
329
|
+
if (rootProject.ext.has('openIapStoreResolution')) {
|
|
330
|
+
def cached = rootProject.ext.openIapStoreResolution
|
|
331
|
+
// The opt-out is per caller: a library that needs a store SDK still refuses it.
|
|
332
|
+
if (cached.store == 'none' && !allowNone) {
|
|
333
|
+
throw new GradleException("${label}: ${openIapOptOutKey()} is not supported by this library")
|
|
334
|
+
}
|
|
335
|
+
return cached
|
|
336
|
+
}
|
|
337
|
+
def explicit = openIapExplicitStore(label, allowNone)
|
|
338
|
+
// Opting out links nothing, so the tasks may build several store flavors.
|
|
339
|
+
def variant = explicit == 'none' ? null : openIapTaskStore(label)
|
|
340
|
+
// A pin against the task's flavor would ship the wrong SDK. The opt-out links
|
|
341
|
+
// nothing, so it may sit beside any flavor.
|
|
342
|
+
if (explicit != null && explicit != 'none' && variant != null && explicit != variant) {
|
|
343
|
+
throw new GradleException(
|
|
344
|
+
"${label}: openiapStore=${explicit} conflicts with the ${variant} flavor in '${openIapRequestedTasks().join(' ')}'"
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
def resolution = null
|
|
348
|
+
if (explicit != null) {
|
|
349
|
+
resolution = [store: explicit, source: 'explicit', reason: 'openiapStore or a legacy flag']
|
|
350
|
+
} else if (variant != null) {
|
|
351
|
+
resolution = [store: variant, source: 'variant', reason: "task ${openIapRequestedTasks().join(' ')}"]
|
|
352
|
+
} else if (openIapDebugBuild()) {
|
|
353
|
+
def device = openIapDeviceStore()
|
|
354
|
+
if (device != null) {
|
|
355
|
+
resolution = [store: device.store, source: 'device', reason: device.reason]
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (resolution == null) {
|
|
359
|
+
resolution = [store: 'play', source: 'default', reason: 'no store signal']
|
|
360
|
+
}
|
|
361
|
+
rootProject.ext.openIapStoreResolution = resolution
|
|
362
|
+
logger.lifecycle("openiap: store=${resolution.store} (source=${resolution.source}; ${resolution.reason})")
|
|
363
|
+
openIapVerifyTaskGraph(label, resolution)
|
|
364
|
+
return resolution
|
|
365
|
+
}
|
|
@@ -272,10 +272,7 @@ object ExpoIapHelper {
|
|
|
272
272
|
)
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
/**
|
|
276
|
-
* Helper to safely emit an event with error fallback.
|
|
277
|
-
* Reduces code duplication across listener handlers.
|
|
278
|
-
*/
|
|
275
|
+
/** Emits an event, falling back to a purchase error if emitting fails. */
|
|
279
276
|
private fun safeEmitEvent(
|
|
280
277
|
module: Module,
|
|
281
278
|
scope: CoroutineScope,
|
|
@@ -362,7 +359,7 @@ object ExpoIapHelper {
|
|
|
362
359
|
)
|
|
363
360
|
}.onFailure { error ->
|
|
364
361
|
ExpoIapLog.failure("buffer/send PURCHASE_ERROR", error)
|
|
365
|
-
//
|
|
362
|
+
// If the original error cannot be emitted, try a generic one.
|
|
366
363
|
val fallbackPayload =
|
|
367
364
|
mapOf(
|
|
368
365
|
"code" to OpenIapError.UnknownError.CODE,
|
|
@@ -148,8 +148,8 @@ class ExpoIapModule : Module() {
|
|
|
148
148
|
scope.launch {
|
|
149
149
|
connectionMutex.withLock {
|
|
150
150
|
try {
|
|
151
|
-
//
|
|
152
|
-
//
|
|
151
|
+
// Set the Activity before initConnection: the Horizon SDK needs it to
|
|
152
|
+
// initialize OVRPlatform with the proper returnComponent.
|
|
153
153
|
// https://github.com/meta-quest/Meta-Spatial-SDK-Samples/issues/82#issuecomment-3452577530
|
|
154
154
|
runCatching { currentActivity }
|
|
155
155
|
.onSuccess {
|
|
@@ -501,7 +501,6 @@ class ExpoIapModule : Module() {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
// New name: consumePurchaseAndroid
|
|
505
504
|
AsyncFunction("consumePurchaseAndroid") { token: String, promise: Promise ->
|
|
506
505
|
ExpoIapLog.payload("consumePurchaseAndroid", mapOf("token" to token))
|
|
507
506
|
scope.launch {
|
|
@@ -610,9 +609,6 @@ class ExpoIapModule : Module() {
|
|
|
610
609
|
scope.launch {
|
|
611
610
|
try {
|
|
612
611
|
val openIapProgram = mapBillingProgram(program)
|
|
613
|
-
// Note: enableBillingProgram should be called before initConnection
|
|
614
|
-
// for proper BillingClient configuration. Here it's called as a fallback
|
|
615
|
-
// but may have no effect if BillingClient is already initialized.
|
|
616
612
|
val result = openIapStore.isBillingProgramAvailable(openIapProgram)
|
|
617
613
|
val response =
|
|
618
614
|
mapOf(
|
package/build/ExpoIapModule.d.ts
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
|
+
import { type EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { BillingProgramAndroid, BillingProgramReportingDetailsAndroid, DeveloperBillingTypeAndroid, Mutation, MutationField, ProductQueryType, PurchaseInput, PurchaseOptions, PurchaseUpdatedListenerOptions, Query, QueryField, SubscriptionStatusIOS } from './types';
|
|
3
|
+
/** Members read from the raw module; every store module provides them. */
|
|
4
|
+
type NativeEventModule = {
|
|
5
|
+
ERROR_CODES?: Record<string, unknown>;
|
|
6
|
+
addListener<T>(eventName: string, listener: (payload: T) => void): EventSubscription | undefined;
|
|
7
|
+
removeListener?<T>(eventName: string, listener: (payload: T) => void): void;
|
|
8
|
+
setPurchaseUpdatedListenerOptions?(options?: PurchaseUpdatedListenerOptions | null): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
type QueryFields<K extends keyof Query> = {
|
|
11
|
+
[P in K]: QueryField<P>;
|
|
12
|
+
};
|
|
13
|
+
type MutationFields<K extends keyof Mutation> = {
|
|
14
|
+
[P in K]: MutationField<P>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Native surface behind the default export. Results the wrappers decode stay
|
|
18
|
+
* `unknown`; the rest match the generated operation signatures.
|
|
19
|
+
*/
|
|
20
|
+
export type ExpoIapNativeModule = NativeEventModule & QueryFields<'canPresentExternalPurchaseNoticeIOS' | 'currentEntitlementIOS' | 'getActiveSubscriptions' | 'getAppTransactionIOS' | 'getBillingChoiceInfoAndroid' | 'getExternalPurchaseCustomLinkTokenIOS' | 'getPromotedProductIOS' | 'getReceiptDataIOS' | 'getTransactionJwsIOS' | 'hasActiveSubscriptions' | 'isEligibleForExternalPurchaseCustomLinkIOS' | 'isEligibleForIntroOfferIOS' | 'isTransactionVerifiedIOS' | 'latestTransactionIOS'> & MutationFields<'beginRefundRequestIOS' | 'clearTransactionIOS' | 'endConnection' | 'initConnection' | 'isBillingProgramAvailableAndroid' | 'launchExternalLinkAndroid' | 'openRedeemOfferCodeAndroid' | 'presentCodeRedemptionSheetIOS' | 'presentExternalPurchaseLinkIOS' | 'presentExternalPurchaseNoticeSheetIOS' | 'showBillingProgramInformationDialogAndroid' | 'showExternalPurchaseCustomLinkNoticeIOS' | 'showInAppMessagesAndroid' | 'syncIOS' | 'verifyPurchase' | 'verifyPurchaseWithProvider'> & {
|
|
21
|
+
USING_ONSIDE_SDK: boolean;
|
|
22
|
+
USING_VEGA_SDK: boolean;
|
|
23
|
+
fetchProducts(request: {
|
|
24
|
+
skus: string[];
|
|
25
|
+
type: ProductQueryType;
|
|
26
|
+
}): Promise<unknown[]>;
|
|
27
|
+
fetchProducts(type: ProductQueryType, skus: string[]): Promise<unknown[]>;
|
|
28
|
+
getAvailableItems(alsoPublishToEventListenerIOS: boolean, onlyIncludeActiveItemsIOS: boolean): Promise<unknown>;
|
|
29
|
+
getAvailableItems(options: PurchaseOptions): Promise<unknown>;
|
|
30
|
+
getAllTransactionsIOS(): Promise<unknown>;
|
|
31
|
+
getPendingTransactionsIOS(): Promise<unknown>;
|
|
32
|
+
showManageSubscriptionsIOS(): Promise<unknown>;
|
|
33
|
+
requestPurchase(request: object): Promise<unknown>;
|
|
34
|
+
finishTransaction(purchase: PurchaseInput, isConsumable: boolean | null): Promise<boolean>;
|
|
35
|
+
acknowledgePurchaseAndroid(purchaseToken: string): Promise<unknown>;
|
|
36
|
+
consumePurchaseAndroid(purchaseToken: string): Promise<unknown>;
|
|
37
|
+
createBillingProgramReportingDetailsAndroid(program: BillingProgramAndroid, developerBillingType: DeveloperBillingTypeAndroid | null): Promise<BillingProgramReportingDetailsAndroid>;
|
|
38
|
+
subscriptionStatusIOS(sku: string): Promise<SubscriptionStatusIOS[] | null>;
|
|
39
|
+
requestReceiptRefreshIOS(): Promise<string>;
|
|
40
|
+
deepLinkToSubscriptionsAndroid?(options: {
|
|
41
|
+
skuAndroid?: string;
|
|
42
|
+
packageNameAndroid?: string;
|
|
43
|
+
}): Promise<void> | void;
|
|
44
|
+
getStorefront?(): Promise<string> | string;
|
|
45
|
+
restorePurchases?(): Promise<boolean>;
|
|
46
|
+
};
|
|
1
47
|
export declare const NATIVE_ERROR_CODES: Record<string, unknown>;
|
|
2
48
|
/**
|
|
3
|
-
* Returns the raw native module
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* "native state unsupported on Proxy" on New Architecture / Hermes.
|
|
49
|
+
* Returns the raw native module, not the Proxy. Use it for addListener: a JSI
|
|
50
|
+
* HostObject needs the real module as `this`, and a Proxy throws "native state
|
|
51
|
+
* unsupported on Proxy" on New Architecture / Hermes.
|
|
7
52
|
*/
|
|
8
|
-
export declare function getNativeModule():
|
|
9
|
-
declare const _default:
|
|
53
|
+
export declare function getNativeModule(): NativeEventModule;
|
|
54
|
+
declare const _default: ExpoIapNativeModule;
|
|
10
55
|
export default _default;
|
|
11
56
|
//# sourceMappingURL=ExpoIapModule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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"}
|
package/build/ExpoIapModule.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { requireNativeModule, UnavailabilityError } from 'expo-modules-core';
|
|
1
|
+
import { requireNativeModule, UnavailabilityError, } from 'expo-modules-core';
|
|
2
2
|
import { installedFromOnside } from './onside';
|
|
3
3
|
import { getVegaIapModule, isVegaOS } from './vega';
|
|
4
4
|
const ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';
|
|
@@ -47,7 +47,10 @@ function getResolved() {
|
|
|
47
47
|
throw new UnavailabilityError('expo-iap', 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.');
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
module: requireNativeModule('ExpoIap'),
|
|
52
|
+
name: 'ExpoIap',
|
|
53
|
+
};
|
|
51
54
|
}
|
|
52
55
|
const expectedName = getExpectedModuleName();
|
|
53
56
|
if (!cached || cached.name !== expectedName) {
|
|
@@ -68,18 +71,19 @@ export const NATIVE_ERROR_CODES = new Proxy({}, {
|
|
|
68
71
|
get(target, prop) {
|
|
69
72
|
if (typeof prop === 'symbol')
|
|
70
73
|
return Reflect.get(target, prop);
|
|
71
|
-
|
|
74
|
+
const errorCodes = getResolved().module.ERROR_CODES || {};
|
|
75
|
+
return errorCodes[prop];
|
|
72
76
|
},
|
|
73
77
|
});
|
|
74
78
|
/**
|
|
75
|
-
* Returns the raw native module
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* "native state unsupported on Proxy" on New Architecture / Hermes.
|
|
79
|
+
* Returns the raw native module, not the Proxy. Use it for addListener: a JSI
|
|
80
|
+
* HostObject needs the real module as `this`, and a Proxy throws "native state
|
|
81
|
+
* unsupported on Proxy" on New Architecture / Hermes.
|
|
79
82
|
*/
|
|
80
83
|
export function getNativeModule() {
|
|
81
84
|
return getResolved().module;
|
|
82
85
|
}
|
|
86
|
+
// The Proxy forwards every member to the lazily resolved store module.
|
|
83
87
|
export default new Proxy({}, {
|
|
84
88
|
get(target, prop) {
|
|
85
89
|
if (typeof prop === 'symbol')
|
|
@@ -91,7 +95,7 @@ export default new Proxy({}, {
|
|
|
91
95
|
if (prop === 'USING_VEGA_SDK') {
|
|
92
96
|
return resolved.name === 'ExpoIapVega';
|
|
93
97
|
}
|
|
94
|
-
const value = resolved.module
|
|
98
|
+
const value = Reflect.get(resolved.module, prop);
|
|
95
99
|
if (value !== undefined || resolved.name !== 'ExpoIapOnside') {
|
|
96
100
|
return value;
|
|
97
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {requireNativeModule, UnavailabilityError} from 'expo-modules-core';\nimport {installedFromOnside} from './onside';\nimport {getVegaIapModule, isVegaOS} from './vega';\n\ntype NativeIapModuleName = 'ExpoIapVega' | 'ExpoIapOnside' | 'ExpoIap';\nconst ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';\n\nlet cached: {module: any; name: NativeIapModuleName} | null = null;\nlet onsideModuleUnavailable = false;\n\nfunction getResolved(): {module: any; name: NativeIapModuleName} {\n function shouldUseOnsideModule(): boolean {\n if (installedFromOnside === true) {\n return true;\n }\n\n if (typeof installedFromOnside !== 'string') {\n return false;\n }\n\n const normalized = installedFromOnside.trim().toLowerCase();\n return normalized === 'true' || normalized === ONSIDE_MARKETPLACE_ID;\n }\n\n function getExpectedModuleName(): NativeIapModuleName {\n if (isVegaOS()) {\n return 'ExpoIapVega';\n }\n\n return shouldUseOnsideModule() ? 'ExpoIapOnside' : 'ExpoIap';\n }\n\n function resolveNativeModule(): {\n module: any;\n name: NativeIapModuleName;\n } {\n if (isVegaOS()) {\n const vegaModule = getVegaIapModule();\n if (!vegaModule) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'Amazon Vega IAP module is unavailable. Install @amazon-devices/keplerscript-appstore-iap-lib in the Vega app target and build with the React Native for Vega kepler platform.',\n );\n }\n return {module: vegaModule, name: 'ExpoIapVega'};\n }\n\n if (shouldUseOnsideModule()) {\n if (onsideModuleUnavailable) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n try {\n return {\n module: requireNativeModule('ExpoIapOnside'),\n name: 'ExpoIapOnside',\n };\n } catch (error) {\n if (!isMissingModuleError(error, 'ExpoIapOnside')) {\n throw error;\n }\n onsideModuleUnavailable = true;\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n }\n\n return {module: requireNativeModule('ExpoIap'), name: 'ExpoIap'};\n }\n\n const expectedName = getExpectedModuleName();\n if (!cached || cached.name !== expectedName) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction isMissingModuleError(error: unknown, moduleName: string): boolean {\n if (error instanceof UnavailabilityError) {\n return true;\n }\n\n if (error instanceof Error) {\n return error.message.includes(`Cannot find native module '${moduleName}'`);\n }\n\n return false;\n}\n\nexport const NATIVE_ERROR_CODES: Record<string, unknown> = new Proxy(\n {} as Record<string, unknown>,\n {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n return (getResolved().module.ERROR_CODES || {})[prop as string];\n },\n },\n);\n\n/**\n * Returns the raw native module (not wrapped in a Proxy).\n * Use this for EventEmitter / addListener calls — JSI HostObjects\n * require the real native module as `this`; a Proxy triggers\n * \"native state unsupported on Proxy\" on New Architecture / Hermes.\n */\nexport function getNativeModule() {\n return getResolved().module;\n}\n\nexport default new Proxy({} as any, {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const resolved = getResolved();\n if (prop === 'USING_ONSIDE_SDK') {\n return resolved.name === 'ExpoIapOnside';\n }\n if (prop === 'USING_VEGA_SDK') {\n return resolved.name === 'ExpoIapVega';\n }\n\n const value = resolved.module[prop];\n if (value !== undefined || resolved.name !== 'ExpoIapOnside') {\n return value;\n }\n\n return () => {\n throw new UnavailabilityError(\n 'expo-iap',\n `The Onside marketplace does not support ${String(prop)}. The call was not routed through Apple StoreKit.`,\n );\n };\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n requireNativeModule,\n UnavailabilityError,\n type EventSubscription,\n} from 'expo-modules-core';\nimport {installedFromOnside} from './onside';\nimport type {\n BillingProgramAndroid,\n BillingProgramReportingDetailsAndroid,\n DeveloperBillingTypeAndroid,\n Mutation,\n MutationField,\n ProductQueryType,\n PurchaseInput,\n PurchaseOptions,\n PurchaseUpdatedListenerOptions,\n Query,\n QueryField,\n SubscriptionStatusIOS,\n} from './types';\nimport {getVegaIapModule, isVegaOS} from './vega';\n\ntype NativeIapModuleName = 'ExpoIapVega' | 'ExpoIapOnside' | 'ExpoIap';\nconst ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';\n\n/** Members read from the raw module; every store module provides them. */\ntype NativeEventModule = {\n ERROR_CODES?: Record<string, unknown>;\n addListener<T>(\n eventName: string,\n listener: (payload: T) => void,\n ): EventSubscription | undefined;\n removeListener?<T>(eventName: string, listener: (payload: T) => void): void;\n setPurchaseUpdatedListenerOptions?(\n options?: PurchaseUpdatedListenerOptions | null,\n ): Promise<void>;\n};\n\ntype QueryFields<K extends keyof Query> = {[P in K]: QueryField<P>};\ntype MutationFields<K extends keyof Mutation> = {[P in K]: MutationField<P>};\n\n/**\n * Native surface behind the default export. Results the wrappers decode stay\n * `unknown`; the rest match the generated operation signatures.\n */\nexport type ExpoIapNativeModule = NativeEventModule &\n QueryFields<\n | 'canPresentExternalPurchaseNoticeIOS'\n | 'currentEntitlementIOS'\n | 'getActiveSubscriptions'\n | 'getAppTransactionIOS'\n | 'getBillingChoiceInfoAndroid'\n | 'getExternalPurchaseCustomLinkTokenIOS'\n | 'getPromotedProductIOS'\n | 'getReceiptDataIOS'\n | 'getTransactionJwsIOS'\n | 'hasActiveSubscriptions'\n | 'isEligibleForExternalPurchaseCustomLinkIOS'\n | 'isEligibleForIntroOfferIOS'\n | 'isTransactionVerifiedIOS'\n | 'latestTransactionIOS'\n > &\n MutationFields<\n | 'beginRefundRequestIOS'\n | 'clearTransactionIOS'\n | 'endConnection'\n | 'initConnection'\n | 'isBillingProgramAvailableAndroid'\n | 'launchExternalLinkAndroid'\n | 'openRedeemOfferCodeAndroid'\n | 'presentCodeRedemptionSheetIOS'\n | 'presentExternalPurchaseLinkIOS'\n | 'presentExternalPurchaseNoticeSheetIOS'\n | 'showBillingProgramInformationDialogAndroid'\n | 'showExternalPurchaseCustomLinkNoticeIOS'\n | 'showInAppMessagesAndroid'\n | 'syncIOS'\n | 'verifyPurchase'\n | 'verifyPurchaseWithProvider'\n > & {\n USING_ONSIDE_SDK: boolean;\n USING_VEGA_SDK: boolean;\n fetchProducts(request: {\n skus: string[];\n type: ProductQueryType;\n }): Promise<unknown[]>;\n fetchProducts(type: ProductQueryType, skus: string[]): Promise<unknown[]>;\n getAvailableItems(\n alsoPublishToEventListenerIOS: boolean,\n onlyIncludeActiveItemsIOS: boolean,\n ): Promise<unknown>;\n getAvailableItems(options: PurchaseOptions): Promise<unknown>;\n getAllTransactionsIOS(): Promise<unknown>;\n getPendingTransactionsIOS(): Promise<unknown>;\n showManageSubscriptionsIOS(): Promise<unknown>;\n requestPurchase(request: object): Promise<unknown>;\n finishTransaction(\n purchase: PurchaseInput,\n isConsumable: boolean | null,\n ): Promise<boolean>;\n acknowledgePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n consumePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n createBillingProgramReportingDetailsAndroid(\n program: BillingProgramAndroid,\n developerBillingType: DeveloperBillingTypeAndroid | null,\n ): Promise<BillingProgramReportingDetailsAndroid>;\n subscriptionStatusIOS(sku: string): Promise<SubscriptionStatusIOS[] | null>;\n requestReceiptRefreshIOS(): Promise<string>;\n deepLinkToSubscriptionsAndroid?(options: {\n skuAndroid?: string;\n packageNameAndroid?: string;\n }): Promise<void> | void;\n getStorefront?(): Promise<string> | string;\n restorePurchases?(): Promise<boolean>;\n };\n\ntype ResolvedNativeModule = {\n module: NativeEventModule;\n name: NativeIapModuleName;\n};\n\nlet cached: ResolvedNativeModule | null = null;\nlet onsideModuleUnavailable = false;\n\nfunction getResolved(): ResolvedNativeModule {\n function shouldUseOnsideModule(): boolean {\n if (installedFromOnside === true) {\n return true;\n }\n\n if (typeof installedFromOnside !== 'string') {\n return false;\n }\n\n const normalized = installedFromOnside.trim().toLowerCase();\n return normalized === 'true' || normalized === ONSIDE_MARKETPLACE_ID;\n }\n\n function getExpectedModuleName(): NativeIapModuleName {\n if (isVegaOS()) {\n return 'ExpoIapVega';\n }\n\n return shouldUseOnsideModule() ? 'ExpoIapOnside' : 'ExpoIap';\n }\n\n function resolveNativeModule(): ResolvedNativeModule {\n if (isVegaOS()) {\n const vegaModule = getVegaIapModule();\n if (!vegaModule) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'Amazon Vega IAP module is unavailable. Install @amazon-devices/keplerscript-appstore-iap-lib in the Vega app target and build with the React Native for Vega kepler platform.',\n );\n }\n return {module: vegaModule, name: 'ExpoIapVega'};\n }\n\n if (shouldUseOnsideModule()) {\n if (onsideModuleUnavailable) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n try {\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIapOnside'),\n name: 'ExpoIapOnside',\n };\n } catch (error) {\n if (!isMissingModuleError(error, 'ExpoIapOnside')) {\n throw error;\n }\n onsideModuleUnavailable = true;\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n }\n\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIap'),\n name: 'ExpoIap',\n };\n }\n\n const expectedName = getExpectedModuleName();\n if (!cached || cached.name !== expectedName) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction isMissingModuleError(error: unknown, moduleName: string): boolean {\n if (error instanceof UnavailabilityError) {\n return true;\n }\n\n if (error instanceof Error) {\n return error.message.includes(`Cannot find native module '${moduleName}'`);\n }\n\n return false;\n}\n\nexport const NATIVE_ERROR_CODES: Record<string, unknown> = new Proxy(\n {} as Record<string, unknown>,\n {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const errorCodes: Record<string, unknown> =\n getResolved().module.ERROR_CODES || {};\n return errorCodes[prop];\n },\n },\n);\n\n/**\n * Returns the raw native module, not the Proxy. Use it for addListener: a JSI\n * HostObject needs the real module as `this`, and a Proxy throws \"native state\n * unsupported on Proxy\" on New Architecture / Hermes.\n */\nexport function getNativeModule(): NativeEventModule {\n return getResolved().module;\n}\n\n// The Proxy forwards every member to the lazily resolved store module.\nexport default new Proxy({} as ExpoIapNativeModule, {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const resolved = getResolved();\n if (prop === 'USING_ONSIDE_SDK') {\n return resolved.name === 'ExpoIapOnside';\n }\n if (prop === 'USING_VEGA_SDK') {\n return resolved.name === 'ExpoIapVega';\n }\n\n const value: unknown = Reflect.get(resolved.module, prop);\n if (value !== undefined || resolved.name !== 'ExpoIapOnside') {\n return value;\n }\n\n return () => {\n throw new UnavailabilityError(\n 'expo-iap',\n `The Onside marketplace does not support ${String(\n prop,\n )}. The call was not routed through Apple StoreKit.`,\n );\n };\n },\n});\n"]}
|