react-native-iap 12.4.13 → 12.5.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.
@@ -0,0 +1,10 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+
5
+ interface EventSender {
6
+ fun sendEvent(
7
+ eventName: String,
8
+ params: WritableMap?
9
+ )
10
+ }
@@ -0,0 +1,33 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import android.app.Activity
4
+ import android.util.Log
5
+ import com.amazon.device.iap.PurchasingService
6
+
7
+ /**
8
+ * In order of the IAP process to show correctly, AmazonPurchasingService must be registered on Activity.onCreate
9
+ * registering it in on Application.onCreate will not throw an error but it will now show the Native purchasing screen
10
+ */
11
+ class RNIapActivityListener {
12
+ companion object {
13
+ @JvmStatic
14
+ var hasListener = false
15
+
16
+ @JvmStatic
17
+ var amazonListener: RNIapAmazonListener? = null
18
+
19
+ @JvmStatic
20
+ fun registerActivity(activity: Activity) {
21
+ amazonListener = RNIapAmazonListener(null, null)
22
+ try {
23
+ PurchasingService.registerListener(activity, amazonListener)
24
+ hasListener = true
25
+ // Prefetch user and purchases as per Amazon SDK documentation:
26
+ PurchasingService.getUserData()
27
+ PurchasingService.getPurchaseUpdates(false)
28
+ } catch (e: Exception) {
29
+ Log.e(RNIapAmazonModule.TAG, "Error initializing Amazon appstore sdk", e)
30
+ }
31
+ }
32
+ }
33
+ }
@@ -10,19 +10,17 @@ import com.amazon.device.iap.model.Receipt
10
10
  import com.amazon.device.iap.model.UserData
11
11
  import com.amazon.device.iap.model.UserDataResponse
12
12
  import com.facebook.react.bridge.Arguments
13
- import com.facebook.react.bridge.ReactContext
14
13
  import com.facebook.react.bridge.WritableMap
15
14
  import com.facebook.react.bridge.WritableNativeArray
16
15
  import com.facebook.react.bridge.WritableNativeMap
17
- import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
18
16
  import java.lang.NumberFormatException
19
17
 
20
18
  val ProductType.typeString: String
21
19
  get() = if (this == ProductType.ENTITLED || this == ProductType.CONSUMABLE) "inapp" else "subs"
22
20
 
23
21
  class RNIapAmazonListener(
24
- private val reactContext: ReactContext,
25
- private val purchasingService: PurchasingServiceProxy
22
+ var eventSender: EventSender?,
23
+ var purchasingService: PurchasingServiceProxy?
26
24
  ) : PurchasingListener {
27
25
 
28
26
  override fun onProductDataResponse(response: ProductDataResponse) {
@@ -96,11 +94,11 @@ class RNIapAmazonListener(
96
94
  val item = receiptToMap(userData, receipt)
97
95
  promiseItem = WritableNativeMap()
98
96
  promiseItem.merge(item)
99
- sendEvent(reactContext, "purchase-updated", item)
97
+ eventSender?.sendEvent("purchase-updated", item)
100
98
  availableItems.pushMap(promiseItem)
101
99
  }
102
100
  if (response.hasMore()) {
103
- purchasingService.getPurchaseUpdates(false)
101
+ purchasingService?.getPurchaseUpdates(false)
104
102
  } else {
105
103
  if (purchases.size > 0 && promiseItem != null) {
106
104
  PromiseUtils
@@ -129,7 +127,7 @@ class RNIapAmazonListener(
129
127
  error.putString("debugMessage", debugMessage)
130
128
  error.putString("code", errorCode)
131
129
  error.putString("message", debugMessage)
132
- sendEvent(reactContext, "purchase-error", error)
130
+ eventSender?.sendEvent("purchase-error", error)
133
131
  PromiseUtils
134
132
  .rejectPromisesForKey(
135
133
  RNIapAmazonModule.PROMISE_QUERY_PURCHASES,
@@ -153,7 +151,7 @@ class RNIapAmazonListener(
153
151
  error.putString("debugMessage", debugMessage)
154
152
  error.putString("code", errorCode)
155
153
  error.putString("message", debugMessage)
156
- sendEvent(reactContext, "purchase-error", error)
154
+ eventSender?.sendEvent("purchase-error", error)
157
155
  PromiseUtils
158
156
  .rejectPromisesForKey(
159
157
  RNIapAmazonModule.PROMISE_QUERY_PURCHASES,
@@ -198,7 +196,7 @@ class RNIapAmazonListener(
198
196
  val item = receiptToMap(userData, receipt)
199
197
  val promiseItem: WritableMap = Arguments.createMap()
200
198
  promiseItem.merge(item)
201
- sendEvent(reactContext, "purchase-updated", item)
199
+ eventSender?.sendEvent("purchase-updated", item)
202
200
  PromiseUtils
203
201
  .resolvePromisesForKey(
204
202
  RNIapAmazonModule.PROMISE_BUY_ITEM,
@@ -213,7 +211,7 @@ class RNIapAmazonListener(
213
211
  error.putString("debugMessage", debugMessage)
214
212
  error.putString("code", errorCode)
215
213
  error.putString("message", debugMessage)
216
- sendEvent(reactContext, "purchase-error", error)
214
+ eventSender?.sendEvent("purchase-error", error)
217
215
  PromiseUtils
218
216
  .rejectPromisesForKey(
219
217
  RNIapAmazonModule.PROMISE_BUY_ITEM,
@@ -231,7 +229,7 @@ class RNIapAmazonListener(
231
229
  error.putString("debugMessage", debugMessage)
232
230
  error.putString("code", errorCode)
233
231
  error.putString("message", debugMessage)
234
- sendEvent(reactContext, "purchase-error", error)
232
+ eventSender?.sendEvent("purchase-error", error)
235
233
  PromiseUtils
236
234
  .rejectPromisesForKey(
237
235
  RNIapAmazonModule.PROMISE_BUY_ITEM,
@@ -248,7 +246,7 @@ class RNIapAmazonListener(
248
246
  error.putString("debugMessage", debugMessage)
249
247
  error.putString("code", errorCode)
250
248
  error.putString("message", debugMessage)
251
- sendEvent(reactContext, "purchase-error", error)
249
+ eventSender?.sendEvent("purchase-error", error)
252
250
  PromiseUtils
253
251
  .rejectPromisesForKey(
254
252
  RNIapAmazonModule.PROMISE_BUY_ITEM,
@@ -265,7 +263,7 @@ class RNIapAmazonListener(
265
263
  error.putString("debugMessage", debugMessage)
266
264
  error.putString("code", errorCode)
267
265
  error.putString("message", debugMessage)
268
- sendEvent(reactContext, "purchase-error", error)
266
+ eventSender?.sendEvent("purchase-error", error)
269
267
  PromiseUtils
270
268
  .rejectPromisesForKey(
271
269
  RNIapAmazonModule.PROMISE_BUY_ITEM,
@@ -307,16 +305,6 @@ class RNIapAmazonListener(
307
305
  }
308
306
  }
309
307
 
310
- fun sendEvent(
311
- reactContext: ReactContext,
312
- eventName: String,
313
- params: WritableMap?
314
- ) {
315
- reactContext
316
- .getJSModule(RCTDeviceEventEmitter::class.java)
317
- .emit(eventName, params)
318
- }
319
-
320
308
  companion object {
321
309
  private const val E_PRODUCT_DATA_RESPONSE_FAILED = "E_PRODUCT_DATA_RESPONSE_FAILED"
322
310
  private const val E_PRODUCT_DATA_RESPONSE_NOT_SUPPORTED =
@@ -1,11 +1,8 @@
1
1
  package com.dooboolab.RNIap
2
2
 
3
- import android.os.Handler
4
- import android.os.Looper
5
3
  import android.util.Log
6
4
  import com.amazon.device.drm.LicensingService
7
5
  import com.amazon.device.drm.model.LicenseResponse
8
- import com.amazon.device.iap.PurchasingListener
9
6
  import com.amazon.device.iap.model.FulfillmentResult
10
7
  import com.facebook.react.bridge.LifecycleEventListener
11
8
  import com.facebook.react.bridge.Promise
@@ -13,37 +10,41 @@ import com.facebook.react.bridge.ReactApplicationContext
13
10
  import com.facebook.react.bridge.ReactContextBaseJavaModule
14
11
  import com.facebook.react.bridge.ReactMethod
15
12
  import com.facebook.react.bridge.ReadableArray
13
+ import com.facebook.react.bridge.WritableMap
16
14
  import com.facebook.react.module.annotations.ReactModule
15
+ import com.facebook.react.modules.core.DeviceEventManagerModule
17
16
 
18
17
  @ReactModule(name = RNIapAmazonModule.TAG)
19
18
  class RNIapAmazonModule(
20
- reactContext: ReactApplicationContext,
19
+ private val reactContext: ReactApplicationContext,
21
20
  private val purchasingService: PurchasingServiceProxy = PurchasingServiceProxyAmazonImpl(),
22
- private val handler: Handler = Handler(Looper.getMainLooper()),
23
- private val amazonListener: PurchasingListener = RNIapAmazonListener(reactContext, purchasingService)
21
+ private var eventSender: EventSender? = null
24
22
  ) :
25
23
  ReactContextBaseJavaModule(reactContext) {
26
- var hasListener = false
27
24
  override fun getName(): String {
28
25
  return TAG
29
26
  }
30
27
 
31
28
  @ReactMethod
32
29
  fun initConnection(promise: Promise) {
33
- val context = reactApplicationContext
34
-
35
- handler.postDelayed({
36
- try {
37
- purchasingService.registerListener(context.applicationContext, amazonListener)
38
- hasListener = true
39
- // Prefetch user and purchases as per Amazon SDK documentation:
40
- purchasingService.getUserData()
41
- purchasingService.getPurchaseUpdates(false)
42
- promise.safeResolve(true)
43
- } catch (e: Exception) {
44
- promise.safeReject("Error initializing Amazon appstore sdk", e)
30
+ if (RNIapActivityListener.amazonListener == null) {
31
+ promise.safeReject(PromiseUtils.E_DEVELOPER_ERROR, Exception("RNIapActivityListener is not registered in your MainActivity.onCreate"))
32
+ return
33
+ }
34
+ if (eventSender == null) {
35
+ eventSender = object : EventSender {
36
+ private val rctDeviceEventEmitter = reactContext
37
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
38
+
39
+ override fun sendEvent(eventName: String, params: WritableMap?) {
40
+ rctDeviceEventEmitter
41
+ .emit(eventName, params)
42
+ }
45
43
  }
46
- }, 0L)
44
+ }
45
+ RNIapActivityListener.amazonListener?.eventSender = eventSender
46
+ RNIapActivityListener.amazonListener?.purchasingService = purchasingService
47
+ promise.resolve(true)
47
48
  }
48
49
 
49
50
  @ReactMethod
@@ -88,7 +89,7 @@ class RNIapAmazonModule(
88
89
  @ReactMethod
89
90
  fun endConnection(promise: Promise) {
90
91
  PromiseUtils.rejectAllPendingPromises()
91
- hasListener = false
92
+ RNIapActivityListener.hasListener = false
92
93
  promise.resolve(true)
93
94
  }
94
95
 
@@ -191,7 +192,7 @@ class RNIapAmazonModule(
191
192
  * We should fetch updates on resume
192
193
  */
193
194
  override fun onHostResume() {
194
- if (hasListener) {
195
+ if (RNIapActivityListener.hasListener) {
195
196
  purchasingService.getUserData()
196
197
  purchasingService.getPurchaseUpdates(false)
197
198
  }
@@ -0,0 +1,15 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import android.app.Activity
4
+
5
+ /**
6
+ * Currently only needed for Amazon IAP
7
+ */
8
+ class RNIapActivityListener {
9
+ companion object {
10
+ @JvmStatic
11
+ fun registerActivity(activity: Activity) {
12
+ // No op
13
+ }
14
+ }
15
+ }
@@ -1,6 +1,5 @@
1
1
  package com.dooboolab.RNIap
2
2
 
3
- import android.os.Handler
4
3
  import com.amazon.device.iap.model.PurchaseResponse
5
4
  import com.amazon.device.iap.model.Receipt
6
5
  import com.amazon.device.iap.model.RequestId
@@ -21,6 +20,7 @@ import io.mockk.slot
21
20
  import io.mockk.spyk
22
21
  import io.mockk.verify
23
22
  import org.junit.Assert.assertEquals
23
+ import org.junit.Assert.assertNotNull
24
24
  import org.junit.Before
25
25
  import org.junit.Test
26
26
  import java.util.*
@@ -34,7 +34,7 @@ class RNIapAmazonModuleTest {
34
34
  lateinit var purchasingServiceProxy: PurchasingServiceProxy
35
35
 
36
36
  @MockK
37
- lateinit var mainThreadHandler: Handler
37
+ lateinit var eventSender: EventSender
38
38
 
39
39
  private lateinit var listener: RNIapAmazonListener
40
40
 
@@ -43,21 +43,36 @@ class RNIapAmazonModuleTest {
43
43
  @Before
44
44
  fun setUp() {
45
45
  MockKAnnotations.init(this, relaxUnitFun = true)
46
- listener = spyk(RNIapAmazonListener(context, purchasingServiceProxy))
47
- module = RNIapAmazonModule(context, purchasingServiceProxy, mainThreadHandler, listener)
46
+ listener = spyk(RNIapAmazonListener(eventSender, purchasingServiceProxy))
47
+ module = RNIapAmazonModule(context, purchasingServiceProxy, eventSender)
48
48
  }
49
49
 
50
50
  @Test
51
- fun `initConnection should resolve to true`() {
51
+ fun `initConnection should resolve to true if RNIapActivityListener is configured`() {
52
52
  every { context.applicationContext } returns mockk()
53
53
 
54
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)
55
+
58
56
  verify(exactly = 0) { promise.reject(any(), any<String>()) }
57
+
58
+ RNIapActivityListener.registerActivity(mockk())
59
+
60
+ module.initConnection(promise)
61
+ // should set eventSender and purchase service
62
+ assertNotNull(RNIapActivityListener.amazonListener?.purchasingService)
63
+ assertNotNull(RNIapActivityListener.amazonListener?.eventSender)
64
+
59
65
  verify { promise.resolve(true) }
60
- verify { purchasingServiceProxy.registerListener(any(), any()) }
66
+ }
67
+
68
+ @Test
69
+ fun `initConnection should reject if RNIapActivityListener is not configured`() {
70
+ every { context.applicationContext } returns mockk()
71
+
72
+ val promise = mockk<Promise>(relaxed = true)
73
+ module.initConnection(promise)
74
+ verify(exactly = 0) { promise.reject(any(), any<String>()) }
75
+ verify { promise.reject(PromiseUtils.E_DEVELOPER_ERROR, any(), any<Throwable>()) }
61
76
  }
62
77
 
63
78
  @Test
@@ -78,7 +93,7 @@ class RNIapAmazonModuleTest {
78
93
  every { userData } returns mUserData
79
94
  }
80
95
 
81
- every { listener.sendEvent(any(), any(), any()) } just Runs
96
+ every { eventSender.sendEvent(any(), any()) } just Runs
82
97
 
83
98
  every { purchasingServiceProxy.purchase(any()) } answers {
84
99
  listener.onPurchaseResponse(
@@ -100,7 +115,7 @@ class RNIapAmazonModuleTest {
100
115
  val response = slot<WritableMap>()
101
116
  verify { promise.resolve(capture(response)) }
102
117
  assertEquals("mySku", response.captured.getString("productId"))
103
- verify { listener.sendEvent(any(), "purchase-updated", any()) }
118
+ verify { eventSender.sendEvent("purchase-updated", any()) }
104
119
  verify(exactly = 0) { purchasingServiceProxy.getPurchaseUpdates(false) }
105
120
  }
106
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "12.4.13",
3
+ "version": "12.5.0",
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)",