react-native-iap 9.0.0-beta5 → 9.0.0-beta9

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 (93) hide show
  1. package/README.md +4 -20
  2. package/RNIap.podspec +33 -18
  3. package/android/build.gradle +137 -38
  4. package/android/gradle.properties +8 -0
  5. package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +68 -57
  6. package/android/src/play/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -1
  7. package/android/src/testPlay/java/com/dooboolab/RNIap/{RNIapModuleTestV4.kt → RNIapModuleTest.kt} +48 -34
  8. package/ios/RNIapIos-Bridging-Header.h +2 -0
  9. package/ios/RNIapIos.m +14 -2
  10. package/ios/RNIapIos.swift +903 -881
  11. package/ios/{RNIap.xcodeproj → RNIapIos.xcodeproj}/project.pbxproj +29 -116
  12. package/lib/commonjs/__test__/iap.test.js +21 -0
  13. package/lib/commonjs/__test__/iap.test.js.map +1 -0
  14. package/lib/commonjs/hooks/useIAP.js +78 -0
  15. package/lib/commonjs/hooks/useIAP.js.map +1 -0
  16. package/lib/commonjs/hooks/withIAPContext.js +92 -0
  17. package/lib/commonjs/hooks/withIAPContext.js.map +1 -0
  18. package/lib/commonjs/iap.js +585 -0
  19. package/lib/commonjs/iap.js.map +1 -0
  20. package/lib/commonjs/index.js +59 -0
  21. package/lib/commonjs/index.js.map +1 -0
  22. package/lib/commonjs/types/amazon.js +2 -0
  23. package/lib/commonjs/types/amazon.js.map +1 -0
  24. package/lib/commonjs/types/android.js +55 -0
  25. package/lib/commonjs/types/android.js.map +1 -0
  26. package/lib/commonjs/types/apple.js +165 -0
  27. package/lib/commonjs/types/apple.js.map +1 -0
  28. package/lib/commonjs/types/index.js +59 -0
  29. package/lib/commonjs/types/index.js.map +1 -0
  30. package/lib/module/__test__/iap.test.js +17 -0
  31. package/lib/module/__test__/iap.test.js.map +1 -0
  32. package/lib/module/hooks/useIAP.js +68 -0
  33. package/lib/module/hooks/useIAP.js.map +1 -0
  34. package/lib/module/hooks/withIAPContext.js +76 -0
  35. package/lib/module/hooks/withIAPContext.js.map +1 -0
  36. package/lib/module/iap.js +493 -0
  37. package/lib/module/iap.js.map +1 -0
  38. package/lib/module/index.js +6 -0
  39. package/lib/module/index.js.map +1 -0
  40. package/lib/module/types/amazon.js +2 -0
  41. package/lib/module/types/amazon.js.map +1 -0
  42. package/lib/module/types/android.js +44 -0
  43. package/lib/module/types/android.js.map +1 -0
  44. package/lib/module/types/apple.js +153 -0
  45. package/lib/module/types/apple.js.map +1 -0
  46. package/lib/module/types/index.js +48 -0
  47. package/lib/module/types/index.js.map +1 -0
  48. package/{src → lib/typescript}/__test__/iap.test.d.ts +0 -0
  49. package/{src → lib/typescript}/hooks/useIAP.d.ts +1 -1
  50. package/{src → lib/typescript}/hooks/withIAPContext.d.ts +1 -1
  51. package/{src → lib/typescript}/iap.d.ts +13 -11
  52. package/{src → lib/typescript}/index.d.ts +2 -1
  53. package/{src → lib/typescript}/types/amazon.d.ts +0 -0
  54. package/{src → lib/typescript}/types/android.d.ts +0 -0
  55. package/{src → lib/typescript}/types/apple.d.ts +0 -0
  56. package/{src → lib/typescript}/types/index.d.ts +25 -8
  57. package/package.json +87 -57
  58. package/src/__test__/iap.test.ts +20 -0
  59. package/src/hooks/useIAP.ts +130 -0
  60. package/src/hooks/withIAPContext.tsx +160 -0
  61. package/src/iap.ts +699 -0
  62. package/src/{index.js → index.ts} +4 -1
  63. package/src/types/amazon.ts +23 -0
  64. package/src/types/android.ts +51 -0
  65. package/src/types/apple.ts +467 -0
  66. package/src/types/index.ts +209 -0
  67. package/.editorconfig +0 -10
  68. package/.flowconfig +0 -11
  69. package/.monolinterrc +0 -3
  70. package/.yarn/install-state.gz +0 -0
  71. package/.yarn/releases/yarn-3.2.0.cjs +0 -785
  72. package/.yarnrc.yml +0 -3
  73. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  74. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  75. package/android/gradlew +0 -160
  76. package/android/gradlew.bat +0 -90
  77. package/android/src/play/java/com/dooboolab/RNIap/RNIapModuleInterface.kt +0 -44
  78. package/android/src/play/java/com/dooboolab/RNIap/RNIapModuleV4.kt +0 -658
  79. package/babel.config.js +0 -10
  80. package/index.d.ts +0 -3
  81. package/index.js +0 -3
  82. package/index.js.flow +0 -9
  83. package/ios/RNIap.xcodeproj/xcshareddata/xcschemes/RNIap.xcscheme +0 -80
  84. package/jest.config.js +0 -194
  85. package/src/__test__/iap.test.js +0 -59
  86. package/src/hooks/useIAP.js +0 -141
  87. package/src/hooks/withIAPContext.js +0 -150
  88. package/src/iap.js +0 -638
  89. package/src/types/amazon.js +0 -1
  90. package/src/types/android.js +0 -22
  91. package/src/types/apple.js +0 -165
  92. package/src/types/index.js +0 -40
  93. package/test/mocks/react-native-modules.js +0 -14
package/README.md CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  [![Download](http://img.shields.io/npm/dm/react-native-iap.svg?style=flat-square)](https://npmjs.org/package/react-native-iap)
7
7
  [![CI](https://github.com/dooboolab/react-native-iap/actions/workflows/ci.yml/badge.svg)](https://github.com/dooboolab/react-native-iap/actions/workflows/ci.yml)
8
- [![document](https://github.com/dooboolab/react-native-iap/actions/workflows/deploy-document.yml/badge.svg)](https://github.com/dooboolab/react-native-iap/actions/workflows/deploy-document.yml)
8
+ [![document](https://github.com/dooboolab/react-native-iap/actions/workflows/deploy-documentation.yml/badge.svg)](https://github.com/dooboolab/react-native-iap/actions/workflows/deploy-documentation.yml)
9
9
  [![License](https://img.shields.io/npm/l/react-native-iap.svg)](https://npmjs.org/package/react-native-iap)
10
10
  [![Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap)
11
11
  [![Issue Opened](https://img.shields.io/opencollective/all/react-native-iap.svg)](https://opencollective.com/react-native-iap#backers)
12
12
  [![Issue Opened](https://img.shields.io/github/issues/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap/issues)
13
13
  [![Issue Closed](https://img.shields.io/github/issues-closed/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap/issues?q=is%3Aissue+is%3Aclosed)
14
14
  [![PR Opened](https://img.shields.io/github/issues-pr/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap/pulls)
15
- [![PR Closed](https://img.shields.io/github/issues-pr-closed/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap/pulls?q=is%3Apr+is%3Aclosed) [![Greenkeeper badge](https://badges.greenkeeper.io/dooboolab/react-native-iap.svg)](https://greenkeeper.io/)
15
+ [![PR Closed](https://img.shields.io/github/issues-pr-closed/dooboolab/react-native-iap.svg)](https://github.com/dooboolab/react-native-iap/pulls?q=is%3Apr+is%3Aclosed)
16
16
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fdooboolab%2Freact-native-iap.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fdooboolab%2Freact-native-iap?ref=badge_shield)
17
17
 
18
18
  ## Documentation
@@ -141,25 +141,9 @@ If you're looking for a module going further than react-native-iap, we recommend
141
141
 
142
142
  - Please refer to [Blog][blog-config-steps].
143
143
 
144
- ## [Deprecated README][readme-deprecated]
144
+ ## Example
145
145
 
146
- - If you are using `react-native-iap@^2.*`, please follow the above README.
147
-
148
- ## Usage
149
-
150
- You can look in the [`RNIapExample/`][example] folder to try the example.
151
-
152
- NOTE: To run `RNIapExample` on Android use the variant flag as follows:
153
-
154
- ```
155
- yarn android --variant=MY_VARIANT
156
- ```
157
-
158
- where `MY_VARIANT` is `PlayDebug` or `AmazonDebug`
159
-
160
- Below is basic implementation which is also provided in `RNIapExample` project.
161
-
162
- If you want more advanced one please refer to [dooboolab.com/sponsor.tsx](https://github.com/hyochan/dooboolab.com/blob/main/src/components/pages/Sponsor.tsx)
146
+ Follow [this guide](./IapExample/README.md) to get the example running.
163
147
 
164
148
  ## Sponsoring
165
149
 
package/RNIap.podspec CHANGED
@@ -1,23 +1,38 @@
1
- require 'json'
2
- package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+ folly_version = '2021.06.28.00-v2'
5
+ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
3
6
 
4
7
  Pod::Spec.new do |s|
5
- s.name = "RNIap"
6
- s.version = package['version']
7
- s.summary = package['description']
8
- s.homepage = "https://github.com/dooboolab/react-native-iap"
9
- s.license = "MIT"
10
- s.author = package['author']
11
- s.platforms = { :ios => "9.0", :tvos => "9.0" }
12
- s.source = { :git => "https://github.com/dooboolab/react-native-iap.git", :tag => "#{s.version}" }
13
- s.source_files = "ios/*.{h,m,swift}"
14
- s.script_phase = {
15
- :name => 'Copy Swift Header',
16
- :script => 'ditto "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h" "${PODS_ROOT}/Headers/Public/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}-Swift.h"',
17
- :execution_position => :after_compile
18
- }
19
- s.swift_version = "4.2"
8
+ s.name = "RNIap"
9
+ s.version = package["version"]
10
+ s.summary = package["description"]
11
+ s.homepage = package["homepage"]
12
+ s.license = package["license"]
13
+ s.authors = package["author"]
14
+
15
+ s.platforms = { :ios => "10.0" }
16
+ s.source = { :git => "https://github.com/dooboolab/react-native-iap.git", :tag => "#{s.version}" }
17
+
18
+ s.source_files = "ios/*.{h,m,mm,swift}"
19
+
20
20
  s.requires_arc = true
21
21
 
22
- s.dependency 'React-Core'
22
+ s.dependency "React-Core"
23
+
24
+ # Don't install the dependencies when we run `pod install` in the old architecture.
25
+ if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
26
+ s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
27
+ s.pod_target_xcconfig = {
28
+ "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
29
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
30
+ }
31
+
32
+ s.dependency "React-Codegen"
33
+ s.dependency "RCT-Folly", folly_version
34
+ s.dependency "RCTRequired"
35
+ s.dependency "RCTTypeSafety"
36
+ s.dependency "ReactCommon/turbomodule/core"
37
+ end
23
38
  end
@@ -1,91 +1,190 @@
1
1
  buildscript {
2
- ext.DEFAULT_KOTLIN_VERSION = '1.7.10'
3
- ext.safeExtGet={prop, fallback->
4
- return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5
- }
6
- repositories {
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlinVersion = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["RNIap_kotlinVersion"]
4
+
5
+ repositories {
7
6
  google()
8
7
  mavenCentral()
9
8
  }
10
9
 
11
-
12
10
  dependencies {
13
- classpath 'com.android.tools.build:gradle:4.2.2'
14
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion',DEFAULT_KOTLIN_VERSION)}"
11
+ classpath "com.android.tools.build:gradle:4.2.2"
12
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
15
13
  }
16
14
  }
17
15
 
18
- apply plugin: 'com.android.library'
19
- apply plugin: 'kotlin-android'
16
+ def isNewArchitectureEnabled() {
17
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
18
+ }
20
19
 
20
+ apply plugin: "com.android.library"
21
+ apply plugin: "kotlin-android"
21
22
 
22
- def DEFAULT_COMPILE_SDK_VERSION = 30
23
- def DEFAULT_BUILD_TOOLS_VERSION = "30.0.2"
24
- def DEFAULT_MIN_SDK_VERSION = 16
25
- def DEFAULT_TARGET_SDK_VERSION = 30
26
- def DEFAULT_PLAY_SERVICES_VERSION = "18.1.0"
23
+ if (isNewArchitectureEnabled()) {
24
+ apply plugin: "com.facebook.react"
25
+ }
26
+
27
+ def getExtOrDefault(name) {
28
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["RNIap_" + name]
29
+ }
30
+
31
+ def getExtOrIntegerDefault(name) {
32
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RNIap_" + name]).toInteger()
33
+ }
27
34
 
28
35
  android {
29
- compileSdkVersion safeExtGet("compileSdkVersion", DEFAULT_COMPILE_SDK_VERSION)
36
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
30
37
 
31
38
  defaultConfig {
32
- minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
33
- targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
34
- versionCode 3
35
- versionName "3.0.0"
39
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
40
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
41
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
42
+ }
43
+
44
+ buildTypes {
45
+ release {
46
+ minifyEnabled false
47
+ }
36
48
  }
49
+
37
50
  lintOptions {
38
51
  abortOnError false
52
+ disable "GradleCompatible"
39
53
  }
54
+
40
55
  compileOptions {
41
56
  sourceCompatibility JavaVersion.VERSION_1_8
42
57
  targetCompatibility JavaVersion.VERSION_1_8
43
58
  }
59
+
44
60
  flavorDimensions "store"
61
+
45
62
  productFlavors {
46
- amazon{
63
+ amazon {
47
64
  dimension "store"
48
65
  }
49
- play{
66
+
67
+ play {
50
68
  dimension "store"
51
69
  }
52
70
  }
71
+
53
72
  testOptions {
54
73
  unitTests.returnDefaultValues = true
55
74
  }
56
75
  }
57
76
 
58
77
  repositories {
59
- maven {
60
- // All of React Native (JS, Android binaries) is installed from npm
61
- url "$rootDir/../node_modules/react-native/android"
62
- }
63
- google()
64
78
  mavenCentral()
79
+ google()
80
+
81
+ def found = false
82
+ def defaultDir = null
83
+ def androidSourcesName = "React Native sources"
84
+
85
+ if (rootProject.ext.has("reactNativeAndroidRoot")) {
86
+ defaultDir = rootProject.ext.get("reactNativeAndroidRoot")
87
+ } else {
88
+ defaultDir = new File(
89
+ projectDir,
90
+ "/../../../node_modules/react-native/android"
91
+ )
92
+ }
93
+
94
+ if (defaultDir.exists()) {
95
+ maven {
96
+ url defaultDir.toString()
97
+ name androidSourcesName
98
+ }
99
+
100
+ logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
101
+ found = true
102
+ } else {
103
+ def parentDir = rootProject.projectDir
104
+
105
+ 1.upto(5, {
106
+ if (found) return true
107
+ parentDir = parentDir.parentFile
108
+
109
+ def androidSourcesDir = new File(
110
+ parentDir,
111
+ "node_modules/react-native"
112
+ )
113
+
114
+ def androidPrebuiltBinaryDir = new File(
115
+ parentDir,
116
+ "node_modules/react-native/android"
117
+ )
118
+
119
+ if (androidPrebuiltBinaryDir.exists()) {
120
+ maven {
121
+ url androidPrebuiltBinaryDir.toString()
122
+ name androidSourcesName
123
+ }
124
+
125
+ logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
126
+ found = true
127
+ } else if (androidSourcesDir.exists()) {
128
+ maven {
129
+ url androidSourcesDir.toString()
130
+ name androidSourcesName
131
+ }
132
+
133
+ logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
134
+ found = true
135
+ }
136
+ })
137
+ }
138
+
139
+ if (!found) {
140
+ throw new GradleException(
141
+ "${project.name}: unable to locate React Native android sources. " +
142
+ "Ensure you have you installed React Native as a dependency in your project and try again."
143
+ )
144
+ }
65
145
  }
66
146
 
147
+ def kotlinVersion = getExtOrDefault("kotlinVersion")
148
+ def playServicesVersion = getExtOrDefault("playServicesVersion")
149
+ def supportLibVersion = getExtOrDefault("supportLibVersion")
150
+ def androidXVersion = getExtOrDefault("androidXVersion")
151
+ def androidXAnnotation = getExtOrDefault("androidXAnnotation")
152
+ def androidXBrowser = getExtOrDefault("androidXBrowser")
153
+
67
154
  dependencies {
68
- implementation 'com.facebook.react:react-native:+'
69
- testImplementation 'junit:junit:4.13.1'
155
+ implementation "com.facebook.react:react-native:+"
156
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
157
+
158
+ testImplementation "junit:junit:4.13.2"
70
159
  testImplementation "io.mockk:mockk:1.12.4"
71
- playImplementation 'com.android.billingclient:billing:5.0.0'
72
- def playServicesVersion = safeExtGet('playServicesVersion', DEFAULT_PLAY_SERVICES_VERSION)
160
+
161
+ playImplementation "com.android.billingclient:billing-ktx:5.0.0"
73
162
  playImplementation "com.google.android.gms:play-services-base:$playServicesVersion"
74
- amazonImplementation fileTree(dir: 'libs', include: ['*.jar'])
75
163
 
76
- def supportLibVersion = safeExtGet('supportLibVersion', safeExtGet('supportVersion', null))
77
- def androidXVersion = safeExtGet('androidXVersion', null)
164
+ amazonImplementation fileTree(dir: "libs", include: ["*.jar"])
165
+
78
166
  if (supportLibVersion && androidXVersion == null) {
79
167
  implementation "com.android.support:support-annotations:$supportLibVersion"
80
168
  implementation "com.android.support:customtabs:$supportLibVersion"
81
169
  } else {
82
170
  def defaultAndroidXVersion = "1.2.0"
171
+
83
172
  if (androidXVersion == null) {
84
173
  androidXVersion = defaultAndroidXVersion
85
174
  }
86
- def androidXAnnotation = safeExtGet('androidXAnnotation', androidXVersion)
87
- def androidXBrowser = safeExtGet('androidXBrowser', androidXVersion)
88
- implementation "androidx.annotation:annotation:$androidXAnnotation"
89
- implementation "androidx.browser:browser:$androidXBrowser"
175
+
176
+ def annotationVersion = androidXAnnotation ? androidXAnnotation : androidXVersion
177
+ def browserVersion = androidXBrowser ? androidXBrowser : androidXVersion
178
+
179
+ implementation "androidx.annotation:annotation:$annotationVersion"
180
+ implementation "androidx.browser:browser:$browserVersion"
181
+ }
182
+ }
183
+
184
+ if (isNewArchitectureEnabled()) {
185
+ react {
186
+ jsRootDir = file("../src/")
187
+ libraryName = "RNIap"
188
+ codegenJavaPackageName = "com.reactnativeiap"
90
189
  }
91
190
  }
@@ -1,2 +1,10 @@
1
+ RNIap_kotlinVersion=1.7.0
2
+ RNIap_minSdkVersion=21
3
+ RNIap_targetSdkVersion=31
4
+ RNIap_compileSdkVersion=31
5
+ RNIap_buildToolsVersion=30.0.2
6
+ RNIap_ndkversion=21.4.7075529
7
+ RNIap_playServicesVersion=17.6.0
8
+
1
9
  android.useAndroidX=true
2
10
  android.enableJetifier=true
@@ -26,6 +26,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
26
26
  import com.facebook.react.bridge.ReactMethod
27
27
  import com.facebook.react.bridge.ReadableArray
28
28
  import com.facebook.react.bridge.ReadableType
29
+ import com.facebook.react.bridge.WritableArray
29
30
  import com.facebook.react.bridge.WritableMap
30
31
  import com.facebook.react.bridge.WritableNativeArray
31
32
  import com.facebook.react.bridge.WritableNativeMap
@@ -40,8 +41,7 @@ class RNIapModule(
40
41
  private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance()
41
42
  ) :
42
43
  ReactContextBaseJavaModule(reactContext),
43
- PurchasesUpdatedListener,
44
- RNIapModuleInterface {
44
+ PurchasesUpdatedListener {
45
45
 
46
46
  private var billingClientCache: BillingClient? = null
47
47
  private val skus: MutableMap<String, ProductDetails> = mutableMapOf()
@@ -49,7 +49,7 @@ class RNIapModule(
49
49
  return TAG
50
50
  }
51
51
 
52
- internal fun ensureConnection(
52
+ fun ensureConnection(
53
53
  promise: Promise,
54
54
  callback: (billingClient: BillingClient) -> Unit
55
55
  ) {
@@ -87,7 +87,7 @@ class RNIapModule(
87
87
  }
88
88
 
89
89
  @ReactMethod
90
- override fun initConnection(promise: Promise) {
90
+ fun initConnection(promise: Promise) {
91
91
  if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
92
92
  != ConnectionResult.SUCCESS
93
93
  ) {
@@ -108,13 +108,13 @@ class RNIapModule(
108
108
  billingClientCache = it
109
109
  it.startConnection(
110
110
  object : BillingClientStateListener {
111
- override fun onBillingSetupFinished(billingResult: BillingResult) {
111
+ override fun onBillingSetupFinished(billingResult: BillingResult) {
112
112
  if (!isValidResult(billingResult, promise)) return
113
113
 
114
114
  promise.safeResolve(true)
115
115
  }
116
116
 
117
- override fun onBillingServiceDisconnected() {
117
+ override fun onBillingServiceDisconnected() {
118
118
  Log.i(TAG, "Billing service disconnected")
119
119
  }
120
120
  }
@@ -123,7 +123,7 @@ class RNIapModule(
123
123
  }
124
124
 
125
125
  @ReactMethod
126
- override fun endConnection(promise: Promise) {
126
+ fun endConnection(promise: Promise) {
127
127
  billingClientCache?.endConnection()
128
128
  billingClientCache = null
129
129
  promise.safeResolve(true)
@@ -160,7 +160,7 @@ class RNIapModule(
160
160
  }
161
161
 
162
162
  @ReactMethod
163
- override fun flushFailedPurchasesCachedAsPending(promise: Promise) {
163
+ fun flushFailedPurchasesCachedAsPending(promise: Promise) {
164
164
  ensureConnection(
165
165
  promise
166
166
  ) { billingClient ->
@@ -193,7 +193,7 @@ class RNIapModule(
193
193
  }
194
194
 
195
195
  @ReactMethod
196
- override fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
196
+ fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
197
197
  ensureConnection(
198
198
  promise
199
199
  ) { billingClient ->
@@ -306,7 +306,7 @@ class RNIapModule(
306
306
  }
307
307
 
308
308
  @ReactMethod
309
- override fun getAvailableItemsByType(type: String, promise: Promise) {
309
+ fun getAvailableItemsByType(type: String, promise: Promise) {
310
310
  ensureConnection(
311
311
  promise
312
312
  ) { billingClient ->
@@ -321,7 +321,10 @@ class RNIapModule(
321
321
  for (i in purchases.indices) {
322
322
  val purchase = purchases[i]
323
323
  val item = WritableNativeMap()
324
- item.putString("productId", purchase.products[0]) // TODO: should be a list
324
+ item.putString("productId", purchase.products[0])// kept for convenience/backward-compatibility. productIds has the complete list
325
+ val products = Arguments.createArray()
326
+ purchase.products.forEach { products.pushString(it) }
327
+ item.putArray("productIds", products)
325
328
  item.putString("transactionId", purchase.orderId)
326
329
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
327
330
  item.putString("transactionReceipt", purchase.originalJson)
@@ -352,7 +355,7 @@ class RNIapModule(
352
355
  }
353
356
 
354
357
  @ReactMethod
355
- override fun getPurchaseHistoryByType(type: String, promise: Promise) {
358
+ fun getPurchaseHistoryByType(type: String, promise: Promise) {
356
359
  ensureConnection(
357
360
  promise
358
361
  ) { billingClient ->
@@ -370,6 +373,9 @@ class RNIapModule(
370
373
  purchaseHistoryRecordList?.forEach { purchase ->
371
374
  val item = Arguments.createMap()
372
375
  item.putString("productId", purchase.products[0])
376
+ val products = Arguments.createArray()
377
+ purchase.products.forEach { products.pushString(it) }
378
+ item.putArray("productIds", products)
373
379
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
374
380
  item.putString("transactionReceipt", purchase.originalJson)
375
381
  item.putString("purchaseToken", purchase.purchaseToken)
@@ -384,14 +390,14 @@ class RNIapModule(
384
390
  }
385
391
 
386
392
  @ReactMethod
387
- override fun buyItemByType(
393
+ fun buyItemByType(
388
394
  type: String,
389
- sku: String, // TODO: should this now be an array?
395
+ skuArr: ReadableArray,
390
396
  purchaseToken: String?,
391
397
  prorationMode: Int,
392
398
  obfuscatedAccountId: String?,
393
399
  obfuscatedProfileId: String?,
394
- selectedOfferIndex: Int, // New optional parameter in V5, TODO: should it be an array?
400
+ selectedOfferIndexArr: ReadableArray, // New optional parameter in V5
395
401
  promise: Promise
396
402
  ) {
397
403
  val activity = currentActivity
@@ -406,32 +412,36 @@ class RNIapModule(
406
412
  PROMISE_BUY_ITEM,
407
413
  promise
408
414
  )
409
- val builder = BillingFlowParams.newBuilder()
410
- val selectedSku: ProductDetails? = skus[sku]
411
- if (selectedSku == null) {
412
- val debugMessage =
413
- "The sku was not found. Please fetch products first by calling getItems"
414
- val error = Arguments.createMap()
415
- error.putString("debugMessage", debugMessage)
416
- error.putString("code", PROMISE_BUY_ITEM)
417
- error.putString("message", debugMessage)
418
- error.putString("productId", sku)
419
- sendEvent(reactContext, "purchase-error", error)
420
- promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
421
- return@ensureConnection
422
- }
423
- var productParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
424
- if (selectedOfferIndex > -1 && (
425
- selectedSku.subscriptionOfferDetails?.size
426
- ?: 0
427
- ) > selectedOfferIndex
428
- ) {
429
- val offerToken =
430
- selectedSku.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
431
- offerToken?.let { productParams = productParams.setOfferToken(offerToken) }
415
+ val productParamsList =
416
+ skuArr.toArrayList().map { it.toString() }.mapIndexed{ index,sku ->
417
+ val selectedSku: ProductDetails? = skus[sku]
418
+ if (selectedSku == null) {
419
+ val debugMessage =
420
+ "The sku was not found. Please fetch products first by calling getItems"
421
+ val error = Arguments.createMap()
422
+ error.putString("debugMessage", debugMessage)
423
+ error.putString("code", PROMISE_BUY_ITEM)
424
+ error.putString("message", debugMessage)
425
+ error.putString("productId", sku)
426
+ sendEvent(reactContext, "purchase-error", error)
427
+ promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
428
+ return@ensureConnection
429
+ }
430
+ var productParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
431
+ val selectedOfferIndex = selectedOfferIndexArr.getInt(index)
432
+ if (selectedOfferIndex > -1 && (
433
+ selectedSku.subscriptionOfferDetails?.size
434
+ ?: 0
435
+ ) > selectedOfferIndex
436
+ ) {
437
+ val offerToken =
438
+ selectedSku.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
439
+ offerToken?.let { productParams = productParams.setOfferToken(offerToken) }
440
+ }
441
+ productParams.build()
432
442
  }
433
-
434
- builder.setProductDetailsParamsList(listOf(productParams.build()))
443
+ val builder = BillingFlowParams.newBuilder()
444
+ builder.setProductDetailsParamsList(productParamsList)
435
445
  val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
436
446
  if (purchaseToken != null) {
437
447
  subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
@@ -442,14 +452,14 @@ class RNIapModule(
442
452
  if (obfuscatedProfileId != null) {
443
453
  builder.setObfuscatedProfileId(obfuscatedProfileId)
444
454
  }
445
- if (prorationMode != null && prorationMode != -1) {
455
+ if (prorationMode != -1) {
446
456
  if (prorationMode
447
457
  == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
448
458
  ) {
449
459
  subscriptionUpdateParamsBuilder.setReplaceProrationMode(
450
460
  BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
451
461
  )
452
- if (type != BillingClient.SkuType.SUBS) {
462
+ if (type != BillingClient.ProductType.SUBS) {
453
463
  val debugMessage =
454
464
  (
455
465
  "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
@@ -459,7 +469,7 @@ class RNIapModule(
459
469
  error.putString("debugMessage", debugMessage)
460
470
  error.putString("code", PROMISE_BUY_ITEM)
461
471
  error.putString("message", debugMessage)
462
- error.putString("productId", sku)
472
+ error.putArray("productIds", skuArr)
463
473
  sendEvent(reactContext, "purchase-error", error)
464
474
  promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
465
475
  return@ensureConnection
@@ -510,7 +520,7 @@ class RNIapModule(
510
520
  }
511
521
 
512
522
  @ReactMethod
513
- override fun acknowledgePurchase(
523
+ fun acknowledgePurchase(
514
524
  token: String,
515
525
  developerPayLoad: String?,
516
526
  promise: Promise
@@ -540,7 +550,7 @@ class RNIapModule(
540
550
  }
541
551
 
542
552
  @ReactMethod
543
- override fun consumeProduct(
553
+ fun consumeProduct(
544
554
  token: String,
545
555
  developerPayLoad: String?,
546
556
  promise: Promise
@@ -561,6 +571,7 @@ class RNIapModule(
561
571
  .getBillingResponseData(billingResult.responseCode)
562
572
  map.putString("code", errorData[0])
563
573
  map.putString("message", errorData[1])
574
+ map.putString("purchaseToken",purchaseToken)
564
575
  promise.safeResolve(map)
565
576
  }
566
577
  }
@@ -581,11 +592,13 @@ class RNIapModule(
581
592
  return
582
593
  }
583
594
  if (purchases != null) {
584
- var promiseItem: WritableMap? = null
585
- for (i in purchases.indices) {
595
+ val promiseItems: WritableArray = Arguments.createArray()
596
+ purchases.forEach { purchase ->
586
597
  val item = Arguments.createMap()
587
- val purchase = purchases[i]
588
598
  item.putString("productId", purchase.products[0])
599
+ val products = Arguments.createArray()
600
+ purchase.products.forEach { products.pushString(it) }
601
+ item.putArray("productIds", products)
589
602
  item.putString("transactionId", purchase.orderId)
590
603
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
591
604
  item.putString("transactionReceipt", purchase.originalJson)
@@ -608,13 +621,10 @@ class RNIapModule(
608
621
  accountIdentifiers.obfuscatedProfileId
609
622
  )
610
623
  }
611
- promiseItem = WritableNativeMap()
612
- promiseItem.merge(item)
624
+ promiseItems.pushMap(item)
613
625
  sendEvent(reactContext, "purchase-updated", item)
614
626
  }
615
- if (promiseItem != null) {
616
- DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItem)
617
- }
627
+ DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItems)
618
628
  } else {
619
629
  val result = Arguments.createMap()
620
630
  result.putInt("responseCode", billingResult.responseCode)
@@ -651,22 +661,22 @@ class RNIapModule(
651
661
  }
652
662
 
653
663
  @ReactMethod
654
- override fun startListening(promise: Promise) {
664
+ fun startListening(promise: Promise) {
655
665
  sendUnconsumedPurchases(promise)
656
666
  }
657
667
 
658
668
  @ReactMethod
659
- override fun addListener(eventName: String) {
669
+ fun addListener(eventName: String) {
660
670
  // Keep: Required for RN built-in Event Emitter Calls.
661
671
  }
662
672
 
663
673
  @ReactMethod
664
- override fun removeListeners(count: Double) {
674
+ fun removeListeners(count: Double) {
665
675
  // Keep: Required for RN built-in Event Emitter Calls.
666
676
  }
667
677
 
668
678
  @ReactMethod
669
- override fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
679
+ fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
670
680
 
671
681
  private fun sendEvent(
672
682
  reactContext: ReactContext,
@@ -689,6 +699,7 @@ class RNIapModule(
689
699
  override fun onHostPause() {}
690
700
  override fun onHostDestroy() {
691
701
  billingClientCache?.endConnection()
702
+ billingClientCache = null
692
703
  }
693
704
  }
694
705
  reactContext.addLifecycleEventListener(lifecycleEventListener)
@@ -15,7 +15,6 @@ class RNIapPackage : ReactPackage {
15
15
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
16
16
  val modules: MutableList<NativeModule> = ArrayList()
17
17
  modules.add(RNIapModule(reactContext))
18
- modules.add(RNIapModuleV4(reactContext))
19
18
  return modules
20
19
  }
21
20
  }