react-native-iap 8.1.0 → 8.1.3

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
@@ -17,6 +17,7 @@ import com.facebook.react.bridge.Arguments
17
17
  import com.facebook.react.bridge.LifecycleEventListener
18
18
  import com.facebook.react.bridge.ObjectAlreadyConsumedException
19
19
  import com.facebook.react.bridge.Promise
20
+ import com.facebook.react.bridge.PromiseImpl
20
21
  import com.facebook.react.bridge.ReactApplicationContext
21
22
  import com.facebook.react.bridge.ReactContext
22
23
  import com.facebook.react.bridge.ReactContextBaseJavaModule
@@ -28,37 +29,50 @@ import com.facebook.react.bridge.WritableNativeMap
28
29
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
29
30
  import com.google.android.gms.common.ConnectionResult
30
31
  import com.google.android.gms.common.GoogleApiAvailability
31
- import java.lang.Exception
32
32
  import java.math.BigDecimal
33
33
  import java.util.ArrayList
34
34
 
35
- class RNIapModule(reactContext: ReactApplicationContext) :
35
+ class RNIapModule(private val reactContext: ReactApplicationContext) :
36
36
  ReactContextBaseJavaModule(reactContext),
37
37
  PurchasesUpdatedListener {
38
- val TAG = "RNIapModule"
39
- private val reactContext: ReactContext
40
- private var billingClientCache: BillingClient? = null
41
- private val skus: MutableMap<String, SkuDetails>
38
+
39
+ private val billingClient: BillingClient = BillingClient.newBuilder(reactContext).enablePendingPurchases().setListener(this)
40
+ .build()
41
+ private val skus: MutableMap<String, SkuDetails> = mutableMapOf()
42
42
  override fun getName(): String {
43
43
  return "RNIapModule"
44
44
  }
45
45
 
46
- private interface EnsureConnectionCallback {
47
- fun run(billingClient: BillingClient)
48
- }
49
-
50
- private fun ensureConnection(promise: Promise, callback: EnsureConnectionCallback) {
51
- val billingClient = billingClientCache
52
- if (billingClient != null && billingClient.isReady) {
53
- callback.run(billingClient)
46
+ private fun ensureConnection(promise: Promise, callback: () -> Unit) {
47
+ if (billingClient.isReady) {
48
+ callback()
54
49
  return
50
+ } else {
51
+ val nested = PromiseImpl(
52
+ {
53
+ if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
54
+ callback()
55
+ } else {
56
+ Log.i(TAG, "Incorrect parameter in resolve")
57
+ }
58
+ },
59
+ {
60
+ if (it.size > 1 && it[0] is String && it[1] is String) {
61
+ promise.reject(
62
+ it[0] as String, it[1] as String
63
+ )
64
+ } else {
65
+ Log.i(TAG, "Incorrect parameters in reject")
66
+ }
67
+ }
68
+ )
69
+ initConnection(nested)
55
70
  }
56
- promise.reject(DoobooUtils.E_NOT_PREPARED, "Not initialized, Please call initConnection()")
57
71
  }
58
72
 
59
73
  @ReactMethod
60
74
  fun initConnection(promise: Promise) {
61
- if (billingClientCache != null) {
75
+ if (billingClient.isReady) {
62
76
  Log.i(
63
77
  TAG,
64
78
  "Already initialized, you should only call initConnection() once when your app starts"
@@ -70,13 +84,11 @@ class RNIapModule(reactContext: ReactApplicationContext) :
70
84
  != ConnectionResult.SUCCESS
71
85
  ) {
72
86
  Log.i(TAG, "Google Play Services are not available on this device")
73
- promise.resolve(false)
87
+ promise.reject(DoobooUtils.E_NOT_PREPARED, "Google Play Services are not available on this device")
74
88
  return
75
89
  }
76
- billingClientCache =
77
- BillingClient.newBuilder(reactContext).enablePendingPurchases().setListener(this)
78
- .build()
79
- billingClientCache!!.startConnection(
90
+
91
+ billingClient.startConnection(
80
92
  object : BillingClientStateListener {
81
93
  override fun onBillingSetupFinished(billingResult: BillingResult) {
82
94
  val responseCode = billingResult.responseCode
@@ -93,31 +105,15 @@ class RNIapModule(reactContext: ReactApplicationContext) :
93
105
  }
94
106
 
95
107
  override fun onBillingServiceDisconnected() {
96
- try {
97
- promise.reject("initConnection", "Billing service disconnected")
98
- } catch (oce: ObjectAlreadyConsumedException) {
99
- Log.e(TAG, oce.message!!)
100
- }
108
+ Log.i(TAG, "Billing service disconnected")
101
109
  }
102
110
  })
103
111
  }
104
112
 
105
113
  @ReactMethod
106
114
  fun endConnection(promise: Promise) {
107
- if (billingClientCache != null) {
108
- billingClientCache = try {
109
- billingClientCache!!.endConnection()
110
- null
111
- } catch (e: Exception) {
112
- promise.reject("endConnection", e.message)
113
- return
114
- }
115
- }
116
- try {
117
- promise.resolve(true)
118
- } catch (oce: ObjectAlreadyConsumedException) {
119
- Log.e(TAG, oce.message!!)
120
- }
115
+ billingClient.endConnection()
116
+ promise.resolve(true)
121
117
  }
122
118
 
123
119
  private fun consumeItems(
@@ -127,251 +123,236 @@ class RNIapModule(reactContext: ReactApplicationContext) :
127
123
  ) {
128
124
  for (purchase in purchases) {
129
125
  ensureConnection(
130
- promise,
131
- object : EnsureConnectionCallback {
132
- override fun run(billingClient: BillingClient) {
133
- val consumeParams =
134
- ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
135
- .build()
136
- val listener =
137
- ConsumeResponseListener { billingResult: BillingResult, outToken: String? ->
138
- if (billingResult.responseCode != expectedResponseCode) {
139
- PlayUtils.instance
140
- .rejectPromiseWithBillingError(
141
- promise,
142
- billingResult.responseCode
143
- )
144
- return@ConsumeResponseListener
145
- }
146
- try {
147
- promise.resolve(true)
148
- } catch (oce: ObjectAlreadyConsumedException) {
149
- promise.reject(oce.message)
150
- }
151
- }
152
- billingClient.consumeAsync(consumeParams, listener)
126
+ promise
127
+ ) {
128
+ val consumeParams =
129
+ ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
130
+ .build()
131
+ val listener =
132
+ ConsumeResponseListener { billingResult: BillingResult, outToken: String? ->
133
+ if (billingResult.responseCode != expectedResponseCode) {
134
+ PlayUtils.instance
135
+ .rejectPromiseWithBillingError(
136
+ promise,
137
+ billingResult.responseCode
138
+ )
139
+ return@ConsumeResponseListener
140
+ }
141
+ try {
142
+ promise.resolve(true)
143
+ } catch (oce: ObjectAlreadyConsumedException) {
144
+ promise.reject(oce.message)
145
+ }
153
146
  }
154
- }
155
- )
147
+ billingClient.consumeAsync(consumeParams, listener)
148
+ }
156
149
  }
157
150
  }
158
151
 
159
152
  @ReactMethod
160
153
  fun flushFailedPurchasesCachedAsPending(promise: Promise) {
161
154
  ensureConnection(
162
- promise,
163
- object : EnsureConnectionCallback {
164
- override fun run(billingClient: BillingClient) {
165
- val array = WritableNativeArray()
166
- billingClient.queryPurchasesAsync(
167
- BillingClient.SkuType.INAPP
168
- ) { billingResult: BillingResult?, list: List<Purchase>? ->
169
- if (list == null) {
170
- // No purchases found
171
- promise.resolve(false)
172
- return@queryPurchasesAsync
173
- }
174
- val pendingPurchases: MutableList<Purchase> = ArrayList()
175
- for (purchase in list) {
176
- // we only want to try to consume PENDING items, in order to force cache-refresh
177
- // for
178
- // them
179
- if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {
180
- pendingPurchases.add(purchase)
181
- }
182
- }
183
- if (pendingPurchases.size == 0) {
184
- promise.resolve(false)
185
- return@queryPurchasesAsync
186
- }
187
- consumeItems(
188
- pendingPurchases,
189
- promise,
190
- BillingClient.BillingResponseCode.ITEM_NOT_OWNED
191
- )
155
+ promise
156
+ ) {
157
+ val array = WritableNativeArray()
158
+ billingClient.queryPurchasesAsync(
159
+ BillingClient.SkuType.INAPP
160
+ ) { _: BillingResult?, list: List<Purchase>? ->
161
+ if (list == null) {
162
+ // No purchases found
163
+ promise.resolve(false)
164
+ return@queryPurchasesAsync
165
+ }
166
+ val pendingPurchases: MutableList<Purchase> = ArrayList()
167
+ for (purchase in list) {
168
+ // we only want to try to consume PENDING items, in order to force cache-refresh
169
+ // for
170
+ // them
171
+ if (purchase.purchaseState == Purchase.PurchaseState.PENDING) {
172
+ pendingPurchases.add(purchase)
192
173
  }
193
174
  }
175
+ if (pendingPurchases.size == 0) {
176
+ promise.resolve(false)
177
+ return@queryPurchasesAsync
178
+ }
179
+ consumeItems(
180
+ pendingPurchases,
181
+ promise,
182
+ BillingClient.BillingResponseCode.ITEM_NOT_OWNED
183
+ )
194
184
  }
195
- )
185
+ }
196
186
  }
197
187
 
198
188
  @ReactMethod
199
189
  fun getItemsByType(type: String?, skuArr: ReadableArray, promise: Promise) {
200
190
  ensureConnection(
201
- promise,
202
- object : EnsureConnectionCallback {
203
- override fun run(billingClient: BillingClient) {
204
- val skuList = ArrayList<String>()
205
- for (i in 0 until skuArr.size()) {
206
- val sku = skuArr.getString(i)
207
- if (sku is String) {
208
- skuList.add(sku)
209
- }
210
- }
211
- val params = SkuDetailsParams.newBuilder()
212
- params.setSkusList(skuList).setType(type!!)
213
- billingClient.querySkuDetailsAsync(
214
- params.build()
215
- ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
216
- Log.d(TAG, "responseCode: " + billingResult.responseCode)
217
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
218
- PlayUtils.instance
219
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
220
- return@querySkuDetailsAsync
221
- }
222
- if (skuDetailsList != null) {
223
- for (sku in skuDetailsList) {
224
- skus.put(sku.getSku(), sku)
225
- }
226
- }
227
- val items = WritableNativeArray()
228
- for (skuDetails in skuDetailsList!!) {
229
- val item = Arguments.createMap()
230
- item.putString("productId", skuDetails.sku)
231
- val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
232
- val priceAmountMicros = skuDetails.priceAmountMicros
233
- // Use valueOf instead of constructors.
234
- // See:
235
- // https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
236
- val priceAmount = BigDecimal.valueOf(priceAmountMicros)
237
- val introductoryPriceAmount =
238
- BigDecimal.valueOf(introductoryPriceMicros)
239
- val microUnitsDivisor = BigDecimal.valueOf(1000000)
240
- val price = priceAmount.divide(microUnitsDivisor).toString()
241
- val introductoryPriceAsAmountAndroid =
242
- introductoryPriceAmount.divide(microUnitsDivisor).toString()
243
- item.putString("price", price)
244
- item.putString("currency", skuDetails.priceCurrencyCode)
245
- item.putString("type", skuDetails.type)
246
- item.putString("localizedPrice", skuDetails.price)
247
- item.putString("title", skuDetails.title)
248
- item.putString("description", skuDetails.description)
249
- item.putString("introductoryPrice", skuDetails.introductoryPrice)
250
- item.putString("typeAndroid", skuDetails.type)
251
- item.putString("packageNameAndroid", skuDetails.zzc())
252
- item.putString("originalPriceAndroid", skuDetails.originalPrice)
253
- item.putString(
254
- "subscriptionPeriodAndroid",
255
- skuDetails.subscriptionPeriod
256
- )
257
- item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
258
- item.putString(
259
- "introductoryPriceCyclesAndroid",
260
- skuDetails.introductoryPriceCycles.toString()
261
- )
262
- item.putString(
263
- "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
264
- )
265
- item.putString(
266
- "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
267
- )
268
- item.putString("iconUrl", skuDetails.iconUrl)
269
- item.putString("originalJson", skuDetails.originalJson)
270
- val originalPriceAmountMicros =
271
- BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
272
- val originalPrice =
273
- originalPriceAmountMicros.divide(microUnitsDivisor).toString()
274
- item.putString("originalPrice", originalPrice)
275
- items.pushMap(item)
276
- }
277
- try {
278
- promise.resolve(items)
279
- } catch (oce: ObjectAlreadyConsumedException) {
280
- Log.e(TAG, oce.message!!)
281
- }
191
+ promise
192
+ ) {
193
+ val skuList = ArrayList<String>()
194
+ for (i in 0 until skuArr.size()) {
195
+ val sku = skuArr.getString(i)
196
+ if (sku is String) {
197
+ skuList.add(sku)
198
+ }
199
+ }
200
+ val params = SkuDetailsParams.newBuilder()
201
+ params.setSkusList(skuList).setType(type!!)
202
+ billingClient.querySkuDetailsAsync(
203
+ params.build()
204
+ ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
205
+ Log.d(TAG, "responseCode: " + billingResult.responseCode)
206
+ if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
207
+ PlayUtils.instance
208
+ .rejectPromiseWithBillingError(promise, billingResult.responseCode)
209
+ return@querySkuDetailsAsync
210
+ }
211
+ if (skuDetailsList != null) {
212
+ for (sku in skuDetailsList) {
213
+ skus[sku.sku] = sku
282
214
  }
283
215
  }
216
+ val items = WritableNativeArray()
217
+ for (skuDetails in skuDetailsList!!) {
218
+ val item = Arguments.createMap()
219
+ item.putString("productId", skuDetails.sku)
220
+ val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
221
+ val priceAmountMicros = skuDetails.priceAmountMicros
222
+ // Use valueOf instead of constructors.
223
+ // See:
224
+ // https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
225
+ val priceAmount = BigDecimal.valueOf(priceAmountMicros)
226
+ val introductoryPriceAmount =
227
+ BigDecimal.valueOf(introductoryPriceMicros)
228
+ val microUnitsDivisor = BigDecimal.valueOf(1000000)
229
+ val price = priceAmount.divide(microUnitsDivisor).toString()
230
+ val introductoryPriceAsAmountAndroid =
231
+ introductoryPriceAmount.divide(microUnitsDivisor).toString()
232
+ item.putString("price", price)
233
+ item.putString("currency", skuDetails.priceCurrencyCode)
234
+ item.putString("type", skuDetails.type)
235
+ item.putString("localizedPrice", skuDetails.price)
236
+ item.putString("title", skuDetails.title)
237
+ item.putString("description", skuDetails.description)
238
+ item.putString("introductoryPrice", skuDetails.introductoryPrice)
239
+ item.putString("typeAndroid", skuDetails.type)
240
+ item.putString("packageNameAndroid", skuDetails.zzc())
241
+ item.putString("originalPriceAndroid", skuDetails.originalPrice)
242
+ item.putString(
243
+ "subscriptionPeriodAndroid",
244
+ skuDetails.subscriptionPeriod
245
+ )
246
+ item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
247
+ item.putString(
248
+ "introductoryPriceCyclesAndroid",
249
+ skuDetails.introductoryPriceCycles.toString()
250
+ )
251
+ item.putString(
252
+ "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
253
+ )
254
+ item.putString(
255
+ "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
256
+ )
257
+ item.putString("iconUrl", skuDetails.iconUrl)
258
+ item.putString("originalJson", skuDetails.originalJson)
259
+ val originalPriceAmountMicros =
260
+ BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
261
+ val originalPrice =
262
+ originalPriceAmountMicros.divide(microUnitsDivisor).toString()
263
+ item.putString("originalPrice", originalPrice)
264
+ items.pushMap(item)
265
+ }
266
+ try {
267
+ promise.resolve(items)
268
+ } catch (oce: ObjectAlreadyConsumedException) {
269
+ Log.e(TAG, oce.message!!)
270
+ }
284
271
  }
285
- )
272
+ }
286
273
  }
287
274
 
288
275
  @ReactMethod
289
276
  fun getAvailableItemsByType(type: String, promise: Promise) {
290
277
  ensureConnection(
291
- promise,
292
- object : EnsureConnectionCallback {
293
- override fun run(billingClient: BillingClient) {
294
- val items = WritableNativeArray()
295
- billingClient.queryPurchasesAsync(
296
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
297
- ) { billingResult: BillingResult?, purchases: List<Purchase>? ->
298
- if (purchases != null) {
299
- for (i in purchases.indices) {
300
- val purchase = purchases[i]
301
- val item = WritableNativeMap()
302
- item.putString("productId", purchase.skus[0])
303
- item.putString("transactionId", purchase.orderId)
304
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
305
- item.putString("transactionReceipt", purchase.originalJson)
306
- item.putString("orderId", purchase.orderId)
307
- item.putString("purchaseToken", purchase.purchaseToken)
308
- item.putString("developerPayloadAndroid", purchase.developerPayload)
309
- item.putString("signatureAndroid", purchase.signature)
310
- item.putInt("purchaseStateAndroid", purchase.purchaseState)
311
- item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
312
- item.putString("packageNameAndroid", purchase.packageName)
313
- item.putString(
314
- "obfuscatedAccountIdAndroid",
315
- purchase.accountIdentifiers!!.obfuscatedAccountId
316
- )
317
- item.putString(
318
- "obfuscatedProfileIdAndroid",
319
- purchase.accountIdentifiers!!.obfuscatedProfileId
320
- )
321
- if (type == BillingClient.SkuType.SUBS) {
322
- item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
323
- }
324
- items.pushMap(item)
325
- }
326
- }
327
- try {
328
- promise.resolve(items)
329
- } catch (oce: ObjectAlreadyConsumedException) {
330
- Log.e(TAG, oce.message!!)
278
+ promise
279
+ ) {
280
+ val items = WritableNativeArray()
281
+ billingClient.queryPurchasesAsync(
282
+ if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
283
+ ) { billingResult: BillingResult?, purchases: List<Purchase>? ->
284
+ if (purchases != null) {
285
+ for (i in purchases.indices) {
286
+ val purchase = purchases[i]
287
+ val item = WritableNativeMap()
288
+ item.putString("productId", purchase.skus[0])
289
+ item.putString("transactionId", purchase.orderId)
290
+ item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
291
+ item.putString("transactionReceipt", purchase.originalJson)
292
+ item.putString("orderId", purchase.orderId)
293
+ item.putString("purchaseToken", purchase.purchaseToken)
294
+ item.putString("developerPayloadAndroid", purchase.developerPayload)
295
+ item.putString("signatureAndroid", purchase.signature)
296
+ item.putInt("purchaseStateAndroid", purchase.purchaseState)
297
+ item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
298
+ item.putString("packageNameAndroid", purchase.packageName)
299
+ item.putString(
300
+ "obfuscatedAccountIdAndroid",
301
+ purchase.accountIdentifiers!!.obfuscatedAccountId
302
+ )
303
+ item.putString(
304
+ "obfuscatedProfileIdAndroid",
305
+ purchase.accountIdentifiers!!.obfuscatedProfileId
306
+ )
307
+ if (type == BillingClient.SkuType.SUBS) {
308
+ item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
331
309
  }
310
+ items.pushMap(item)
332
311
  }
333
312
  }
313
+ try {
314
+ promise.resolve(items)
315
+ } catch (oce: ObjectAlreadyConsumedException) {
316
+ Log.e(TAG, oce.message!!)
317
+ }
334
318
  }
335
- )
319
+ }
336
320
  }
337
321
 
338
322
  @ReactMethod
339
323
  fun getPurchaseHistoryByType(type: String, promise: Promise) {
340
324
  ensureConnection(
341
- promise,
342
- object : EnsureConnectionCallback {
343
- override fun run(billingClient: BillingClient) {
344
- billingClient.queryPurchaseHistoryAsync(
345
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
346
- ) { billingResult, purchaseHistoryRecordList ->
347
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
348
- PlayUtils.instance
349
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
350
- return@queryPurchaseHistoryAsync
351
- }
352
- Log.d(TAG, purchaseHistoryRecordList.toString())
353
- val items = Arguments.createArray()
354
- for (i in purchaseHistoryRecordList!!.indices) {
355
- val item = Arguments.createMap()
356
- val purchase = purchaseHistoryRecordList[i]
357
- item.putString("productId", purchase.skus[0])
358
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
359
- item.putString("transactionReceipt", purchase.originalJson)
360
- item.putString("purchaseToken", purchase.purchaseToken)
361
- item.putString("dataAndroid", purchase.originalJson)
362
- item.putString("signatureAndroid", purchase.signature)
363
- item.putString("developerPayload", purchase.developerPayload)
364
- items.pushMap(item)
365
- }
366
- try {
367
- promise.resolve(items)
368
- } catch (oce: ObjectAlreadyConsumedException) {
369
- Log.e(TAG, oce.message!!)
370
- }
371
- }
325
+ promise
326
+ ) {
327
+ billingClient.queryPurchaseHistoryAsync(
328
+ if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
329
+ ) { billingResult, purchaseHistoryRecordList ->
330
+ if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
331
+ PlayUtils.instance
332
+ .rejectPromiseWithBillingError(promise, billingResult.responseCode)
333
+ return@queryPurchaseHistoryAsync
334
+ }
335
+ Log.d(TAG, purchaseHistoryRecordList.toString())
336
+ val items = Arguments.createArray()
337
+ for (i in purchaseHistoryRecordList!!.indices) {
338
+ val item = Arguments.createMap()
339
+ val purchase = purchaseHistoryRecordList[i]
340
+ item.putString("productId", purchase.skus[0])
341
+ item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
342
+ item.putString("transactionReceipt", purchase.originalJson)
343
+ item.putString("purchaseToken", purchase.purchaseToken)
344
+ item.putString("dataAndroid", purchase.originalJson)
345
+ item.putString("signatureAndroid", purchase.signature)
346
+ item.putString("developerPayload", purchase.developerPayload)
347
+ items.pushMap(item)
348
+ }
349
+ try {
350
+ promise.resolve(items)
351
+ } catch (oce: ObjectAlreadyConsumedException) {
352
+ Log.e(TAG, oce.message!!)
372
353
  }
373
354
  }
374
- )
355
+ }
375
356
  }
376
357
 
377
358
  @ReactMethod
@@ -390,17 +371,49 @@ class RNIapModule(reactContext: ReactApplicationContext) :
390
371
  return
391
372
  }
392
373
  ensureConnection(
393
- promise,
394
- object : EnsureConnectionCallback {
395
- override fun run(billingClient: BillingClient) {
396
- DoobooUtils.instance.addPromiseForKey(
397
- PROMISE_BUY_ITEM, promise
374
+ promise
375
+ ) {
376
+ DoobooUtils.instance.addPromiseForKey(
377
+ PROMISE_BUY_ITEM, promise
378
+ )
379
+ val builder = BillingFlowParams.newBuilder()
380
+ val selectedSku: SkuDetails? = skus[sku]
381
+ if (selectedSku == null) {
382
+ val debugMessage =
383
+ "The sku was not found. Please fetch products first by calling getItems"
384
+ val error = Arguments.createMap()
385
+ error.putString("debugMessage", debugMessage)
386
+ error.putString("code", PROMISE_BUY_ITEM)
387
+ error.putString("message", debugMessage)
388
+ error.putString("productId", sku)
389
+ sendEvent(reactContext, "purchase-error", error)
390
+ promise.reject(PROMISE_BUY_ITEM, debugMessage)
391
+ return@ensureConnection
392
+ }
393
+ builder.setSkuDetails(selectedSku)
394
+ val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
395
+ if (purchaseToken != null) {
396
+ subscriptionUpdateParamsBuilder.setOldSkuPurchaseToken(purchaseToken)
397
+ }
398
+ if (obfuscatedAccountId != null) {
399
+ builder.setObfuscatedAccountId(obfuscatedAccountId)
400
+ }
401
+ if (obfuscatedProfileId != null) {
402
+ builder.setObfuscatedProfileId(obfuscatedProfileId)
403
+ }
404
+ if (prorationMode != null && prorationMode != -1) {
405
+ if (prorationMode
406
+ == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
407
+ ) {
408
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
409
+ BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
398
410
  )
399
- val builder = BillingFlowParams.newBuilder()
400
- var selectedSku: SkuDetails? = skus.get(sku)
401
- if (selectedSku == null) {
411
+ if (type != BillingClient.SkuType.SUBS) {
402
412
  val debugMessage =
403
- "The sku was not found. Please fetch products first by calling getItems"
413
+ (
414
+ "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
415
+ " subscription purchase."
416
+ )
404
417
  val error = Arguments.createMap()
405
418
  error.putString("debugMessage", debugMessage)
406
419
  error.putString("code", PROMISE_BUY_ITEM)
@@ -408,80 +421,45 @@ class RNIapModule(reactContext: ReactApplicationContext) :
408
421
  error.putString("productId", sku)
409
422
  sendEvent(reactContext, "purchase-error", error)
410
423
  promise.reject(PROMISE_BUY_ITEM, debugMessage)
411
- return
412
- }
413
- builder.setSkuDetails(selectedSku)
414
- val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
415
- if (purchaseToken != null) {
416
- subscriptionUpdateParamsBuilder.setOldSkuPurchaseToken(purchaseToken)
417
- }
418
- if (obfuscatedAccountId != null) {
419
- builder.setObfuscatedAccountId(obfuscatedAccountId)
420
- }
421
- if (obfuscatedProfileId != null) {
422
- builder.setObfuscatedProfileId(obfuscatedProfileId)
423
- }
424
- if (prorationMode != null && prorationMode != -1) {
425
- if (prorationMode
426
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
427
- ) {
428
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
429
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
430
- )
431
- if (type != BillingClient.SkuType.SUBS) {
432
- val debugMessage =
433
- (
434
- "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
435
- " subscription purchase."
436
- )
437
- val error = Arguments.createMap()
438
- error.putString("debugMessage", debugMessage)
439
- error.putString("code", PROMISE_BUY_ITEM)
440
- error.putString("message", debugMessage)
441
- error.putString("productId", sku)
442
- sendEvent(reactContext, "purchase-error", error)
443
- promise.reject(PROMISE_BUY_ITEM, debugMessage)
444
- return
445
- }
446
- } else if (prorationMode
447
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
448
- ) {
449
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
450
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
451
- )
452
- } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
453
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
454
- BillingFlowParams.ProrationMode.DEFERRED
455
- )
456
- } else if (prorationMode
457
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
458
- ) {
459
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
460
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
461
- )
462
- } else if (prorationMode
463
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
464
- ) {
465
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
466
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
467
- )
468
- } else {
469
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
470
- BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
471
- )
472
- }
473
- }
474
- if (purchaseToken != null) {
475
- val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build()
476
- builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
424
+ return@ensureConnection
477
425
  }
478
- val flowParams = builder.build()
479
- val billingResult = billingClient.launchBillingFlow(activity, flowParams)
480
- val errorData: Array<String?> =
481
- PlayUtils.instance.getBillingResponseData(billingResult.responseCode)
426
+ } else if (prorationMode
427
+ == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
428
+ ) {
429
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
430
+ BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
431
+ )
432
+ } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
433
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
434
+ BillingFlowParams.ProrationMode.DEFERRED
435
+ )
436
+ } else if (prorationMode
437
+ == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
438
+ ) {
439
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
440
+ BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
441
+ )
442
+ } else if (prorationMode
443
+ == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
444
+ ) {
445
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
446
+ BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
447
+ )
448
+ } else {
449
+ subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
450
+ BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
451
+ )
482
452
  }
483
453
  }
484
- )
454
+ if (purchaseToken != null) {
455
+ val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build()
456
+ builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
457
+ }
458
+ val flowParams = builder.build()
459
+ val billingResult = billingClient.launchBillingFlow(activity, flowParams)
460
+ val errorData: Array<String?> =
461
+ PlayUtils.instance.getBillingResponseData(billingResult.responseCode)
462
+ }
485
463
  }
486
464
 
487
465
  @ReactMethod
@@ -491,36 +469,33 @@ class RNIapModule(reactContext: ReactApplicationContext) :
491
469
  promise: Promise
492
470
  ) {
493
471
  ensureConnection(
494
- promise,
495
- object : EnsureConnectionCallback {
496
- override fun run(billingClient: BillingClient) {
497
- val acknowledgePurchaseParams =
498
- AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
499
- token!!
500
- ).build()
501
- billingClient.acknowledgePurchase(
502
- acknowledgePurchaseParams
503
- ) { billingResult: BillingResult ->
504
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
505
- PlayUtils.instance
506
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
507
- }
508
- try {
509
- val map = Arguments.createMap()
510
- map.putInt("responseCode", billingResult.responseCode)
511
- map.putString("debugMessage", billingResult.debugMessage)
512
- val errorData: Array<String?> = PlayUtils.instance
513
- .getBillingResponseData(billingResult.responseCode)
514
- map.putString("code", errorData[0])
515
- map.putString("message", errorData[1])
516
- promise.resolve(map)
517
- } catch (oce: ObjectAlreadyConsumedException) {
518
- Log.e(TAG, oce.message!!)
519
- }
520
- }
472
+ promise
473
+ ) {
474
+ val acknowledgePurchaseParams =
475
+ AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
476
+ token!!
477
+ ).build()
478
+ billingClient.acknowledgePurchase(
479
+ acknowledgePurchaseParams
480
+ ) { billingResult: BillingResult ->
481
+ if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
482
+ PlayUtils.instance
483
+ .rejectPromiseWithBillingError(promise, billingResult.responseCode)
484
+ }
485
+ try {
486
+ val map = Arguments.createMap()
487
+ map.putInt("responseCode", billingResult.responseCode)
488
+ map.putString("debugMessage", billingResult.debugMessage)
489
+ val errorData: Array<String?> = PlayUtils.instance
490
+ .getBillingResponseData(billingResult.responseCode)
491
+ map.putString("code", errorData[0])
492
+ map.putString("message", errorData[1])
493
+ promise.resolve(map)
494
+ } catch (oce: ObjectAlreadyConsumedException) {
495
+ Log.e(TAG, oce.message!!)
521
496
  }
522
497
  }
523
- )
498
+ }
524
499
  }
525
500
 
526
501
  @ReactMethod
@@ -531,32 +506,29 @@ class RNIapModule(reactContext: ReactApplicationContext) :
531
506
  ) {
532
507
  val params = ConsumeParams.newBuilder().setPurchaseToken(token!!).build()
533
508
  ensureConnection(
534
- promise,
535
- object : EnsureConnectionCallback {
536
- override fun run(billingClient: BillingClient) {
537
- billingClient.consumeAsync(
538
- params
539
- ) { billingResult: BillingResult, purchaseToken: String? ->
540
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
541
- PlayUtils.instance
542
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
543
- }
544
- try {
545
- val map = Arguments.createMap()
546
- map.putInt("responseCode", billingResult.responseCode)
547
- map.putString("debugMessage", billingResult.debugMessage)
548
- val errorData: Array<String?> = PlayUtils.instance
549
- .getBillingResponseData(billingResult.responseCode)
550
- map.putString("code", errorData[0])
551
- map.putString("message", errorData[1])
552
- promise.resolve(map)
553
- } catch (oce: ObjectAlreadyConsumedException) {
554
- promise.reject(oce.message)
555
- }
556
- }
509
+ promise
510
+ ) {
511
+ billingClient.consumeAsync(
512
+ params
513
+ ) { billingResult: BillingResult, purchaseToken: String? ->
514
+ if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
515
+ PlayUtils.instance
516
+ .rejectPromiseWithBillingError(promise, billingResult.responseCode)
517
+ }
518
+ try {
519
+ val map = Arguments.createMap()
520
+ map.putInt("responseCode", billingResult.responseCode)
521
+ map.putString("debugMessage", billingResult.debugMessage)
522
+ val errorData: Array<String?> = PlayUtils.instance
523
+ .getBillingResponseData(billingResult.responseCode)
524
+ map.putString("code", errorData[0])
525
+ map.putString("message", errorData[1])
526
+ promise.resolve(map)
527
+ } catch (oce: ObjectAlreadyConsumedException) {
528
+ promise.reject(oce.message)
557
529
  }
558
530
  }
559
- )
531
+ }
560
532
  }
561
533
 
562
534
  override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
@@ -624,30 +596,25 @@ class RNIapModule(reactContext: ReactApplicationContext) :
624
596
 
625
597
  private fun sendUnconsumedPurchases(promise: Promise) {
626
598
  ensureConnection(
627
- promise,
628
- object : EnsureConnectionCallback {
629
- override fun run(billingClient: BillingClient) {
630
- val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
631
- for (type in types) {
632
- billingClient.queryPurchasesAsync(
633
- type
634
- ) { billingResult: BillingResult, list: List<Purchase>? ->
635
- val unacknowledgedPurchases = ArrayList<Purchase>()
636
- if (list == null || list.size == 0) {
637
- // continue;
638
- }
639
- for (purchase in list!!) {
640
- if (!purchase.isAcknowledged) {
641
- unacknowledgedPurchases.add(purchase)
642
- }
643
- }
644
- onPurchasesUpdated(billingResult, unacknowledgedPurchases)
599
+ promise
600
+ ) {
601
+ val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
602
+ for (type in types) {
603
+ billingClient.queryPurchasesAsync(
604
+ type
605
+ ) { billingResult: BillingResult, list: List<Purchase>? ->
606
+ val unacknowledgedPurchases = ArrayList<Purchase>()
607
+
608
+ for (purchase in list!!) {
609
+ if (!purchase.isAcknowledged) {
610
+ unacknowledgedPurchases.add(purchase)
645
611
  }
646
612
  }
647
- promise.resolve(true)
613
+ onPurchasesUpdated(billingResult, unacknowledgedPurchases)
648
614
  }
649
615
  }
650
- )
616
+ promise.resolve(true)
617
+ }
651
618
  }
652
619
 
653
620
  @ReactMethod
@@ -681,19 +648,15 @@ class RNIapModule(reactContext: ReactApplicationContext) :
681
648
 
682
649
  companion object {
683
650
  private const val PROMISE_BUY_ITEM = "PROMISE_BUY_ITEM"
651
+ const val TAG = "RNIapModule"
684
652
  }
685
653
 
686
654
  init {
687
- this.reactContext = reactContext
688
- skus = mutableMapOf<String, SkuDetails>()
689
655
  val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener {
690
656
  override fun onHostResume() {}
691
657
  override fun onHostPause() {}
692
658
  override fun onHostDestroy() {
693
- if (billingClientCache != null) {
694
- billingClientCache!!.endConnection()
695
- billingClientCache = null
696
- }
659
+ billingClient.endConnection()
697
660
  }
698
661
  }
699
662
  reactContext.addLifecycleEventListener(lifecycleEventListener)
@@ -773,7 +773,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
773
773
  for discount in product.discounts {
774
774
  let formatter = NumberFormatter()
775
775
  formatter.numberStyle = .currency
776
- guard formatter.locale = discount.priceLocale else { continue }
776
+ formatter.locale = discount.priceLocale
777
777
  localizedPrice = formatter.string(from: discount.price)
778
778
  var numberOfPeriods: String?
779
779
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "8.1.0",
3
+ "version": "8.1.3",
4
4
  "packageManager": "yarn@3.2.0",
5
5
  "description": "React Native In App Purchase Module.",
6
6
  "main": "index.js",