react-native-iap 15.2.4 → 15.3.1

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 (34) hide show
  1. package/README.md +29 -49
  2. package/android/build.gradle +92 -22
  3. package/android/gradle.properties +5 -1
  4. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +1 -1
  5. package/android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt +3 -1
  6. package/ios/HybridRnIap.swift +12 -6
  7. package/lib/module/index.js +73 -158
  8. package/lib/module/index.js.map +1 -1
  9. package/lib/typescript/src/hooks/useIAP.d.ts +15 -15
  10. package/lib/typescript/src/index.d.ts +59 -88
  11. package/lib/typescript/src/index.d.ts.map +1 -1
  12. package/lib/typescript/src/specs/RnIap.nitro.d.ts +2 -15
  13. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  14. package/lib/typescript/src/types.d.ts +47 -47
  15. package/nitro.json +0 -1
  16. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +7 -7
  17. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -1
  18. package/nitrogen/generated/android/c++/{JNitroPurchaseUpdatedListenerOptions.hpp → JPurchaseUpdatedListenerOptions.hpp} +10 -10
  19. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +2 -2
  20. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/{NitroPurchaseUpdatedListenerOptions.kt → PurchaseUpdatedListenerOptions.kt} +5 -5
  21. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +10 -10
  22. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -3
  23. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +4 -4
  24. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -1
  25. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +1 -1
  26. package/nitrogen/generated/ios/swift/{NitroPurchaseUpdatedListenerOptions.swift → PurchaseUpdatedListenerOptions.swift} +5 -5
  27. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +4 -4
  28. package/nitrogen/generated/shared/c++/{NitroPurchaseUpdatedListenerOptions.hpp → PurchaseUpdatedListenerOptions.hpp} +11 -11
  29. package/openiap-versions.json +2 -2
  30. package/package.json +4 -6
  31. package/src/hooks/useIAP.ts +15 -15
  32. package/src/index.ts +93 -204
  33. package/src/specs/RnIap.nitro.ts +2 -15
  34. package/src/types.ts +47 -47
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # React Native IAP
2
2
 
3
3
  <div align="center">
4
- <img src="https://hyochan.github.io/react-native-iap/img/icon.png" alt="React Native IAP Logo" width="150" />
4
+ <img src="https://openiap.dev/frameworks/react-native.webp" alt="React Native IAP Logo" width="150" />
5
5
 
6
6
  [![Version](http://img.shields.io/npm/v/react-native-iap.svg?style=flat-square)](https://npmjs.org/package/react-native-iap)
7
7
  [![Download](http://img.shields.io/npm/dm/react-native-iap.svg?style=flat-square)](https://npmjs.org/package/react-native-iap)
@@ -33,7 +33,7 @@
33
33
 
34
34
  ## 📚 Documentation
35
35
 
36
- **[📖 Visit our comprehensive documentation site →](https://hyochan.github.io/react-native-iap)**
36
+ **[📖 Visit our comprehensive documentation site →](https://openiap.dev/docs/setup/react-native)**
37
37
 
38
38
  ## ⚠️ Notice
39
39
 
@@ -46,7 +46,7 @@
46
46
  - Seeing Swift 6 C++ interop errors in Nitro (e.g., `AnyMap.swift` with `cppPart.pointee.*`)? Temporarily pin Swift to **5.10** for the `NitroModules` pod (see Installation docs) or upgrade RN and Nitro deps.
47
47
  - Recommended: upgrade to RN 0.79+, update `react-native-nitro-modules`/`nitro-codegen`, then `pod install` and clean build.
48
48
 
49
- More details and the Podfile snippet are in the docs: https://hyochan.github.io/react-native-iap/docs/installation#ios
49
+ More details and the Podfile snippet are in the docs: https://openiap.dev/docs/setup/react-native#ios
50
50
 
51
51
  ## ✨ Features
52
52
 
@@ -55,7 +55,7 @@ More details and the Podfile snippet are in the docs: https://hyochan.github.io/
55
55
  - 🎯 **TypeScript First**: Full TypeScript support with comprehensive type definitions
56
56
  - 🛡️ **Centralized Error Handling**: Unified error management with platform-specific error code mapping
57
57
  - 🎣 **React Hooks**: Modern React hooks API with `useIAP`
58
- - 📱 **Expo Compatible**: Works with Expo development builds
58
+ - 📱 **React Native Focused**: Use `expo-iap` for Expo projects
59
59
  - 🔍 **Receipt Validation**: Built-in receipt validation for both platforms
60
60
  - 💎 **Products & Subscriptions**: Support for both one-time purchases and subscriptions
61
61
  - 🚀 **Performance Optimized**: Efficient caching and minimal re-renders
@@ -68,7 +68,7 @@ npm install react-native-iap react-native-nitro-modules
68
68
  yarn add react-native-iap react-native-nitro-modules
69
69
  ```
70
70
 
71
- **[📖 See the complete installation guide and quick start tutorial →](https://hyochan.github.io/react-native-iap/docs/installation)**
71
+ **[📖 See the complete installation guide and quick start tutorial →](https://openiap.dev/docs/setup/react-native#installation)**
72
72
 
73
73
  ## 🏗️ Architecture
74
74
 
@@ -82,13 +82,13 @@ React Native IAP is built with a modern architecture that emphasizes:
82
82
 
83
83
  ## 📱 Platform Support
84
84
 
85
- | Platform | Support | Notes |
86
- | ----------------- | ------- | --------------------------------------- |
87
- | iOS | ✅ | StoreKit 2 (requires iOS 15+) |
88
- | Android | ✅ | Google Play Billing v8.0.0+ |
89
- | Expo Go | ❌ | Not supported (requires native modules) |
90
- | Expo Dev Client | | Full support |
91
- | Bare React Native | ✅ | Full support |
85
+ | Platform | Support | Notes |
86
+ | ----------------- | ------- | --------------------------------- |
87
+ | iOS | ✅ | StoreKit 2 (requires iOS 15+) |
88
+ | Android | ✅ | Google Play Billing v8.0.0+ |
89
+ | Expo Go | ❌ | Use `expo-iap` for Expo projects |
90
+ | Expo Dev Client | | Use `expo-iap` for Expo projects |
91
+ | Bare React Native | ✅ | Full support |
92
92
 
93
93
  ## 📦 Installation & Configuration
94
94
 
@@ -96,7 +96,7 @@ React Native IAP is built with a modern architecture that emphasizes:
96
96
 
97
97
  Before installing React Native IAP, make sure you have:
98
98
 
99
- - React Native 0.64 or later, or Expo SDK 45 or later
99
+ - React Native 0.64 or later
100
100
  - Node.js 16 or later
101
101
  - iOS 15+ for iOS apps (StoreKit 2 requirement)
102
102
  - Android API level 21+ for Android apps
@@ -112,7 +112,7 @@ In your root `android/build.gradle`:
112
112
  ```gradle
113
113
  buildscript {
114
114
  ext {
115
- kotlinVersion = "2.1.20"
115
+ kotlinVersion = "2.2.0"
116
116
  }
117
117
  dependencies {
118
118
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
@@ -134,59 +134,39 @@ buildscript {
134
134
  - Go to "Signing & Capabilities"
135
135
  - Click "+ Capability" and add "In-App Purchase"
136
136
 
137
- #### Expo Configuration
138
-
139
- For Expo projects, add the plugin to your `app.json` or `expo.json`:
140
-
141
- ```json
142
- {
143
- "expo": {
144
- "plugins": [
145
- "react-native-iap",
146
- [
147
- "expo-build-properties",
148
- {
149
- "android": {
150
- "kotlinVersion": "2.2.0"
151
- }
152
- }
153
- ]
154
- ]
155
- }
156
- }
157
- ```
137
+ #### Expo Projects
158
138
 
159
- **Note:** Expo projects require [development build (dev-client)](https://docs.expo.dev/develop/development-builds/introduction/) as this library contains native code.
139
+ Use [`expo-iap`](https://github.com/hyodotdev/openiap/tree/main/libraries/expo-iap) for Expo apps. This package targets bare React Native/Nitro projects.
160
140
 
161
141
  ### Store Configuration
162
142
 
163
143
  React Native IAP is **OpenIAP compliant**. For detailed store configuration:
164
144
 
165
- - **[iOS Setup →](https://www.openiap.dev/docs/ios-setup)** - App Store Connect configuration
166
- - **[Android Setup →](https://www.openiap.dev/docs/android-setup)** - Google Play Console configuration
145
+ - **[iOS Setup →](https://openiap.dev/docs/ios-setup)** - App Store Connect configuration
146
+ - **[Android Setup →](https://openiap.dev/docs/android-setup)** - Google Play Console configuration
167
147
 
168
148
  ## 🤖 Using with AI Assistants
169
149
 
170
150
  React Native IAP provides AI-friendly documentation for Cursor, GitHub Copilot, Claude, and ChatGPT.
171
151
 
172
- **[📖 AI Assistants Guide →](https://hyochan.github.io/react-native-iap/docs/guides/ai-assistants)**
152
+ **[📖 AI Assistants Guide →](https://openiap.dev/docs/guides/ai-assistants)**
173
153
 
174
154
  Quick links:
175
155
 
176
- - [llms.txt](https://hyochan.github.io/react-native-iap/llms.txt) - Quick reference
177
- - [llms-full.txt](https://hyochan.github.io/react-native-iap/llms-full.txt) - Full API reference
156
+ - [llms.txt](https://openiap.dev/llms.txt) - Quick reference
157
+ - [llms-full.txt](https://openiap.dev/llms-full.txt) - Full API reference
178
158
 
179
159
  ## 🎯 What's Next?
180
160
 
181
- **[📖 Visit our comprehensive documentation site →](https://hyochan.github.io/react-native-iap)**
161
+ **[📖 Visit our comprehensive documentation site →](https://openiap.dev/docs/setup/react-native)**
182
162
 
183
163
  ### Key Resources
184
164
 
185
- - **[Installation & Quick Start](https://hyochan.github.io/react-native-iap/docs/installation)** - Get started in minutes
186
- - **[API Reference](https://hyochan.github.io/react-native-iap/docs/api)** - Complete useIAP hook documentation
187
- - **[Examples](https://hyochan.github.io/react-native-iap/docs/examples/basic-store)** - Production-ready implementations
188
- - **[Error Handling](https://hyochan.github.io/react-native-iap/docs/api/error-codes)** - OpenIAP compliant error codes
189
- - **[Troubleshooting](https://hyochan.github.io/react-native-iap/docs/guides/troubleshooting)** - Common issues and solutions
165
+ - **[Installation & Quick Start](https://openiap.dev/docs/setup/react-native#installation)** - Get started in minutes
166
+ - **[API Reference](https://openiap.dev/docs/apis)** - Complete useIAP hook documentation
167
+ - **[Examples](https://openiap.dev/docs/example)** - Production-ready implementations
168
+ - **[Error Handling](https://openiap.dev/docs/errors)** - OpenIAP compliant error codes
169
+ - **[Troubleshooting](https://openiap.dev/docs/features/debugging)** - Common issues and solutions
190
170
 
191
171
  ## Powered by OpenIAP
192
172
 
@@ -217,10 +197,10 @@ Other libraries built on OpenIAP: [expo-iap](https://github.com/hyodotdev/openia
217
197
 
218
198
  <div style="display: flex; align-items:center; gap: 10px;">
219
199
  <a href="https://namiml.com" style="opacity: 50%">
220
- <img src="https://github.com/hyochan/react-native-iap/assets/27461460/89d71f61-bb73-400a-83bd-fe0f96eb726e" alt="Nami ML" width="140"/>
200
+ <img src="https://openiap.dev/sponsors/nami.webp" alt="Nami ML" width="140"/>
221
201
  </a>
222
202
  <a href="https://www.courier.com/?utm_source=react-native-iap&utm_campaign=osssponsors" style="opacity: 50%;">
223
- <img width="80" alt="courier_dot_com" src="https://github.com/user-attachments/assets/319d8966-6839-498d-8ead-ce8cc72c3bca" />
203
+ <img width="80" alt="courier_dot_com" src="https://openiap.dev/sponsors/courier.webp" />
224
204
  </a>
225
205
  </div>
226
206
 
@@ -1,14 +1,50 @@
1
1
  import groovy.json.JsonSlurper
2
+ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2
3
 
3
4
  buildscript {
5
+ def googleRootBuildFile = [
6
+ new File(projectDir, '../../../packages/google/build.gradle.kts'),
7
+ new File(rootDir, '../../../packages/google/build.gradle.kts'),
8
+ new File(rootProject.projectDir, '../../../packages/google/build.gradle.kts')
9
+ ].find { it.exists() }
10
+
11
+ def googlePluginVersion = { pluginId ->
12
+ if (googleRootBuildFile == null) {
13
+ return null
14
+ }
15
+ def marker = "id(\"${pluginId}\") version \""
16
+ def line = googleRootBuildFile.readLines().find { it.contains(marker) }
17
+ if (line == null) {
18
+ return null
19
+ }
20
+ def matcher = line =~ /version "([^"]+)"/
21
+ return matcher.find() ? matcher.group(1) : null
22
+ }
23
+
24
+ def configuredVersion = { extName, propertyName ->
25
+ if (rootProject.ext.has(extName)) {
26
+ return rootProject.ext.get(extName).toString()
27
+ }
28
+ def propertyValue = project.findProperty(propertyName)
29
+ if (propertyValue == null || propertyValue.toString().trim().isEmpty()) {
30
+ throw new GradleException("react-native-iap: missing ${propertyName} in android/gradle.properties")
31
+ }
32
+ return propertyValue.toString()
33
+ }
34
+
35
+ def androidGradlePluginVersion = googlePluginVersion('com.android.library')
36
+ ?: configuredVersion('androidGradlePluginVersion', 'NitroIap_androidGradlePluginVersion')
37
+ def kotlinGradlePluginVersion = googlePluginVersion('org.jetbrains.kotlin.android')
38
+ ?: configuredVersion('kotlinVersion', 'NitroIap_kotlinVersion')
39
+
4
40
  repositories {
5
41
  google()
6
42
  mavenCentral()
7
43
  }
8
44
 
9
45
  dependencies {
10
- classpath "com.android.tools.build:gradle:8.12.1"
11
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20"
46
+ classpath "com.android.tools.build:gradle:$androidGradlePluginVersion"
47
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinGradlePluginVersion"
12
48
  }
13
49
  }
14
50
 
@@ -45,9 +81,6 @@ def googleVersionString = googleVersion.trim()
45
81
  apply plugin: "com.android.library"
46
82
  apply plugin: 'org.jetbrains.kotlin.android'
47
83
 
48
- // Get kotlinVersion from root project or use default
49
- def kotlinVersion = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : '2.0.21'
50
-
51
84
  // Only apply Nitro autolinking if the file exists
52
85
  def nitroAutolinkingFile = file('../nitrogen/generated/android/NitroIap+autolinking.gradle')
53
86
  if (nitroAutolinkingFile.exists()) {
@@ -63,25 +96,61 @@ apply from: "./fix-prefab.gradle"
63
96
  // }
64
97
 
65
98
  def getExtOrDefault(name) {
66
- return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroIap_" + name]
99
+ def value = rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroIap_" + name]
100
+ if (value == null || value.toString().trim().isEmpty()) {
101
+ throw new GradleException("react-native-iap: missing NitroIap_${name} in android/gradle.properties")
102
+ }
103
+ return value
67
104
  }
68
105
 
69
106
  def getExtOrIntegerDefault(name) {
70
- return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroIap_" + name]).toInteger()
107
+ return getExtOrDefault(name).toString().toInteger()
71
108
  }
72
109
 
73
110
  // Read horizonEnabled from gradle.properties, default to false (play)
74
111
  def horizonEnabled = project.findProperty('horizonEnabled')?.toBoolean() ?: false
75
112
 
113
+ def resolveOpenIapGoogleBuildFile() {
114
+ def candidates = [
115
+ new File(projectDir, '../../../packages/google/openiap/build.gradle.kts'),
116
+ new File(rootDir, '../../../packages/google/openiap/build.gradle.kts'),
117
+ new File(rootProject.projectDir, '../../../packages/google/openiap/build.gradle.kts')
118
+ ]
119
+ return candidates.find { it.exists() }
120
+ }
121
+
122
+ def readOpenIapGoogleVariable(File buildFile, String variableName) {
123
+ if (buildFile == null) {
124
+ return null
125
+ }
126
+ def matcher = buildFile.text =~ /val\s+${variableName}\s*=\s*"([^"]+)"/
127
+ return matcher.find() ? matcher.group(1) : null
128
+ }
129
+
130
+ def readOpenIapGoogleDependencyVersion(File buildFile, String coordinate) {
131
+ if (buildFile == null) {
132
+ return null
133
+ }
134
+ def matcher = buildFile.text =~ /${java.util.regex.Pattern.quote(coordinate)}:([^"$)]+)/
135
+ return matcher.find() ? matcher.group(1) : null
136
+ }
137
+
138
+ def googleOpenIapBuildFile = resolveOpenIapGoogleBuildFile()
139
+ def coroutinesVersion = readOpenIapGoogleVariable(googleOpenIapBuildFile, 'coroutinesVersion')
140
+ ?: getExtOrDefault("coroutinesVersion")
141
+ def playServicesBaseVersion = getExtOrDefault("playServicesBaseVersion")
142
+ def junitVersion = readOpenIapGoogleDependencyVersion(googleOpenIapBuildFile, 'junit:junit')
143
+ ?: getExtOrDefault("junitVersion")
144
+
76
145
  android {
77
- namespace "com.margelo.nitro.iap"
146
+ namespace = "com.margelo.nitro.iap"
78
147
 
79
- ndkVersion getExtOrDefault("ndkVersion")
148
+ ndkVersion = getExtOrDefault("ndkVersion")
80
149
  compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
81
150
 
82
151
  defaultConfig {
83
- minSdkVersion getExtOrIntegerDefault("minSdkVersion")
84
- targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
152
+ minSdkVersion = getExtOrIntegerDefault("minSdkVersion")
153
+ targetSdkVersion = getExtOrIntegerDefault("targetSdkVersion")
85
154
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
86
155
  // Ship consumer keep rules so Nitro HybridObjects aren't stripped in app release builds
87
156
  consumerProguardFiles 'consumer-rules.pro'
@@ -136,13 +205,13 @@ android {
136
205
  }
137
206
 
138
207
  buildFeatures {
139
- buildConfig true
140
- prefab true
208
+ buildConfig = true
209
+ prefab = true
141
210
  }
142
211
 
143
212
  buildTypes {
144
213
  release {
145
- minifyEnabled false
214
+ minifyEnabled = false
146
215
  }
147
216
  }
148
217
 
@@ -151,11 +220,6 @@ android {
151
220
  targetCompatibility JavaVersion.VERSION_17
152
221
  }
153
222
 
154
- // Configure Kotlin compiler to match Java compatibility
155
- kotlinOptions {
156
- jvmTarget = "17"
157
- }
158
-
159
223
  lintOptions {
160
224
  disable "GradleCompatible"
161
225
  }
@@ -163,6 +227,12 @@ android {
163
227
  // Removed sourceSets configuration for codegen as it's not needed for library modules
164
228
  }
165
229
 
230
+ kotlin {
231
+ compilerOptions {
232
+ jvmTarget.set(JvmTarget.JVM_17)
233
+ }
234
+ }
235
+
166
236
  repositories {
167
237
  mavenCentral()
168
238
  google()
@@ -181,10 +251,10 @@ dependencies {
181
251
  }
182
252
 
183
253
  // Google Play Services
184
- implementation 'com.google.android.gms:play-services-base:18.5.0'
254
+ implementation "com.google.android.gms:play-services-base:$playServicesBaseVersion"
185
255
 
186
256
  // Kotlin coroutines
187
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0'
257
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
188
258
 
189
259
  // Determine which OpenIAP dependency to use
190
260
  // In monorepo: use local packages/google source if available
@@ -198,7 +268,7 @@ dependencies {
198
268
  }
199
269
 
200
270
  // Test dependencies
201
- testImplementation 'junit:junit:4.13.2'
271
+ testImplementation "junit:junit:$junitVersion"
202
272
  testImplementation 'org.jetbrains.kotlin:kotlin-test'
203
273
  testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
204
274
  }
@@ -1,4 +1,8 @@
1
- NitroIap_kotlinVersion=2.1.20
1
+ NitroIap_kotlinVersion=2.2.0
2
+ NitroIap_androidGradlePluginVersion=8.13.2
3
+ NitroIap_coroutinesVersion=1.9.0
4
+ NitroIap_playServicesBaseVersion=18.5.0
5
+ NitroIap_junitVersion=4.13.2
2
6
  NitroIap_minSdkVersion=23
3
7
  NitroIap_targetSdkVersion=36
4
8
  NitroIap_compileSdkVersion=36
@@ -793,7 +793,7 @@ class HybridRnIap : HybridRnIapSpec() {
793
793
  // Event listener methods
794
794
  override fun addPurchaseUpdatedListener(
795
795
  listener: (purchase: NitroPurchase) -> Unit,
796
- options: NitroPurchaseUpdatedListenerOptions?
796
+ options: PurchaseUpdatedListenerOptions?
797
797
  ): Double {
798
798
  return synchronized(purchaseUpdatedListeners) {
799
799
  val token = nextPurchaseUpdatedListenerToken
@@ -20,7 +20,9 @@ internal object RnIapLog {
20
20
  }
21
21
 
22
22
  fun debug(message: String) {
23
- Log.d(TAG, message)
23
+ if (BuildConfig.DEBUG || Log.isLoggable(TAG, Log.DEBUG)) {
24
+ Log.d(TAG, message)
25
+ }
24
26
  }
25
27
 
26
28
  fun warn(message: String) {
@@ -388,7 +388,10 @@ class HybridRnIap: HybridRnIapSpec {
388
388
 
389
389
  RnIapLog.payload("validateReceiptIOS", ["sku": sku])
390
390
  let props = try OpenIapSerialization.verifyPurchaseProps(from: ["apple": ["sku": sku]])
391
- let result = try await OpenIapModule.shared.validateReceiptIOS(props)
391
+ let verifyResult = try await OpenIapModule.shared.verifyPurchase(props)
392
+ guard case let .verifyPurchaseResultIos(result) = verifyResult else {
393
+ throw OpenIapException.make(code: .featureNotSupported, message: "Expected iOS validation result")
394
+ }
392
395
  var encoded = RnIapHelper.sanitizeDictionary(OpenIapSerialization.encode(result))
393
396
  if encoded["receiptData"] != nil {
394
397
  encoded["receiptData"] = "<receipt>"
@@ -488,7 +491,7 @@ class HybridRnIap: HybridRnIapSpec {
488
491
  return Promise.async {
489
492
  do {
490
493
  RnIapLog.payload("getStorefront", nil)
491
- let storefront = try await OpenIapModule.shared.getStorefrontIOS()
494
+ let storefront = try await OpenIapModule.shared.getStorefront()
492
495
  RnIapLog.result("getStorefront", storefront)
493
496
  return storefront
494
497
  } catch let purchaseError as PurchaseError {
@@ -578,9 +581,12 @@ class HybridRnIap: HybridRnIapSpec {
578
581
  RnIapLog.payload("buyPromotedProductIOS", nil)
579
582
  let ok = try await OpenIapModule.shared.requestPurchaseOnPromotedProductIOS()
580
583
  RnIapLog.result("buyPromotedProductIOS", ok)
584
+ } catch let purchaseError as PurchaseError {
585
+ RnIapLog.failure("buyPromotedProductIOS", error: purchaseError)
586
+ throw OpenIapException.from(purchaseError)
581
587
  } catch {
582
- // Event-only: OpenIAP will emit purchaseError for this flow. Avoid Promise rejection.
583
588
  RnIapLog.failure("buyPromotedProductIOS", error: error)
589
+ throw OpenIapException.make(code: .featureNotSupported, message: error.localizedDescription)
584
590
  }
585
591
  }
586
592
  }
@@ -954,7 +960,7 @@ class HybridRnIap: HybridRnIapSpec {
954
960
 
955
961
  func addPurchaseUpdatedListener(
956
962
  listener: @escaping (NitroPurchase) -> Void,
957
- options: NitroPurchaseUpdatedListenerOptions?
963
+ options: PurchaseUpdatedListenerOptions?
958
964
  ) throws -> Double {
959
965
  let dedupeTransactionIOS = purchaseUpdatedDedupeTransactionIOS(from: options)
960
966
  let receiveDuplicateTransactionUpdatesIOS = !dedupeTransactionIOS
@@ -998,7 +1004,7 @@ class HybridRnIap: HybridRnIapSpec {
998
1004
  }
999
1005
 
1000
1006
  private func purchaseUpdatedDedupeTransactionIOS(
1001
- from options: NitroPurchaseUpdatedListenerOptions?
1007
+ from options: PurchaseUpdatedListenerOptions?
1002
1008
  ) -> Bool {
1003
1009
  guard let dedupeTransactionIOS = options?.dedupeTransactionIOS else {
1004
1010
  return true
@@ -1147,7 +1153,7 @@ class HybridRnIap: HybridRnIapSpec {
1147
1153
  return
1148
1154
  }
1149
1155
  RnIapLog.payload("purchaseUpdatedListener.register.duplicates", nil)
1150
- let options = PurchaseUpdatedListenerOptions(
1156
+ let options = OpenIAP.PurchaseUpdatedListenerOptions(
1151
1157
  dedupeTransactionIOS: false
1152
1158
  )
1153
1159
  purchaseUpdatedDuplicateSub = OpenIapModule.shared.purchaseUpdatedListener({ [weak self] openIapPurchase in