react-native-iap 12.4.3 → 12.4.4

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.
@@ -70,6 +70,9 @@ android {
70
70
  }
71
71
 
72
72
  testOptions {
73
+ unitTests.all {
74
+ jvmArgs '-noverify'
75
+ }
73
76
  unitTests.returnDefaultValues = true
74
77
  }
75
78
  }
@@ -0,0 +1,20 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import android.content.Context
4
+ import com.amazon.device.iap.PurchasingListener
5
+ import com.amazon.device.iap.model.FulfillmentResult
6
+ import com.amazon.device.iap.model.RequestId
7
+
8
+ interface PurchasingServiceProxy {
9
+ fun registerListener(var0: Context?, var1: PurchasingListener?)
10
+
11
+ fun getUserData(): RequestId
12
+
13
+ fun purchase(var0: String?): RequestId
14
+
15
+ fun getProductData(var0: Set<String?>?): RequestId
16
+
17
+ fun getPurchaseUpdates(var0: Boolean): RequestId
18
+
19
+ fun notifyFulfillment(var0: String?, var1: FulfillmentResult?)
20
+ }
@@ -0,0 +1,33 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import android.content.Context
4
+ import com.amazon.device.iap.PurchasingListener
5
+ import com.amazon.device.iap.PurchasingService
6
+ import com.amazon.device.iap.model.FulfillmentResult
7
+ import com.amazon.device.iap.model.RequestId
8
+
9
+ class PurchasingServiceProxyAmazonImpl : PurchasingServiceProxy {
10
+ override fun registerListener(var0: Context?, var1: PurchasingListener?) {
11
+ return PurchasingService.registerListener(var0, var1)
12
+ }
13
+
14
+ override fun getUserData(): RequestId {
15
+ return PurchasingService.getUserData()
16
+ }
17
+
18
+ override fun purchase(var0: String?): RequestId {
19
+ return PurchasingService.purchase(var0)
20
+ }
21
+
22
+ override fun getProductData(var0: Set<String?>?): RequestId {
23
+ return PurchasingService.getProductData(var0)
24
+ }
25
+
26
+ override fun getPurchaseUpdates(var0: Boolean): RequestId {
27
+ return PurchasingService.getPurchaseUpdates(var0)
28
+ }
29
+
30
+ override fun notifyFulfillment(var0: String?, var1: FulfillmentResult?) {
31
+ return PurchasingService.notifyFulfillment(var0, var1)
32
+ }
33
+ }
@@ -2,8 +2,6 @@ package com.dooboolab.RNIap
2
2
 
3
3
  import android.util.Log
4
4
  import com.amazon.device.iap.PurchasingListener
5
- import com.amazon.device.iap.PurchasingService
6
- import com.amazon.device.iap.model.Product
7
5
  import com.amazon.device.iap.model.ProductDataResponse
8
6
  import com.amazon.device.iap.model.ProductType
9
7
  import com.amazon.device.iap.model.PurchaseResponse
@@ -18,26 +16,21 @@ import com.facebook.react.bridge.WritableNativeArray
18
16
  import com.facebook.react.bridge.WritableNativeMap
19
17
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
20
18
  import java.lang.NumberFormatException
21
- import java.util.ArrayList
22
19
 
23
20
  val ProductType.typeString: String
24
21
  get() = if (this == ProductType.ENTITLED || this == ProductType.CONSUMABLE) "inapp" else "subs"
25
22
 
26
- class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingListener {
27
- private val skus: MutableList<Product>
23
+ class RNIapAmazonListener(
24
+ private val reactContext: ReactContext,
25
+ private val purchasingService: PurchasingServiceProxy
26
+ ) : PurchasingListener {
28
27
 
29
28
  override fun onProductDataResponse(response: ProductDataResponse) {
30
- val requestId = response.requestId.toString()
31
29
  when (response.requestStatus) {
32
30
  ProductDataResponse.RequestStatus.SUCCESSFUL -> {
33
31
  val productData = response.productData
34
- val unavailableSkus = response.unavailableSkus
35
32
  val items = WritableNativeArray()
36
33
  for ((_, product) in productData) {
37
- if (!skus.contains(product)) {
38
- skus.add(product)
39
- }
40
-
41
34
  var priceNumber: Number = 0.00
42
35
  val priceString = product.price
43
36
  try {
@@ -107,7 +100,7 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
107
100
  availableItems.pushMap(promiseItem)
108
101
  }
109
102
  if (response.hasMore()) {
110
- PurchasingService.getPurchaseUpdates(false)
103
+ purchasingService.getPurchaseUpdates(false)
111
104
  } else {
112
105
  if (purchases.size > 0 && promiseItem != null) {
113
106
  PromiseUtils
@@ -197,13 +190,8 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
197
190
  override fun onPurchaseResponse(response: PurchaseResponse) {
198
191
  val requestId = response.requestId.toString()
199
192
  val userId = response.userData.userId
200
- val status = response.requestStatus
201
193
 
202
- // Info for potential error reporting
203
- val debugMessage: String?
204
- var errorCode = PromiseUtils.E_UNKNOWN
205
- val error = Arguments.createMap()
206
- when (status) {
194
+ when (response.requestStatus) {
207
195
  PurchaseResponse.RequestStatus.SUCCESSFUL -> {
208
196
  val receipt = response.receipt
209
197
  val userData = response.userData
@@ -218,8 +206,9 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
218
206
  )
219
207
  }
220
208
  PurchaseResponse.RequestStatus.ALREADY_PURCHASED -> {
221
- debugMessage = "You already own this item."
222
- errorCode = PromiseUtils.E_ALREADY_OWNED
209
+ val error = Arguments.createMap()
210
+ val debugMessage = "You already own this item."
211
+ val errorCode = PromiseUtils.E_ALREADY_OWNED
223
212
  error.putInt("responseCode", 0)
224
213
  error.putString("debugMessage", debugMessage)
225
214
  error.putString("code", errorCode)
@@ -234,9 +223,10 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
234
223
  )
235
224
  }
236
225
  PurchaseResponse.RequestStatus.FAILED -> {
237
- debugMessage =
226
+ val error = Arguments.createMap()
227
+ val debugMessage =
238
228
  "An unknown or unexpected error has occurred. Please try again later."
239
- errorCode = PromiseUtils.E_UNKNOWN
229
+ val errorCode = PromiseUtils.E_UNKNOWN
240
230
  error.putInt("responseCode", 0)
241
231
  error.putString("debugMessage", debugMessage)
242
232
  error.putString("code", errorCode)
@@ -251,8 +241,9 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
251
241
  )
252
242
  }
253
243
  PurchaseResponse.RequestStatus.INVALID_SKU -> {
254
- debugMessage = "That item is unavailable."
255
- errorCode = PromiseUtils.E_ITEM_UNAVAILABLE
244
+ val error = Arguments.createMap()
245
+ val debugMessage = "That item is unavailable."
246
+ val errorCode = PromiseUtils.E_ITEM_UNAVAILABLE
256
247
  error.putInt("responseCode", 0)
257
248
  error.putString("debugMessage", debugMessage)
258
249
  error.putString("code", errorCode)
@@ -267,8 +258,9 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
267
258
  )
268
259
  }
269
260
  PurchaseResponse.RequestStatus.NOT_SUPPORTED -> {
270
- debugMessage = "This feature is not available on your device."
271
- errorCode = PromiseUtils.E_SERVICE_ERROR
261
+ val error = Arguments.createMap()
262
+ val debugMessage = "This feature is not available on your device."
263
+ val errorCode = PromiseUtils.E_SERVICE_ERROR
272
264
  error.putInt("responseCode", 0)
273
265
  error.putString("debugMessage", debugMessage)
274
266
  error.putString("code", errorCode)
@@ -314,11 +306,8 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
314
306
  )
315
307
  }
316
308
  }
317
- fun clear() {
318
- skus.clear()
319
- }
320
309
 
321
- private fun sendEvent(
310
+ fun sendEvent(
322
311
  reactContext: ReactContext,
323
312
  eventName: String,
324
313
  params: WritableMap?
@@ -344,8 +333,4 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
344
333
  private const val E_USER_DATA_RESPONSE_NOT_SUPPORTED = "E_USER_DATA_RESPONSE_NOT_SUPPORTED"
345
334
  const val TAG = "RNIapAmazonListener"
346
335
  }
347
-
348
- init {
349
- skus = ArrayList()
350
- }
351
336
  }
@@ -1,9 +1,11 @@
1
1
  package com.dooboolab.RNIap
2
2
 
3
+ import android.os.Handler
4
+ import android.os.Looper
3
5
  import android.util.Log
4
6
  import com.amazon.device.drm.LicensingService
5
7
  import com.amazon.device.drm.model.LicenseResponse
6
- import com.amazon.device.iap.PurchasingService
8
+ import com.amazon.device.iap.PurchasingListener
7
9
  import com.amazon.device.iap.model.FulfillmentResult
8
10
  import com.facebook.react.bridge.LifecycleEventListener
9
11
  import com.facebook.react.bridge.Promise
@@ -11,14 +13,17 @@ import com.facebook.react.bridge.ReactApplicationContext
11
13
  import com.facebook.react.bridge.ReactContextBaseJavaModule
12
14
  import com.facebook.react.bridge.ReactMethod
13
15
  import com.facebook.react.bridge.ReadableArray
14
- import com.facebook.react.bridge.UiThreadUtil
15
16
  import com.facebook.react.module.annotations.ReactModule
16
17
 
17
18
  @ReactModule(name = RNIapAmazonModule.TAG)
18
- class RNIapAmazonModule(reactContext: ReactApplicationContext) :
19
+ class RNIapAmazonModule(
20
+ reactContext: ReactApplicationContext,
21
+ private val purchasingService: PurchasingServiceProxy = PurchasingServiceProxyAmazonImpl(),
22
+ private val handler: Handler = Handler(Looper.getMainLooper()),
23
+ private val amazonListener: PurchasingListener = RNIapAmazonListener(reactContext, purchasingService)
24
+ ) :
19
25
  ReactContextBaseJavaModule(reactContext) {
20
26
  var hasListener = false
21
- private var amazonListener: RNIapAmazonListener? = null
22
27
  override fun getName(): String {
23
28
  return TAG
24
29
  }
@@ -26,20 +31,19 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
26
31
  @ReactMethod
27
32
  fun initConnection(promise: Promise) {
28
33
  val context = reactApplicationContext
29
- val amazonListener = RNIapAmazonListener(context)
30
- this.amazonListener = amazonListener
31
- UiThreadUtil.runOnUiThread {
34
+
35
+ handler.postDelayed({
32
36
  try {
33
- PurchasingService.registerListener(context.applicationContext, amazonListener)
37
+ purchasingService.registerListener(context.applicationContext, amazonListener)
34
38
  hasListener = true
35
39
  // Prefetch user and purchases as per Amazon SDK documentation:
36
- PurchasingService.getUserData()
37
- PurchasingService.getPurchaseUpdates(false)
40
+ purchasingService.getUserData()
41
+ purchasingService.getPurchaseUpdates(false)
38
42
  promise.safeResolve(true)
39
43
  } catch (e: Exception) {
40
44
  promise.safeReject("Error initializing Amazon appstore sdk", e)
41
45
  }
42
- }
46
+ }, 0L)
43
47
  }
44
48
 
45
49
  @ReactMethod
@@ -84,14 +88,13 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
84
88
  @ReactMethod
85
89
  fun endConnection(promise: Promise) {
86
90
  PromiseUtils.rejectAllPendingPromises()
87
- amazonListener?.clear()
88
91
  hasListener = false
89
92
  promise.resolve(true)
90
93
  }
91
94
 
92
95
  @ReactMethod
93
96
  fun getUser(promise: Promise) {
94
- val requestId = PurchasingService.getUserData()
97
+ val requestId = purchasingService.getUserData()
95
98
  PromiseUtils.addPromiseForKey(PROMISE_GET_USER_DATA, promise)
96
99
  }
97
100
 
@@ -114,13 +117,13 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
114
117
  ii++
115
118
  }
116
119
  PromiseUtils.addPromiseForKey(PROMISE_GET_PRODUCT_DATA, promise)
117
- val requestId = PurchasingService.getProductData(productSkus)
120
+ val requestId = purchasingService.getProductData(productSkus)
118
121
  }
119
122
 
120
123
  @ReactMethod
121
124
  fun getAvailableItems(promise: Promise) {
122
125
  PromiseUtils.addPromiseForKey(PROMISE_QUERY_AVAILABLE_ITEMS, promise)
123
- PurchasingService.getPurchaseUpdates(true)
126
+ purchasingService.getPurchaseUpdates(true)
124
127
  }
125
128
 
126
129
  @ReactMethod
@@ -129,7 +132,7 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
129
132
  promise: Promise
130
133
  ) {
131
134
  PromiseUtils.addPromiseForKey(PROMISE_BUY_ITEM, promise)
132
- val requestId = PurchasingService.purchase(sku)
135
+ val requestId = purchasingService.purchase(sku)
133
136
  }
134
137
 
135
138
  @ReactMethod
@@ -138,7 +141,7 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
138
141
  developerPayLoad: String?,
139
142
  promise: Promise
140
143
  ) {
141
- PurchasingService.notifyFulfillment(token, FulfillmentResult.FULFILLED)
144
+ purchasingService.notifyFulfillment(token, FulfillmentResult.FULFILLED)
142
145
  promise.resolve(true)
143
146
  }
144
147
 
@@ -148,13 +151,13 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
148
151
  developerPayLoad: String?,
149
152
  promise: Promise
150
153
  ) {
151
- PurchasingService.notifyFulfillment(token, FulfillmentResult.FULFILLED)
154
+ purchasingService.notifyFulfillment(token, FulfillmentResult.FULFILLED)
152
155
  promise.resolve(true)
153
156
  }
154
157
 
155
158
  private fun sendUnconsumedPurchases(promise: Promise) {
156
159
  PromiseUtils.addPromiseForKey(PROMISE_QUERY_PURCHASES, promise)
157
- PurchasingService.getPurchaseUpdates(false)
160
+ purchasingService.getPurchaseUpdates(false)
158
161
  }
159
162
 
160
163
  @ReactMethod
@@ -189,8 +192,8 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
189
192
  */
190
193
  override fun onHostResume() {
191
194
  if (hasListener) {
192
- PurchasingService.getUserData()
193
- PurchasingService.getPurchaseUpdates(false)
195
+ purchasingService.getUserData()
196
+ purchasingService.getPurchaseUpdates(false)
194
197
  }
195
198
  }
196
199
  override fun onHostPause() {}
@@ -0,0 +1,151 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import android.os.Handler
4
+ import com.amazon.device.iap.model.PurchaseResponse
5
+ import com.amazon.device.iap.model.Receipt
6
+ import com.amazon.device.iap.model.RequestId
7
+ import com.amazon.device.iap.model.UserData
8
+ import com.facebook.react.bridge.Arguments
9
+ import com.facebook.react.bridge.Promise
10
+ import com.facebook.react.bridge.ReactApplicationContext
11
+ import com.facebook.react.bridge.WritableMap
12
+ import io.mockk.MockKAnnotations
13
+ import io.mockk.Runs
14
+ import io.mockk.every
15
+ import io.mockk.impl.annotations.MockK
16
+ import io.mockk.impl.annotations.RelaxedMockK
17
+ import io.mockk.just
18
+ import io.mockk.mockk
19
+ import io.mockk.mockkStatic
20
+ import io.mockk.slot
21
+ import io.mockk.spyk
22
+ import io.mockk.verify
23
+ import org.junit.Assert.assertEquals
24
+ import org.junit.Before
25
+ import org.junit.Test
26
+ import java.util.*
27
+
28
+ class RNIapAmazonModuleTest {
29
+
30
+ @MockK
31
+ lateinit var context: ReactApplicationContext
32
+
33
+ @RelaxedMockK
34
+ lateinit var purchasingServiceProxy: PurchasingServiceProxy
35
+
36
+ @MockK
37
+ lateinit var mainThreadHandler: Handler
38
+
39
+ private lateinit var listener: RNIapAmazonListener
40
+
41
+ private lateinit var module: RNIapAmazonModule
42
+
43
+ @Before
44
+ fun setUp() {
45
+ MockKAnnotations.init(this, relaxUnitFun = true)
46
+ listener = spyk(RNIapAmazonListener(context, purchasingServiceProxy))
47
+ module = RNIapAmazonModule(context, purchasingServiceProxy, mainThreadHandler, listener)
48
+ }
49
+
50
+ @Test
51
+ fun `initConnection should resolve to true`() {
52
+ every { context.applicationContext } returns mockk()
53
+
54
+ val promise = mockk<Promise>(relaxed = true)
55
+ val slot = slot<Runnable>()
56
+ every { mainThreadHandler.postDelayed(capture(slot), any()) } answers { slot.captured.run(); true }
57
+ module.initConnection(promise)
58
+ verify(exactly = 0) { promise.reject(any(), any<String>()) }
59
+ verify { promise.resolve(true) }
60
+ verify { purchasingServiceProxy.registerListener(any(), any()) }
61
+ }
62
+
63
+ @Test
64
+ fun `Purchase Item`() {
65
+ val purchaseResponse = mockk<PurchaseResponse>() {
66
+ every { requestId } returns RequestId.fromString("0")
67
+
68
+ every { requestStatus } returns PurchaseResponse.RequestStatus.SUCCESSFUL
69
+ val mReceipt = mockk<Receipt>(relaxed = true) {
70
+ every { sku } returns "mySku"
71
+ every { purchaseDate } returns Date()
72
+ every { receiptId } returns "rId"
73
+ }
74
+ every { receipt } returns mReceipt
75
+ val mUserData = mockk<UserData>(relaxed = true) {
76
+ every { userId } returns "uid1"
77
+ }
78
+ every { userData } returns mUserData
79
+ }
80
+
81
+ every { listener.sendEvent(any(), any(), any()) } just Runs
82
+
83
+ every { purchasingServiceProxy.purchase(any()) } answers {
84
+ listener.onPurchaseResponse(
85
+ purchaseResponse
86
+ ); RequestId.fromString("0")
87
+ }
88
+
89
+ val itemsMap = mockk<WritableMap>(relaxed = true) {
90
+ every { getString("productId") } returns "mySku"
91
+ }
92
+ mockkStatic(Arguments::class)
93
+
94
+ every { Arguments.createMap() } returns itemsMap
95
+
96
+ val promise = mockk<Promise>(relaxed = true)
97
+
98
+ module.buyItemByType("mySku", promise)
99
+ verify(exactly = 0) { promise.reject(any(), any<String>()) }
100
+ val response = slot<WritableMap>()
101
+ verify { promise.resolve(capture(response)) }
102
+ assertEquals("mySku", response.captured.getString("productId"))
103
+ verify { listener.sendEvent(any(), "purchase-updated", any()) }
104
+ verify(exactly = 0) { purchasingServiceProxy.getPurchaseUpdates(false) }
105
+ }
106
+
107
+ // @Test
108
+ // fun `initConnection Play Services not available on device should reject`() {
109
+ // }
110
+ //
111
+ // @Test
112
+ // fun `initConnection start new connection fails`() {
113
+ // }
114
+ //
115
+ // @Test
116
+ // fun `endConnection resolves`() {
117
+ // }
118
+ //
119
+ // @Test
120
+ // fun `flushFailedPurchasesCachedAsPending resolves to false if no pending purchases`() {
121
+ // }
122
+ //
123
+ // @Test
124
+ // fun `flushFailedPurchasesCachedAsPending resolves to true if pending purchases`() {
125
+ // }
126
+ //
127
+ // @Test
128
+ // fun getItemsByType() {
129
+ // }
130
+ //
131
+ // @Test
132
+ // fun getAvailableItemsByType() {
133
+ // }
134
+ //
135
+ // @Test
136
+ // fun getPurchaseHistoryByType() {
137
+ // }
138
+ //
139
+ //
140
+ // @Test
141
+ // fun acknowledgePurchase() {
142
+ // }
143
+ //
144
+ // @Test
145
+ // fun consumeProduct() {
146
+ // }
147
+ //
148
+ // @Test
149
+ // fun onPurchasesUpdated() {
150
+ // }
151
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "12.4.3",
3
+ "version": "12.4.4",
4
4
  "description": "React Native In App Purchase Module.",
5
5
  "repository": "https://github.com/dooboolab/react-native-iap",
6
6
  "author": "dooboolab <support@dooboolab.com> (https://github.com/dooboolab)",