react-native-iap 8.1.3 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/android/build.gradle +5 -0
- package/android/src/play/java/com/dooboolab/RNIap/PlayUtils.kt +1 -1
- package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +14 -15
- package/android/src/testPlay/java/com/dooboolab/RNIap/RNIapModuleTest.kt +144 -0
- package/package.json +1 -1
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/android/build.gradle
CHANGED
|
@@ -50,6 +50,9 @@ android {
|
|
|
50
50
|
dimension "store"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
testOptions {
|
|
54
|
+
unitTests.returnDefaultValues = true
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
repositories {
|
|
@@ -63,6 +66,8 @@ repositories {
|
|
|
63
66
|
|
|
64
67
|
dependencies {
|
|
65
68
|
implementation 'com.facebook.react:react-native:+'
|
|
69
|
+
testImplementation 'junit:junit:4.12'
|
|
70
|
+
testImplementation "io.mockk:mockk:1.12.4"
|
|
66
71
|
playImplementation 'com.android.billingclient:billing:4.0.0'
|
|
67
72
|
def playServicesVersion = safeExtGet('playServicesVersion', DEFAULT_PLAY_SERVICES_VERSION)
|
|
68
73
|
playImplementation "com.google.android.gms:play-services-base:$playServicesVersion"
|
|
@@ -49,7 +49,7 @@ class PlayUtils {
|
|
|
49
49
|
}
|
|
50
50
|
BillingClient.BillingResponseCode.ERROR -> {
|
|
51
51
|
errorData[0] = DoobooUtils.E_UNKNOWN
|
|
52
|
-
errorData[1] = "An unknown or unexpected error has
|
|
52
|
+
errorData[1] = "An unknown or unexpected error has occurred. Please try again later."
|
|
53
53
|
}
|
|
54
54
|
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
|
|
55
55
|
errorData[0] = DoobooUtils.E_ALREADY_OWNED
|
|
@@ -32,12 +32,15 @@ import com.google.android.gms.common.GoogleApiAvailability
|
|
|
32
32
|
import java.math.BigDecimal
|
|
33
33
|
import java.util.ArrayList
|
|
34
34
|
|
|
35
|
-
class RNIapModule(
|
|
35
|
+
class RNIapModule(
|
|
36
|
+
private val reactContext: ReactApplicationContext,
|
|
37
|
+
private val builder: BillingClient.Builder = BillingClient.newBuilder(reactContext).enablePendingPurchases(),
|
|
38
|
+
private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance()
|
|
39
|
+
) :
|
|
36
40
|
ReactContextBaseJavaModule(reactContext),
|
|
37
41
|
PurchasesUpdatedListener {
|
|
38
42
|
|
|
39
|
-
private
|
|
40
|
-
.build()
|
|
43
|
+
private var billingClient: BillingClient = builder.setListener(this).build()
|
|
41
44
|
private val skus: MutableMap<String, SkuDetails> = mutableMapOf()
|
|
42
45
|
override fun getName(): String {
|
|
43
46
|
return "RNIapModule"
|
|
@@ -80,7 +83,7 @@ class RNIapModule(private val reactContext: ReactApplicationContext) :
|
|
|
80
83
|
promise.resolve(true)
|
|
81
84
|
return
|
|
82
85
|
}
|
|
83
|
-
if (
|
|
86
|
+
if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
|
|
84
87
|
!= ConnectionResult.SUCCESS
|
|
85
88
|
) {
|
|
86
89
|
Log.i(TAG, "Google Play Services are not available on this device")
|
|
@@ -88,6 +91,8 @@ class RNIapModule(private val reactContext: ReactApplicationContext) :
|
|
|
88
91
|
return
|
|
89
92
|
}
|
|
90
93
|
|
|
94
|
+
billingClient = builder.setListener(this).build()
|
|
95
|
+
|
|
91
96
|
billingClient.startConnection(
|
|
92
97
|
object : BillingClientStateListener {
|
|
93
98
|
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
|
@@ -154,7 +159,6 @@ class RNIapModule(private val reactContext: ReactApplicationContext) :
|
|
|
154
159
|
ensureConnection(
|
|
155
160
|
promise
|
|
156
161
|
) {
|
|
157
|
-
val array = WritableNativeArray()
|
|
158
162
|
billingClient.queryPurchasesAsync(
|
|
159
163
|
BillingClient.SkuType.INAPP
|
|
160
164
|
) { _: BillingResult?, list: List<Purchase>? ->
|
|
@@ -163,16 +167,11 @@ class RNIapModule(private val reactContext: ReactApplicationContext) :
|
|
|
163
167
|
promise.resolve(false)
|
|
164
168
|
return@queryPurchasesAsync
|
|
165
169
|
}
|
|
166
|
-
|
|
167
|
-
for
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {
|
|
172
|
-
pendingPurchases.add(purchase)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (pendingPurchases.size == 0) {
|
|
170
|
+
// we only want to try to consume PENDING items, in order to force cache-refresh
|
|
171
|
+
// for them
|
|
172
|
+
val pendingPurchases = list.filter { it.purchaseState == Purchase.PurchaseState.PENDING }
|
|
173
|
+
|
|
174
|
+
if (pendingPurchases.isEmpty()) {
|
|
176
175
|
promise.resolve(false)
|
|
177
176
|
return@queryPurchasesAsync
|
|
178
177
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
package com.dooboolab.RNIap
|
|
2
|
+
|
|
3
|
+
import com.android.billingclient.api.BillingClient
|
|
4
|
+
import com.android.billingclient.api.BillingClientStateListener
|
|
5
|
+
import com.android.billingclient.api.BillingResult
|
|
6
|
+
import com.android.billingclient.api.PurchasesResponseListener
|
|
7
|
+
import com.facebook.react.bridge.Promise
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
+
import com.google.android.gms.common.ConnectionResult
|
|
10
|
+
import com.google.android.gms.common.GoogleApiAvailability
|
|
11
|
+
import io.mockk.MockKAnnotations
|
|
12
|
+
import io.mockk.every
|
|
13
|
+
import io.mockk.impl.annotations.MockK
|
|
14
|
+
import io.mockk.mockk
|
|
15
|
+
import io.mockk.slot
|
|
16
|
+
import io.mockk.verify
|
|
17
|
+
import org.junit.Before
|
|
18
|
+
import org.junit.Test
|
|
19
|
+
|
|
20
|
+
class RNIapModuleTest {
|
|
21
|
+
|
|
22
|
+
@MockK
|
|
23
|
+
lateinit var context: ReactApplicationContext
|
|
24
|
+
@MockK
|
|
25
|
+
lateinit var builder: BillingClient.Builder
|
|
26
|
+
@MockK
|
|
27
|
+
lateinit var billingClient: BillingClient
|
|
28
|
+
@MockK
|
|
29
|
+
lateinit var availability: GoogleApiAvailability
|
|
30
|
+
|
|
31
|
+
private lateinit var module: RNIapModule
|
|
32
|
+
|
|
33
|
+
@Before
|
|
34
|
+
fun setUp() {
|
|
35
|
+
MockKAnnotations.init(this, relaxUnitFun = true)
|
|
36
|
+
every { builder.setListener(any()) } returns builder
|
|
37
|
+
every { builder.build() } returns billingClient
|
|
38
|
+
module = RNIapModule(context, builder, availability)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun `initConnection Already connected should resolve to true`() {
|
|
43
|
+
every { billingClient.isReady } returns true
|
|
44
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
45
|
+
|
|
46
|
+
module.initConnection(promise)
|
|
47
|
+
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
48
|
+
verify { promise.resolve(true) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `initConnection Play Services not available on device should reject`() {
|
|
53
|
+
every { billingClient.isReady } returns false
|
|
54
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.DEVELOPER_ERROR
|
|
55
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
56
|
+
|
|
57
|
+
module.initConnection(promise)
|
|
58
|
+
verify { promise.reject(any(), any<String>()) }
|
|
59
|
+
verify(exactly = 0) { promise.resolve(any()) }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
fun `initConnection start new connection succeeds`() {
|
|
64
|
+
every { billingClient.isReady } returns false
|
|
65
|
+
val listener = slot<BillingClientStateListener>()
|
|
66
|
+
every { billingClient.startConnection(capture(listener)) } answers {
|
|
67
|
+
listener.captured.onBillingSetupFinished(BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK).build())
|
|
68
|
+
}
|
|
69
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
70
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
71
|
+
|
|
72
|
+
module.initConnection(promise)
|
|
73
|
+
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
74
|
+
verify { promise.resolve(any()) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Test
|
|
78
|
+
fun `initConnection start new connection fails`() {
|
|
79
|
+
every { billingClient.isReady } returns false
|
|
80
|
+
val listener = slot<BillingClientStateListener>()
|
|
81
|
+
every { billingClient.startConnection(capture(listener)) } answers {
|
|
82
|
+
listener.captured.onBillingSetupFinished(BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.ERROR).build())
|
|
83
|
+
}
|
|
84
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
85
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
86
|
+
|
|
87
|
+
module.initConnection(promise)
|
|
88
|
+
verify { promise.reject(any(), any<String>()) }
|
|
89
|
+
verify(exactly = 0) { promise.resolve(any()) }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun `endConnection resolves`() {
|
|
94
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
95
|
+
|
|
96
|
+
module.endConnection(promise)
|
|
97
|
+
|
|
98
|
+
verify { billingClient.endConnection() }
|
|
99
|
+
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
100
|
+
verify { promise.resolve(true) }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Test
|
|
104
|
+
fun `flushFailedPurchasesCachedAsPending resolves to false if no pending purchases`() {
|
|
105
|
+
every { billingClient.isReady } returns true
|
|
106
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
107
|
+
val listener = slot<PurchasesResponseListener>()
|
|
108
|
+
every { billingClient.queryPurchasesAsync(any(), capture(listener)) } answers {
|
|
109
|
+
listener.captured.onQueryPurchasesResponse(BillingResult.newBuilder().build(), listOf())
|
|
110
|
+
}
|
|
111
|
+
module.flushFailedPurchasesCachedAsPending(promise)
|
|
112
|
+
|
|
113
|
+
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
114
|
+
verify { promise.resolve(false) } // empty list
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Test
|
|
118
|
+
fun getItemsByType() {
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
fun getAvailableItemsByType() {
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Test
|
|
126
|
+
fun getPurchaseHistoryByType() {
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@Test
|
|
130
|
+
fun buyItemByType() {
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@Test
|
|
134
|
+
fun acknowledgePurchase() {
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun consumeProduct() {
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@Test
|
|
142
|
+
fun onPurchasesUpdated() {
|
|
143
|
+
}
|
|
144
|
+
}
|