expo-iap 4.2.8 → 4.3.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.
Files changed (42) hide show
  1. package/CLAUDE.md +8 -8
  2. package/CONTRIBUTING.md +2 -2
  3. package/README.md +13 -14
  4. package/android/build.gradle +36 -18
  5. package/android/openiap-android-sdk.gradle +30 -0
  6. package/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +7 -9
  7. package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +3 -1
  8. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +12 -11
  9. package/android/src/main/java/expo/modules/iap/PromiseUtils.kt +2 -3
  10. package/build/ExpoIapModule.d.ts.map +1 -1
  11. package/build/ExpoIapModule.js +53 -14
  12. package/build/ExpoIapModule.js.map +1 -1
  13. package/build/index.d.ts +18 -18
  14. package/build/index.d.ts.map +1 -1
  15. package/build/index.js +80 -60
  16. package/build/index.js.map +1 -1
  17. package/build/modules/android.d.ts +8 -8
  18. package/build/modules/android.js +8 -8
  19. package/build/modules/android.js.map +1 -1
  20. package/build/modules/ios.d.ts +24 -24
  21. package/build/modules/ios.js +25 -25
  22. package/build/modules/ios.js.map +1 -1
  23. package/build/types.d.ts +47 -47
  24. package/build/types.js.map +1 -1
  25. package/build/useIAP.d.ts +1 -1
  26. package/build/useIAP.js +11 -11
  27. package/build/useIAP.js.map +1 -1
  28. package/bun.lock +30 -529
  29. package/ios/ExpoIapModule.swift +22 -12
  30. package/ios/onside/OnsideIapModule.swift +128 -41
  31. package/openiap-versions.json +2 -2
  32. package/package.json +1 -1
  33. package/plugin/build/withLocalOpenIAP.js +45 -9
  34. package/plugin/src/withLocalOpenIAP.ts +94 -14
  35. package/plugin/tsconfig.tsbuildinfo +1 -1
  36. package/scripts/test-coverage.sh +5 -1
  37. package/src/ExpoIapModule.ts +60 -19
  38. package/src/index.ts +99 -73
  39. package/src/modules/android.ts +8 -8
  40. package/src/modules/ios.ts +25 -25
  41. package/src/types.ts +47 -47
  42. package/src/useIAP.ts +11 -11
package/CLAUDE.md CHANGED
@@ -79,11 +79,11 @@ Before committing any changes:
79
79
 
80
80
  - **ID fields**: Use `Id` instead of `ID` (e.g., `productId`, `transactionId`, not `productID`, `transactionID`)
81
81
  - **Consistent naming**: This applies to functions, types, and file names
82
- - **Deprecation**: Fields without platform suffixes will be removed in v2.9.0
82
+ - **Deprecation**: Fields without platform suffixes are legacy and should only be removed in a planned major release.
83
83
 
84
84
  ### Type System
85
85
 
86
- For complete type definitions and documentation, see: <https://www.openiap.dev/docs/types>
86
+ For complete type definitions and documentation, see: <https://openiap.dev/docs/types>
87
87
 
88
88
  The library follows the OpenIAP type specifications with platform-specific extensions using iOS/Android suffixes.
89
89
 
@@ -109,7 +109,7 @@ The library follows the OpenIAP type specifications with platform-specific exten
109
109
  ### API Method Naming
110
110
 
111
111
  - Functions that depend on event results should use `request` prefix (e.g., `requestPurchase`)
112
- - Follow OpenIAP terminology: <https://www.openiap.dev/docs/apis#terminology>
112
+ - Follow OpenIAP terminology: <https://openiap.dev/docs/apis#terminology>
113
113
  - Do not use generic prefixes like `get`, `find` - refer to the official terminology
114
114
 
115
115
  ## IAP-Specific Guidelines
@@ -118,10 +118,10 @@ The library follows the OpenIAP type specifications with platform-specific exten
118
118
 
119
119
  All implementations must follow the OpenIAP specification:
120
120
 
121
- - **APIs**: <https://www.openiap.dev/docs/apis>
122
- - **Types**: <https://www.openiap.dev/docs/types>
123
- - **Events**: <https://www.openiap.dev/docs/events>
124
- - **Errors**: <https://www.openiap.dev/docs/errors>
121
+ - **APIs**: <https://openiap.dev/docs/apis>
122
+ - **Types**: <https://openiap.dev/docs/types>
123
+ - **Events**: <https://openiap.dev/docs/events>
124
+ - **Errors**: <https://openiap.dev/docs/errors>
125
125
 
126
126
  ### Feature Development Process
127
127
 
@@ -251,7 +251,7 @@ const {requestPurchase} = useIAP({
251
251
 
252
252
  For complete error handling documentation, see:
253
253
 
254
- - [Error Codes Reference](https://www.openiap.dev/docs/errors)
254
+ - [Error Codes Reference](https://openiap.dev/docs/errors)
255
255
  - [Error Handling Guide](https://docs.expo-iap.dev/docs/guides/error-handling)
256
256
 
257
257
  ## Documentation Guidelines
package/CONTRIBUTING.md CHANGED
@@ -446,8 +446,8 @@ We welcome feature requests! Please:
446
446
 
447
447
  ## 📚 Additional Resources
448
448
 
449
- - [Documentation Site](https://hyochan.github.io/expo-iap)
450
- - [API Reference](https://hyochan.github.io/expo-iap/docs/api/use-iap)
449
+ - [Documentation Site](https://openiap.dev/docs/setup/expo)
450
+ - [API Reference](https://openiap.dev/docs/apis)
451
451
  - [Example App](./example)
452
452
 
453
453
  Thank you for contributing to expo-iap! 🎉
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Expo IAP
2
2
 
3
3
  <div align="center">
4
- <img src="https://hyochan.github.io/expo-iap/img/icon.png" alt="Expo IAP Logo" width="150" />
4
+ <img src="https://openiap.dev/frameworks/expo.svg" alt="Expo IAP Logo" width="150" />
5
5
 
6
6
  [![Version](http://img.shields.io/npm/v/expo-iap.svg?style=flat-square)](https://npmjs.org/package/expo-iap) [![Download](http://img.shields.io/npm/dm/expo-iap.svg?style=flat-square)](https://npmjs.org/package/expo-iap) [![OpenIAP](https://img.shields.io/badge/OpenIAP-Compliant-green?style=flat-square)](https://openiap.dev) [![CI](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml/badge.svg)](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/hyodotdev/openiap/graph/badge.svg?token=47VMTY5NyM)](https://codecov.io/gh/hyodotdev/openiap) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhyochan%2Fexpo-iap.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhyochan%2Fexpo-iap?ref=badge_shield&issueType=license)
7
7
 
8
8
  Expo IAP is a powerful in-app purchase solution for Expo and React Native applications that conforms to the Open IAP specification. 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
- If you're shipping an app with expo-iap, we’d love to hear about it—please share your product and feedback in [Who's using Expo IAP?](https://github.com/hyochan/expo-iap/discussions/143). Community stories help us keep improving the ecosystem.
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
 
12
12
  <a href="https://openiap.dev"><img src="https://raw.githubusercontent.com/hyodotdev/openiap/main/logo.png" alt="Open IAP" height="40" /></a>
13
13
 
@@ -30,19 +30,19 @@ If you're shipping an app with expo-iap, we’d love to hear about it—please s
30
30
 
31
31
  ## 📚 Documentation
32
32
 
33
- **[📖 Visit our comprehensive documentation site →](https://hyochan.github.io/expo-iap)**
33
+ **[📖 Visit our comprehensive documentation site →](https://openiap.dev/docs/setup/expo)**
34
34
 
35
35
  ## Using with AI Assistants
36
36
 
37
37
  expo-iap provides AI-friendly documentation for Cursor, GitHub Copilot, Claude, and ChatGPT.
38
38
 
39
- **[📖 AI Assistants Guide →](https://hyochan.github.io/expo-iap/guides/ai-assistants)**
39
+ **[📖 AI Assistants Guide →](https://openiap.dev/docs/guides/ai-assistants)**
40
40
 
41
41
  Quick links:
42
42
 
43
- - [llms.txt](https://hyochan.github.io/expo-iap/llms.txt) - Quick reference
44
- - [llms-full.txt](https://hyochan.github.io/expo-iap/llms-full.txt) - Full API reference
45
- - [Onside Integration](https://hyochan.github.io/expo-iap/guides/onside-integration) - Using Onside marketplace payments on iOS
43
+ - [llms.txt](https://openiap.dev/llms.txt) - Quick reference
44
+ - [llms-full.txt](https://openiap.dev/llms-full.txt) - Full API reference
45
+ - [Onside Integration](https://openiap.dev/docs/features/alternative-marketplace/onside) - Using Onside marketplace payments on iOS
46
46
 
47
47
  ## Notice
48
48
 
@@ -53,8 +53,7 @@ The `expo-iap` module has been migrated from [react-native-iap](https://github.c
53
53
 
54
54
  Both libraries will continue to be maintained in parallel going forward.
55
55
 
56
- 📖 See the [Future Roadmap and Discussion](https://github.com/hyochan/react-native-iap/discussions/2754) for more details.
57
- 👉 Stay updated via the [Current Project Status comment](https://github.com/hyochan/react-native-iap/discussions/2754#discussioncomment-10510249).
56
+ 📖 See the [OpenIAP discussions](https://github.com/hyodotdev/openiap/discussions) for roadmap and project status updates.
58
57
 
59
58
  ## Installation
60
59
 
@@ -62,7 +61,7 @@ Both libraries will continue to be maintained in parallel going forward.
62
61
  npx expo install expo-iap
63
62
  ```
64
63
 
65
- For platform-specific configuration (Android Kotlin version, iOS deployment target, etc.), see the [Installation Guide](https://hyochan.github.io/expo-iap/getting-started/installation#important-for-expo-managed-workflow).
64
+ For platform-specific configuration (Android Kotlin version, iOS deployment target, etc.), see the [Installation Guide](https://openiap.dev/docs/setup/expo#installation).
66
65
 
67
66
  ## Contributing
68
67
 
@@ -74,7 +73,7 @@ We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md)
74
73
  - Code style and conventions
75
74
  - Submitting pull requests
76
75
 
77
- For detailed usage examples and error handling, see the [documentation](https://hyochan.github.io/expo-iap).
76
+ For detailed usage examples and error handling, see the [documentation](https://openiap.dev/docs/setup/expo).
78
77
 
79
78
  > Sharing your thoughts—any feedback would be greatly appreciated!
80
79
 
@@ -107,7 +106,7 @@ For bug reports, please [open an issue](https://github.com/hyodotdev/openiap/iss
107
106
 
108
107
  <a href="https://meta.com">
109
108
  <div style="display: inline-flex; flex-direction: column; align-items: center; gap: 0.25rem; padding: 0.75rem 1rem; border-radius: 12px; background: rgba(212, 165, 116, 0.12);">
110
- <img alt="Meta" src="https://www.openiap.dev/meta.svg" style="width: 120px;" />
109
+ <img alt="Meta" src="https://openiap.dev/meta.svg" style="width: 120px;" />
111
110
  <span style="font-size: 0.85rem; font-weight: 600; color: rgb(107, 78, 61); text-align: center; width: 100%;">Meta</span>
112
111
  </div>
113
112
  </a>
@@ -116,9 +115,9 @@ For bug reports, please [open an issue](https://github.com/hyodotdev/openiap/iss
116
115
 
117
116
  <div style="display: flex; align-items:center; gap: 10px;">
118
117
  <a href="https://namiml.com" style="opacity: 50%">
119
- <img src="https://github.com/hyochan/react-native-iap/assets/27461460/89d71f61-bb73-400a-83bd-fe0f96eb726e" alt="Nami ML" width="140"/>
118
+ <img src="https://openiap.dev/sponsors/nami.webp" alt="Nami ML" width="140"/>
120
119
  </a>
121
120
  <a href="https://www.courier.com/?utm_source=react-native-iap&utm_campaign=osssponsors" style="opacity: 50%;">
122
- <img width="80" alt="courier_dot_com" src="https://github.com/user-attachments/assets/319d8966-6839-498d-8ead-ce8cc72c3bca" />
121
+ <img width="80" alt="courier_dot_com" src="https://openiap.dev/sponsors/courier.webp" />
123
122
  </a>
124
123
  </div>
@@ -1,10 +1,26 @@
1
1
  import groovy.json.JsonSlurper
2
+ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2
3
 
3
4
  apply plugin: 'com.android.library'
4
5
  apply plugin: 'kotlin-android'
5
6
 
6
7
  group = 'expo.modules.iap'
7
- version = '0.1.0'
8
+
9
+ def resolvePackageJsonFile() {
10
+ def packageJsonFile = new File(projectDir.parentFile, 'package.json')
11
+ if (!packageJsonFile.isFile()) {
12
+ throw new GradleException("expo-iap: Unable to locate package.json")
13
+ }
14
+ return packageJsonFile
15
+ }
16
+
17
+ def expoIapPackageJson = new JsonSlurper().parse(resolvePackageJsonFile())
18
+ def expoIapPackageVersion = (expoIapPackageJson instanceof Map) ? expoIapPackageJson.version : null
19
+ if (!(expoIapPackageVersion instanceof String) || !expoIapPackageVersion.trim()) {
20
+ throw new GradleException("expo-iap: 'version' missing or invalid in package.json")
21
+ }
22
+ expoIapPackageVersion = expoIapPackageVersion.trim()
23
+ version = expoIapPackageVersion
8
24
 
9
25
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
26
  apply from: expoModulesCorePlugin
@@ -32,6 +48,7 @@ if (!(googleVersion instanceof String) || !googleVersion.trim()) {
32
48
  throw new GradleException("expo-iap: 'google' version missing or invalid in openiap-versions.json")
33
49
  }
34
50
  def googleVersionString = googleVersion.trim()
51
+ apply from: project.file('openiap-android-sdk.gradle')
35
52
 
36
53
  // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
37
54
  // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
@@ -40,26 +57,24 @@ def useManagedAndroidSdkVersions = false
40
57
  if (useManagedAndroidSdkVersions) {
41
58
  useDefaultAndroidSdkVersions()
42
59
  } else {
43
- buildscript {
44
- // Simple helper that allows the root project to override versions declared by this library.
45
- ext.safeExtGet = { prop, fallback ->
46
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
47
- }
48
- }
60
+ def openIapCompileSdkVersion = openIapResolveAndroidSdkVersion('compileSdkVersion', 'compileSdk', 35)
61
+ def openIapMinSdkVersion = openIapResolveAndroidSdkVersion('minSdkVersion', 'minSdk', 23)
62
+ def openIapTargetSdkVersion = openIapResolveAndroidSdkVersion('targetSdkVersion', 'compileSdk', 35)
63
+
49
64
  project.android {
50
- compileSdkVersion safeExtGet("compileSdkVersion", 34)
65
+ compileSdk = openIapCompileSdkVersion
51
66
  defaultConfig {
52
- minSdkVersion safeExtGet("minSdkVersion", 21)
53
- targetSdkVersion safeExtGet("targetSdkVersion", 34)
67
+ minSdk = openIapMinSdkVersion
68
+ targetSdk = openIapTargetSdkVersion
54
69
  }
55
70
  }
56
71
  }
57
72
 
58
73
  android {
59
- namespace "expo.modules.iap"
74
+ namespace = "expo.modules.iap"
60
75
  defaultConfig {
61
- versionCode 1
62
- versionName "0.1.0"
76
+ versionCode = 1
77
+ versionName = expoIapPackageVersion
63
78
  // When using local openiap-google with flavors, select the appropriate flavor
64
79
  // Read horizonEnabled from gradle.properties, default to play
65
80
  def horizonEnabled = project.findProperty('horizonEnabled')?.toBoolean() ?: false
@@ -67,11 +82,7 @@ android {
67
82
  missingDimensionStrategy "platform", flavor
68
83
  }
69
84
  lintOptions {
70
- abortOnError false
71
- }
72
- kotlinOptions {
73
- jvmTarget = "17"
74
- freeCompilerArgs += ["-Xskip-metadata-version-check"]
85
+ abortOnError = false
75
86
  }
76
87
  compileOptions {
77
88
  sourceCompatibility JavaVersion.VERSION_17
@@ -79,6 +90,13 @@ android {
79
90
  }
80
91
  }
81
92
 
93
+ kotlin {
94
+ compilerOptions {
95
+ jvmTarget.set(JvmTarget.JVM_17)
96
+ freeCompilerArgs.add("-Xskip-metadata-version-check")
97
+ }
98
+ }
99
+
82
100
  dependencies {
83
101
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7"
84
102
 
@@ -0,0 +1,30 @@
1
+ ext.openIapReadGoogleAndroidSdkVersion = { String propertyName ->
2
+ File current = projectDir
3
+ while (current != null) {
4
+ File candidate = new File(current, 'packages/google/openiap/build.gradle.kts')
5
+ if (candidate.isFile()) {
6
+ def matcher = candidate.text =~ /(?m)^\s*${propertyName}\s*=\s*(\d+).*$/
7
+ return matcher.find() ? matcher.group(1).toInteger() : null
8
+ }
9
+ current = current.parentFile
10
+ }
11
+ return null
12
+ }
13
+
14
+ ext.openIapToIntegerVersion = { Object value, String label ->
15
+ if (value instanceof Number) {
16
+ return value.toInteger()
17
+ }
18
+ if (value instanceof CharSequence && value.toString() ==~ /\d+/) {
19
+ return value.toString().toInteger()
20
+ }
21
+ throw new GradleException("expo-iap: ${label} must be an integer, got ${value}")
22
+ }
23
+
24
+ ext.openIapResolveAndroidSdkVersion = { String extName, String googlePropertyName, int fallback ->
25
+ if (rootProject.ext.has(extName)) {
26
+ return openIapToIntegerVersion(rootProject.ext.get(extName), extName)
27
+ }
28
+ def googleValue = openIapReadGoogleAndroidSdkVersion(googlePropertyName)
29
+ return googleValue ?: fallback
30
+ }
@@ -1,6 +1,5 @@
1
1
  package expo.modules.iap
2
2
 
3
- import android.util.Log
4
3
  import dev.hyo.openiap.AndroidSubscriptionOfferInput
5
4
  import dev.hyo.openiap.OpenIapError
6
5
  import dev.hyo.openiap.OpenIapModule
@@ -15,7 +14,6 @@ import java.util.Locale
15
14
  import java.util.concurrent.ConcurrentLinkedQueue
16
15
 
17
16
  object ExpoIapHelper {
18
- private const val TAG = "ExpoIapHelper"
19
17
  private const val MAX_BUFFERED_EVENTS = 200
20
18
 
21
19
  fun emitOrQueue(
@@ -67,7 +65,7 @@ object ExpoIapHelper {
67
65
  val flat = mutableMapOf<String, Any?>()
68
66
  // Carry over top-level fields like type, useAlternativeBilling
69
67
  for ((k, v) in params) {
70
- if (k is String && k != "request") flat[k] = v
68
+ if (k != "request") flat[k] = v
71
69
  }
72
70
  // Overlay platform-specific fields
73
71
  for ((k, v) in nested) {
@@ -205,7 +203,7 @@ object ExpoIapHelper {
205
203
  runCatching {
206
204
  emitOrQueue(module, scope, connectionReady, pendingEvents, eventName, payload)
207
205
  }.onFailure { error ->
208
- android.util.Log.e(TAG, "Failed to buffer/send $logTag", error)
206
+ ExpoIapLog.failure("buffer/send $logTag", error)
209
207
  val errorPayload =
210
208
  mapOf(
211
209
  "code" to fallbackErrorCode,
@@ -213,7 +211,7 @@ object ExpoIapHelper {
213
211
  )
214
212
  runCatching {
215
213
  emitOrQueue(module, scope, connectionReady, pendingEvents, eventPurchaseError, errorPayload)
216
- }.onFailure { android.util.Log.e(TAG, "Failed to send error event", it) }
214
+ }.onFailure { ExpoIapLog.failure("send error event", it) }
217
215
  }
218
216
  }
219
217
 
@@ -240,7 +238,7 @@ object ExpoIapHelper {
240
238
  p.toJson(),
241
239
  )
242
240
  }.onFailure { error ->
243
- android.util.Log.e(TAG, "Failed to buffer/send PURCHASE_UPDATED", error)
241
+ ExpoIapLog.failure("buffer/send PURCHASE_UPDATED", error)
244
242
  // Emit as purchase error so user knows something went wrong
245
243
  val errorPayload =
246
244
  mapOf(
@@ -256,7 +254,7 @@ object ExpoIapHelper {
256
254
  eventPurchaseError,
257
255
  errorPayload,
258
256
  )
259
- }.onFailure { android.util.Log.e(TAG, "Failed to send error event", it) }
257
+ }.onFailure { ExpoIapLog.failure("send error event", it) }
260
258
  }
261
259
  }
262
260
  openIap.addPurchaseErrorListener { e ->
@@ -271,7 +269,7 @@ object ExpoIapHelper {
271
269
  errorJson,
272
270
  )
273
271
  }.onFailure { error ->
274
- android.util.Log.e(TAG, "Failed to buffer/send PURCHASE_ERROR", error)
272
+ ExpoIapLog.failure("buffer/send PURCHASE_ERROR", error)
275
273
  // Critical: if we can't emit the original error, at least try to emit a generic one
276
274
  val fallbackPayload =
277
275
  mapOf(
@@ -287,7 +285,7 @@ object ExpoIapHelper {
287
285
  eventPurchaseError,
288
286
  fallbackPayload,
289
287
  )
290
- }.onFailure { android.util.Log.e(TAG, "Failed to send fallback error event", it) }
288
+ }.onFailure { ExpoIapLog.failure("send fallback error event", it) }
291
289
  }
292
290
  // Also reject any pending purchase promises to match iOS behavior
293
291
  val errorCode = errorJson["code"] as? String ?: OpenIapError.PurchaseFailed.CODE
@@ -33,7 +33,9 @@ internal object ExpoIapLog {
33
33
  }
34
34
 
35
35
  fun debug(message: String) {
36
- Log.d(TAG, message)
36
+ if (BuildConfig.DEBUG || Log.isLoggable(TAG, Log.DEBUG)) {
37
+ Log.d(TAG, message)
38
+ }
37
39
  }
38
40
 
39
41
  private fun stringify(value: Any?): String {
@@ -1,7 +1,6 @@
1
1
  package expo.modules.iap
2
2
 
3
3
  import android.content.Context
4
- import android.util.Log
5
4
  import dev.hyo.openiap.AndroidSubscriptionOfferInput
6
5
  import dev.hyo.openiap.DeepLinkOptions
7
6
  import dev.hyo.openiap.FetchProductsResultProducts
@@ -143,7 +142,7 @@ class ExpoIapModule : Module() {
143
142
  val ev = pendingEvents.poll() ?: break
144
143
  // Already on main dispatcher here; emit directly
145
144
  runCatching { sendEvent(ev.first, ev.second) }
146
- .onFailure { Log.e(TAG, "Failed to flush buffered event: ${ev.first}", it) }
145
+ .onFailure { ExpoIapLog.failure("flush buffered event ${ev.first}", it) }
147
146
  }
148
147
 
149
148
  ExpoIapLog.result("initConnection", true)
@@ -354,11 +353,7 @@ class ExpoIapModule : Module() {
354
353
  errorMap,
355
354
  )
356
355
  }.onFailure { ex ->
357
- Log.e(
358
- TAG,
359
- "Failed to send PURCHASE_ERROR event (requestPurchase)",
360
- ex,
361
- )
356
+ ExpoIapLog.failure("send PURCHASE_ERROR event requestPurchase", ex)
362
357
  }
363
358
  ExpoIapHelper.rejectPurchasePromises(
364
359
  errorCode,
@@ -391,7 +386,10 @@ class ExpoIapModule : Module() {
391
386
  try {
392
387
  openIap.consumePurchaseAndroid(token)
393
388
  val response = mapOf("responseCode" to 0, "purchaseToken" to token)
394
- ExpoIapLog.result("consumePurchaseAndroid", response)
389
+ ExpoIapLog.result(
390
+ "consumePurchaseAndroid",
391
+ response,
392
+ )
395
393
  promise.resolve(response)
396
394
  } catch (e: Exception) {
397
395
  ExpoIapLog.failure("consumePurchaseAndroid", e)
@@ -423,7 +421,7 @@ class ExpoIapModule : Module() {
423
421
  val activity =
424
422
  runCatching { currentActivity }
425
423
  .onFailure {
426
- Log.e(TAG, "showAlternativeBillingDialogAndroid: Activity missing", it)
424
+ ExpoIapLog.failure("showAlternativeBillingDialogAndroid activity", it)
427
425
  }.getOrNull() ?: run {
428
426
  promise.reject(OpenIapError.ServiceUnavailable.CODE, "Activity not available", null)
429
427
  return@launch
@@ -587,7 +585,10 @@ class ExpoIapModule : Module() {
587
585
  "billingProgram" to program,
588
586
  "externalTransactionToken" to result.externalTransactionToken,
589
587
  )
590
- ExpoIapLog.result("createBillingProgramReportingDetailsAndroid", response)
588
+ ExpoIapLog.result(
589
+ "createBillingProgramReportingDetailsAndroid",
590
+ response,
591
+ )
591
592
  promise.resolve(response)
592
593
  } catch (e: Exception) {
593
594
  ExpoIapLog.failure("createBillingProgramReportingDetailsAndroid", e)
@@ -603,7 +604,7 @@ class ExpoIapModule : Module() {
603
604
  val activity =
604
605
  runCatching { currentActivity }
605
606
  .onFailure {
606
- Log.e(TAG, "launchExternalLinkAndroid: Activity missing", it)
607
+ ExpoIapLog.failure("launchExternalLinkAndroid activity", it)
607
608
  }.getOrNull() ?: run {
608
609
  promise.reject(OpenIapError.ServiceUnavailable.CODE, "Activity not available", null)
609
610
  return@launch
@@ -1,6 +1,5 @@
1
1
  package expo.modules.iap
2
2
 
3
- import android.util.Log
4
3
  import dev.hyo.openiap.OpenIapError
5
4
  import expo.modules.kotlin.Promise
6
5
 
@@ -54,7 +53,7 @@ fun Promise.safeResolve(value: Any?) {
54
53
  try {
55
54
  this.resolve(value)
56
55
  } catch (e: RuntimeException) {
57
- Log.d(PromiseUtils.TAG, "Already consumed ${e.message}")
56
+ ExpoIapLog.debug("Already consumed ${e.message}")
58
57
  }
59
58
  }
60
59
 
@@ -78,6 +77,6 @@ fun Promise.safeReject(
78
77
  try {
79
78
  this.reject(code, message, throwable)
80
79
  } catch (e: RuntimeException) {
81
- Log.d(PromiseUtils.TAG, "Already consumed ${e.message}")
80
+ ExpoIapLog.debug("Already consumed ${e.message}")
82
81
  }
83
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAsDA,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAQtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,QAE9B;;AAED,wBAQG"}
1
+ {"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAsEA,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAQtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,QAE9B;;AAoBD,wBAeG"}
@@ -1,30 +1,47 @@
1
1
  import { requireNativeModule, UnavailabilityError } from 'expo-modules-core';
2
2
  import { installedFromOnside } from './onside';
3
+ const ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';
3
4
  let cached = null;
5
+ let expoIapFallback;
6
+ let onsideModuleUnavailable = false;
7
+ function isOnsideInstallation() {
8
+ if (installedFromOnside === true) {
9
+ return true;
10
+ }
11
+ if (typeof installedFromOnside !== 'string') {
12
+ return false;
13
+ }
14
+ const normalized = installedFromOnside.trim().toLowerCase();
15
+ return normalized === 'true' || normalized === ONSIDE_MARKETPLACE_ID;
16
+ }
17
+ function shouldUseOnsideModule() {
18
+ return isOnsideInstallation() && !onsideModuleUnavailable;
19
+ }
4
20
  function getResolved() {
5
- if (!cached) {
21
+ const expectedName = shouldUseOnsideModule()
22
+ ? 'ExpoIapOnside'
23
+ : 'ExpoIap';
24
+ if (!cached || cached.name !== expectedName) {
6
25
  cached = resolveNativeModule();
7
26
  }
8
27
  return cached;
9
28
  }
10
29
  function resolveNativeModule() {
11
- const candidates = ['ExpoIapOnside', 'ExpoIap'];
12
- for (const name of candidates) {
30
+ if (isOnsideInstallation()) {
13
31
  try {
14
- const module = requireNativeModule(name);
15
- if (name === 'ExpoIapOnside' && !installedFromOnside) {
16
- continue;
17
- }
18
- return { module, name };
32
+ return {
33
+ module: requireNativeModule('ExpoIapOnside'),
34
+ name: 'ExpoIapOnside',
35
+ };
19
36
  }
20
37
  catch (error) {
21
- if (name === 'ExpoIapOnside' && isMissingModuleError(error, name)) {
22
- continue;
38
+ if (!isMissingModuleError(error, 'ExpoIapOnside')) {
39
+ throw error;
23
40
  }
24
- throw error;
41
+ onsideModuleUnavailable = true;
25
42
  }
26
43
  }
27
- throw new UnavailabilityError('expo-iap', 'ExpoIap native module is unavailable');
44
+ return { module: requireNativeModule('ExpoIap'), name: 'ExpoIap' };
28
45
  }
29
46
  function isMissingModuleError(error, moduleName) {
30
47
  if (error instanceof UnavailabilityError) {
@@ -51,14 +68,36 @@ export const NATIVE_ERROR_CODES = new Proxy({}, {
51
68
  export function getNativeModule() {
52
69
  return getResolved().module;
53
70
  }
71
+ function getExpoIapFallbackModule() {
72
+ if (expoIapFallback !== undefined) {
73
+ return expoIapFallback;
74
+ }
75
+ try {
76
+ expoIapFallback = requireNativeModule('ExpoIap');
77
+ }
78
+ catch (error) {
79
+ if (isMissingModuleError(error, 'ExpoIap')) {
80
+ expoIapFallback = null;
81
+ }
82
+ else {
83
+ throw error;
84
+ }
85
+ }
86
+ return expoIapFallback;
87
+ }
54
88
  export default new Proxy({}, {
55
89
  get(target, prop) {
56
90
  if (typeof prop === 'symbol')
57
91
  return Reflect.get(target, prop);
92
+ const resolved = getResolved();
58
93
  if (prop === 'USING_ONSIDE_SDK') {
59
- return getResolved().name === 'ExpoIapOnside';
94
+ return resolved.name === 'ExpoIapOnside';
95
+ }
96
+ const value = resolved.module[prop];
97
+ if (value !== undefined || resolved.name !== 'ExpoIapOnside') {
98
+ return value;
60
99
  }
61
- return getResolved().module[prop];
100
+ return getExpoIapFallbackModule()?.[prop];
62
101
  },
63
102
  });
64
103
  //# sourceMappingURL=ExpoIapModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAE,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAC,mBAAmB,EAAC,MAAM,UAAU,CAAC;AAI7C,IAAI,MAAM,GAAoD,IAAI,CAAC;AAEnE,SAAS,WAAW;IAClB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACjC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB;IAI1B,MAAM,UAAU,GAA0B,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAEvE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI,KAAK,eAAe,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBACrD,SAAS;YACX,CAAC;YACD,OAAO,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,KAAK,eAAe,IAAI,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClE,SAAS;YACX,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,IAAI,mBAAmB,CAC3B,UAAU,EACV,sCAAsC,CACvC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAc,EAAE,UAAkB;IAC9D,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,UAAU,GAAG,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAA4B,IAAI,KAAK,CAClE,EAA6B,EAC7B;IACE,GAAG,CAAC,MAAM,EAAE,IAAI;QACd,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/D,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAc,CAAC,CAAC;IAClE,CAAC;CACF,CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC;AAC9B,CAAC;AAED,eAAe,IAAI,KAAK,CAAC,EAAS,EAAE;IAClC,GAAG,CAAC,MAAM,EAAE,IAAI;QACd,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/D,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAChC,OAAO,WAAW,EAAE,CAAC,IAAI,KAAK,eAAe,CAAC;QAChD,CAAC;QACD,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;CACF,CAAC,CAAC","sourcesContent":["import {requireNativeModule, UnavailabilityError} from 'expo-modules-core';\nimport {installedFromOnside} from './onside';\n\ntype NativeIapModuleName = 'ExpoIapOnside' | 'ExpoIap';\n\nlet cached: {module: any; name: NativeIapModuleName} | null = null;\n\nfunction getResolved(): {module: any; name: NativeIapModuleName} {\n if (!cached) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction resolveNativeModule(): {\n module: any;\n name: NativeIapModuleName;\n} {\n const candidates: NativeIapModuleName[] = ['ExpoIapOnside', 'ExpoIap'];\n\n for (const name of candidates) {\n try {\n const module = requireNativeModule(name);\n if (name === 'ExpoIapOnside' && !installedFromOnside) {\n continue;\n }\n return {module, name};\n } catch (error) {\n if (name === 'ExpoIapOnside' && isMissingModuleError(error, name)) {\n continue;\n }\n\n throw error;\n }\n }\n\n throw new UnavailabilityError(\n 'expo-iap',\n 'ExpoIap native module is unavailable',\n );\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 if (prop === 'USING_ONSIDE_SDK') {\n return getResolved().name === 'ExpoIapOnside';\n }\n return getResolved().module[prop];\n },\n});\n"]}
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';\n\ntype NativeIapModuleName = 'ExpoIapOnside' | 'ExpoIap';\nconst ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';\n\nlet cached: {module: any; name: NativeIapModuleName} | null = null;\nlet expoIapFallback: any | null | undefined;\nlet onsideModuleUnavailable = false;\n\nfunction isOnsideInstallation(): 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\nfunction shouldUseOnsideModule(): boolean {\n return isOnsideInstallation() && !onsideModuleUnavailable;\n}\n\nfunction getResolved(): {module: any; name: NativeIapModuleName} {\n const expectedName: NativeIapModuleName = shouldUseOnsideModule()\n ? 'ExpoIapOnside'\n : 'ExpoIap';\n if (!cached || cached.name !== expectedName) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction resolveNativeModule(): {\n module: any;\n name: NativeIapModuleName;\n} {\n if (isOnsideInstallation()) {\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 }\n }\n\n return {module: requireNativeModule('ExpoIap'), name: 'ExpoIap'};\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\nfunction getExpoIapFallbackModule(): any | null {\n if (expoIapFallback !== undefined) {\n return expoIapFallback;\n }\n\n try {\n expoIapFallback = requireNativeModule('ExpoIap');\n } catch (error) {\n if (isMissingModuleError(error, 'ExpoIap')) {\n expoIapFallback = null;\n } else {\n throw error;\n }\n }\n\n return expoIapFallback;\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\n const value = resolved.module[prop];\n if (value !== undefined || resolved.name !== 'ExpoIapOnside') {\n return value;\n }\n\n return getExpoIapFallbackModule()?.[prop];\n },\n});\n"]}