expo-iap 1.0.3 → 2.0.0-rc.1

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.
Files changed (103) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.prettierrc.js +9 -0
  3. package/.swiftlint.yml +10 -0
  4. package/README.md +28 -21
  5. package/android/build.gradle +34 -72
  6. package/android/src/main/AndroidManifest.xml +1 -4
  7. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +535 -0
  8. package/android/src/main/java/expo/modules/iap/MissingCurrentActivityException.kt +6 -0
  9. package/android/src/main/java/expo/modules/iap/PlayUtils.kt +124 -0
  10. package/build/ExpoIap.types.d.ts +89 -0
  11. package/build/ExpoIap.types.d.ts.map +1 -0
  12. package/build/ExpoIap.types.js +59 -0
  13. package/build/ExpoIap.types.js.map +1 -0
  14. package/build/ExpoIapModule.d.ts +3 -0
  15. package/build/ExpoIapModule.d.ts.map +1 -0
  16. package/build/ExpoIapModule.js +5 -0
  17. package/build/ExpoIapModule.js.map +1 -0
  18. package/build/index.d.ts +38 -0
  19. package/build/index.d.ts.map +1 -0
  20. package/build/index.js +202 -0
  21. package/build/index.js.map +1 -0
  22. package/build/modules/android.d.ts +40 -0
  23. package/build/modules/android.d.ts.map +1 -0
  24. package/build/modules/android.js +54 -0
  25. package/build/modules/android.js.map +1 -0
  26. package/build/modules/ios.d.ts +41 -0
  27. package/build/modules/ios.d.ts.map +1 -0
  28. package/build/modules/ios.js +44 -0
  29. package/build/modules/ios.js.map +1 -0
  30. package/build/types/ExpoIapAndroid.types.d.ts +113 -0
  31. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -0
  32. package/build/types/ExpoIapAndroid.types.js +23 -0
  33. package/build/types/ExpoIapAndroid.types.js.map +1 -0
  34. package/build/types/ExpoIapIos.types.d.ts +122 -0
  35. package/build/types/ExpoIapIos.types.d.ts.map +1 -0
  36. package/build/types/ExpoIapIos.types.js +2 -0
  37. package/build/types/ExpoIapIos.types.js.map +1 -0
  38. package/bun.lockb +0 -0
  39. package/expo-module.config.json +4 -8
  40. package/ios/ExpoIap.podspec +27 -0
  41. package/ios/ExpoIapModule.swift +498 -0
  42. package/ios/ProductStore.swift +27 -0
  43. package/ios/Types.swift +54 -0
  44. package/package.json +33 -62
  45. package/src/ExpoIap.types.ts +125 -0
  46. package/src/ExpoIapModule.ts +5 -0
  47. package/src/index.ts +397 -0
  48. package/src/modules/android.ts +89 -0
  49. package/src/modules/ios.ts +81 -0
  50. package/src/types/ExpoIapAndroid.types.ts +123 -0
  51. package/src/types/ExpoIapIos.types.ts +141 -0
  52. package/tsconfig.json +9 -0
  53. package/.editorconfig +0 -10
  54. package/.flowconfig +0 -11
  55. package/.monolinterrc +0 -3
  56. package/.yarn/install-state.gz +0 -0
  57. package/.yarn/releases/yarn-3.1.1.cjs +0 -768
  58. package/.yarnrc.yml +0 -3
  59. package/LICENSE +0 -21
  60. package/RNIap.podspec +0 -18
  61. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  62. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  63. package/android/gradle.properties +0 -2
  64. package/android/gradlew +0 -160
  65. package/android/gradlew.bat +0 -90
  66. package/android/libs/in-app-purchasing-2.0.76.jar +0 -0
  67. package/android/src/amazon/AndroidManifest.xml +0 -12
  68. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonListener.kt +0 -356
  69. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +0 -128
  70. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
  71. package/android/src/main/java/com/dooboolab/RNIap/DoobooUtils.kt +0 -180
  72. package/android/src/play/java/com/dooboolab/RNIap/PlayUtils.kt +0 -77
  73. package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +0 -698
  74. package/android/src/play/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
  75. package/babel.config.js +0 -10
  76. package/index.d.ts +0 -3
  77. package/index.js +0 -3
  78. package/index.js.flow +0 -9
  79. package/ios/RNIap.xcodeproj/project.pbxproj +0 -370
  80. package/ios/RNIap.xcodeproj/xcshareddata/xcschemes/RNIap.xcscheme +0 -80
  81. package/ios/RNIapIos.m +0 -60
  82. package/ios/RNIapIos.swift +0 -932
  83. package/ios/RNIapQueue.swift +0 -35
  84. package/jest.config.js +0 -194
  85. package/src/__test__/iap.test.d.ts +0 -1
  86. package/src/__test__/iap.test.js +0 -59
  87. package/src/hooks/useIAP.d.ts +0 -21
  88. package/src/hooks/useIAP.js +0 -140
  89. package/src/hooks/withIAPContext.d.ts +0 -21
  90. package/src/hooks/withIAPContext.js +0 -142
  91. package/src/iap.d.ts +0 -197
  92. package/src/iap.js +0 -625
  93. package/src/index.d.ts +0 -4
  94. package/src/index.js +0 -4
  95. package/src/types/amazon.d.ts +0 -23
  96. package/src/types/amazon.js +0 -1
  97. package/src/types/android.d.ts +0 -47
  98. package/src/types/android.js +0 -22
  99. package/src/types/apple.d.ts +0 -424
  100. package/src/types/apple.js +0 -165
  101. package/src/types/index.d.ts +0 -117
  102. package/src/types/index.js +0 -40
  103. package/test/mocks/react-native-modules.js +0 -14
@@ -1,698 +0,0 @@
1
- package com.dooboolab.RNIap
2
-
3
- import android.util.Log
4
- import com.android.billingclient.api.AcknowledgePurchaseParams
5
- import com.android.billingclient.api.BillingClient
6
- import com.android.billingclient.api.BillingClientStateListener
7
- import com.android.billingclient.api.BillingFlowParams
8
- import com.android.billingclient.api.BillingFlowParams.SubscriptionUpdateParams
9
- import com.android.billingclient.api.BillingResult
10
- import com.android.billingclient.api.ConsumeParams
11
- import com.android.billingclient.api.ConsumeResponseListener
12
- import com.android.billingclient.api.Purchase
13
- import com.android.billingclient.api.PurchasesUpdatedListener
14
- import com.android.billingclient.api.SkuDetails
15
- import com.android.billingclient.api.SkuDetailsParams
16
- import com.facebook.react.bridge.Arguments
17
- import com.facebook.react.bridge.LifecycleEventListener
18
- import com.facebook.react.bridge.ObjectAlreadyConsumedException
19
- import com.facebook.react.bridge.Promise
20
- import com.facebook.react.bridge.ReactApplicationContext
21
- import com.facebook.react.bridge.ReactContext
22
- import com.facebook.react.bridge.ReactContextBaseJavaModule
23
- import com.facebook.react.bridge.ReactMethod
24
- import com.facebook.react.bridge.ReadableArray
25
- import com.facebook.react.bridge.WritableMap
26
- import com.facebook.react.bridge.WritableNativeArray
27
- import com.facebook.react.bridge.WritableNativeMap
28
- import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
29
- import com.google.android.gms.common.ConnectionResult
30
- import com.google.android.gms.common.GoogleApiAvailability
31
- import java.lang.Exception
32
- import java.math.BigDecimal
33
- import java.util.ArrayList
34
-
35
- class RNIapModule(reactContext: ReactApplicationContext) :
36
- ReactContextBaseJavaModule(reactContext),
37
- PurchasesUpdatedListener {
38
- val TAG = "RNIapModule"
39
- private val reactContext: ReactContext
40
- private var billingClientCache: BillingClient? = null
41
- private val skus: MutableMap<String, SkuDetails>
42
- override fun getName(): String {
43
- return "RNIapModule"
44
- }
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)
54
- return
55
- }
56
- promise.reject(DoobooUtils.E_NOT_PREPARED, "Not initialized, Please call initConnection()")
57
- }
58
-
59
- @ReactMethod
60
- fun initConnection(promise: Promise) {
61
- if (billingClientCache != null) {
62
- Log.i(
63
- TAG,
64
- "Already initialized, you should only call initConnection() once when your app starts"
65
- )
66
- promise.resolve(true)
67
- return
68
- }
69
- if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(reactContext)
70
- != ConnectionResult.SUCCESS
71
- ) {
72
- Log.i(TAG, "Google Play Services are not available on this device")
73
- promise.resolve(false)
74
- return
75
- }
76
- billingClientCache =
77
- BillingClient.newBuilder(reactContext).enablePendingPurchases().setListener(this)
78
- .build()
79
- billingClientCache!!.startConnection(
80
- object : BillingClientStateListener {
81
- override fun onBillingSetupFinished(billingResult: BillingResult) {
82
- val responseCode = billingResult.responseCode
83
- try {
84
- if (responseCode == BillingClient.BillingResponseCode.OK) {
85
- promise.resolve(true)
86
- } else {
87
- PlayUtils.instance
88
- .rejectPromiseWithBillingError(promise, responseCode)
89
- }
90
- } catch (oce: ObjectAlreadyConsumedException) {
91
- Log.e(TAG, oce.message!!)
92
- }
93
- }
94
-
95
- override fun onBillingServiceDisconnected() {
96
- try {
97
- promise.reject("initConnection", "Billing service disconnected")
98
- } catch (oce: ObjectAlreadyConsumedException) {
99
- Log.e(TAG, oce.message!!)
100
- }
101
- }
102
- })
103
- }
104
-
105
- @ReactMethod
106
- 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
- }
121
- }
122
-
123
- private fun consumeItems(
124
- purchases: List<Purchase>,
125
- promise: Promise,
126
- expectedResponseCode: Int = BillingClient.BillingResponseCode.OK
127
- ) {
128
- for (purchase in purchases) {
129
- 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)
153
- }
154
- }
155
- )
156
- }
157
- }
158
-
159
- @ReactMethod
160
- fun flushFailedPurchasesCachedAsPending(promise: Promise) {
161
- 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
- )
192
- }
193
- }
194
- }
195
- )
196
- }
197
-
198
- @ReactMethod
199
- fun getItemsByType(type: String?, skuArr: ReadableArray, promise: Promise) {
200
- 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
- skuList.add(skuArr.getString(i))
207
- }
208
- val params = SkuDetailsParams.newBuilder()
209
- params.setSkusList(skuList).setType(type!!)
210
- billingClient.querySkuDetailsAsync(
211
- params.build()
212
- ) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
213
- Log.d(TAG, "responseCode: " + billingResult.responseCode)
214
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
215
- PlayUtils.instance
216
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
217
- return@querySkuDetailsAsync
218
- }
219
- if (skuDetailsList != null) {
220
- for (sku in skuDetailsList) {
221
- skus.put(sku.getSku(), sku)
222
- }
223
- }
224
- val items = WritableNativeArray()
225
- for (skuDetails in skuDetailsList!!) {
226
- val item = Arguments.createMap()
227
- item.putString("productId", skuDetails.sku)
228
- val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
229
- val priceAmountMicros = skuDetails.priceAmountMicros
230
- // Use valueOf instead of constructors.
231
- // See:
232
- // https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
233
- val priceAmount = BigDecimal.valueOf(priceAmountMicros)
234
- val introductoryPriceAmount =
235
- BigDecimal.valueOf(introductoryPriceMicros)
236
- val microUnitsDivisor = BigDecimal.valueOf(1000000)
237
- val price = priceAmount.divide(microUnitsDivisor).toString()
238
- val introductoryPriceAsAmountAndroid =
239
- introductoryPriceAmount.divide(microUnitsDivisor).toString()
240
- item.putString("price", price)
241
- item.putString("currency", skuDetails.priceCurrencyCode)
242
- item.putString("type", skuDetails.type)
243
- item.putString("localizedPrice", skuDetails.price)
244
- item.putString("title", skuDetails.title)
245
- item.putString("description", skuDetails.description)
246
- item.putString("introductoryPrice", skuDetails.introductoryPrice)
247
- item.putString("typeAndroid", skuDetails.type)
248
- item.putString("packageNameAndroid", skuDetails.zzc())
249
- item.putString("originalPriceAndroid", skuDetails.originalPrice)
250
- item.putString(
251
- "subscriptionPeriodAndroid",
252
- skuDetails.subscriptionPeriod
253
- )
254
- item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
255
- item.putString(
256
- "introductoryPriceCyclesAndroid",
257
- skuDetails.introductoryPriceCycles.toString()
258
- )
259
- item.putString(
260
- "introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
261
- )
262
- item.putString(
263
- "introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
264
- )
265
- item.putString("iconUrl", skuDetails.iconUrl)
266
- item.putString("originalJson", skuDetails.originalJson)
267
- val originalPriceAmountMicros =
268
- BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
269
- val originalPrice =
270
- originalPriceAmountMicros.divide(microUnitsDivisor).toString()
271
- item.putString("originalPrice", originalPrice)
272
- items.pushMap(item)
273
- }
274
- try {
275
- promise.resolve(items)
276
- } catch (oce: ObjectAlreadyConsumedException) {
277
- Log.e(TAG, oce.message!!)
278
- }
279
- }
280
- }
281
- }
282
- )
283
- }
284
-
285
- @ReactMethod
286
- fun getAvailableItemsByType(type: String, promise: Promise) {
287
- ensureConnection(
288
- promise,
289
- object : EnsureConnectionCallback {
290
- override fun run(billingClient: BillingClient) {
291
- val items = WritableNativeArray()
292
- billingClient.queryPurchasesAsync(
293
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
294
- ) { billingResult: BillingResult?, purchases: List<Purchase>? ->
295
- if (purchases != null) {
296
- for (i in purchases.indices) {
297
- val purchase = purchases[i]
298
- val item = WritableNativeMap()
299
- item.putString("productId", purchase.skus[0])
300
- item.putString("transactionId", purchase.orderId)
301
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
302
- item.putString("transactionReceipt", purchase.originalJson)
303
- item.putString("orderId", purchase.orderId)
304
- item.putString("purchaseToken", purchase.purchaseToken)
305
- item.putString("developerPayloadAndroid", purchase.developerPayload)
306
- item.putString("signatureAndroid", purchase.signature)
307
- item.putInt("purchaseStateAndroid", purchase.purchaseState)
308
- item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
309
- item.putString("packageNameAndroid", purchase.packageName)
310
- item.putString(
311
- "obfuscatedAccountIdAndroid",
312
- purchase.accountIdentifiers!!.obfuscatedAccountId
313
- )
314
- item.putString(
315
- "obfuscatedProfileIdAndroid",
316
- purchase.accountIdentifiers!!.obfuscatedProfileId
317
- )
318
- if (type == BillingClient.SkuType.SUBS) {
319
- item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
320
- }
321
- items.pushMap(item)
322
- }
323
- }
324
- try {
325
- promise.resolve(items)
326
- } catch (oce: ObjectAlreadyConsumedException) {
327
- Log.e(TAG, oce.message!!)
328
- }
329
- }
330
- }
331
- }
332
- )
333
- }
334
-
335
- @ReactMethod
336
- fun getPurchaseHistoryByType(type: String, promise: Promise) {
337
- ensureConnection(
338
- promise,
339
- object : EnsureConnectionCallback {
340
- override fun run(billingClient: BillingClient) {
341
- billingClient.queryPurchaseHistoryAsync(
342
- if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
343
- ) { billingResult, purchaseHistoryRecordList ->
344
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
345
- PlayUtils.instance
346
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
347
- return@queryPurchaseHistoryAsync
348
- }
349
- Log.d(TAG, purchaseHistoryRecordList.toString())
350
- val items = Arguments.createArray()
351
- for (i in purchaseHistoryRecordList!!.indices) {
352
- val item = Arguments.createMap()
353
- val purchase = purchaseHistoryRecordList[i]
354
- item.putString("productId", purchase.skus[0])
355
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
356
- item.putString("transactionReceipt", purchase.originalJson)
357
- item.putString("purchaseToken", purchase.purchaseToken)
358
- item.putString("dataAndroid", purchase.originalJson)
359
- item.putString("signatureAndroid", purchase.signature)
360
- item.putString("developerPayload", purchase.developerPayload)
361
- items.pushMap(item)
362
- }
363
- try {
364
- promise.resolve(items)
365
- } catch (oce: ObjectAlreadyConsumedException) {
366
- Log.e(TAG, oce.message!!)
367
- }
368
- }
369
- }
370
- }
371
- )
372
- }
373
-
374
- @ReactMethod
375
- fun buyItemByType(
376
- type: String,
377
- sku: String,
378
- purchaseToken: String?,
379
- prorationMode: Int?,
380
- obfuscatedAccountId: String?,
381
- obfuscatedProfileId: String?,
382
- promise: Promise
383
- ) {
384
- val activity = currentActivity
385
- if (activity == null) {
386
- promise.reject(DoobooUtils.E_UNKNOWN, "getCurrentActivity returned null")
387
- return
388
- }
389
- ensureConnection(
390
- promise,
391
- object : EnsureConnectionCallback {
392
- override fun run(billingClient: BillingClient) {
393
- DoobooUtils.instance.addPromiseForKey(
394
- PROMISE_BUY_ITEM, promise
395
- )
396
- val builder = BillingFlowParams.newBuilder()
397
- var selectedSku: SkuDetails? = skus.get(sku)
398
- if (selectedSku == null) {
399
- val debugMessage =
400
- "The sku was not found. Please fetch products first by calling getItems"
401
- val error = Arguments.createMap()
402
- error.putString("debugMessage", debugMessage)
403
- error.putString("code", PROMISE_BUY_ITEM)
404
- error.putString("message", debugMessage)
405
- error.putString("productId", sku)
406
- sendEvent(reactContext, "purchase-error", error)
407
- promise.reject(PROMISE_BUY_ITEM, debugMessage)
408
- return
409
- }
410
- builder.setSkuDetails(selectedSku)
411
- val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
412
- if (purchaseToken != null) {
413
- subscriptionUpdateParamsBuilder.setOldSkuPurchaseToken(purchaseToken)
414
- }
415
- if (obfuscatedAccountId != null) {
416
- builder.setObfuscatedAccountId(obfuscatedAccountId)
417
- }
418
- if (obfuscatedProfileId != null) {
419
- builder.setObfuscatedProfileId(obfuscatedProfileId)
420
- }
421
- if (prorationMode != null && prorationMode != -1) {
422
- if (prorationMode
423
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
424
- ) {
425
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
426
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
427
- )
428
- if (type != BillingClient.SkuType.SUBS) {
429
- val debugMessage =
430
- (
431
- "IMMEDIATE_AND_CHARGE_PRORATED_PRICE for proration mode only works in" +
432
- " subscription purchase."
433
- )
434
- val error = Arguments.createMap()
435
- error.putString("debugMessage", debugMessage)
436
- error.putString("code", PROMISE_BUY_ITEM)
437
- error.putString("message", debugMessage)
438
- error.putString("productId", sku)
439
- sendEvent(reactContext, "purchase-error", error)
440
- promise.reject(PROMISE_BUY_ITEM, debugMessage)
441
- return
442
- }
443
- } else if (prorationMode
444
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
445
- ) {
446
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
447
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
448
- )
449
- } else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
450
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
451
- BillingFlowParams.ProrationMode.DEFERRED
452
- )
453
- } else if (prorationMode
454
- == BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
455
- ) {
456
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
457
- BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
458
- )
459
- } else if (prorationMode
460
- == BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
461
- ) {
462
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
463
- BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
464
- )
465
- } else {
466
- subscriptionUpdateParamsBuilder.setReplaceSkusProrationMode(
467
- BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
468
- )
469
- }
470
- }
471
- if (purchaseToken != null) {
472
- val subscriptionUpdateParams = subscriptionUpdateParamsBuilder.build()
473
- builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
474
- }
475
- val flowParams = builder.build()
476
- val billingResult = billingClient.launchBillingFlow(activity, flowParams)
477
- val errorData: Array<String?> =
478
- PlayUtils.instance.getBillingResponseData(billingResult.responseCode)
479
- }
480
- }
481
- )
482
- }
483
-
484
- @ReactMethod
485
- fun acknowledgePurchase(
486
- token: String?,
487
- developerPayLoad: String?,
488
- promise: Promise
489
- ) {
490
- ensureConnection(
491
- promise,
492
- object : EnsureConnectionCallback {
493
- override fun run(billingClient: BillingClient) {
494
- val acknowledgePurchaseParams =
495
- AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
496
- token!!
497
- ).build()
498
- billingClient.acknowledgePurchase(
499
- acknowledgePurchaseParams
500
- ) { billingResult: BillingResult ->
501
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
502
- PlayUtils.instance
503
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
504
- }
505
- try {
506
- val map = Arguments.createMap()
507
- map.putInt("responseCode", billingResult.responseCode)
508
- map.putString("debugMessage", billingResult.debugMessage)
509
- val errorData: Array<String?> = PlayUtils.instance
510
- .getBillingResponseData(billingResult.responseCode)
511
- map.putString("code", errorData[0])
512
- map.putString("message", errorData[1])
513
- promise.resolve(map)
514
- } catch (oce: ObjectAlreadyConsumedException) {
515
- Log.e(TAG, oce.message!!)
516
- }
517
- }
518
- }
519
- }
520
- )
521
- }
522
-
523
- @ReactMethod
524
- fun consumeProduct(
525
- token: String?,
526
- developerPayLoad: String?,
527
- promise: Promise
528
- ) {
529
- val params = ConsumeParams.newBuilder().setPurchaseToken(token!!).build()
530
- ensureConnection(
531
- promise,
532
- object : EnsureConnectionCallback {
533
- override fun run(billingClient: BillingClient) {
534
- billingClient.consumeAsync(
535
- params
536
- ) { billingResult: BillingResult, purchaseToken: String? ->
537
- if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
538
- PlayUtils.instance
539
- .rejectPromiseWithBillingError(promise, billingResult.responseCode)
540
- }
541
- try {
542
- val map = Arguments.createMap()
543
- map.putInt("responseCode", billingResult.responseCode)
544
- map.putString("debugMessage", billingResult.debugMessage)
545
- val errorData: Array<String?> = PlayUtils.instance
546
- .getBillingResponseData(billingResult.responseCode)
547
- map.putString("code", errorData[0])
548
- map.putString("message", errorData[1])
549
- promise.resolve(map)
550
- } catch (oce: ObjectAlreadyConsumedException) {
551
- promise.reject(oce.message)
552
- }
553
- }
554
- }
555
- }
556
- )
557
- }
558
-
559
- override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
560
- val responseCode = billingResult.responseCode
561
- if (responseCode != BillingClient.BillingResponseCode.OK) {
562
- val error = Arguments.createMap()
563
- error.putInt("responseCode", responseCode)
564
- error.putString("debugMessage", billingResult.debugMessage)
565
- val errorData: Array<String?> =
566
- PlayUtils.instance.getBillingResponseData(responseCode)
567
- error.putString("code", errorData[0])
568
- error.putString("message", errorData[1])
569
- sendEvent(reactContext, "purchase-error", error)
570
- PlayUtils.instance.rejectPromisesWithBillingError(PROMISE_BUY_ITEM, responseCode)
571
- return
572
- }
573
- if (purchases != null) {
574
- var promiseItem: WritableMap? = null
575
- for (i in purchases.indices) {
576
- val item = Arguments.createMap()
577
- val purchase = purchases[i]
578
- item.putString("productId", purchase.skus[0])
579
- item.putString("transactionId", purchase.orderId)
580
- item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
581
- item.putString("transactionReceipt", purchase.originalJson)
582
- item.putString("purchaseToken", purchase.purchaseToken)
583
- item.putString("dataAndroid", purchase.originalJson)
584
- item.putString("signatureAndroid", purchase.signature)
585
- item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
586
- item.putBoolean("isAcknowledgedAndroid", purchase.isAcknowledged)
587
- item.putInt("purchaseStateAndroid", purchase.purchaseState)
588
- item.putString("packageNameAndroid", purchase.packageName)
589
- item.putString("developerPayloadAndroid", purchase.developerPayload)
590
- val accountIdentifiers = purchase.accountIdentifiers
591
- if (accountIdentifiers != null) {
592
- item.putString(
593
- "obfuscatedAccountIdAndroid",
594
- accountIdentifiers.obfuscatedAccountId
595
- )
596
- item.putString(
597
- "obfuscatedProfileIdAndroid",
598
- accountIdentifiers.obfuscatedProfileId
599
- )
600
- }
601
- promiseItem = WritableNativeMap()
602
- promiseItem.merge(item)
603
- sendEvent(reactContext, "purchase-updated", item)
604
- }
605
- if (promiseItem != null) {
606
- DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItem)
607
- }
608
- } else {
609
- val result = Arguments.createMap()
610
- result.putInt("responseCode", billingResult.responseCode)
611
- result.putString("debugMessage", billingResult.debugMessage)
612
- result.putString(
613
- "extraMessage",
614
- "The purchases are null. This is a normal behavior if you have requested DEFERRED" +
615
- " proration. If not please report an issue."
616
- )
617
- sendEvent(reactContext, "purchase-updated", result)
618
- DoobooUtils.instance.resolvePromisesForKey(PROMISE_BUY_ITEM, null)
619
- }
620
- }
621
-
622
- private fun sendUnconsumedPurchases(promise: Promise) {
623
- ensureConnection(
624
- promise,
625
- object : EnsureConnectionCallback {
626
- override fun run(billingClient: BillingClient) {
627
- val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
628
- for (type in types) {
629
- billingClient.queryPurchasesAsync(
630
- type
631
- ) { billingResult: BillingResult, list: List<Purchase>? ->
632
- val unacknowledgedPurchases = ArrayList<Purchase>()
633
- if (list == null || list.size == 0) {
634
- // continue;
635
- }
636
- for (purchase in list!!) {
637
- if (!purchase.isAcknowledged) {
638
- unacknowledgedPurchases.add(purchase)
639
- }
640
- }
641
- onPurchasesUpdated(billingResult, unacknowledgedPurchases)
642
- }
643
- }
644
- promise.resolve(true)
645
- }
646
- }
647
- )
648
- }
649
-
650
- @ReactMethod
651
- fun startListening(promise: Promise) {
652
- sendUnconsumedPurchases(promise)
653
- }
654
-
655
- @ReactMethod
656
- fun addListener(eventName: String) {
657
- // Keep: Required for RN built-in Event Emitter Calls.
658
- }
659
-
660
- @ReactMethod
661
- fun removeListeners(count: Double) {
662
- // Keep: Required for RN built-in Event Emitter Calls.
663
- }
664
-
665
- @get:ReactMethod
666
- val packageName: String
667
- get() = reactApplicationContext.packageName
668
-
669
- private fun sendEvent(
670
- reactContext: ReactContext,
671
- eventName: String,
672
- params: WritableMap?
673
- ) {
674
- reactContext
675
- .getJSModule(RCTDeviceEventEmitter::class.java)
676
- .emit(eventName, params)
677
- }
678
-
679
- companion object {
680
- private const val PROMISE_BUY_ITEM = "PROMISE_BUY_ITEM"
681
- }
682
-
683
- init {
684
- this.reactContext = reactContext
685
- skus = mutableMapOf<String, SkuDetails>()
686
- val lifecycleEventListener: LifecycleEventListener = object : LifecycleEventListener {
687
- override fun onHostResume() {}
688
- override fun onHostPause() {}
689
- override fun onHostDestroy() {
690
- if (billingClientCache != null) {
691
- billingClientCache!!.endConnection()
692
- billingClientCache = null
693
- }
694
- }
695
- }
696
- reactContext.addLifecycleEventListener(lifecycleEventListener)
697
- }
698
- }