react-native-iap 15.2.3 → 15.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 (39) hide show
  1. package/README.md +31 -51
  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 +24 -8
  5. package/android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt +3 -1
  6. package/ios/HybridRnIap.swift +172 -73
  7. package/lib/module/hooks/useIAP.js +1 -1
  8. package/lib/module/hooks/useIAP.js.map +1 -1
  9. package/lib/module/index.js +138 -165
  10. package/lib/module/index.js.map +1 -1
  11. package/lib/module/types.js.map +1 -1
  12. package/lib/typescript/src/hooks/useIAP.d.ts +22 -16
  13. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  14. package/lib/typescript/src/index.d.ts +61 -90
  15. package/lib/typescript/src/index.d.ts.map +1 -1
  16. package/lib/typescript/src/specs/RnIap.nitro.d.ts +5 -16
  17. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  18. package/lib/typescript/src/types.d.ts +63 -49
  19. package/lib/typescript/src/types.d.ts.map +1 -1
  20. package/nitro.json +0 -1
  21. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +11 -6
  22. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +2 -2
  23. package/nitrogen/generated/android/c++/JPurchaseUpdatedListenerOptions.hpp +61 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +4 -9
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PurchaseUpdatedListenerOptions.kt +38 -0
  26. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +27 -0
  27. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -0
  28. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +9 -4
  29. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +2 -2
  30. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +8 -12
  31. package/nitrogen/generated/ios/swift/PurchaseUpdatedListenerOptions.swift +61 -0
  32. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +5 -2
  33. package/nitrogen/generated/shared/c++/PurchaseUpdatedListenerOptions.hpp +85 -0
  34. package/openiap-versions.json +3 -3
  35. package/package.json +4 -6
  36. package/src/hooks/useIAP.ts +23 -15
  37. package/src/index.ts +198 -215
  38. package/src/specs/RnIap.nitro.ts +10 -18
  39. package/src/types.ts +66 -49
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
 
@@ -195,11 +175,11 @@ Quick links:
195
175
  React Native IAP conforms to the **[OpenIAP specification](https://openiap.dev)** — an open, vendor-neutral interoperability standard for in-app purchases. OpenIAP provides:
196
176
 
197
177
  - **Shared specification** — Common types, error codes, and purchase flows across all platforms
198
- - **Generated type-safe bindings** — Swift, Kotlin, Dart, and GDScript from a single GraphQL schema
178
+ - **Generated type-safe bindings** — Swift, Kotlin, TypeScript, Dart, C#, and GDScript from a single GraphQL schema
199
179
  - **Platform implementations** — [openiap-apple](https://github.com/hyodotdev/openiap/tree/main/packages/apple) (StoreKit 2) and [openiap-google](https://github.com/hyodotdev/openiap/tree/main/packages/google) (Play Billing 8.x)
200
180
  - **Verification profiles** — Standardized receipt validation and purchase verification patterns
201
181
 
202
- Other libraries built on OpenIAP: [expo-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/expo-iap) · [flutter_inapp_purchase](https://github.com/hyodotdev/openiap/tree/main/libraries/flutter_inapp_purchase) · [kmp-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/kmp-iap) · [godot-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/godot-iap)
182
+ Other libraries built on OpenIAP: [expo-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/expo-iap) · [flutter_inapp_purchase](https://github.com/hyodotdev/openiap/tree/main/libraries/flutter_inapp_purchase) · [kmp-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/kmp-iap) · [maui-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/maui-iap) · [godot-iap](https://github.com/hyodotdev/openiap/tree/main/libraries/godot-iap)
203
183
 
204
184
  **[Learn more about the OpenIAP standard →](https://openiap.dev/docs/foundation/about)**
205
185
 
@@ -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
@@ -68,6 +68,10 @@ class OpenIapException(private val errorJson: String) : Exception() {
68
68
  }
69
69
 
70
70
  class HybridRnIap : HybridRnIapSpec() {
71
+ private data class PurchaseUpdatedListenerRegistration(
72
+ val token: Double,
73
+ val listener: (NitroPurchase) -> Unit
74
+ )
71
75
 
72
76
  // Get ReactApplicationContext lazily from NitroModules
73
77
  private val context: ReactApplicationContext by lazy {
@@ -79,7 +83,8 @@ class HybridRnIap : HybridRnIapSpec() {
79
83
  private val productTypeBySku = mutableMapOf<String, String>()
80
84
 
81
85
  // Event listeners
82
- private val purchaseUpdatedListeners = mutableListOf<(NitroPurchase) -> Unit>()
86
+ private val purchaseUpdatedListeners = mutableListOf<PurchaseUpdatedListenerRegistration>()
87
+ private var nextPurchaseUpdatedListenerToken = 1.0
83
88
  private val purchaseErrorListeners = mutableListOf<(NitroPurchaseResult) -> Unit>()
84
89
  private val promotedProductListenersIOS = mutableListOf<(NitroProduct) -> Unit>()
85
90
  private val userChoiceBillingListenersAndroid = mutableListOf<(UserChoiceBillingDetails) -> Unit>()
@@ -302,7 +307,10 @@ class HybridRnIap : HybridRnIapSpec() {
302
307
  productTypeBySku.clear()
303
308
  isInitialized = false
304
309
  listenersAttached = false
305
- synchronized(purchaseUpdatedListeners) { purchaseUpdatedListeners.clear() }
310
+ synchronized(purchaseUpdatedListeners) {
311
+ purchaseUpdatedListeners.clear()
312
+ nextPurchaseUpdatedListenerToken = 1.0
313
+ }
306
314
  synchronized(purchaseErrorListeners) { purchaseErrorListeners.clear() }
307
315
  promotedProductListenersIOS.clear()
308
316
  synchronized(userChoiceBillingListenersAndroid) { userChoiceBillingListenersAndroid.clear() }
@@ -783,9 +791,15 @@ class HybridRnIap : HybridRnIapSpec() {
783
791
  get() = 0L
784
792
 
785
793
  // Event listener methods
786
- override fun addPurchaseUpdatedListener(listener: (purchase: NitroPurchase) -> Unit) {
787
- synchronized(purchaseUpdatedListeners) {
788
- purchaseUpdatedListeners.add(listener)
794
+ override fun addPurchaseUpdatedListener(
795
+ listener: (purchase: NitroPurchase) -> Unit,
796
+ options: PurchaseUpdatedListenerOptions?
797
+ ): Double {
798
+ return synchronized(purchaseUpdatedListeners) {
799
+ val token = nextPurchaseUpdatedListenerToken
800
+ nextPurchaseUpdatedListenerToken += 1.0
801
+ purchaseUpdatedListeners.add(PurchaseUpdatedListenerRegistration(token, listener))
802
+ token
789
803
  }
790
804
  }
791
805
 
@@ -795,9 +809,9 @@ class HybridRnIap : HybridRnIapSpec() {
795
809
  }
796
810
  }
797
811
 
798
- override fun removePurchaseUpdatedListener(listener: (purchase: NitroPurchase) -> Unit) {
812
+ override fun removePurchaseUpdatedListener(token: Double) {
799
813
  synchronized(purchaseUpdatedListeners) {
800
- purchaseUpdatedListeners.remove(listener)
814
+ purchaseUpdatedListeners.removeAll { it.token == token }
801
815
  }
802
816
  }
803
817
 
@@ -832,7 +846,9 @@ class HybridRnIap : HybridRnIapSpec() {
832
846
  "sendPurchaseUpdate",
833
847
  mapOf("productId" to purchase.productId, "platform" to purchase.platform)
834
848
  )
835
- val snapshot = synchronized(purchaseUpdatedListeners) { ArrayList(purchaseUpdatedListeners) }
849
+ val snapshot = synchronized(purchaseUpdatedListeners) {
850
+ purchaseUpdatedListeners.map { it.listener }
851
+ }
836
852
  snapshot.forEach { it(purchase) }
837
853
  }
838
854
 
@@ -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) {