react-native-iap 8.2.2 → 9.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/RNIap.podspec +5 -0
- package/android/build.gradle +3 -3
- package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonListener.kt +16 -4
- package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +199 -144
- package/android/src/play/java/com/dooboolab/RNIap/RNIapModuleInterface.kt +44 -0
- package/android/src/play/java/com/dooboolab/RNIap/RNIapModuleV4.kt +656 -0
- package/android/src/play/java/com/dooboolab/RNIap/RNIapPackage.kt +1 -0
- package/android/src/testPlay/java/com/dooboolab/RNIap/{RNIapModuleTest.kt → RNIapModuleTestV4.kt} +30 -10
- package/ios/RNIapIos.m +1 -0
- package/ios/RNIapIos.swift +3 -1
- package/ios/RNIapQueue.swift +4 -3
- package/package.json +1 -1
- package/src/hooks/useIAP.d.ts +1 -0
- package/src/hooks/useIAP.js +2 -1
- package/src/hooks/withIAPContext.d.ts +1 -0
- package/src/hooks/withIAPContext.js +9 -1
- package/src/iap.d.ts +2 -1
- package/src/iap.js +49 -27
- package/src/types/index.d.ts +24 -2
|
@@ -9,10 +9,13 @@ import com.android.billingclient.api.BillingFlowParams.SubscriptionUpdateParams
|
|
|
9
9
|
import com.android.billingclient.api.BillingResult
|
|
10
10
|
import com.android.billingclient.api.ConsumeParams
|
|
11
11
|
import com.android.billingclient.api.ConsumeResponseListener
|
|
12
|
+
import com.android.billingclient.api.ProductDetails
|
|
12
13
|
import com.android.billingclient.api.Purchase
|
|
14
|
+
import com.android.billingclient.api.PurchaseHistoryRecord
|
|
13
15
|
import com.android.billingclient.api.PurchasesUpdatedListener
|
|
14
|
-
import com.android.billingclient.api.
|
|
15
|
-
import com.android.billingclient.api.
|
|
16
|
+
import com.android.billingclient.api.QueryProductDetailsParams
|
|
17
|
+
import com.android.billingclient.api.QueryPurchaseHistoryParams
|
|
18
|
+
import com.android.billingclient.api.QueryPurchasesParams
|
|
16
19
|
import com.facebook.react.bridge.Arguments
|
|
17
20
|
import com.facebook.react.bridge.LifecycleEventListener
|
|
18
21
|
import com.facebook.react.bridge.Promise
|
|
@@ -22,13 +25,13 @@ import com.facebook.react.bridge.ReactContext
|
|
|
22
25
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
23
26
|
import com.facebook.react.bridge.ReactMethod
|
|
24
27
|
import com.facebook.react.bridge.ReadableArray
|
|
28
|
+
import com.facebook.react.bridge.ReadableType
|
|
25
29
|
import com.facebook.react.bridge.WritableMap
|
|
26
30
|
import com.facebook.react.bridge.WritableNativeArray
|
|
27
31
|
import com.facebook.react.bridge.WritableNativeMap
|
|
28
32
|
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
|
29
33
|
import com.google.android.gms.common.ConnectionResult
|
|
30
34
|
import com.google.android.gms.common.GoogleApiAvailability
|
|
31
|
-
import java.math.BigDecimal
|
|
32
35
|
import java.util.ArrayList
|
|
33
36
|
|
|
34
37
|
class RNIapModule(
|
|
@@ -37,23 +40,33 @@ class RNIapModule(
|
|
|
37
40
|
private val googleApiAvailability: GoogleApiAvailability = GoogleApiAvailability.getInstance()
|
|
38
41
|
) :
|
|
39
42
|
ReactContextBaseJavaModule(reactContext),
|
|
40
|
-
PurchasesUpdatedListener
|
|
43
|
+
PurchasesUpdatedListener,
|
|
44
|
+
RNIapModuleInterface {
|
|
41
45
|
|
|
42
|
-
private var
|
|
43
|
-
private val skus: MutableMap<String,
|
|
46
|
+
private var billingClientCache: BillingClient? = null
|
|
47
|
+
private val skus: MutableMap<String, ProductDetails> = mutableMapOf()
|
|
44
48
|
override fun getName(): String {
|
|
45
49
|
return TAG
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
internal fun ensureConnection(
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
internal fun ensureConnection(
|
|
53
|
+
promise: Promise,
|
|
54
|
+
callback: (billingClient: BillingClient) -> Unit
|
|
55
|
+
) {
|
|
56
|
+
val billingClient = billingClientCache
|
|
57
|
+
if (billingClient?.isReady == true) {
|
|
58
|
+
callback(billingClient)
|
|
51
59
|
return
|
|
52
60
|
} else {
|
|
53
61
|
val nested = PromiseImpl(
|
|
54
62
|
{
|
|
55
63
|
if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
|
|
56
|
-
|
|
64
|
+
val connectedBillingClient = billingClientCache
|
|
65
|
+
if (connectedBillingClient?.isReady == true) {
|
|
66
|
+
callback(connectedBillingClient)
|
|
67
|
+
} else {
|
|
68
|
+
promise.safeReject(DoobooUtils.E_NOT_PREPARED, "Unable to auto-initialize connection")
|
|
69
|
+
}
|
|
57
70
|
} else {
|
|
58
71
|
Log.i(TAG, "Incorrect parameter in resolve")
|
|
59
72
|
}
|
|
@@ -61,7 +74,8 @@ class RNIapModule(
|
|
|
61
74
|
{
|
|
62
75
|
if (it.size > 1 && it[0] is String && it[1] is String) {
|
|
63
76
|
promise.safeReject(
|
|
64
|
-
it[0] as String,
|
|
77
|
+
it[0] as String,
|
|
78
|
+
it[1] as String
|
|
65
79
|
)
|
|
66
80
|
} else {
|
|
67
81
|
Log.i(TAG, "Incorrect parameters in reject")
|
|
@@ -73,15 +87,7 @@ class RNIapModule(
|
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
@ReactMethod
|
|
76
|
-
fun initConnection(promise: Promise) {
|
|
77
|
-
if (billingClient.isReady) {
|
|
78
|
-
Log.i(
|
|
79
|
-
TAG,
|
|
80
|
-
"Already initialized, you should only call initConnection() once when your app starts"
|
|
81
|
-
)
|
|
82
|
-
promise.safeResolve(true)
|
|
83
|
-
return
|
|
84
|
-
}
|
|
90
|
+
override fun initConnection(promise: Promise) {
|
|
85
91
|
if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
|
|
86
92
|
!= ConnectionResult.SUCCESS
|
|
87
93
|
) {
|
|
@@ -90,25 +96,36 @@ class RNIapModule(
|
|
|
90
96
|
return
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
if (billingClientCache?.isReady == true) {
|
|
100
|
+
Log.i(
|
|
101
|
+
TAG,
|
|
102
|
+
"Already initialized, you should only call initConnection() once when your app starts"
|
|
103
|
+
)
|
|
104
|
+
promise.safeResolve(true)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
builder.setListener(this).build().also {
|
|
108
|
+
billingClientCache = it
|
|
109
|
+
it.startConnection(
|
|
110
|
+
object : BillingClientStateListener {
|
|
111
|
+
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
|
112
|
+
if (!isValidResult(billingResult, promise)) return
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
promise.safeResolve(true)
|
|
115
|
+
}
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
|
|
117
|
+
override fun onBillingServiceDisconnected() {
|
|
118
|
+
Log.i(TAG, "Billing service disconnected")
|
|
119
|
+
}
|
|
105
120
|
}
|
|
106
|
-
|
|
121
|
+
)
|
|
122
|
+
}
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
@ReactMethod
|
|
110
|
-
fun endConnection(promise: Promise) {
|
|
111
|
-
|
|
126
|
+
override fun endConnection(promise: Promise) {
|
|
127
|
+
billingClientCache?.endConnection()
|
|
128
|
+
billingClientCache = null
|
|
112
129
|
promise.safeResolve(true)
|
|
113
130
|
}
|
|
114
131
|
|
|
@@ -120,7 +137,7 @@ class RNIapModule(
|
|
|
120
137
|
for (purchase in purchases) {
|
|
121
138
|
ensureConnection(
|
|
122
139
|
promise
|
|
123
|
-
) {
|
|
140
|
+
) { billingClient ->
|
|
124
141
|
val consumeParams =
|
|
125
142
|
ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
|
|
126
143
|
.build()
|
|
@@ -143,12 +160,14 @@ class RNIapModule(
|
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
@ReactMethod
|
|
146
|
-
fun flushFailedPurchasesCachedAsPending(promise: Promise) {
|
|
163
|
+
override fun flushFailedPurchasesCachedAsPending(promise: Promise) {
|
|
147
164
|
ensureConnection(
|
|
148
165
|
promise
|
|
149
|
-
) {
|
|
166
|
+
) { billingClient ->
|
|
150
167
|
billingClient.queryPurchasesAsync(
|
|
151
|
-
|
|
168
|
+
QueryPurchasesParams.newBuilder().setProductType(
|
|
169
|
+
BillingClient.ProductType.INAPP
|
|
170
|
+
).build()
|
|
152
171
|
) { billingResult: BillingResult, list: List<Purchase>? ->
|
|
153
172
|
if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
|
|
154
173
|
if (list == null) {
|
|
@@ -174,77 +193,93 @@ class RNIapModule(
|
|
|
174
193
|
}
|
|
175
194
|
|
|
176
195
|
@ReactMethod
|
|
177
|
-
fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
|
|
196
|
+
override fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
|
|
178
197
|
ensureConnection(
|
|
179
198
|
promise
|
|
180
|
-
) {
|
|
181
|
-
val skuList = ArrayList<
|
|
199
|
+
) { billingClient ->
|
|
200
|
+
val skuList = ArrayList<QueryProductDetailsParams.Product>()
|
|
182
201
|
for (i in 0 until skuArr.size()) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
skuList.add(
|
|
202
|
+
if (skuArr.getType(i) == ReadableType.String) {
|
|
203
|
+
val sku = skuArr.getString(i)
|
|
204
|
+
skuList.add(
|
|
205
|
+
QueryProductDetailsParams.Product.newBuilder().setProductId(sku)
|
|
206
|
+
.setProductType(type).build()
|
|
207
|
+
)
|
|
186
208
|
}
|
|
187
209
|
}
|
|
188
|
-
val params =
|
|
189
|
-
|
|
190
|
-
billingClient.querySkuDetailsAsync(
|
|
210
|
+
val params = QueryProductDetailsParams.newBuilder().setProductList(skuList)
|
|
211
|
+
billingClient.queryProductDetailsAsync(
|
|
191
212
|
params.build()
|
|
192
|
-
) { billingResult: BillingResult, skuDetailsList: List<
|
|
193
|
-
if (!isValidResult(billingResult, promise)) return@
|
|
213
|
+
) { billingResult: BillingResult, skuDetailsList: List<ProductDetails> ->
|
|
214
|
+
if (!isValidResult(billingResult, promise)) return@queryProductDetailsAsync
|
|
194
215
|
|
|
195
|
-
if (skuDetailsList != null) {
|
|
196
|
-
for (sku in skuDetailsList) {
|
|
197
|
-
skus[sku.sku] = sku
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
216
|
val items = Arguments.createArray()
|
|
201
|
-
for (skuDetails in skuDetailsList
|
|
217
|
+
for (skuDetails in skuDetailsList) {
|
|
218
|
+
skus[skuDetails.productId] = skuDetails
|
|
219
|
+
|
|
202
220
|
val item = Arguments.createMap()
|
|
203
|
-
item.putString("productId", skuDetails.
|
|
204
|
-
val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
|
|
205
|
-
val priceAmountMicros = skuDetails.priceAmountMicros
|
|
206
|
-
// Use valueOf instead of constructors.
|
|
207
|
-
// See:
|
|
208
|
-
// https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
|
|
209
|
-
val priceAmount = BigDecimal.valueOf(priceAmountMicros)
|
|
210
|
-
val introductoryPriceAmount =
|
|
211
|
-
BigDecimal.valueOf(introductoryPriceMicros)
|
|
212
|
-
val microUnitsDivisor = BigDecimal.valueOf(1000000)
|
|
213
|
-
val price = priceAmount.divide(microUnitsDivisor).toString()
|
|
214
|
-
val introductoryPriceAsAmountAndroid =
|
|
215
|
-
introductoryPriceAmount.divide(microUnitsDivisor).toString()
|
|
216
|
-
item.putString("price", price)
|
|
217
|
-
item.putString("currency", skuDetails.priceCurrencyCode)
|
|
218
|
-
item.putString("type", skuDetails.type)
|
|
219
|
-
item.putString("localizedPrice", skuDetails.price)
|
|
221
|
+
item.putString("productId", skuDetails.productId)
|
|
220
222
|
item.putString("title", skuDetails.title)
|
|
221
223
|
item.putString("description", skuDetails.description)
|
|
222
|
-
item.putString("
|
|
223
|
-
item.putString("
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
224
|
+
item.putString("productType", skuDetails.productType)
|
|
225
|
+
item.putString("name", skuDetails.name)
|
|
226
|
+
val oneTimePurchaseOfferDetails = Arguments.createMap()
|
|
227
|
+
skuDetails.oneTimePurchaseOfferDetails?.let {
|
|
228
|
+
oneTimePurchaseOfferDetails.putString(
|
|
229
|
+
"priceCurrencyCode",
|
|
230
|
+
it.priceCurrencyCode
|
|
231
|
+
)
|
|
232
|
+
oneTimePurchaseOfferDetails.putString("formattedPrice", it.formattedPrice)
|
|
233
|
+
oneTimePurchaseOfferDetails.putString(
|
|
234
|
+
"priceAmountMicros",
|
|
235
|
+
it.priceAmountMicros.toString()
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
item.putMap("oneTimePurchaseOfferDetails", oneTimePurchaseOfferDetails)
|
|
239
|
+
|
|
240
|
+
val subscriptionOfferDetails = Arguments.createArray()
|
|
241
|
+
skuDetails.subscriptionOfferDetails?.forEach { subscriptionOfferDetailsItem ->
|
|
242
|
+
val offerDetails = Arguments.createMap()
|
|
243
|
+
offerDetails.putString(
|
|
244
|
+
"offerToken",
|
|
245
|
+
subscriptionOfferDetailsItem.offerToken
|
|
246
|
+
)
|
|
247
|
+
val offerTags = Arguments.createArray()
|
|
248
|
+
subscriptionOfferDetailsItem.offerTags.forEach { offerTag ->
|
|
249
|
+
offerTags.pushString(offerTag)
|
|
250
|
+
}
|
|
251
|
+
offerDetails.putArray("offerTags", offerTags)
|
|
252
|
+
|
|
253
|
+
val pricingPhasesList = Arguments.createArray()
|
|
254
|
+
subscriptionOfferDetailsItem.pricingPhases.pricingPhaseList.forEach { pricingPhaseItem ->
|
|
255
|
+
val pricingPhase = Arguments.createMap()
|
|
256
|
+
pricingPhase.putString(
|
|
257
|
+
"formattedPrice",
|
|
258
|
+
pricingPhaseItem.formattedPrice
|
|
259
|
+
)
|
|
260
|
+
pricingPhase.putString(
|
|
261
|
+
"priceCurrencyCode",
|
|
262
|
+
pricingPhaseItem.priceCurrencyCode
|
|
263
|
+
)
|
|
264
|
+
pricingPhase.putString("billingPeriod", pricingPhaseItem.billingPeriod)
|
|
265
|
+
pricingPhase.putInt(
|
|
266
|
+
"billingCycleCount",
|
|
267
|
+
pricingPhaseItem.billingCycleCount
|
|
268
|
+
)
|
|
269
|
+
pricingPhase.putString(
|
|
270
|
+
"priceAmountMicros",
|
|
271
|
+
pricingPhaseItem.priceAmountMicros.toString()
|
|
272
|
+
)
|
|
273
|
+
pricingPhase.putInt("recurrenceMode", pricingPhaseItem.recurrenceMode)
|
|
274
|
+
|
|
275
|
+
pricingPhasesList.pushMap(pricingPhase)
|
|
276
|
+
}
|
|
277
|
+
val pricingPhases = Arguments.createMap()
|
|
278
|
+
pricingPhases.putArray("pricingPhaseList", pricingPhasesList)
|
|
279
|
+
offerDetails.putMap("pricingPhases", pricingPhases)
|
|
280
|
+
subscriptionOfferDetails.pushMap(offerDetails)
|
|
281
|
+
}
|
|
282
|
+
item.putArray("subscriptionOfferDetails", subscriptionOfferDetails)
|
|
248
283
|
items.pushMap(item)
|
|
249
284
|
}
|
|
250
285
|
promise.safeResolve(items)
|
|
@@ -269,20 +304,22 @@ class RNIapModule(
|
|
|
269
304
|
}
|
|
270
305
|
|
|
271
306
|
@ReactMethod
|
|
272
|
-
fun getAvailableItemsByType(type: String, promise: Promise) {
|
|
307
|
+
override fun getAvailableItemsByType(type: String, promise: Promise) {
|
|
273
308
|
ensureConnection(
|
|
274
309
|
promise
|
|
275
|
-
) {
|
|
310
|
+
) { billingClient ->
|
|
276
311
|
val items = WritableNativeArray()
|
|
277
312
|
billingClient.queryPurchasesAsync(
|
|
278
|
-
|
|
313
|
+
QueryPurchasesParams.newBuilder().setProductType(
|
|
314
|
+
if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
|
|
315
|
+
).build()
|
|
279
316
|
) { billingResult: BillingResult, purchases: List<Purchase>? ->
|
|
280
317
|
if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
|
|
281
318
|
if (purchases != null) {
|
|
282
319
|
for (i in purchases.indices) {
|
|
283
320
|
val purchase = purchases[i]
|
|
284
321
|
val item = WritableNativeMap()
|
|
285
|
-
item.putString("productId", purchase.
|
|
322
|
+
item.putString("productId", purchase.products[0]) // TODO: should be a list
|
|
286
323
|
item.putString("transactionId", purchase.orderId)
|
|
287
324
|
item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
|
|
288
325
|
item.putString("transactionReceipt", purchase.originalJson)
|
|
@@ -295,13 +332,13 @@ class RNIapModule(
|
|
|
295
332
|
item.putString("packageNameAndroid", purchase.packageName)
|
|
296
333
|
item.putString(
|
|
297
334
|
"obfuscatedAccountIdAndroid",
|
|
298
|
-
purchase.accountIdentifiers
|
|
335
|
+
purchase.accountIdentifiers?.obfuscatedAccountId
|
|
299
336
|
)
|
|
300
337
|
item.putString(
|
|
301
338
|
"obfuscatedProfileIdAndroid",
|
|
302
|
-
purchase.accountIdentifiers
|
|
339
|
+
purchase.accountIdentifiers?.obfuscatedProfileId
|
|
303
340
|
)
|
|
304
|
-
if (type == BillingClient.
|
|
341
|
+
if (type == BillingClient.ProductType.SUBS) {
|
|
305
342
|
item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
|
|
306
343
|
}
|
|
307
344
|
items.pushMap(item)
|
|
@@ -313,20 +350,24 @@ class RNIapModule(
|
|
|
313
350
|
}
|
|
314
351
|
|
|
315
352
|
@ReactMethod
|
|
316
|
-
fun getPurchaseHistoryByType(type: String, promise: Promise) {
|
|
353
|
+
override fun getPurchaseHistoryByType(type: String, promise: Promise) {
|
|
317
354
|
ensureConnection(
|
|
318
355
|
promise
|
|
319
|
-
) {
|
|
356
|
+
) { billingClient ->
|
|
320
357
|
billingClient.queryPurchaseHistoryAsync(
|
|
321
|
-
|
|
322
|
-
|
|
358
|
+
QueryPurchaseHistoryParams.newBuilder().setProductType(
|
|
359
|
+
if (type == "subs") BillingClient.ProductType.SUBS else BillingClient.ProductType.INAPP
|
|
360
|
+
).build()
|
|
361
|
+
) {
|
|
362
|
+
billingResult: BillingResult, purchaseHistoryRecordList: MutableList<PurchaseHistoryRecord>? ->
|
|
363
|
+
|
|
323
364
|
if (!isValidResult(billingResult, promise)) return@queryPurchaseHistoryAsync
|
|
324
365
|
|
|
325
366
|
Log.d(TAG, purchaseHistoryRecordList.toString())
|
|
326
367
|
val items = Arguments.createArray()
|
|
327
368
|
purchaseHistoryRecordList?.forEach { purchase ->
|
|
328
369
|
val item = Arguments.createMap()
|
|
329
|
-
item.putString("productId", purchase.
|
|
370
|
+
item.putString("productId", purchase.products[0])
|
|
330
371
|
item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
|
|
331
372
|
item.putString("transactionReceipt", purchase.originalJson)
|
|
332
373
|
item.putString("purchaseToken", purchase.purchaseToken)
|
|
@@ -341,13 +382,14 @@ class RNIapModule(
|
|
|
341
382
|
}
|
|
342
383
|
|
|
343
384
|
@ReactMethod
|
|
344
|
-
fun buyItemByType(
|
|
385
|
+
override fun buyItemByType(
|
|
345
386
|
type: String,
|
|
346
|
-
sku: String,
|
|
387
|
+
sku: String, // TODO: should this now be an array?
|
|
347
388
|
purchaseToken: String?,
|
|
348
389
|
prorationMode: Int?,
|
|
349
390
|
obfuscatedAccountId: String?,
|
|
350
391
|
obfuscatedProfileId: String?,
|
|
392
|
+
selectedOfferIndex: Int?, // New optional parameter in V5, TODO: should it be an array?
|
|
351
393
|
promise: Promise
|
|
352
394
|
) {
|
|
353
395
|
val activity = currentActivity
|
|
@@ -357,12 +399,13 @@ class RNIapModule(
|
|
|
357
399
|
}
|
|
358
400
|
ensureConnection(
|
|
359
401
|
promise
|
|
360
|
-
) {
|
|
402
|
+
) { billingClient ->
|
|
361
403
|
DoobooUtils.instance.addPromiseForKey(
|
|
362
|
-
PROMISE_BUY_ITEM,
|
|
404
|
+
PROMISE_BUY_ITEM,
|
|
405
|
+
promise
|
|
363
406
|
)
|
|
364
407
|
val builder = BillingFlowParams.newBuilder()
|
|
365
|
-
val selectedSku:
|
|
408
|
+
val selectedSku: ProductDetails? = skus[sku]
|
|
366
409
|
if (selectedSku == null) {
|
|
367
410
|
val debugMessage =
|
|
368
411
|
"The sku was not found. Please fetch products first by calling getItems"
|
|
@@ -375,10 +418,21 @@ class RNIapModule(
|
|
|
375
418
|
promise.safeReject(PROMISE_BUY_ITEM, debugMessage)
|
|
376
419
|
return@ensureConnection
|
|
377
420
|
}
|
|
378
|
-
|
|
421
|
+
var productParams = BillingFlowParams.ProductDetailsParams.newBuilder().setProductDetails(selectedSku)
|
|
422
|
+
if (selectedOfferIndex != null && (
|
|
423
|
+
selectedSku.subscriptionOfferDetails?.size
|
|
424
|
+
?: 0
|
|
425
|
+
) > selectedOfferIndex
|
|
426
|
+
) {
|
|
427
|
+
val offerToken =
|
|
428
|
+
selectedSku.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
|
|
429
|
+
offerToken?.let { productParams = productParams.setOfferToken(offerToken) }
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
builder.setProductDetailsParamsList(listOf(productParams.build()))
|
|
379
433
|
val subscriptionUpdateParamsBuilder = SubscriptionUpdateParams.newBuilder()
|
|
380
434
|
if (purchaseToken != null) {
|
|
381
|
-
subscriptionUpdateParamsBuilder.
|
|
435
|
+
subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken)
|
|
382
436
|
}
|
|
383
437
|
if (obfuscatedAccountId != null) {
|
|
384
438
|
builder.setObfuscatedAccountId(obfuscatedAccountId)
|
|
@@ -390,7 +444,7 @@ class RNIapModule(
|
|
|
390
444
|
if (prorationMode
|
|
391
445
|
== BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
|
|
392
446
|
) {
|
|
393
|
-
subscriptionUpdateParamsBuilder.
|
|
447
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
394
448
|
BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE
|
|
395
449
|
)
|
|
396
450
|
if (type != BillingClient.SkuType.SUBS) {
|
|
@@ -411,27 +465,27 @@ class RNIapModule(
|
|
|
411
465
|
} else if (prorationMode
|
|
412
466
|
== BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
|
|
413
467
|
) {
|
|
414
|
-
subscriptionUpdateParamsBuilder.
|
|
468
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
415
469
|
BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
|
|
416
470
|
)
|
|
417
471
|
} else if (prorationMode == BillingFlowParams.ProrationMode.DEFERRED) {
|
|
418
|
-
subscriptionUpdateParamsBuilder.
|
|
472
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
419
473
|
BillingFlowParams.ProrationMode.DEFERRED
|
|
420
474
|
)
|
|
421
475
|
} else if (prorationMode
|
|
422
476
|
== BillingFlowParams.ProrationMode.IMMEDIATE_WITH_TIME_PRORATION
|
|
423
477
|
) {
|
|
424
|
-
subscriptionUpdateParamsBuilder.
|
|
478
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
425
479
|
BillingFlowParams.ProrationMode.IMMEDIATE_WITHOUT_PRORATION
|
|
426
480
|
)
|
|
427
481
|
} else if (prorationMode
|
|
428
482
|
== BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
|
|
429
483
|
) {
|
|
430
|
-
subscriptionUpdateParamsBuilder.
|
|
484
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
431
485
|
BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE
|
|
432
486
|
)
|
|
433
487
|
} else {
|
|
434
|
-
subscriptionUpdateParamsBuilder.
|
|
488
|
+
subscriptionUpdateParamsBuilder.setReplaceProrationMode(
|
|
435
489
|
BillingFlowParams.ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY
|
|
436
490
|
)
|
|
437
491
|
}
|
|
@@ -441,30 +495,30 @@ class RNIapModule(
|
|
|
441
495
|
builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
|
|
442
496
|
}
|
|
443
497
|
val flowParams = builder.build()
|
|
444
|
-
val
|
|
445
|
-
if (
|
|
498
|
+
val billingResultCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
|
|
499
|
+
if (billingResultCode == BillingClient.BillingResponseCode.OK) {
|
|
446
500
|
promise.safeResolve(true)
|
|
447
501
|
return@ensureConnection
|
|
448
502
|
} else {
|
|
449
503
|
val errorData: Array<String?> =
|
|
450
|
-
PlayUtils.instance.getBillingResponseData(
|
|
504
|
+
PlayUtils.instance.getBillingResponseData(billingResultCode)
|
|
451
505
|
promise.safeReject(errorData[0], errorData[1])
|
|
452
506
|
}
|
|
453
507
|
}
|
|
454
508
|
}
|
|
455
509
|
|
|
456
510
|
@ReactMethod
|
|
457
|
-
fun acknowledgePurchase(
|
|
458
|
-
token: String
|
|
511
|
+
override fun acknowledgePurchase(
|
|
512
|
+
token: String,
|
|
459
513
|
developerPayLoad: String?,
|
|
460
514
|
promise: Promise
|
|
461
515
|
) {
|
|
462
516
|
ensureConnection(
|
|
463
517
|
promise
|
|
464
|
-
) {
|
|
518
|
+
) { billingClient ->
|
|
465
519
|
val acknowledgePurchaseParams =
|
|
466
520
|
AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
|
|
467
|
-
token
|
|
521
|
+
token
|
|
468
522
|
).build()
|
|
469
523
|
billingClient.acknowledgePurchase(
|
|
470
524
|
acknowledgePurchaseParams
|
|
@@ -484,15 +538,15 @@ class RNIapModule(
|
|
|
484
538
|
}
|
|
485
539
|
|
|
486
540
|
@ReactMethod
|
|
487
|
-
fun consumeProduct(
|
|
488
|
-
token: String
|
|
541
|
+
override fun consumeProduct(
|
|
542
|
+
token: String,
|
|
489
543
|
developerPayLoad: String?,
|
|
490
544
|
promise: Promise
|
|
491
545
|
) {
|
|
492
|
-
val params = ConsumeParams.newBuilder().setPurchaseToken(token
|
|
546
|
+
val params = ConsumeParams.newBuilder().setPurchaseToken(token).build()
|
|
493
547
|
ensureConnection(
|
|
494
548
|
promise
|
|
495
|
-
) {
|
|
549
|
+
) { billingClient ->
|
|
496
550
|
billingClient.consumeAsync(
|
|
497
551
|
params
|
|
498
552
|
) { billingResult: BillingResult, purchaseToken: String? ->
|
|
@@ -529,7 +583,7 @@ class RNIapModule(
|
|
|
529
583
|
for (i in purchases.indices) {
|
|
530
584
|
val item = Arguments.createMap()
|
|
531
585
|
val purchase = purchases[i]
|
|
532
|
-
item.putString("productId", purchase.
|
|
586
|
+
item.putString("productId", purchase.products[0])
|
|
533
587
|
item.putString("transactionId", purchase.orderId)
|
|
534
588
|
item.putDouble("transactionDate", purchase.purchaseTime.toDouble())
|
|
535
589
|
item.putString("transactionReceipt", purchase.originalJson)
|
|
@@ -576,11 +630,13 @@ class RNIapModule(
|
|
|
576
630
|
private fun sendUnconsumedPurchases(promise: Promise) {
|
|
577
631
|
ensureConnection(
|
|
578
632
|
promise
|
|
579
|
-
) {
|
|
580
|
-
val types = arrayOf(BillingClient.
|
|
633
|
+
) { billingClient ->
|
|
634
|
+
val types = arrayOf(BillingClient.ProductType.INAPP, BillingClient.ProductType.SUBS)
|
|
581
635
|
for (type in types) {
|
|
582
636
|
billingClient.queryPurchasesAsync(
|
|
583
|
-
|
|
637
|
+
QueryPurchasesParams.newBuilder().setProductType(
|
|
638
|
+
type
|
|
639
|
+
).build()
|
|
584
640
|
) { billingResult: BillingResult, list: List<Purchase> ->
|
|
585
641
|
if (!isValidResult(billingResult, promise)) return@queryPurchasesAsync
|
|
586
642
|
|
|
@@ -593,23 +649,22 @@ class RNIapModule(
|
|
|
593
649
|
}
|
|
594
650
|
|
|
595
651
|
@ReactMethod
|
|
596
|
-
fun startListening(promise: Promise) {
|
|
652
|
+
override fun startListening(promise: Promise) {
|
|
597
653
|
sendUnconsumedPurchases(promise)
|
|
598
654
|
}
|
|
599
655
|
|
|
600
656
|
@ReactMethod
|
|
601
|
-
fun addListener(eventName: String) {
|
|
657
|
+
override fun addListener(eventName: String) {
|
|
602
658
|
// Keep: Required for RN built-in Event Emitter Calls.
|
|
603
659
|
}
|
|
604
660
|
|
|
605
661
|
@ReactMethod
|
|
606
|
-
fun removeListeners(count: Double) {
|
|
662
|
+
override fun removeListeners(count: Double) {
|
|
607
663
|
// Keep: Required for RN built-in Event Emitter Calls.
|
|
608
664
|
}
|
|
609
665
|
|
|
610
|
-
@
|
|
611
|
-
|
|
612
|
-
get() = reactApplicationContext.packageName
|
|
666
|
+
@ReactMethod
|
|
667
|
+
override fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
|
|
613
668
|
|
|
614
669
|
private fun sendEvent(
|
|
615
670
|
reactContext: ReactContext,
|
|
@@ -631,7 +686,7 @@ class RNIapModule(
|
|
|
631
686
|
override fun onHostResume() {}
|
|
632
687
|
override fun onHostPause() {}
|
|
633
688
|
override fun onHostDestroy() {
|
|
634
|
-
|
|
689
|
+
billingClientCache?.endConnection()
|
|
635
690
|
}
|
|
636
691
|
}
|
|
637
692
|
reactContext.addLifecycleEventListener(lifecycleEventListener)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package com.dooboolab.RNIap
|
|
2
|
+
|
|
3
|
+
import com.android.billingclient.api.BillingResult
|
|
4
|
+
import com.android.billingclient.api.Purchase
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
import com.facebook.react.bridge.ReadableArray
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Common interface for consistency
|
|
10
|
+
*/
|
|
11
|
+
interface RNIapModuleInterface {
|
|
12
|
+
fun getName(): String
|
|
13
|
+
fun initConnection(promise: Promise)
|
|
14
|
+
fun endConnection(promise: Promise)
|
|
15
|
+
fun flushFailedPurchasesCachedAsPending(promise: Promise)
|
|
16
|
+
fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise)
|
|
17
|
+
fun getAvailableItemsByType(type: String, promise: Promise)
|
|
18
|
+
fun getPurchaseHistoryByType(type: String, promise: Promise)
|
|
19
|
+
fun buyItemByType(
|
|
20
|
+
type: String,
|
|
21
|
+
sku: String,
|
|
22
|
+
purchaseToken: String?,
|
|
23
|
+
prorationMode: Int?,
|
|
24
|
+
obfuscatedAccountId: String?,
|
|
25
|
+
obfuscatedProfileId: String?,
|
|
26
|
+
selectedOfferIndex: Int?, // New optional parameter in V5 (added to maintain interface consistency)
|
|
27
|
+
promise: Promise
|
|
28
|
+
)
|
|
29
|
+
fun acknowledgePurchase(
|
|
30
|
+
token: String,
|
|
31
|
+
developerPayLoad: String?,
|
|
32
|
+
promise: Promise
|
|
33
|
+
)
|
|
34
|
+
fun consumeProduct(
|
|
35
|
+
token: String,
|
|
36
|
+
developerPayLoad: String?,
|
|
37
|
+
promise: Promise
|
|
38
|
+
)
|
|
39
|
+
fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?)
|
|
40
|
+
fun startListening(promise: Promise)
|
|
41
|
+
fun addListener(eventName: String)
|
|
42
|
+
fun removeListeners(count: Double)
|
|
43
|
+
fun getPackageName(promise: Promise)
|
|
44
|
+
}
|