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.
package/.yarn/install-state.gz
CHANGED
|
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
|
-
|
|
39
|
-
private val
|
|
40
|
-
|
|
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
|
|
47
|
-
|
|
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 (
|
|
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.
|
|
87
|
+
promise.reject(DoobooUtils.E_NOT_PREPARED, "Google Play Services are not available on this device")
|
|
74
88
|
return
|
|
75
89
|
}
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
400
|
-
var selectedSku: SkuDetails? = skus.get(sku)
|
|
401
|
-
if (selectedSku == null) {
|
|
411
|
+
if (type != BillingClient.SkuType.SUBS) {
|
|
402
412
|
val debugMessage =
|
|
403
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
-
|
|
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
|
-
|
|
694
|
-
billingClientCache!!.endConnection()
|
|
695
|
-
billingClientCache = null
|
|
696
|
-
}
|
|
659
|
+
billingClient.endConnection()
|
|
697
660
|
}
|
|
698
661
|
}
|
|
699
662
|
reactContext.addLifecycleEventListener(lifecycleEventListener)
|
package/ios/RNIapIos.swift
CHANGED
|
@@ -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
|
-
|
|
776
|
+
formatter.locale = discount.priceLocale
|
|
777
777
|
localizedPrice = formatter.string(from: discount.price)
|
|
778
778
|
var numberOfPeriods: String?
|
|
779
779
|
|