react-native-iap 8.4.0 → 9.0.0-beta

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
@@ -1,5 +1,5 @@
1
1
  buildscript {
2
- ext.DEFAULT_KOTLIN_VERSION = '1.5.10'
2
+ ext.DEFAULT_KOTLIN_VERSION = '1.7.10'
3
3
  ext.safeExtGet={prop, fallback->
4
4
  return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
5
5
  }
@@ -23,7 +23,7 @@ def DEFAULT_COMPILE_SDK_VERSION = 30
23
23
  def DEFAULT_BUILD_TOOLS_VERSION = "30.0.2"
24
24
  def DEFAULT_MIN_SDK_VERSION = 16
25
25
  def DEFAULT_TARGET_SDK_VERSION = 30
26
- def DEFAULT_PLAY_SERVICES_VERSION = "17.6.0"
26
+ def DEFAULT_PLAY_SERVICES_VERSION = "18.1.0"
27
27
 
28
28
  android {
29
29
  compileSdkVersion safeExtGet("compileSdkVersion", DEFAULT_COMPILE_SDK_VERSION)
@@ -68,7 +68,7 @@ dependencies {
68
68
  implementation 'com.facebook.react:react-native:+'
69
69
  testImplementation 'junit:junit:4.13.1'
70
70
  testImplementation "io.mockk:mockk:1.12.4"
71
- playImplementation 'com.android.billingclient:billing:4.0.0'
71
+ playImplementation 'com.android.billingclient:billing:5.0.0'
72
72
  def playServicesVersion = safeExtGet('playServicesVersion', DEFAULT_PLAY_SERVICES_VERSION)
73
73
  playImplementation "com.google.android.gms:play-services-base:$playServicesVersion"
74
74
  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
@@ -29,7 +32,6 @@ import com.facebook.react.bridge.WritableNativeMap
29
32
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
30
33
  import com.google.android.gms.common.ConnectionResult
31
34
  import com.google.android.gms.common.GoogleApiAvailability
32
- import java.math.BigDecimal
33
35
  import java.util.ArrayList
34
36
 
35
37
  class RNIapModule(
@@ -38,10 +40,11 @@ class RNIapModule(
38
40
  private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance()
39
41
  ) :
40
42
  ReactContextBaseJavaModule(reactContext),
41
- PurchasesUpdatedListener {
43
+ PurchasesUpdatedListener,
44
+ RNIapModuleInterface {
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
  }
@@ -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
+ override fun initConnection(promise: Promise) {
87
91
  if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
88
92
  != ConnectionResult.SUCCESS
89
93
  ) {
@@ -113,12 +117,13 @@ class RNIapModule(
113
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
+ override 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
+ override 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,94 @@ class RNIapModule(
186
193
  }
187
194
 
188
195
  @ReactMethod
189
- fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
196
+ override 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
+ skuList.add(
205
+ QueryProductDetailsParams.Product.newBuilder().setProductId(sku)
206
+ .setProductType(type).build()
207
+ )
198
208
  }
199
209
  }
200
- val params = SkuDetailsParams.newBuilder()
201
- params.setSkusList(skuList).setType(type)
202
- billingClient.querySkuDetailsAsync(
210
+ val params = QueryProductDetailsParams.newBuilder().setProductList(skuList)
211
+ billingClient.queryProductDetailsAsync(
203
212
  params.build()
204
- ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
205
- if (!isValidResult(billingResult, promise)) return@querySkuDetailsAsync
213
+ ) { billingResult: BillingResult, skuDetailsList: List<ProductDetails> ->
214
+ if (!isValidResult(billingResult, promise)) return@queryProductDetailsAsync
206
215
 
207
216
  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()
217
+ for (skuDetails in skuDetailsList) {
218
+ skus[skuDetails.productId] = skuDetails
219
+
220
+ val item = Arguments.createMap()
221
+ item.putString("productId", skuDetails.productId)
222
+ item.putString("title", skuDetails.title)
223
+ item.putString("description", skuDetails.description)
224
+ item.putString("productType", skuDetails.productType)
225
+ item.putString("name", skuDetails.name)
226
+ val oneTimePurchaseOfferDetails = Arguments.createMap()
227
+ skuDetails.oneTimePurchaseOfferDetails?.let {
228
+ oneTimePurchaseOfferDetails.putString(
229
+ "priceCurrencyCode",
230
+ it.priceCurrencyCode
244
231
  )
245
- item.putString(
246
- "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
232
+ oneTimePurchaseOfferDetails.putString("formattedPrice", it.formattedPrice)
233
+ oneTimePurchaseOfferDetails.putString(
234
+ "priceAmountMicros",
235
+ it.priceAmountMicros.toString()
247
236
  )
248
- item.putString(
249
- "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
237
+ }
238
+ item.putMap("oneTimePurchaseOfferDetails", oneTimePurchaseOfferDetails)
239
+
240
+ val subscriptionOfferDetails = Arguments.createArray()
241
+ skuDetails.subscriptionOfferDetails?.forEach { subscriptionOfferDetailsItem ->
242
+ val offerDetails = Arguments.createMap()
243
+ offerDetails.putString(
244
+ "offerToken",
245
+ subscriptionOfferDetailsItem.offerToken
250
246
  )
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)
247
+ val offerTags = Arguments.createArray()
248
+ subscriptionOfferDetailsItem.offerTags.forEach { offerTag ->
249
+ offerTags.pushString(offerTag)
250
+ }
251
+ offerDetails.putArray("offerTags", offerTags)
252
+
253
+ val pricingPhasesList = Arguments.createArray()
254
+ subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem ->
255
+ val pricingPhase = Arguments.createMap()
256
+ pricingPhase.putString(
257
+ "formattedPrice",
258
+ pricingPhaseItem.formattedPrice
259
+ )
260
+ pricingPhase.putString(
261
+ "priceCurrencyCode",
262
+ pricingPhaseItem.priceCurrencyCode
263
+ )
264
+ pricingPhase.putString("billingPeriod", pricingPhaseItem.billingPeriod)
265
+ pricingPhase.putInt(
266
+ "billingCycleCount",
267
+ pricingPhaseItem.billingCycleCount
268
+ )
269
+ pricingPhase.putString(
270
+ "priceAmountMicros",
271
+ pricingPhaseItem.priceAmountMicros.toString()
272
+ )
273
+ pricingPhase.putInt("recurrenceMode", pricingPhaseItem.recurrenceMode)
274
+
275
+ pricingPhasesList.pushMap(pricingPhase)
276
+ }
277
+ val pricingPhases = Arguments.createMap()
278
+ pricingPhases.putArray("pricingPhaseList", pricingPhasesList)
279
+ offerDetails.putMap("pricingPhases", pricingPhases)
280
+ subscriptionOfferDetails.pushMap(offerDetails)
259
281
  }
282
+ item.putArray("subscriptionOfferDetails", subscriptionOfferDetails)
283
+ items.pushMap(item)
260
284
  }
261
285
  promise.safeResolve(items)
262
286
  }
@@ -280,20 +304,22 @@ class RNIapModule(
280
304
  }
281
305
 
282
306
  @ReactMethod
283
- fun getAvailableItemsByType(type: String, promise: Promise) {
307
+ override fun getAvailableItemsByType(type: String, promise: Promise) {
284
308
  ensureConnection(
285
309
  promise
286
310
  ) { billingClient ->
287
311
  val items = WritableNativeArray()
288
312
  billingClient.queryPurchasesAsync(
289
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
313
+ QueryPurchasesParams.newBuilder().setProductType(
314
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
315
+ ).build()
290
316
  ) { billingResult: BillingResult, purchases: List<Purchase>? ->
291
317
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
292
318
  if (purchases != null) {
293
319
  for (i in purchases.indices) {
294
320
  val purchase = purchases[i]
295
321
  val item = WritableNativeMap()
296
- item.putString("productId", purchase.skus[0])
322
+ item.putString("productId", purchase.products[0]) // TODO: should be a list
297
323
  item.putString("transactionId", purchase.orderId)
298
324
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
299
325
  item.putString("transactionReceipt", purchase.originalJson)
@@ -312,7 +338,7 @@ class RNIapModule(
312
338
  "obfuscatedProfileIdAndroid",
313
339
  purchase.accountIdentifiers?.obfuscatedProfileId
314
340
  )
315
- if (type == BillingClient.SkuType.SUBS) {
341
+ if (type == BillingClient.ProductType.SUBS) {
316
342
  item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
317
343
  }
318
344
  items.pushMap(item)
@@ -324,20 +350,24 @@ class RNIapModule(
324
350
  }
325
351
 
326
352
  @ReactMethod
327
- fun getPurchaseHistoryByType(type: String, promise: Promise) {
353
+ override fun getPurchaseHistoryByType(type: String, promise: Promise) {
328
354
  ensureConnection(
329
355
  promise
330
356
  ) { billingClient ->
331
357
  billingClient.queryPurchaseHistoryAsync(
332
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
333
- ) { billingResult, purchaseHistoryRecordList ->
358
+ QueryPurchaseHistoryParams.newBuilder().setProductType(
359
+ if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
360
+ ).build()
361
+ ) {
362
+ billingResult: BillingResult, purchaseHistoryRecordList: MutableList<PurchaseHistoryRecord>? ->
363
+
334
364
  if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync
335
365
 
336
366
  Log.d(TAG, purchaseHistoryRecordList.toString())
337
367
  val items = Arguments.createArray()
338
368
  purchaseHistoryRecordList?.forEach { purchase ->
339
369
  val item = Arguments.createMap()
340
- item.putString("productId", purchase.skus[0])
370
+ item.putString("productId", purchase.products[0])
341
371
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
342
372
  item.putString("transactionReceipt", purchase.originalJson)
343
373
  item.putString("purchaseToken", purchase.purchaseToken)
@@ -352,13 +382,14 @@ class RNIapModule(
352
382
  }
353
383
 
354
384
  @ReactMethod
355
- fun buyItemByType(
385
+ override fun buyItemByType(
356
386
  type: String,
357
- sku: String,
387
+ sku: String, // TODO: should this now be an array?
358
388
  purchaseToken: String?,
359
389
  prorationMode: Int?,
360
390
  obfuscatedAccountId: String?,
361
391
  obfuscatedProfileId: String?,
392
+ selectedOfferIndex: Int?, // New optional parameter in V5, TODO: should it be an array?
362
393
  promise: Promise
363
394
  ) {
364
395
  val activity = currentActivity
@@ -370,10 +401,11 @@ class RNIapModule(
370
401
  promise
371
402
  ) { billingClient ->
372
403
  DoobooUtils.instance.addPromiseForKey(
373
- PROMISE_BUY_ITEM, promise
404
+ PROMISE_BUY_ITEM,
405
+ promise
374
406
  )
375
407
  val builder = BillingFlowParams.newBuilder()
376
- val selectedSku: SkuDetails? = skus[sku]
408
+ val selectedSku: ProductDetails? = skus[sku]
377
409
  if (selectedSku == null) {
378
410
  val debugMessage =
379
411
  "The sku was not found. Please fetch products first by calling getItems"
@@ -386,10 +418,21 @@ class RNIapModule(
386
418
  promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
387
419
  return@ensureConnection
388
420
  }
389
- builder.setSkuDetails(selectedSku)
421
+ var productParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
422
+ if (selectedOfferIndex != null && (
423
+ selectedSku.subscriptionOfferDetails?.size
424
+ ?: 0
425
+ ) > selectedOfferIndex
426
+ ) {
427
+ val offerToken =
428
+ selectedSku.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
429
+ offerToken?.let { productParams = productParams.setOfferToken(offerToken) }
430
+ }
431
+
432
+ builder.setProductDetailsParamsList(listOf(productParams.build()))
390
433
  val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
391
434
  if (purchaseToken != null) {
392
- subscriptionUpdateParamsBuilder.setOldSkuPurchaseToken(purchaseToken)
435
+ subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
393
436
  }
394
437
  if (obfuscatedAccountId != null) {
395
438
  builder.setObfuscatedAccountId(obfuscatedAccountId)
@@ -401,7 +444,7 @@ class RNIapModule(
401
444
  if (prorationMode
402
445
  == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
403
446
  ) {
404
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
447
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
405
448
  BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
406
449
  )
407
450
  if (type != BillingClient.SkuType.SUBS) {
@@ -422,27 +465,27 @@ class RNIapModule(
422
465
  } else if (prorationMode
423
466
  == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
424
467
  ) {
425
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
468
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
426
469
  BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
427
470
  )
428
471
  } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
429
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
472
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
430
473
  BillingFlowParams.ProrationMode.DEFERRED
431
474
  )
432
475
  } else if (prorationMode
433
476
  == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
434
477
  ) {
435
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
478
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
436
479
  BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
437
480
  )
438
481
  } else if (prorationMode
439
482
  == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
440
483
  ) {
441
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
484
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
442
485
  BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
443
486
  )
444
487
  } else {
445
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
488
+ subscriptionUpdateParamsBuilder.setReplaceProrationMode(
446
489
  BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
447
490
  )
448
491
  }
@@ -452,7 +495,7 @@ class RNIapModule(
452
495
  builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
453
496
  }
454
497
  val flowParams = builder.build()
455
- val billingResultCode = billingClient.launchBillingFlow(activity, flowParams)?.responseCode ?: BillingClient.BillingResponseCode.ERROR
498
+ val billingResultCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
456
499
  if (billingResultCode == BillingClient.BillingResponseCode.OK) {
457
500
  promise.safeResolve(true)
458
501
  return@ensureConnection
@@ -465,7 +508,7 @@ class RNIapModule(
465
508
  }
466
509
 
467
510
  @ReactMethod
468
- fun acknowledgePurchase(
511
+ override fun acknowledgePurchase(
469
512
  token: String,
470
513
  developerPayLoad: String?,
471
514
  promise: Promise
@@ -495,7 +538,7 @@ class RNIapModule(
495
538
  }
496
539
 
497
540
  @ReactMethod
498
- fun consumeProduct(
541
+ override fun consumeProduct(
499
542
  token: String,
500
543
  developerPayLoad: String?,
501
544
  promise: Promise
@@ -540,7 +583,7 @@ class RNIapModule(
540
583
  for (i in purchases.indices) {
541
584
  val item = Arguments.createMap()
542
585
  val purchase = purchases[i]
543
- item.putString("productId", purchase.skus[0])
586
+ item.putString("productId", purchase.products[0])
544
587
  item.putString("transactionId", purchase.orderId)
545
588
  item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
546
589
  item.putString("transactionReceipt", purchase.originalJson)
@@ -588,10 +631,12 @@ class RNIapModule(
588
631
  ensureConnection(
589
632
  promise
590
633
  ) { billingClient ->
591
- val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
634
+ val types = arrayOf(BillingClient.ProductType.INAPP, BillingClient.ProductType.SUBS)
592
635
  for (type in types) {
593
636
  billingClient.queryPurchasesAsync(
594
- type
637
+ QueryPurchasesParams.newBuilder().setProductType(
638
+ type
639
+ ).build()
595
640
  ) { billingResult: BillingResult, list: List<Purchase> ->
596
641
  if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
597
642
 
@@ -604,22 +649,22 @@ class RNIapModule(
604
649
  }
605
650
 
606
651
  @ReactMethod
607
- fun startListening(promise: Promise) {
652
+ override fun startListening(promise: Promise) {
608
653
  sendUnconsumedPurchases(promise)
609
654
  }
610
655
 
611
656
  @ReactMethod
612
- fun addListener(eventName: String) {
657
+ override fun addListener(eventName: String) {
613
658
  // Keep: Required for RN built-in Event Emitter Calls.
614
659
  }
615
660
 
616
661
  @ReactMethod
617
- fun removeListeners(count: Double) {
662
+ override fun removeListeners(count: Double) {
618
663
  // Keep: Required for RN built-in Event Emitter Calls.
619
664
  }
620
665
 
621
666
  @ReactMethod
622
- fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
667
+ override fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
623
668
 
624
669
  private fun sendEvent(
625
670
  reactContext: ReactContext,
@@ -0,0 +1,44 @@
1
+ package com.dooboolab.RNIap
2
+
3
+ import com.android.billingclient.api.BillingResult
4
+ import com.android.billingclient.api.Purchase
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReadableArray
7
+
8
+ /**
9
+ * Common interface for consistency
10
+ */
11
+ interface RNIapModuleInterface {
12
+ fun getName(): String
13
+ fun initConnection(promise: Promise)
14
+ fun endConnection(promise: Promise)
15
+ fun flushFailedPurchasesCachedAsPending(promise: Promise)
16
+ fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise)
17
+ fun getAvailableItemsByType(type: String, promise: Promise)
18
+ fun getPurchaseHistoryByType(type: String, promise: Promise)
19
+ fun buyItemByType(
20
+ type: String,
21
+ sku: String,
22
+ purchaseToken: String?,
23
+ prorationMode: Int?,
24
+ obfuscatedAccountId: String?,
25
+ obfuscatedProfileId: String?,
26
+ selectedOfferIndex: Int?, // New optional parameter in V5 (added to maintain interface consistency)
27
+ promise: Promise
28
+ )
29
+ fun acknowledgePurchase(
30
+ token: String,
31
+ developerPayLoad: String?,
32
+ promise: Promise
33
+ )
34
+ fun consumeProduct(
35
+ token: String,
36
+ developerPayLoad: String?,
37
+ promise: Promise
38
+ )
39
+ fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?)
40
+ fun startListening(promise: Promise)
41
+ fun addListener(eventName: String)
42
+ fun removeListeners(count: Double)
43
+ fun getPackageName(promise: Promise)
44
+ }