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