react-native-iap 8.2.1 → 8.4.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.
Binary file
package/RNIap.podspec CHANGED
@@ -11,6 +11,11 @@ Pod::Spec.new do |s|
11
11
  s.platforms = { :ios => "9.0", :tvos => "9.0" }
12
12
  s.source = { :git => "https://github.com/dooboolab/react-native-iap.git", :tag => "#{s.version}" }
13
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
+ }
14
19
  s.swift_version = "4.2"
15
20
  s.requires_arc = true
16
21
 
@@ -66,7 +66,7 @@ repositories {
66
66
 
67
67
  dependencies {
68
68
  implementation 'com.facebook.react:react-native:+'
69
- testImplementation 'junit:junit:4.12'
69
+ testImplementation 'junit:junit:4.13.1'
70
70
  testImplementation "io.mockk:mockk:1.12.4"
71
71
  playImplementation 'com.android.billingclient:billing:4.0.0'
72
72
  def playServicesVersion = safeExtGet('playServicesVersion', DEFAULT_PLAY_SERVICES_VERSION)
@@ -2,6 +2,7 @@ package com.dooboolab.RNIap
2
2
 
3
3
  import com.amazon.device.iap.PurchasingService
4
4
  import com.amazon.device.iap.model.FulfillmentResult
5
+ import com.facebook.react.bridge.LifecycleEventListener
5
6
  import com.facebook.react.bridge.Promise
6
7
  import com.facebook.react.bridge.ReactApplicationContext
7
8
  import com.facebook.react.bridge.ReactContextBaseJavaModule
@@ -10,9 +11,8 @@ import com.facebook.react.bridge.ReadableArray
10
11
  import com.facebook.react.bridge.WritableNativeArray
11
12
  import java.util.HashSet
12
13
 
13
- class RNIapAmazonModule(reactContext: ReactApplicationContext?) :
14
+ class RNIapAmazonModule(reactContext: ReactApplicationContext) :
14
15
  ReactContextBaseJavaModule(reactContext) {
15
- val TAG = "RNIapAmazonModule"
16
16
  override fun getName(): String {
17
17
  return TAG
18
18
  }
@@ -127,5 +127,23 @@ class RNIapAmazonModule(reactContext: ReactApplicationContext?) :
127
127
  const val PROMISE_QUERY_PURCHASES = "PROMISE_QUERY_PURCHASES"
128
128
  const val PROMISE_QUERY_AVAILABLE_ITEMS = "PROMISE_QUERY_AVAILABLE_ITEMS"
129
129
  const val PROMISE_GET_USER_DATA = "PROMISE_GET_USER_DATA"
130
+
131
+ const val TAG = "RNIapAmazonModule"
132
+ }
133
+ init {
134
+ val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener {
135
+ /**
136
+ * From https://developer.amazon.com/docs/in-app-purchasing/iap-implement-iap.html#getpurchaseupdates-responses
137
+ * We should fetch updates on resume
138
+ */
139
+ override fun onHostResume() {
140
+ PurchasingService.getUserData()
141
+ PurchasingService.getPurchaseUpdates(false)
142
+ }
143
+ override fun onHostPause() {}
144
+ override fun onHostDestroy() {
145
+ }
146
+ }
147
+ reactContext.addLifecycleEventListener(lifecycleEventListener)
130
148
  }
131
149
  }
@@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactContext
22
22
  import com.facebook.react.bridge.ReactContextBaseJavaModule
23
23
  import com.facebook.react.bridge.ReactMethod
24
24
  import com.facebook.react.bridge.ReadableArray
25
+ import com.facebook.react.bridge.ReadableType
25
26
  import com.facebook.react.bridge.WritableMap
26
27
  import com.facebook.react.bridge.WritableNativeArray
27
28
  import com.facebook.react.bridge.WritableNativeMap
@@ -39,21 +40,30 @@ class RNIapModule(
39
40
  ReactContextBaseJavaModule(reactContext),
40
41
  PurchasesUpdatedListener {
41
42
 
42
- private var billingClient: BillingClient = builder.setListener(this).build()
43
+ private var billingClientCache: BillingClient? = null
43
44
  private val skus: MutableMap<String, SkuDetails> = mutableMapOf()
44
45
  override fun getName(): String {
45
- return "RNIapModule"
46
+ return TAG
46
47
  }
47
48
 
48
- internal fun ensureConnection(promise: Promise, callback: () -> Unit) {
49
- if (billingClient.isReady) {
50
- callback()
49
+ internal fun ensureConnection(
50
+ promise: Promise,
51
+ callback: (billingClient: BillingClient) -> Unit
52
+ ) {
53
+ val billingClient = billingClientCache
54
+ if (billingClient?.isReady == true) {
55
+ callback(billingClient)
51
56
  return
52
57
  } else {
53
58
  val nested = PromiseImpl(
54
59
  {
55
60
  if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
56
- callback()
61
+ val connectedBillingClient = billingClientCache
62
+ if (connectedBillingClient?.isReady == true) {
63
+ callback(connectedBillingClient)
64
+ } else {
65
+ promise.safeReject(DoobooUtils.E_NOT_PREPARED, "Unable to auto-initialize connection")
66
+ }
57
67
  } else {
58
68
  Log.i(TAG, "Incorrect parameter in resolve")
59
69
  }
@@ -74,14 +84,6 @@ class RNIapModule(
74
84
 
75
85
  @ReactMethod
76
86
  fun initConnection(promise: Promise) {
77
- if (billingClient.isReady) {
78
- Log.i(
79
- TAG,
80
- "Already initialized, you should only call initConnection() once when your app starts"
81
- )
82
- promise.safeResolve(true)
83
- return
84
- }
85
87
  if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
86
88
  != ConnectionResult.SUCCESS
87
89
  ) {
@@ -90,30 +92,35 @@ class RNIapModule(
90
92
  return
91
93
  }
92
94
 
93
- billingClient = builder.setListener(this).build()
94
-
95
- billingClient.startConnection(
96
- object : BillingClientStateListener {
97
- override fun onBillingSetupFinished(billingResult: BillingResult) {
98
- val responseCode = billingResult.responseCode
95
+ if (billingClientCache?.isReady == true) {
96
+ Log.i(
97
+ TAG,
98
+ "Already initialized, you should only call initConnection() once when your app starts"
99
+ )
100
+ promise.safeResolve(true)
101
+ return
102
+ }
103
+ builder.setListener(this).build().also {
104
+ billingClientCache = it
105
+ it.startConnection(
106
+ object : BillingClientStateListener {
107
+ override fun onBillingSetupFinished(billingResult: BillingResult) {
108
+ if (!isValidResult(billingResult, promise)) return
99
109
 
100
- if (responseCode == BillingClient.BillingResponseCode.OK) {
101
110
  promise.safeResolve(true)
102
- } else {
103
- PlayUtils.instance
104
- .rejectPromiseWithBillingError(promise, responseCode)
105
111
  }
106
- }
107
112
 
108
- override fun onBillingServiceDisconnected() {
109
- Log.i(TAG, "Billing service disconnected")
110
- }
111
- })
113
+ override fun onBillingServiceDisconnected() {
114
+ Log.i(TAG, "Billing service disconnected")
115
+ }
116
+ })
117
+ }
112
118
  }
113
119
 
114
120
  @ReactMethod
115
121
  fun endConnection(promise: Promise) {
116
- billingClient.endConnection()
122
+ billingClientCache?.endConnection()
123
+ billingClientCache = null
117
124
  promise.safeResolve(true)
118
125
  }
119
126
 
@@ -125,7 +132,7 @@ class RNIapModule(
125
132
  for (purchase in purchases) {
126
133
  ensureConnection(
127
134
  promise
128
- ) {
135
+ ) { billingClient ->
129
136
  val consumeParams =
130
137
  ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
131
138
  .build()
@@ -151,10 +158,11 @@ class RNIapModule(
151
158
  fun flushFailedPurchasesCachedAsPending(promise: Promise) {
152
159
  ensureConnection(
153
160
  promise
154
- ) {
161
+ ) { billingClient ->
155
162
  billingClient.queryPurchasesAsync(
156
163
  BillingClient.SkuType.INAPP
157
- ) { _: BillingResult?, list: List<Purchase>? ->
164
+ ) { billingResult: BillingResult, list: List<Purchase>? ->
165
+ if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
158
166
  if (list == null) {
159
167
  // No purchases found
160
168
  promise.safeResolve(false)
@@ -178,97 +186,109 @@ class RNIapModule(
178
186
  }
179
187
 
180
188
  @ReactMethod
181
- fun getItemsByType(type: String?, skuArr: ReadableArray, promise: Promise) {
189
+ fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
182
190
  ensureConnection(
183
191
  promise
184
- ) {
192
+ ) { billingClient ->
185
193
  val skuList = ArrayList<String>()
186
194
  for (i in 0 until skuArr.size()) {
187
- val sku = skuArr.getString(i)
188
- if (sku is String) {
195
+ if (skuArr.getType(i) == ReadableType.String) {
196
+ val sku = skuArr.getString(i)
189
197
  skuList.add(sku)
190
198
  }
191
199
  }
192
200
  val params = SkuDetailsParams.newBuilder()
193
- params.setSkusList(skuList).setType(type!!)
201
+ params.setSkusList(skuList).setType(type)
194
202
  billingClient.querySkuDetailsAsync(
195
203
  params.build()
196
204
  ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
197
- Log.d(TAG, "responseCode: " + billingResult.responseCode)
198
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
199
- PlayUtils.instance
200
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
201
- return@querySkuDetailsAsync
202
- }
205
+ if (!isValidResult(billingResult, promise)) return@querySkuDetailsAsync
206
+
207
+ val items = Arguments.createArray()
203
208
  if (skuDetailsList != null) {
204
- for (sku in skuDetailsList) {
205
- skus[sku.sku] = sku
209
+ for (skuDetails in skuDetailsList) {
210
+ skus[skuDetails.sku] = skuDetails
211
+
212
+ val item = Arguments.createMap()
213
+ item.putString("productId", skuDetails.sku)
214
+ val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
215
+ val priceAmountMicros = skuDetails.priceAmountMicros
216
+ // Use valueOf instead of constructors.
217
+ // See:
218
+ // https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
219
+ val priceAmount = BigDecimal.valueOf(priceAmountMicros)
220
+ val introductoryPriceAmount =
221
+ BigDecimal.valueOf(introductoryPriceMicros)
222
+ val microUnitsDivisor = BigDecimal.valueOf(1000000)
223
+ val price = priceAmount.divide(microUnitsDivisor).toString()
224
+ val introductoryPriceAsAmountAndroid =
225
+ introductoryPriceAmount.divide(microUnitsDivisor).toString()
226
+ item.putString("price", price)
227
+ item.putString("currency", skuDetails.priceCurrencyCode)
228
+ item.putString("type", skuDetails.type)
229
+ item.putString("localizedPrice", skuDetails.price)
230
+ item.putString("title", skuDetails.title)
231
+ item.putString("description", skuDetails.description)
232
+ item.putString("introductoryPrice", skuDetails.introductoryPrice)
233
+ item.putString("typeAndroid", skuDetails.type)
234
+ item.putString("packageNameAndroid", skuDetails.zzc())
235
+ item.putString("originalPriceAndroid", skuDetails.originalPrice)
236
+ item.putString(
237
+ "subscriptionPeriodAndroid",
238
+ skuDetails.subscriptionPeriod
239
+ )
240
+ item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
241
+ item.putString(
242
+ "introductoryPriceCyclesAndroid",
243
+ skuDetails.introductoryPriceCycles.toString()
244
+ )
245
+ item.putString(
246
+ "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
247
+ )
248
+ item.putString(
249
+ "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
250
+ )
251
+ item.putString("iconUrl", skuDetails.iconUrl)
252
+ item.putString("originalJson", skuDetails.originalJson)
253
+ val originalPriceAmountMicros =
254
+ BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
255
+ val originalPrice =
256
+ originalPriceAmountMicros.divide(microUnitsDivisor).toString()
257
+ item.putString("originalPrice", originalPrice)
258
+ items.pushMap(item)
206
259
  }
207
260
  }
208
- val items = WritableNativeArray()
209
- for (skuDetails in skuDetailsList!!) {
210
- val item = Arguments.createMap()
211
- item.putString("productId", skuDetails.sku)
212
- val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
213
- val priceAmountMicros = skuDetails.priceAmountMicros
214
- // Use valueOf instead of constructors.
215
- // See:
216
- // https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
217
- val priceAmount = BigDecimal.valueOf(priceAmountMicros)
218
- val introductoryPriceAmount =
219
- BigDecimal.valueOf(introductoryPriceMicros)
220
- val microUnitsDivisor = BigDecimal.valueOf(1000000)
221
- val price = priceAmount.divide(microUnitsDivisor).toString()
222
- val introductoryPriceAsAmountAndroid =
223
- introductoryPriceAmount.divide(microUnitsDivisor).toString()
224
- item.putString("price", price)
225
- item.putString("currency", skuDetails.priceCurrencyCode)
226
- item.putString("type", skuDetails.type)
227
- item.putString("localizedPrice", skuDetails.price)
228
- item.putString("title", skuDetails.title)
229
- item.putString("description", skuDetails.description)
230
- item.putString("introductoryPrice", skuDetails.introductoryPrice)
231
- item.putString("typeAndroid", skuDetails.type)
232
- item.putString("packageNameAndroid", skuDetails.zzc())
233
- item.putString("originalPriceAndroid", skuDetails.originalPrice)
234
- item.putString(
235
- "subscriptionPeriodAndroid",
236
- skuDetails.subscriptionPeriod
237
- )
238
- item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
239
- item.putString(
240
- "introductoryPriceCyclesAndroid",
241
- skuDetails.introductoryPriceCycles.toString()
242
- )
243
- item.putString(
244
- "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
245
- )
246
- item.putString(
247
- "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
248
- )
249
- item.putString("iconUrl", skuDetails.iconUrl)
250
- item.putString("originalJson", skuDetails.originalJson)
251
- val originalPriceAmountMicros =
252
- BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
253
- val originalPrice =
254
- originalPriceAmountMicros.divide(microUnitsDivisor).toString()
255
- item.putString("originalPrice", originalPrice)
256
- items.pushMap(item)
257
- }
258
261
  promise.safeResolve(items)
259
262
  }
260
263
  }
261
264
  }
262
265
 
266
+ /**
267
+ * Rejects promise with billing code if BillingResult is not OK
268
+ */
269
+ private fun isValidResult(
270
+ billingResult: BillingResult,
271
+ promise: Promise
272
+ ): Boolean {
273
+ Log.d(TAG, "responseCode: " + billingResult.responseCode)
274
+ if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
275
+ PlayUtils.instance
276
+ .rejectPromiseWithBillingError(promise, billingResult.responseCode)
277
+ return false
278
+ }
279
+ return true
280
+ }
281
+
263
282
  @ReactMethod
264
283
  fun getAvailableItemsByType(type: String, promise: Promise) {
265
284
  ensureConnection(
266
285
  promise
267
- ) {
286
+ ) { billingClient ->
268
287
  val items = WritableNativeArray()
269
288
  billingClient.queryPurchasesAsync(
270
289
  if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
271
- ) { billingResult: BillingResult?, purchases: List<Purchase>? ->
290
+ ) { billingResult: BillingResult, purchases: List<Purchase>? ->
291
+ if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
272
292
  if (purchases != null) {
273
293
  for (i in purchases.indices) {
274
294
  val purchase = purchases[i]
@@ -286,11 +306,11 @@ class RNIapModule(
286
306
  item.putString("packageNameAndroid", purchase.packageName)
287
307
  item.putString(
288
308
  "obfuscatedAccountIdAndroid",
289
- purchase.accountIdentifiers!!.obfuscatedAccountId
309
+ purchase.accountIdentifiers?.obfuscatedAccountId
290
310
  )
291
311
  item.putString(
292
312
  "obfuscatedProfileIdAndroid",
293
- purchase.accountIdentifiers!!.obfuscatedProfileId
313
+ purchase.accountIdentifiers?.obfuscatedProfileId
294
314
  )
295
315
  if (type == BillingClient.SkuType.SUBS) {
296
316
  item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
@@ -307,20 +327,16 @@ class RNIapModule(
307
327
  fun getPurchaseHistoryByType(type: String, promise: Promise) {
308
328
  ensureConnection(
309
329
  promise
310
- ) {
330
+ ) { billingClient ->
311
331
  billingClient.queryPurchaseHistoryAsync(
312
332
  if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
313
333
  ) { billingResult, purchaseHistoryRecordList ->
314
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
315
- PlayUtils.instance
316
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
317
- return@queryPurchaseHistoryAsync
318
- }
334
+ if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync
335
+
319
336
  Log.d(TAG, purchaseHistoryRecordList.toString())
320
337
  val items = Arguments.createArray()
321
- for (i in purchaseHistoryRecordList!!.indices) {
338
+ purchaseHistoryRecordList?.forEach { purchase ->
322
339
  val item = Arguments.createMap()
323
- val purchase = purchaseHistoryRecordList[i]
324
340
  item.putString("productId", purchase.skus[0])
325
341
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
326
342
  item.putString("transactionReceipt", purchase.originalJson)
@@ -352,7 +368,7 @@ class RNIapModule(
352
368
  }
353
369
  ensureConnection(
354
370
  promise
355
- ) {
371
+ ) { billingClient ->
356
372
  DoobooUtils.instance.addPromiseForKey(
357
373
  PROMISE_BUY_ITEM, promise
358
374
  )
@@ -436,32 +452,36 @@ class RNIapModule(
436
452
  builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
437
453
  }
438
454
  val flowParams = builder.build()
439
- val billingResult = billingClient.launchBillingFlow(activity, flowParams)
440
- val errorData: Array<String?> =
441
- PlayUtils.instance.getBillingResponseData(billingResult.responseCode)
455
+ val billingResultCode = billingClient.launchBillingFlow(activity, flowParams)?.responseCode ?: BillingClient.BillingResponseCode.ERROR
456
+ if (billingResultCode == BillingClient.BillingResponseCode.OK) {
457
+ promise.safeResolve(true)
458
+ return@ensureConnection
459
+ } else {
460
+ val errorData: Array<String?> =
461
+ PlayUtils.instance.getBillingResponseData(billingResultCode)
462
+ promise.safeReject(errorData[0], errorData[1])
463
+ }
442
464
  }
443
465
  }
444
466
 
445
467
  @ReactMethod
446
468
  fun acknowledgePurchase(
447
- token: String?,
469
+ token: String,
448
470
  developerPayLoad: String?,
449
471
  promise: Promise
450
472
  ) {
451
473
  ensureConnection(
452
474
  promise
453
- ) {
475
+ ) { billingClient ->
454
476
  val acknowledgePurchaseParams =
455
477
  AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
456
- token!!
478
+ token
457
479
  ).build()
458
480
  billingClient.acknowledgePurchase(
459
481
  acknowledgePurchaseParams
460
482
  ) { billingResult: BillingResult ->
461
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
462
- PlayUtils.instance
463
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
464
- }
483
+ if (!isValidResult(billingResult, promise)) return@acknowledgePurchase
484
+
465
485
  val map = Arguments.createMap()
466
486
  map.putInt("responseCode", billingResult.responseCode)
467
487
  map.putString("debugMessage", billingResult.debugMessage)
@@ -476,21 +496,18 @@ class RNIapModule(
476
496
 
477
497
  @ReactMethod
478
498
  fun consumeProduct(
479
- token: String?,
499
+ token: String,
480
500
  developerPayLoad: String?,
481
501
  promise: Promise
482
502
  ) {
483
- val params = ConsumeParams.newBuilder().setPurchaseToken(token!!).build()
503
+ val params = ConsumeParams.newBuilder().setPurchaseToken(token).build()
484
504
  ensureConnection(
485
505
  promise
486
- ) {
506
+ ) { billingClient ->
487
507
  billingClient.consumeAsync(
488
508
  params
489
509
  ) { billingResult: BillingResult, purchaseToken: String? ->
490
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
491
- PlayUtils.instance
492
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
493
- }
510
+ if (!isValidResult(billingResult, promise)) return@consumeAsync
494
511
 
495
512
  val map = Arguments.createMap()
496
513
  map.putInt("responseCode", billingResult.responseCode)
@@ -570,19 +587,15 @@ class RNIapModule(
570
587
  private fun sendUnconsumedPurchases(promise: Promise) {
571
588
  ensureConnection(
572
589
  promise
573
- ) {
590
+ ) { billingClient ->
574
591
  val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
575
592
  for (type in types) {
576
593
  billingClient.queryPurchasesAsync(
577
594
  type
578
- ) { billingResult: BillingResult, list: List<Purchase>? ->
579
- val unacknowledgedPurchases = ArrayList<Purchase>()
595
+ ) { billingResult: BillingResult, list: List<Purchase> ->
596
+ if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
580
597
 
581
- for (purchase in list!!) {
582
- if (!purchase.isAcknowledged) {
583
- unacknowledgedPurchases.add(purchase)
584
- }
585
- }
598
+ val unacknowledgedPurchases = list.filter { !it.isAcknowledged }
586
599
  onPurchasesUpdated(billingResult, unacknowledgedPurchases)
587
600
  }
588
601
  }
@@ -605,9 +618,8 @@ class RNIapModule(
605
618
  // Keep: Required for RN built-in Event Emitter Calls.
606
619
  }
607
620
 
608
- @get:ReactMethod
609
- val packageName: String
610
- get() = reactApplicationContext.packageName
621
+ @ReactMethod
622
+ fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
611
623
 
612
624
  private fun sendEvent(
613
625
  reactContext: ReactContext,
@@ -629,7 +641,7 @@ class RNIapModule(
629
641
  override fun onHostResume() {}
630
642
  override fun onHostPause() {}
631
643
  override fun onHostDestroy() {
632
- billingClient.endConnection()
644
+ billingClientCache?.endConnection()
633
645
  }
634
646
  }
635
647
  reactContext.addLifecycleEventListener(lifecycleEventListener)
@@ -6,16 +6,26 @@ import com.android.billingclient.api.BillingResult
6
6
  import com.android.billingclient.api.ConsumeResponseListener
7
7
  import com.android.billingclient.api.Purchase
8
8
  import com.android.billingclient.api.PurchasesResponseListener
9
+ import com.android.billingclient.api.SkuDetailsResponseListener
10
+ import com.facebook.react.bridge.Arguments
9
11
  import com.facebook.react.bridge.Promise
10
12
  import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.facebook.react.bridge.ReadableArray
14
+ import com.facebook.react.bridge.ReadableType
15
+ import com.facebook.react.bridge.WritableArray
16
+ import com.facebook.react.bridge.WritableMap
11
17
  import com.google.android.gms.common.ConnectionResult
12
18
  import com.google.android.gms.common.GoogleApiAvailability
13
19
  import io.mockk.MockKAnnotations
14
20
  import io.mockk.every
15
21
  import io.mockk.impl.annotations.MockK
22
+ import io.mockk.just
16
23
  import io.mockk.mockk
24
+ import io.mockk.mockkStatic
25
+ import io.mockk.runs
17
26
  import io.mockk.slot
18
27
  import io.mockk.verify
28
+ import org.junit.Assert.assertEquals
19
29
  import org.junit.Assert.assertTrue
20
30
  import org.junit.Before
21
31
  import org.junit.Test
@@ -24,10 +34,13 @@ class RNIapModuleTest {
24
34
 
25
35
  @MockK
26
36
  lateinit var context: ReactApplicationContext
37
+
27
38
  @MockK
28
39
  lateinit var builder: BillingClient.Builder
40
+
29
41
  @MockK
30
42
  lateinit var billingClient: BillingClient
43
+
31
44
  @MockK
32
45
  lateinit var availability: GoogleApiAvailability
33
46
 
@@ -43,9 +56,13 @@ class RNIapModuleTest {
43
56
 
44
57
  @Test
45
58
  fun `initConnection Already connected should resolve to true`() {
59
+ every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
60
+ module.initConnection(mockk())
61
+
46
62
  every { billingClient.isReady } returns true
47
- val promise = mockk<Promise>(relaxed = true)
48
63
 
64
+ val promise = mockk<Promise>(relaxed = true)
65
+ // Already connected
49
66
  module.initConnection(promise)
50
67
  verify(exactly = 0) { promise.reject(any(), any<String>()) }
51
68
  verify { promise.resolve(true) }
@@ -67,7 +84,10 @@ class RNIapModuleTest {
67
84
  every { billingClient.isReady } returns false
68
85
  val listener = slot<BillingClientStateListener>()
69
86
  every { billingClient.startConnection(capture(listener)) } answers {
70
- listener.captured.onBillingSetupFinished(BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK).build())
87
+ listener.captured.onBillingSetupFinished(
88
+ BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK)
89
+ .build()
90
+ )
71
91
  }
72
92
  every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
73
93
  val promise = mockk<Promise>(relaxed = true)
@@ -82,7 +102,10 @@ class RNIapModuleTest {
82
102
  every { billingClient.isReady } returns false
83
103
  val listener = slot<BillingClientStateListener>()
84
104
  every { billingClient.startConnection(capture(listener)) } answers {
85
- listener.captured.onBillingSetupFinished(BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.ERROR).build())
105
+ listener.captured.onBillingSetupFinished(
106
+ BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.ERROR)
107
+ .build()
108
+ )
86
109
  }
87
110
  every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
88
111
  val promise = mockk<Promise>(relaxed = true)
@@ -94,8 +117,10 @@ class RNIapModuleTest {
94
117
 
95
118
  @Test
96
119
  fun `endConnection resolves`() {
120
+ every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
97
121
  val promise = mockk<Promise>(relaxed = true)
98
122
 
123
+ module.initConnection(mockk())
99
124
  module.endConnection(promise)
100
125
 
101
126
  verify { billingClient.endConnection() }
@@ -105,12 +130,14 @@ class RNIapModuleTest {
105
130
 
106
131
  @Test
107
132
  fun `flushFailedPurchasesCachedAsPending resolves to false if no pending purchases`() {
133
+ every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
108
134
  every { billingClient.isReady } returns true
109
135
  val promise = mockk<Promise>(relaxed = true)
110
136
  val listener = slot<PurchasesResponseListener>()
111
137
  every { billingClient.queryPurchasesAsync(any(), capture(listener)) } answers {
112
138
  listener.captured.onQueryPurchasesResponse(BillingResult.newBuilder().build(), listOf())
113
139
  }
140
+ module.initConnection(mockk())
114
141
  module.flushFailedPurchasesCachedAsPending(promise)
115
142
 
116
143
  verify(exactly = 0) { promise.reject(any(), any<String>()) }
@@ -119,6 +146,7 @@ class RNIapModuleTest {
119
146
 
120
147
  @Test
121
148
  fun `flushFailedPurchasesCachedAsPending resolves to true if pending purchases`() {
149
+ every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
122
150
  every { billingClient.isReady } returns true
123
151
  val promise = mockk<Promise>(relaxed = true)
124
152
  val listener = slot<PurchasesResponseListener>()
@@ -137,9 +165,13 @@ class RNIapModuleTest {
137
165
  }
138
166
  val consumeListener = slot<ConsumeResponseListener>()
139
167
  every { billingClient.consumeAsync(any(), capture(consumeListener)) } answers {
140
- consumeListener.captured.onConsumeResponse(BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.ITEM_NOT_OWNED).build(), "")
168
+ consumeListener.captured.onConsumeResponse(
169
+ BillingResult.newBuilder()
170
+ .setResponseCode(BillingClient.BillingResponseCode.ITEM_NOT_OWNED).build(),
171
+ ""
172
+ )
141
173
  }
142
-
174
+ module.initConnection(mockk())
143
175
  module.flushFailedPurchasesCachedAsPending(promise)
144
176
 
145
177
  verify(exactly = 0) { promise.reject(any(), any<String>()) }
@@ -151,12 +183,20 @@ class RNIapModuleTest {
151
183
  every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
152
184
  val promise = mockk<Promise>(relaxed = true)
153
185
  var isCallbackCalled = false
154
- val callback = {
186
+ val callback = { _: BillingClient ->
155
187
  isCallbackCalled = true
156
188
  promise.resolve(true)
157
189
  }
158
190
 
159
- every { billingClient.isReady } returns false andThen true
191
+ every { billingClient.isReady } returns true
192
+ val listener = slot<BillingClientStateListener>()
193
+ every { billingClient.startConnection(capture(listener)) } answers {
194
+ listener.captured.onBillingSetupFinished(
195
+ BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK)
196
+ .build()
197
+ )
198
+ }
199
+
160
200
  module.ensureConnection(promise, callback)
161
201
  verify { promise.resolve(true) } // at least one pending transactions
162
202
  assertTrue("Should call callback", isCallbackCalled)
@@ -164,6 +204,57 @@ class RNIapModuleTest {
164
204
 
165
205
  @Test
166
206
  fun getItemsByType() {
207
+ every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
208
+ every { billingClient.isReady } returns true
209
+ val promise = mockk<Promise>(relaxed = true)
210
+ val listener = slot<SkuDetailsResponseListener>()
211
+ every { billingClient.querySkuDetailsAsync(any(), capture(listener)) } answers {
212
+ listener.captured.onSkuDetailsResponse(
213
+ BillingResult.newBuilder().build(),
214
+ listOf(
215
+ mockk {
216
+ every { sku } returns "sku1"
217
+ every { introductoryPriceAmountMicros } returns 0
218
+ every { priceAmountMicros } returns 1
219
+ every { priceCurrencyCode } returns "USD"
220
+ every { type } returns "sub"
221
+ every { price } returns "$10.0"
222
+ every { title } returns "My product"
223
+ every { description } returns "My desc"
224
+ every { introductoryPrice } returns "$5.0"
225
+ every { zzc() } returns "com.mypackage"
226
+ every { originalPrice } returns "$13.0"
227
+ every { subscriptionPeriod } returns "3 months"
228
+ every { freeTrialPeriod } returns "1 week"
229
+ every { introductoryPriceCycles } returns 1
230
+ every { introductoryPricePeriod } returns "1"
231
+ every { iconUrl } returns "http://myicon.com/icon"
232
+ every { originalJson } returns "{}"
233
+ every { originalPriceAmountMicros } returns 2
234
+ }
235
+ )
236
+ )
237
+ }
238
+ val skus = mockk<ReadableArray>() {
239
+ every { size() } returns 1
240
+ every { getString(0) } returns "sku0"
241
+ every { getType(0) } returns ReadableType.String
242
+ }
243
+ mockkStatic(Arguments::class)
244
+
245
+ val itemsMap = mockk<WritableMap>()
246
+ val itemsArr = mockk<WritableArray>()
247
+ every { Arguments.createMap() } returns itemsMap
248
+ every { Arguments.createArray() } returns itemsArr
249
+ every { itemsMap.putString(any(), any()) } just runs
250
+ var itemsSize = 0
251
+ every { itemsArr.pushMap(any()) } answers {
252
+ itemsSize++
253
+ }
254
+ module.initConnection(mockk())
255
+ module.getItemsByType("subs", skus, promise)
256
+ verify { promise.resolve(any()) }
257
+ assertEquals(itemsSize, 1)
167
258
  }
168
259
 
169
260
  @Test
package/ios/RNIapIos.m CHANGED
@@ -18,6 +18,7 @@ RCT_EXTERN_METHOD(getAvailableItems:
18
18
  reject:(RCTPromiseRejectBlock)reject)
19
19
  RCT_EXTERN_METHOD(buyProduct:
20
20
  (NSString*)sku
21
+ appAccountToken:(NSString*)appAccountToken
21
22
  andDangerouslyFinishTransactionAutomatically:(BOOL)andDangerouslyFinishTransactionAutomatically
22
23
  resolve:(RCTPromiseResolveBlock)resolve
23
24
  reject:(RCTPromiseRejectBlock)reject)
@@ -178,6 +178,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
178
178
 
179
179
  @objc public func buyProduct(
180
180
  _ sku:String,
181
+ appAccountToken:String,
181
182
  andDangerouslyFinishTransactionAutomatically: Bool,
182
183
  resolve: @escaping RCTPromiseResolveBlock = { _ in },
183
184
  reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
@@ -197,6 +198,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
197
198
  addPromise(forKey: prod.productIdentifier, resolve: resolve, reject: reject)
198
199
 
199
200
  let payment = SKMutablePayment(product: prod)
201
+ payment.applicationUsername = appAccountToken
200
202
  SKPaymentQueue.default().add(payment)
201
203
  } else{
202
204
  if hasListeners {
@@ -341,7 +343,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
341
343
  reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
342
344
  ) {
343
345
  print("\n\n\n *** get promoted product. \n\n.")
344
- resolve(promotedProduct )
346
+ resolve((promotedProduct != nil) ? getProductObject(promotedProduct!) : nil)
345
347
  }
346
348
 
347
349
  @objc public func buyPromotedProduct(
@@ -10,12 +10,13 @@ import StoreKit
10
10
 
11
11
  // Temporarily stores payment information since it is sent by the OS before RN instantiates the RNModule
12
12
  @objc(RNIapQueue)
13
- class RNIapQueue: NSObject, SKPaymentTransactionObserver {
14
- func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
13
+ public class RNIapQueue: NSObject, SKPaymentTransactionObserver {
14
+ public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
15
15
  //No-op
16
16
  }
17
17
 
18
- static let shared = RNIapQueue()
18
+ @objc
19
+ public static let shared = RNIapQueue()
19
20
 
20
21
  var queue: SKPaymentQueue? = nil;
21
22
  var payment: SKPayment? = nil;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "8.2.1",
3
+ "version": "8.4.0",
4
4
  "packageManager": "yarn@3.2.0",
5
5
  "description": "React Native In App Purchase Module.",
6
6
  "main": "index.js",
@@ -9,6 +9,7 @@ declare type IAP_STATUS = {
9
9
  availablePurchases: Purchase[];
10
10
  currentPurchase?: Purchase;
11
11
  currentPurchaseError?: PurchaseError;
12
+ initConnectionError?: Error;
12
13
  finishTransaction: (purchase: Purchase, isConsumable?: boolean, developerPayloadAndroid?: string) => Promise<string | void>;
13
14
  getAvailablePurchases: () => Promise<void>;
14
15
  getPurchaseHistories: () => Promise<void>;
@@ -39,7 +39,7 @@ import { useCallback } from 'react';
39
39
  import { useIAPContext } from './withIAPContext';
40
40
  export function useIAP() {
41
41
  var _this = this;
42
- var _a = useIAPContext(), connected = _a.connected, products = _a.products, promotedProductsIOS = _a.promotedProductsIOS, subscriptions = _a.subscriptions, purchaseHistories = _a.purchaseHistories, availablePurchases = _a.availablePurchases, currentPurchase = _a.currentPurchase, currentPurchaseError = _a.currentPurchaseError, setProducts = _a.setProducts, setSubscriptions = _a.setSubscriptions, setAvailablePurchases = _a.setAvailablePurchases, setPurchaseHistories = _a.setPurchaseHistories, setCurrentPurchase = _a.setCurrentPurchase, setCurrentPurchaseError = _a.setCurrentPurchaseError;
42
+ var _a = useIAPContext(), connected = _a.connected, products = _a.products, promotedProductsIOS = _a.promotedProductsIOS, subscriptions = _a.subscriptions, purchaseHistories = _a.purchaseHistories, availablePurchases = _a.availablePurchases, currentPurchase = _a.currentPurchase, currentPurchaseError = _a.currentPurchaseError, initConnectionError = _a.initConnectionError, setProducts = _a.setProducts, setSubscriptions = _a.setSubscriptions, setAvailablePurchases = _a.setAvailablePurchases, setPurchaseHistories = _a.setPurchaseHistories, setCurrentPurchase = _a.setCurrentPurchase, setCurrentPurchaseError = _a.setCurrentPurchaseError;
43
43
  var getProducts = useCallback(function (skus) { return __awaiter(_this, void 0, void 0, function () {
44
44
  var _a;
45
45
  return __generator(this, function (_b) {
@@ -129,6 +129,7 @@ export function useIAP() {
129
129
  availablePurchases: availablePurchases,
130
130
  currentPurchase: currentPurchase,
131
131
  currentPurchaseError: currentPurchaseError,
132
+ initConnectionError: initConnectionError,
132
133
  finishTransaction: finishTransaction,
133
134
  getProducts: getProducts,
134
135
  getSubscriptions: getSubscriptions,
@@ -9,6 +9,7 @@ declare type IAPContextType = {
9
9
  availablePurchases: Purchase[];
10
10
  currentPurchase?: Purchase;
11
11
  currentPurchaseError?: PurchaseError;
12
+ initConnectionError?: Error;
12
13
  setProducts: (products: Product[]) => void;
13
14
  setSubscriptions: (subscriptions: Subscription[]) => void;
14
15
  setPurchaseHistories: (purchaseHistories: Purchase[]) => void;
@@ -68,6 +68,7 @@ export function withIAPContext(Component) {
68
68
  var _f = useState([]), availablePurchases = _f[0], setAvailablePurchases = _f[1];
69
69
  var _g = useState(), currentPurchase = _g[0], setCurrentPurchase = _g[1];
70
70
  var _h = useState(), currentPurchaseError = _h[0], setCurrentPurchaseError = _h[1];
71
+ var _j = useState(), initConnectionError = _j[0], setInitConnectionError = _j[1];
71
72
  var context = useMemo(function () { return ({
72
73
  connected: connected,
73
74
  products: products,
@@ -77,6 +78,7 @@ export function withIAPContext(Component) {
77
78
  availablePurchases: availablePurchases,
78
79
  currentPurchase: currentPurchase,
79
80
  currentPurchaseError: currentPurchaseError,
81
+ initConnectionError: initConnectionError,
80
82
  setProducts: setProducts,
81
83
  setSubscriptions: setSubscriptions,
82
84
  setPurchaseHistories: setPurchaseHistories,
@@ -92,6 +94,7 @@ export function withIAPContext(Component) {
92
94
  availablePurchases,
93
95
  currentPurchase,
94
96
  currentPurchaseError,
97
+ initConnectionError,
95
98
  setProducts,
96
99
  setSubscriptions,
97
100
  setPurchaseHistories,
@@ -100,7 +103,12 @@ export function withIAPContext(Component) {
100
103
  setCurrentPurchaseError,
101
104
  ]);
102
105
  useEffect(function () {
103
- initConnection().then(setConnected);
106
+ initConnection()
107
+ .then(function (value) {
108
+ setInitConnectionError(undefined);
109
+ setConnected(value);
110
+ })
111
+ .catch(setInitConnectionError);
104
112
  }, []);
105
113
  useEffect(function () {
106
114
  if (!connected) {
package/src/iap.d.ts CHANGED
@@ -44,12 +44,13 @@ export declare const getAvailablePurchases: () => Promise<(InAppPurchase | Subsc
44
44
  /**
45
45
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
46
46
  * @param {string} sku The product's sku/ID
47
+ * @param {string} [appAccountToken] The purchaser's user ID
47
48
  * @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
48
49
  * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
49
50
  * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
50
51
  * @returns {Promise<InAppPurchase>}
51
52
  */
52
- export declare const requestPurchase: (sku: string, andDangerouslyFinishTransactionAutomaticallyIOS?: boolean, obfuscatedAccountIdAndroid?: string | undefined, obfuscatedProfileIdAndroid?: string | undefined) => Promise<InAppPurchase>;
53
+ export declare const requestPurchase: (sku: string, appAccountToken: string, andDangerouslyFinishTransactionAutomaticallyIOS?: boolean, obfuscatedAccountIdAndroid?: string | undefined, obfuscatedProfileIdAndroid?: string | undefined) => Promise<InAppPurchase>;
53
54
  /**
54
55
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
55
56
  * @param {string} [sku] The product's sku/ID
package/src/iap.js CHANGED
@@ -135,13 +135,13 @@ var fillProductsAdditionalData = function (products) { return __awaiter(void 0,
135
135
  export var getProducts = function (skus) {
136
136
  return (Platform.select({
137
137
  ios: function () { return __awaiter(void 0, void 0, void 0, function () {
138
- var items;
139
138
  return __generator(this, function (_a) {
140
139
  switch (_a.label) {
141
- case 0: return [4 /*yield*/, getIosModule().getItems(skus)];
142
- case 1:
143
- items = _a.sent();
144
- return [2 /*return*/, items.filter(function (item) { return skus.includes(item.productId); })];
140
+ case 0: return [4 /*yield*/, getIosModule()
141
+ .getItems(skus)
142
+ .filter(function (item) { return skus.includes(item.productId); })
143
+ .filter(function (item) { return item.type === 'iap'; })];
144
+ case 1: return [2 /*return*/, _a.sent()];
145
145
  }
146
146
  });
147
147
  }); },
@@ -166,15 +166,13 @@ export var getProducts = function (skus) {
166
166
  export var getSubscriptions = function (skus) {
167
167
  return (Platform.select({
168
168
  ios: function () { return __awaiter(void 0, void 0, void 0, function () {
169
- var items;
170
169
  return __generator(this, function (_a) {
171
170
  switch (_a.label) {
172
- case 0: return [4 /*yield*/, getIosModule().getItems(skus)];
173
- case 1:
174
- items = _a.sent();
175
- return [2 /*return*/, items.filter(function (item) {
176
- return skus.includes(item.productId);
177
- })];
171
+ case 0: return [4 /*yield*/, getIosModule()
172
+ .getItems(skus)
173
+ .filter(function (item) { return skus.includes(item.productId); })
174
+ .filter(function (item) { return item.type === 'subs'; })];
175
+ case 1: return [2 /*return*/, _a.sent()];
178
176
  }
179
177
  });
180
178
  }); },
@@ -256,12 +254,13 @@ export var getAvailablePurchases = function () {
256
254
  /**
257
255
  * Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
258
256
  * @param {string} sku The product's sku/ID
257
+ * @param {string} [appAccountToken] The purchaser's user ID
259
258
  * @param {boolean} [andDangerouslyFinishTransactionAutomaticallyIOS] You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.
260
259
  * @param {string} [obfuscatedAccountIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
261
260
  * @param {string} [obfuscatedProfileIdAndroid] Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
262
261
  * @returns {Promise<InAppPurchase>}
263
262
  */
264
- export var requestPurchase = function (sku, andDangerouslyFinishTransactionAutomaticallyIOS, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid) {
263
+ export var requestPurchase = function (sku, appAccountToken, andDangerouslyFinishTransactionAutomaticallyIOS, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid) {
265
264
  if (andDangerouslyFinishTransactionAutomaticallyIOS === void 0) { andDangerouslyFinishTransactionAutomaticallyIOS = false; }
266
265
  if (obfuscatedAccountIdAndroid === void 0) { obfuscatedAccountIdAndroid = undefined; }
267
266
  if (obfuscatedProfileIdAndroid === void 0) { obfuscatedProfileIdAndroid = undefined; }
@@ -274,7 +273,7 @@ export var requestPurchase = function (sku, andDangerouslyFinishTransactionAutom
274
273
  // eslint-disable-next-line max-len
275
274
  'You are dangerously allowing react-native-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.');
276
275
  }
277
- return [2 /*return*/, getIosModule().buyProduct(sku, andDangerouslyFinishTransactionAutomaticallyIOS)];
276
+ return [2 /*return*/, getIosModule().buyProduct(sku, appAccountToken, andDangerouslyFinishTransactionAutomaticallyIOS)];
278
277
  });
279
278
  }); },
280
279
  android: function () { return __awaiter(void 0, void 0, void 0, function () {
@@ -400,10 +399,19 @@ export var acknowledgePurchaseAndroid = function (token, developerPayload) {
400
399
  * @param {string} sku The product's SKU (on Android)
401
400
  * @returns {Promise<void>}
402
401
  */
403
- export var deepLinkToSubscriptionsAndroid = function (sku) {
404
- checkNativeAndroidAvailable();
405
- return Linking.openURL("https://play.google.com/store/account/subscriptions?package=".concat(RNIapModule.getPackageName(), "&sku=").concat(sku));
406
- };
402
+ export var deepLinkToSubscriptionsAndroid = function (sku) { return __awaiter(void 0, void 0, void 0, function () {
403
+ var _a, _b, _c;
404
+ return __generator(this, function (_d) {
405
+ switch (_d.label) {
406
+ case 0:
407
+ checkNativeAndroidAvailable();
408
+ _b = (_a = Linking).openURL;
409
+ _c = "https://play.google.com/store/account/subscriptions?package=".concat;
410
+ return [4 /*yield*/, RNIapModule.getPackageName()];
411
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.apply("https://play.google.com/store/account/subscriptions?package=", [_d.sent(), "&sku="]).concat(sku)])];
412
+ }
413
+ });
414
+ }); };
407
415
  /**
408
416
  * Should Add Store Payment (iOS only)
409
417
  * Indicates the the App Store purchase should continue from the app instead of the App Store.
@@ -35,6 +35,8 @@ export declare enum InstallSourceAndroid {
35
35
  AMAZON = 2
36
36
  }
37
37
  export interface ProductCommon {
38
+ type: 'subs' | 'sub' | 'inapp' | 'iap';
39
+ productId: string;
38
40
  title: string;
39
41
  description: string;
40
42
  price: string;
@@ -96,11 +98,9 @@ export interface Discount {
96
98
  }
97
99
  export interface Product extends ProductCommon {
98
100
  type: 'inapp' | 'iap';
99
- productId: string;
100
101
  }
101
102
  export interface Subscription extends ProductCommon {
102
103
  type: 'subs' | 'sub';
103
- productId: string;
104
104
  discounts?: Discount[];
105
105
  introductoryPrice?: string;
106
106
  introductoryPriceAsAmountIOS?: string;