expo-iap 5.6.2 → 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/AGENTS.md +1 -1
- package/CONTRIBUTING.md +5 -5
- package/README.md +2 -2
- 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/index.kepler.d.ts +2 -2
- package/build/index.kepler.js +2 -2
- package/build/index.kepler.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 +5 -7
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +10 -16
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +12 -30
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +14 -37
- 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/types.d.ts +40 -40
- package/build/types.js.map +1 -1
- package/build/useIAP.d.ts +4 -7
- 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 +7 -24
- 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/openiap-versions.json +1 -1
- package/package.json +2 -2
- package/plugin/build/withIAP.d.ts +16 -5
- package/plugin/build/withIAP.js +194 -166
- package/plugin/build/withLocalOpenIAP.d.ts +15 -12
- package/plugin/build/withLocalOpenIAP.js +139 -113
- package/plugin/build/withVega.d.ts +7 -0
- package/plugin/build/withVega.js +43 -13
- package/plugin/src/expoConfig.augmentation.d.ts +22 -11
- package/plugin/src/withIAP.ts +251 -226
- package/plugin/src/withLocalOpenIAP.ts +221 -159
- package/plugin/src/withVega.ts +45 -13
- package/scripts/update-types.mjs +12 -8
- package/src/ExpoIapModule.ts +136 -18
- package/src/index.kepler.ts +2 -2
- package/src/index.ts +28 -65
- package/src/kit-api.ts +21 -46
- package/src/modules/android.ts +12 -27
- package/src/modules/ios.ts +22 -45
- package/src/onside/index.ts +7 -9
- package/src/types.ts +40 -40
- package/src/useIAP.ts +15 -39
- package/src/utils/debug.ts +13 -31
- package/src/utils/errorMapping.ts +4 -12
- package/src/utils/restorePurchases.ts +3 -4
package/AGENTS.md
CHANGED
|
@@ -84,7 +84,7 @@ For complete type definitions and documentation, see: <https://openiap.dev/docs/
|
|
|
84
84
|
|
|
85
85
|
The library follows the OpenIAP type specifications with platform-specific extensions using iOS/Android suffixes.
|
|
86
86
|
|
|
87
|
-
> **Important:** `src/types.ts` is generated from the OpenIAP schema. Never edit this file manually or commit hand-written changes. In this monorepo, run `cd specs/client && bun run generate` from the repository root so every platform copy stays synchronized. The Expo-local `bun run generate:types` command is only for a standalone checkout: it atomically refreshes the platform-ready file from the raw `
|
|
87
|
+
> **Important:** `src/types.ts` is generated from the OpenIAP schema. Never edit this file manually or commit hand-written changes. In this monorepo, run `cd specs/client && bun run generate` from the repository root so every platform copy stays synchronized. The Expo-local `bun run generate:types` command is only for a standalone checkout: it atomically refreshes the platform-ready file from the raw `openiap-client-protocol-${clientProtocol}` tag pinned by `openiap-versions.json` (or the version passed with `--tag`).
|
|
88
88
|
|
|
89
89
|
- Whenever you need Request/Params/Result types in the JS API surface (`src/index.ts`, hooks, modules, examples), import them directly from the generated `src/types.ts` (e.g., `MutationRequestPurchaseArgs`, `QueryFetchProductsArgs`). Bind exported functions with the generated `QueryField` / `MutationField` helpers so their signatures stay in lockstep with `types.ts` instead of redefining ad-hoc unions like `ProductTypeInput`.
|
|
90
90
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -214,28 +214,28 @@ For detailed code conventions, naming standards, and implementation guidelines,
|
|
|
214
214
|
|
|
215
215
|
### Updating OpenIAP Types
|
|
216
216
|
|
|
217
|
-
The generated TypeScript definitions in `src/types.ts` come from the platform-ready file committed in the [OpenIAP](https://github.com/hyodotdev/openiap) raw `
|
|
217
|
+
The generated TypeScript definitions in `src/types.ts` come from the platform-ready file committed in the [OpenIAP](https://github.com/hyodotdev/openiap) raw `openiap-client-protocol-${clientProtocol}` tag. Never edit this file by hand. When the schema changes or you need to pull newer published types:
|
|
218
218
|
|
|
219
219
|
- Run `bun run generate:types` to download the version pinned by `openiap-versions.json`, validate it, and atomically replace `src/types.ts`.
|
|
220
|
-
- To target a specific published
|
|
220
|
+
- To target a specific published Client Protocol version, pass it: `bun run generate:types --tag <version>` (the script resolves it to `openiap-client-protocol-<version>`).
|
|
221
221
|
- Commit the updated file alongside any related schema or documentation changes.
|
|
222
222
|
|
|
223
223
|
Always ensure the repository builds and tests succeed after regenerating the types.
|
|
224
224
|
|
|
225
225
|
## 🔢 OpenIAP Version Management
|
|
226
226
|
|
|
227
|
-
The native dependency and
|
|
227
|
+
The native dependency and Client Protocol versions are sourced from `openiap-versions.json` at the repository root:
|
|
228
228
|
|
|
229
229
|
- `apple` → iOS Pod dependency (`ios/ExpoIap.podspec`).
|
|
230
230
|
- `google` → Android artifact (`android/build.gradle`, Expo config plugin).
|
|
231
|
-
- `
|
|
231
|
+
- `clientProtocol` → published Client Protocol types release (`scripts/update-types.mjs`).
|
|
232
232
|
|
|
233
233
|
When bumping dependencies:
|
|
234
234
|
|
|
235
235
|
1. Update the relevant fields in `openiap-versions.json`.
|
|
236
236
|
2. For iOS changes, run `cd ios && pod install` (and commit the Pod.lock if required by the workflow).
|
|
237
237
|
3. For Android, re-run Gradle (`bun run android`) so the new artifact is pulled down.
|
|
238
|
-
4. When the `
|
|
238
|
+
4. When the `clientProtocol` value changes, run `bun run generate:types` to refresh `src/types.ts` from the matching raw `openiap-client-protocol-${clientProtocol}` tag.
|
|
239
239
|
5. Commit the updated JSON, regenerated files, and any resulting lockfile changes together.
|
|
240
240
|
|
|
241
241
|
If the JSON file is missing or malformed, build scripts (Gradle, Podspec, the type generator) will fail fast — fix the JSON rather than hard-coding version strings in multiple locations.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
[](https://npmjs.org/package/expo-iap) [](https://npmjs.org/package/expo-iap) [](https://openiap.dev) [](https://github.com/hyodotdev/openiap/actions/workflows/ci-expo-iap.yml?query=branch%3Amain) [](https://app.codecov.io/gh/hyodotdev/openiap/tree/main/libraries/expo-iap) [](https://app.fossa.com/projects/git%2Bgithub.com%2Fhyochan%2Fexpo-iap?ref=badge_shield&issueType=license)
|
|
7
7
|
|
|
8
|
-
Expo IAP is a powerful in-app purchase solution for Expo and React Native applications that conforms to the OpenIAP
|
|
8
|
+
Expo IAP is a powerful in-app purchase solution for Expo and React Native applications that conforms to the OpenIAP Client Protocol. It provides a unified API for handling in-app purchases across iOS and Android platforms with comprehensive error handling and modern TypeScript support.
|
|
9
9
|
|
|
10
10
|
If you're shipping an app with expo-iap, we’d love to hear about it—please share your product and feedback in [expo-iap Q&A Discussions](https://github.com/hyodotdev/openiap/discussions/categories/expo-iap). Community stories help us keep improving the ecosystem.
|
|
11
11
|
|
|
@@ -66,7 +66,7 @@ For detailed usage examples and error handling, see the [documentation](https://
|
|
|
66
66
|
|
|
67
67
|
<a href="https://openiap.dev"><img src="https://raw.githubusercontent.com/hyodotdev/openiap/main/logo.webp" alt="OpenIAP" height="50" /></a>
|
|
68
68
|
|
|
69
|
-
Expo IAP conforms to the **[OpenIAP
|
|
69
|
+
Expo IAP conforms to the **[OpenIAP Client Protocol](https://openiap.dev)** — an open, vendor-neutral interoperability standard for in-app purchases. OpenIAP provides:
|
|
70
70
|
|
|
71
71
|
- **Shared specification** — Common types, error codes, and purchase flows across all platforms
|
|
72
72
|
- **Generated type-safe bindings** — Swift, Kotlin, Dart, and GDScript from a single GraphQL schema
|
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
|
}
|