react-native-iap 8.6.2 → 9.0.0-beta11

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.
@@ -155,10 +155,10 @@ dependencies {
155
155
  implementation "com.facebook.react:react-native:+"
156
156
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
157
157
 
158
- testImplementation "junit:junit:4.13.1"
158
+ testImplementation "junit:junit:4.13.2"
159
159
  testImplementation "io.mockk:mockk:1.12.4"
160
160
 
161
- playImplementation "com.android.billingclient:billing:4.0.0"
161
+ playImplementation "com.android.billingclient:billing-ktx:5.0.0"
162
162
  playImplementation "com.google.android.gms:play-services-base:$playServicesVersion"
163
163
 
164
164
  amazonImplementation fileTree(dir: "libs", include: ["*.jar"])
@@ -245,7 +245,10 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
245
245
  sendEvent(reactContext, "purchase-error", error)
246
246
  DoobooUtils.instance
247
247
  .rejectPromisesForKey(
248
- RNIapAmazonModule.Companion.PROMISE_BUY_ITEM, errorCode, debugMessage, null
248
+ RNIapAmazonModule.Companion.PROMISE_BUY_ITEM,
249
+ errorCode,
250
+ debugMessage,
251
+ null
249
252
  )
250
253
  }
251
254
  PurchaseResponse.RequestStatus.FAILED -> {
@@ -259,7 +262,10 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
259
262
  sendEvent(reactContext, "purchase-error", error)
260
263
  DoobooUtils.instance
261
264
  .rejectPromisesForKey(
262
- RNIapAmazonModule.Companion.PROMISE_BUY_ITEM, errorCode, debugMessage, null
265
+ RNIapAmazonModule.Companion.PROMISE_BUY_ITEM,
266
+ errorCode,
267
+ debugMessage,
268
+ null
263
269
  )
264
270
  }
265
271
  PurchaseResponse.RequestStatus.INVALID_SKU -> {
@@ -272,7 +278,10 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
272
278
  sendEvent(reactContext, "purchase-error", error)
273
279
  DoobooUtils.instance
274
280
  .rejectPromisesForKey(
275
- RNIapAmazonModule.Companion.PROMISE_BUY_ITEM, errorCode, debugMessage, null
281
+ RNIapAmazonModule.Companion.PROMISE_BUY_ITEM,
282
+ errorCode,
283
+ debugMessage,
284
+ null
276
285
  )
277
286
  }
278
287
  PurchaseResponse.RequestStatus.NOT_SUPPORTED -> {
@@ -285,7 +294,10 @@ class RNIapAmazonListener(private val reactContext: ReactContext) : PurchasingLi
285
294
  sendEvent(reactContext, "purchase-error", error)
286
295
  DoobooUtils.instance
287
296
  .rejectPromisesForKey(
288
- RNIapAmazonModule.Companion.PROMISE_BUY_ITEM, errorCode, debugMessage, null
297
+ RNIapAmazonModule.Companion.PROMISE_BUY_ITEM,
298
+ errorCode,
299
+ debugMessage,
300
+ null
289
301
  )
290
302
  }
291
303
  }
@@ -9,10 +9,13 @@ import com.android.billingclient.api.BillingFlowParams.SubscriptionUpdateParams
9
9
  import com.android.billingclient.api.BillingResult
10
10
  import com.android.billingclient.api.ConsumeParams
11
11
  import com.android.billingclient.api.ConsumeResponseListener
12
+ import com.android.billingclient.api.ProductDetails
12
13
  import com.android.billingclient.api.Purchase
14
+ import com.android.billingclient.api.PurchaseHistoryRecord
13
15
  import com.android.billingclient.api.PurchasesUpdatedListener
14
- import com.android.billingclient.api.SkuDetails
15
- import com.android.billingclient.api.SkuDetailsParams
16
+ import com.android.billingclient.api.QueryProductDetailsParams
17
+ import com.android.billingclient.api.QueryPurchaseHistoryParams
18
+ import com.android.billingclient.api.QueryPurchasesParams
16
19
  import com.facebook.react.bridge.Arguments
17
20
  import com.facebook.react.bridge.LifecycleEventListener
18
21
  import com.facebook.react.bridge.Promise
@@ -23,13 +26,13 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
23
26
  import com.facebook.react.bridge.ReactMethod
24
27
  import com.facebook.react.bridge.ReadableArray
25
28
  import com.facebook.react.bridge.ReadableType
29
+ import com.facebook.react.bridge.WritableArray
26
30
  import com.facebook.react.bridge.WritableMap
27
31
  import com.facebook.react.bridge.WritableNativeArray
28
32
  import com.facebook.react.bridge.WritableNativeMap
29
33
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
30
34
  import com.google.android.gms.common.ConnectionResult
31
35
  import com.google.android.gms.common.GoogleApiAvailability
32
- import java.math.BigDecimal
33
36
  import java.util.ArrayList
34
37
 
35
38
  class RNIapModule(
@@ -41,12 +44,12 @@ class RNIapModule(
41
44
  PurchasesUpdatedListener {
42
45
 
43
46
  private var billingClientCache: BillingClient? = null
44
- private val skus: MutableMap<String, SkuDetails> = mutableMapOf()
47
+ private val skus: MutableMap<String, ProductDetails> = mutableMapOf()
45
48
  override fun getName(): String {
46
49
  return TAG
47
50
  }
48
51
 
49
- internal fun ensureConnection(
52
+ fun ensureConnection(
50
53
  promise: Promise,
51
54
  callback: (billingClient: BillingClient) -> Unit
52
55
  ) {
@@ -71,7 +74,8 @@ class RNIapModule(
71
74
  {
72
75
  if (it.size > 1 && it[0] is String && it[1] is String) {
73
76
  promise.safeReject(
74
- it[0] as String, it[1] as String
77
+ it[0] as String,
78
+ it[1] as String
75
79
  )
76
80
  } else {
77
81
  Log.i(TAG, "Incorrect parameters in reject")
@@ -83,7 +87,7 @@ class RNIapModule(
83
87
  }
84
88
 
85
89
  @ReactMethod
86
- fun initConnection(promise: Promise) {
90
+ fun initConnection(promise: Promise) {
87
91
  if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
88
92
  != ConnectionResult.SUCCESS
89
93
  ) {
@@ -104,21 +108,22 @@ class RNIapModule(
104
108
  billingClientCache = it
105
109
  it.startConnection(
106
110
  object : BillingClientStateListener {
107
- override fun onBillingSetupFinished(billingResult: BillingResult) {
111
+ override fun onBillingSetupFinished(billingResult: BillingResult) {
108
112
  if (!isValidResult(billingResult, promise)) return
109
113
 
110
114
  promise.safeResolve(true)
111
115
  }
112
116
 
113
- override fun onBillingServiceDisconnected() {
117
+ override fun onBillingServiceDisconnected() {
114
118
  Log.i(TAG, "Billing service disconnected")
115
119
  }
116
- })
120
+ }
121
+ )
117
122
  }
118
123
  }
119
124
 
120
125
  @ReactMethod
121
- fun endConnection(promise: Promise) {
126
+ fun endConnection(promise: Promise) {
122
127
  billingClientCache?.endConnection()
123
128
  billingClientCache = null
124
129
  promise.safeResolve(true)
@@ -155,12 +160,14 @@ class RNIapModule(
155
160
  }
156
161
 
157
162
  @ReactMethod
158
- fun flushFailedPurchasesCachedAsPending(promise: Promise) {
163
+ fun flushFailedPurchasesCachedAsPending(promise: Promise) {
159
164
  ensureConnection(
160
165
  promise
161
166
  ) { billingClient ->
162
167
  billingClient.queryPurchasesAsync(
163
- BillingClient.SkuType.INAPP
168
+ QueryPurchasesParams.newBuilder().setProductType(
169
+ BillingClient.ProductType.INAPP
170
+ ).build()
164
171
  ) { billingResult: BillingResult, list: List<Purchase>? ->
165
172
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
166
173
  if (list == null) {
@@ -186,77 +193,96 @@ class RNIapModule(
186
193
  }
187
194
 
188
195
  @ReactMethod
189
- fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
196
+ fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
190
197
  ensureConnection(
191
198
  promise
192
199
  ) { billingClient ->
193
- val skuList = ArrayList<String>()
200
+ val skuList = ArrayList<QueryProductDetailsParams.Product>()
194
201
  for (i in 0 until skuArr.size()) {
195
202
  if (skuArr.getType(i) == ReadableType.String) {
196
203
  val sku = skuArr.getString(i)
197
- skuList.add(sku)
204
+ sku?.let {
205
+ skuList.add(
206
+ QueryProductDetailsParams.Product.newBuilder().setProductId(sku)
207
+ .setProductType(type).build()
208
+ )
209
+ }
198
210
  }
199
211
  }
200
- val params = SkuDetailsParams.newBuilder()
201
- params.setSkusList(skuList).setType(type)
202
- billingClient.querySkuDetailsAsync(
212
+ val params = QueryProductDetailsParams.newBuilder().setProductList(skuList)
213
+ billingClient.queryProductDetailsAsync(
203
214
  params.build()
204
- ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
205
- if (!isValidResult(billingResult, promise)) return@querySkuDetailsAsync
215
+ ) { billingResult: BillingResult, skuDetailsList: List<ProductDetails> ->
216
+ if (!isValidResult(billingResult, promise)) return@queryProductDetailsAsync
206
217
 
207
218
  val items = Arguments.createArray()
208
- if (skuDetailsList != null) {
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()
219
+ for (skuDetails in skuDetailsList) {
220
+ skus[skuDetails.productId] = skuDetails
221
+
222
+ val item = Arguments.createMap()
223
+ item.putString("productId", skuDetails.productId)
224
+ item.putString("title", skuDetails.title)
225
+ item.putString("description", skuDetails.description)
226
+ item.putString("productType", skuDetails.productType)
227
+ item.putString("name", skuDetails.name)
228
+ val oneTimePurchaseOfferDetails = Arguments.createMap()
229
+ skuDetails.oneTimePurchaseOfferDetails?.let {
230
+ oneTimePurchaseOfferDetails.putString(
231
+ "priceCurrencyCode",
232
+ it.priceCurrencyCode
244
233
  )
245
- item.putString(
246
- "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
234
+ oneTimePurchaseOfferDetails.putString("formattedPrice", it.formattedPrice)
235
+ oneTimePurchaseOfferDetails.putString(
236
+ "priceAmountMicros",
237
+ it.priceAmountMicros.toString()
247
238
  )
248
- item.putString(
249
- "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
239
+ }
240
+ item.putMap("oneTimePurchaseOfferDetails", oneTimePurchaseOfferDetails)
241
+
242
+ val subscriptionOfferDetails = Arguments.createArray()
243
+ skuDetails.subscriptionOfferDetails?.forEach { subscriptionOfferDetailsItem ->
244
+ val offerDetails = Arguments.createMap()
245
+ offerDetails.putString(
246
+ "offerToken",
247
+ subscriptionOfferDetailsItem.offerToken
250
248
  )
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)
249
+ val offerTags = Arguments.createArray()
250
+ subscriptionOfferDetailsItem.offerTags.forEach { offerTag ->
251
+ offerTags.pushString(offerTag)
252
+ }
253
+ offerDetails.putArray("offerTags", offerTags)
254
+
255
+ val pricingPhasesList = Arguments.createArray()
256
+ subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem ->
257
+ val pricingPhase = Arguments.createMap()
258
+ pricingPhase.putString(
259
+ "formattedPrice",
260
+ pricingPhaseItem.formattedPrice
261
+ )
262
+ pricingPhase.putString(
263
+ "priceCurrencyCode",
264
+ pricingPhaseItem.priceCurrencyCode
265
+ )
266
+ pricingPhase.putString("billingPeriod", pricingPhaseItem.billingPeriod)
267
+ pricingPhase.putInt(
268
+ "billingCycleCount",
269
+ pricingPhaseItem.billingCycleCount
270
+ )
271
+ pricingPhase.putString(
272
+ "priceAmountMicros",
273
+ pricingPhaseItem.priceAmountMicros.toString()
274
+ )
275
+ pricingPhase.putInt("recurrenceMode", pricingPhaseItem.recurrenceMode)
276
+
277
+ pricingPhasesList.pushMap(pricingPhase)
278
+ }
279
+ val pricingPhases = Arguments.createMap()
280
+ pricingPhases.putArray("pricingPhaseList", pricingPhasesList)
281
+ offerDetails.putMap("pricingPhases", pricingPhases)
282
+ subscriptionOfferDetails.pushMap(offerDetails)
259
283
  }
284
+ item.putArray("subscriptionOfferDetails", subscriptionOfferDetails)
285
+ items.pushMap(item)
260
286
  }
261
287
  promise.safeResolve(items)
262
288
  }
@@ -286,14 +312,19 @@ class RNIapModule(
286
312
  ) { billingClient ->
287
313
  val items = WritableNativeArray()
288
314
  billingClient.queryPurchasesAsync(
289
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
315
+ QueryPurchasesParams.newBuilder().setProductType(
316
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
317
+ ).build()
290
318
  ) { billingResult: BillingResult, purchases: List<Purchase>? ->
291
319
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
292
320
  if (purchases != null) {
293
321
  for (i in purchases.indices) {
294
322
  val purchase = purchases[i]
295
323
  val item = WritableNativeMap()
296
- item.putString("productId", purchase.skus[0])
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)
297
328
  item.putString("transactionId", purchase.orderId)
298
329
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
299
330
  item.putString("transactionReceipt", purchase.originalJson)
@@ -312,7 +343,7 @@ class RNIapModule(
312
343
  "obfuscatedProfileIdAndroid",
313
344
  purchase.accountIdentifiers?.obfuscatedProfileId
314
345
  )
315
- if (type == BillingClient.SkuType.SUBS) {
346
+ if (type == BillingClient.ProductType.SUBS) {
316
347
  item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
317
348
  }
318
349
  items.pushMap(item)
@@ -329,15 +360,22 @@ class RNIapModule(
329
360
  promise
330
361
  ) { billingClient ->
331
362
  billingClient.queryPurchaseHistoryAsync(
332
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
333
- ) { billingResult, purchaseHistoryRecordList ->
363
+ QueryPurchaseHistoryParams.newBuilder().setProductType(
364
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
365
+ ).build()
366
+ ) {
367
+ billingResult: BillingResult, purchaseHistoryRecordList: MutableList<PurchaseHistoryRecord>? ->
368
+
334
369
  if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync
335
370
 
336
371
  Log.d(TAG, purchaseHistoryRecordList.toString())
337
372
  val items = Arguments.createArray()
338
373
  purchaseHistoryRecordList?.forEach { purchase ->
339
374
  val item = Arguments.createMap()
340
- item.putString("productId", purchase.skus[0])
375
+ item.putString("productId", purchase.products[0])
376
+ val products = Arguments.createArray()
377
+ purchase.products.forEach { products.pushString(it) }
378
+ item.putArray("productIds", products)
341
379
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
342
380
  item.putString("transactionReceipt", purchase.originalJson)
343
381
  item.putString("purchaseToken", purchase.purchaseToken)
@@ -354,11 +392,12 @@ class RNIapModule(
354
392
  @ReactMethod
355
393
  fun buyItemByType(
356
394
  type: String,
357
- sku: String,
395
+ skuArr: ReadableArray,
358
396
  purchaseToken: String?,
359
- prorationMode: Int?,
397
+ prorationMode: Int,
360
398
  obfuscatedAccountId: String?,
361
399
  obfuscatedProfileId: String?,
400
+ selectedOfferIndexArr: ReadableArray, // New optional parameter in V5
362
401
  promise: Promise
363
402
  ) {
364
403
  val activity = currentActivity
@@ -370,26 +409,42 @@ class RNIapModule(
370
409
  promise
371
410
  ) { billingClient ->
372
411
  DoobooUtils.instance.addPromiseForKey(
373
- PROMISE_BUY_ITEM, promise
412
+ PROMISE_BUY_ITEM,
413
+ promise
374
414
  )
375
- val builder = BillingFlowParams.newBuilder()
376
- val selectedSku: SkuDetails? = skus[sku]
377
- if (selectedSku == null) {
378
- val debugMessage =
379
- "The sku was not found. Please fetch products first by calling getItems"
380
- val error = Arguments.createMap()
381
- error.putString("debugMessage", debugMessage)
382
- error.putString("code", PROMISE_BUY_ITEM)
383
- error.putString("message", debugMessage)
384
- error.putString("productId", sku)
385
- sendEvent(reactContext, "purchase-error", error)
386
- promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
387
- return@ensureConnection
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()
388
442
  }
389
- builder.setSkuDetails(selectedSku)
443
+ val builder = BillingFlowParams.newBuilder()
444
+ builder.setProductDetailsParamsList(productParamsList)
390
445
  val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
391
446
  if (purchaseToken != null) {
392
- subscriptionUpdateParamsBuilder.setOldSkuPurchaseToken(purchaseToken)
447
+ subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
393
448
  }
394
449
  if (obfuscatedAccountId != null) {
395
450
  builder.setObfuscatedAccountId(obfuscatedAccountId)
@@ -397,14 +452,14 @@ class RNIapModule(
397
452
  if (obfuscatedProfileId != null) {
398
453
  builder.setObfuscatedProfileId(obfuscatedProfileId)
399
454
  }
400
- if (prorationMode != null && prorationMode != -1) {
455
+ if (prorationMode != -1) {
401
456
  if (prorationMode
402
457
  == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
403
458
  ) {
404
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
459
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
405
460
  BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
406
461
  )
407
- if (type != BillingClient.SkuType.SUBS) {
462
+ if (type != BillingClient.ProductType.SUBS) {
408
463
  val debugMessage =
409
464
  (
410
465
  "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
@@ -414,7 +469,7 @@ class RNIapModule(
414
469
  error.putString("debugMessage", debugMessage)
415
470
  error.putString("code", PROMISE_BUY_ITEM)
416
471
  error.putString("message", debugMessage)
417
- error.putString("productId", sku)
472
+ error.putArray("productIds", skuArr)
418
473
  sendEvent(reactContext, "purchase-error", error)
419
474
  promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
420
475
  return@ensureConnection
@@ -422,27 +477,27 @@ class RNIapModule(
422
477
  } else if (prorationMode
423
478
  == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
424
479
  ) {
425
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
480
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
426
481
  BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
427
482
  )
428
483
  } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
429
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
484
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
430
485
  BillingFlowParams.ProrationMode.DEFERRED
431
486
  )
432
487
  } else if (prorationMode
433
488
  == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
434
489
  ) {
435
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
490
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
436
491
  BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
437
492
  )
438
493
  } else if (prorationMode
439
494
  == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
440
495
  ) {
441
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
496
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
442
497
  BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
443
498
  )
444
499
  } else {
445
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
500
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
446
501
  BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
447
502
  )
448
503
  }
@@ -452,7 +507,7 @@ class RNIapModule(
452
507
  builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
453
508
  }
454
509
  val flowParams = builder.build()
455
- val billingResultCode = billingClient.launchBillingFlow(activity, flowParams)?.responseCode ?: BillingClient.BillingResponseCode.ERROR
510
+ val billingResultCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
456
511
  if (billingResultCode == BillingClient.BillingResponseCode.OK) {
457
512
  promise.safeResolve(true)
458
513
  return@ensureConnection
@@ -516,6 +571,7 @@ class RNIapModule(
516
571
  .getBillingResponseData(billingResult.responseCode)
517
572
  map.putString("code", errorData[0])
518
573
  map.putString("message", errorData[1])
574
+ map.putString("purchaseToken",purchaseToken)
519
575
  promise.safeResolve(map)
520
576
  }
521
577
  }
@@ -536,11 +592,13 @@ class RNIapModule(
536
592
  return
537
593
  }
538
594
  if (purchases != null) {
539
- var promiseItem: WritableMap? = null
540
- for (i in purchases.indices) {
595
+ val promiseItems: WritableArray = Arguments.createArray()
596
+ purchases.forEach { purchase ->
541
597
  val item = Arguments.createMap()
542
- val purchase = purchases[i]
543
- item.putString("productId", purchase.skus[0])
598
+ item.putString("productId", purchase.products[0])
599
+ val products = Arguments.createArray()
600
+ purchase.products.forEach { products.pushString(it) }
601
+ item.putArray("productIds", products)
544
602
  item.putString("transactionId", purchase.orderId)
545
603
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
546
604
  item.putString("transactionReceipt", purchase.originalJson)
@@ -563,13 +621,10 @@ class RNIapModule(
563
621
  accountIdentifiers.obfuscatedProfileId
564
622
  )
565
623
  }
566
- promiseItem = WritableNativeMap()
567
- promiseItem.merge(item)
624
+ promiseItems.pushMap(item)
568
625
  sendEvent(reactContext, "purchase-updated", item)
569
626
  }
570
- if (promiseItem != null) {
571
- DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItem)
572
- }
627
+ DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItems)
573
628
  } else {
574
629
  val result = Arguments.createMap()
575
630
  result.putInt("responseCode", billingResult.responseCode)
@@ -588,10 +643,12 @@ class RNIapModule(
588
643
  ensureConnection(
589
644
  promise
590
645
  ) { billingClient ->
591
- val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
646
+ val types = arrayOf(BillingClient.ProductType.INAPP, BillingClient.ProductType.SUBS)
592
647
  for (type in types) {
593
648
  billingClient.queryPurchasesAsync(
594
- type
649
+ QueryPurchasesParams.newBuilder().setProductType(
650
+ type
651
+ ).build()
595
652
  ) { billingResult: BillingResult, list: List<Purchase> ->
596
653
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
597
654
 
@@ -642,6 +699,7 @@ class RNIapModule(
642
699
  override fun onHostPause() {}
643
700
  override fun onHostDestroy() {
644
701
  billingClientCache?.endConnection()
702
+ billingClientCache = null
645
703
  }
646
704
  }
647
705
  reactContext.addLifecycleEventListener(lifecycleEventListener)