react-native-iap 8.2.2 → 8.3.0
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/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +112 -102
- package/android/src/testPlay/java/com/dooboolab/RNIap/RNIapModuleTest.kt +25 -5
- package/ios/RNIapQueue.swift +4 -3
- package/package.json +1 -1
- package/src/iap.js +13 -4
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/RNIap.podspec
CHANGED
|
@@ -11,6 +11,11 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.platforms = { :ios => "9.0", :tvos => "9.0" }
|
|
12
12
|
s.source = { :git => "https://github.com/dooboolab/react-native-iap.git", :tag => "#{s.version}" }
|
|
13
13
|
s.source_files = "ios/*.{h,m,swift}"
|
|
14
|
+
s.script_phase = {
|
|
15
|
+
:name => 'Copy Swift Header',
|
|
16
|
+
:script => 'ditto "${DERIVED_SOURCES_DIR}/${PRODUCT_MODULE_NAME}-Swift.h" "${PODS_ROOT}/Headers/Public/${PRODUCT_MODULE_NAME}/${PRODUCT_MODULE_NAME}-Swift.h"',
|
|
17
|
+
:execution_position => :after_compile
|
|
18
|
+
}
|
|
14
19
|
s.swift_version = "4.2"
|
|
15
20
|
s.requires_arc = true
|
|
16
21
|
|
|
@@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactContext
|
|
|
22
22
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
23
23
|
import com.facebook.react.bridge.ReactMethod
|
|
24
24
|
import com.facebook.react.bridge.ReadableArray
|
|
25
|
+
import com.facebook.react.bridge.ReadableType
|
|
25
26
|
import com.facebook.react.bridge.WritableMap
|
|
26
27
|
import com.facebook.react.bridge.WritableNativeArray
|
|
27
28
|
import com.facebook.react.bridge.WritableNativeMap
|
|
@@ -39,21 +40,30 @@ class RNIapModule(
|
|
|
39
40
|
ReactContextBaseJavaModule(reactContext),
|
|
40
41
|
PurchasesUpdatedListener {
|
|
41
42
|
|
|
42
|
-
private var
|
|
43
|
+
private var billingClientCache: BillingClient? = null
|
|
43
44
|
private val skus: MutableMap<String, SkuDetails> = mutableMapOf()
|
|
44
45
|
override fun getName(): String {
|
|
45
46
|
return TAG
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
internal fun ensureConnection(
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
internal fun ensureConnection(
|
|
50
|
+
promise: Promise,
|
|
51
|
+
callback: (billingClient: BillingClient) -> Unit
|
|
52
|
+
) {
|
|
53
|
+
val billingClient = billingClientCache
|
|
54
|
+
if (billingClient?.isReady == true) {
|
|
55
|
+
callback(billingClient)
|
|
51
56
|
return
|
|
52
57
|
} else {
|
|
53
58
|
val nested = PromiseImpl(
|
|
54
59
|
{
|
|
55
60
|
if (it.isNotEmpty() && it[0] is Boolean && it[0] as Boolean) {
|
|
56
|
-
|
|
61
|
+
val connectedBillingClient = billingClientCache
|
|
62
|
+
if (connectedBillingClient?.isReady == true) {
|
|
63
|
+
callback(connectedBillingClient)
|
|
64
|
+
} else {
|
|
65
|
+
promise.safeReject(DoobooUtils.E_NOT_PREPARED, "Unable to auto-initialize connection")
|
|
66
|
+
}
|
|
57
67
|
} else {
|
|
58
68
|
Log.i(TAG, "Incorrect parameter in resolve")
|
|
59
69
|
}
|
|
@@ -74,14 +84,6 @@ class RNIapModule(
|
|
|
74
84
|
|
|
75
85
|
@ReactMethod
|
|
76
86
|
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
|
-
}
|
|
85
87
|
if (googleApiAvailability.isGooglePlayServicesAvailable(reactContext)
|
|
86
88
|
!= ConnectionResult.SUCCESS
|
|
87
89
|
) {
|
|
@@ -90,25 +92,35 @@ class RNIapModule(
|
|
|
90
92
|
return
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
if (billingClientCache?.isReady == true) {
|
|
96
|
+
Log.i(
|
|
97
|
+
TAG,
|
|
98
|
+
"Already initialized, you should only call initConnection() once when your app starts"
|
|
99
|
+
)
|
|
100
|
+
promise.safeResolve(true)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
builder.setListener(this).build().also {
|
|
104
|
+
billingClientCache = it
|
|
105
|
+
it.startConnection(
|
|
106
|
+
object : BillingClientStateListener {
|
|
107
|
+
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
|
108
|
+
if (!isValidResult(billingResult, promise)) return
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
promise.safeResolve(true)
|
|
111
|
+
}
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
override fun onBillingServiceDisconnected() {
|
|
114
|
+
Log.i(TAG, "Billing service disconnected")
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
@ReactMethod
|
|
110
121
|
fun endConnection(promise: Promise) {
|
|
111
|
-
|
|
122
|
+
billingClientCache?.endConnection()
|
|
123
|
+
billingClientCache = null
|
|
112
124
|
promise.safeResolve(true)
|
|
113
125
|
}
|
|
114
126
|
|
|
@@ -120,7 +132,7 @@ class RNIapModule(
|
|
|
120
132
|
for (purchase in purchases) {
|
|
121
133
|
ensureConnection(
|
|
122
134
|
promise
|
|
123
|
-
) {
|
|
135
|
+
) { billingClient ->
|
|
124
136
|
val consumeParams =
|
|
125
137
|
ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
|
|
126
138
|
.build()
|
|
@@ -146,7 +158,7 @@ class RNIapModule(
|
|
|
146
158
|
fun flushFailedPurchasesCachedAsPending(promise: Promise) {
|
|
147
159
|
ensureConnection(
|
|
148
160
|
promise
|
|
149
|
-
) {
|
|
161
|
+
) { billingClient ->
|
|
150
162
|
billingClient.queryPurchasesAsync(
|
|
151
163
|
BillingClient.SkuType.INAPP
|
|
152
164
|
) { billingResult: BillingResult, list: List<Purchase>? ->
|
|
@@ -177,11 +189,11 @@ class RNIapModule(
|
|
|
177
189
|
fun getItemsByType(type: String, skuArr: ReadableArray, promise: Promise) {
|
|
178
190
|
ensureConnection(
|
|
179
191
|
promise
|
|
180
|
-
) {
|
|
192
|
+
) { billingClient ->
|
|
181
193
|
val skuList = ArrayList<String>()
|
|
182
194
|
for (i in 0 until skuArr.size()) {
|
|
183
|
-
|
|
184
|
-
|
|
195
|
+
if (skuArr.getType(i) == ReadableType.String) {
|
|
196
|
+
val sku = skuArr.getString(i)
|
|
185
197
|
skuList.add(sku)
|
|
186
198
|
}
|
|
187
199
|
}
|
|
@@ -192,61 +204,60 @@ class RNIapModule(
|
|
|
192
204
|
) { billingResult: BillingResult, skuDetailsList: List<SkuDetails>? ->
|
|
193
205
|
if (!isValidResult(billingResult, promise)) return@querySkuDetailsAsync
|
|
194
206
|
|
|
207
|
+
val items = Arguments.createArray()
|
|
195
208
|
if (skuDetailsList != null) {
|
|
196
|
-
for (
|
|
197
|
-
skus[
|
|
209
|
+
for (skuDetails in skuDetailsList) {
|
|
210
|
+
skus[skuDetails.sku] = skuDetails
|
|
211
|
+
|
|
212
|
+
val item = Arguments.createMap()
|
|
213
|
+
item.putString("productId", skuDetails.sku)
|
|
214
|
+
val introductoryPriceMicros = skuDetails.introductoryPriceAmountMicros
|
|
215
|
+
val priceAmountMicros = skuDetails.priceAmountMicros
|
|
216
|
+
// Use valueOf instead of constructors.
|
|
217
|
+
// See:
|
|
218
|
+
// https://www.javaworld.com/article/2073176/caution--double-to-bigdecimal-in-java.html
|
|
219
|
+
val priceAmount = BigDecimal.valueOf(priceAmountMicros)
|
|
220
|
+
val introductoryPriceAmount =
|
|
221
|
+
BigDecimal.valueOf(introductoryPriceMicros)
|
|
222
|
+
val microUnitsDivisor = BigDecimal.valueOf(1000000)
|
|
223
|
+
val price = priceAmount.divide(microUnitsDivisor).toString()
|
|
224
|
+
val introductoryPriceAsAmountAndroid =
|
|
225
|
+
introductoryPriceAmount.divide(microUnitsDivisor).toString()
|
|
226
|
+
item.putString("price", price)
|
|
227
|
+
item.putString("currency", skuDetails.priceCurrencyCode)
|
|
228
|
+
item.putString("type", skuDetails.type)
|
|
229
|
+
item.putString("localizedPrice", skuDetails.price)
|
|
230
|
+
item.putString("title", skuDetails.title)
|
|
231
|
+
item.putString("description", skuDetails.description)
|
|
232
|
+
item.putString("introductoryPrice", skuDetails.introductoryPrice)
|
|
233
|
+
item.putString("typeAndroid", skuDetails.type)
|
|
234
|
+
item.putString("packageNameAndroid", skuDetails.zzc())
|
|
235
|
+
item.putString("originalPriceAndroid", skuDetails.originalPrice)
|
|
236
|
+
item.putString(
|
|
237
|
+
"subscriptionPeriodAndroid",
|
|
238
|
+
skuDetails.subscriptionPeriod
|
|
239
|
+
)
|
|
240
|
+
item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
|
|
241
|
+
item.putString(
|
|
242
|
+
"introductoryPriceCyclesAndroid",
|
|
243
|
+
skuDetails.introductoryPriceCycles.toString()
|
|
244
|
+
)
|
|
245
|
+
item.putString(
|
|
246
|
+
"introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
|
|
247
|
+
)
|
|
248
|
+
item.putString(
|
|
249
|
+
"introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
|
|
250
|
+
)
|
|
251
|
+
item.putString("iconUrl", skuDetails.iconUrl)
|
|
252
|
+
item.putString("originalJson", skuDetails.originalJson)
|
|
253
|
+
val originalPriceAmountMicros =
|
|
254
|
+
BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
|
|
255
|
+
val originalPrice =
|
|
256
|
+
originalPriceAmountMicros.divide(microUnitsDivisor).toString()
|
|
257
|
+
item.putString("originalPrice", originalPrice)
|
|
258
|
+
items.pushMap(item)
|
|
198
259
|
}
|
|
199
260
|
}
|
|
200
|
-
val items = Arguments.createArray()
|
|
201
|
-
for (skuDetails in skuDetailsList!!) {
|
|
202
|
-
val item = Arguments.createMap()
|
|
203
|
-
item.putString("productId", skuDetails.sku)
|
|
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)
|
|
220
|
-
item.putString("title", skuDetails.title)
|
|
221
|
-
item.putString("description", skuDetails.description)
|
|
222
|
-
item.putString("introductoryPrice", skuDetails.introductoryPrice)
|
|
223
|
-
item.putString("typeAndroid", skuDetails.type)
|
|
224
|
-
item.putString("packageNameAndroid", skuDetails.zzc())
|
|
225
|
-
item.putString("originalPriceAndroid", skuDetails.originalPrice)
|
|
226
|
-
item.putString(
|
|
227
|
-
"subscriptionPeriodAndroid",
|
|
228
|
-
skuDetails.subscriptionPeriod
|
|
229
|
-
)
|
|
230
|
-
item.putString("freeTrialPeriodAndroid", skuDetails.freeTrialPeriod)
|
|
231
|
-
item.putString(
|
|
232
|
-
"introductoryPriceCyclesAndroid",
|
|
233
|
-
skuDetails.introductoryPriceCycles.toString()
|
|
234
|
-
)
|
|
235
|
-
item.putString(
|
|
236
|
-
"introductoryPricePeriodAndroid", skuDetails.introductoryPricePeriod
|
|
237
|
-
)
|
|
238
|
-
item.putString(
|
|
239
|
-
"introductoryPriceAsAmountAndroid", introductoryPriceAsAmountAndroid
|
|
240
|
-
)
|
|
241
|
-
item.putString("iconUrl", skuDetails.iconUrl)
|
|
242
|
-
item.putString("originalJson", skuDetails.originalJson)
|
|
243
|
-
val originalPriceAmountMicros =
|
|
244
|
-
BigDecimal.valueOf(skuDetails.originalPriceAmountMicros)
|
|
245
|
-
val originalPrice =
|
|
246
|
-
originalPriceAmountMicros.divide(microUnitsDivisor).toString()
|
|
247
|
-
item.putString("originalPrice", originalPrice)
|
|
248
|
-
items.pushMap(item)
|
|
249
|
-
}
|
|
250
261
|
promise.safeResolve(items)
|
|
251
262
|
}
|
|
252
263
|
}
|
|
@@ -272,7 +283,7 @@ class RNIapModule(
|
|
|
272
283
|
fun getAvailableItemsByType(type: String, promise: Promise) {
|
|
273
284
|
ensureConnection(
|
|
274
285
|
promise
|
|
275
|
-
) {
|
|
286
|
+
) { billingClient ->
|
|
276
287
|
val items = WritableNativeArray()
|
|
277
288
|
billingClient.queryPurchasesAsync(
|
|
278
289
|
if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
|
|
@@ -295,11 +306,11 @@ class RNIapModule(
|
|
|
295
306
|
item.putString("packageNameAndroid", purchase.packageName)
|
|
296
307
|
item.putString(
|
|
297
308
|
"obfuscatedAccountIdAndroid",
|
|
298
|
-
purchase.accountIdentifiers
|
|
309
|
+
purchase.accountIdentifiers?.obfuscatedAccountId
|
|
299
310
|
)
|
|
300
311
|
item.putString(
|
|
301
312
|
"obfuscatedProfileIdAndroid",
|
|
302
|
-
purchase.accountIdentifiers
|
|
313
|
+
purchase.accountIdentifiers?.obfuscatedProfileId
|
|
303
314
|
)
|
|
304
315
|
if (type == BillingClient.SkuType.SUBS) {
|
|
305
316
|
item.putBoolean("autoRenewingAndroid", purchase.isAutoRenewing)
|
|
@@ -316,7 +327,7 @@ class RNIapModule(
|
|
|
316
327
|
fun getPurchaseHistoryByType(type: String, promise: Promise) {
|
|
317
328
|
ensureConnection(
|
|
318
329
|
promise
|
|
319
|
-
) {
|
|
330
|
+
) { billingClient ->
|
|
320
331
|
billingClient.queryPurchaseHistoryAsync(
|
|
321
332
|
if (type == "subs") BillingClient.SkuType.SUBS else BillingClient.SkuType.INAPP
|
|
322
333
|
) { billingResult, purchaseHistoryRecordList ->
|
|
@@ -357,7 +368,7 @@ class RNIapModule(
|
|
|
357
368
|
}
|
|
358
369
|
ensureConnection(
|
|
359
370
|
promise
|
|
360
|
-
) {
|
|
371
|
+
) { billingClient ->
|
|
361
372
|
DoobooUtils.instance.addPromiseForKey(
|
|
362
373
|
PROMISE_BUY_ITEM, promise
|
|
363
374
|
)
|
|
@@ -441,13 +452,13 @@ class RNIapModule(
|
|
|
441
452
|
builder.setSubscriptionUpdateParams(subscriptionUpdateParams)
|
|
442
453
|
}
|
|
443
454
|
val flowParams = builder.build()
|
|
444
|
-
val
|
|
445
|
-
if (
|
|
455
|
+
val billingResultCode = billingClient.launchBillingFlow(activity, flowParams)?.responseCode ?: BillingClient.BillingResponseCode.ERROR
|
|
456
|
+
if (billingResultCode == BillingClient.BillingResponseCode.OK) {
|
|
446
457
|
promise.safeResolve(true)
|
|
447
458
|
return@ensureConnection
|
|
448
459
|
} else {
|
|
449
460
|
val errorData: Array<String?> =
|
|
450
|
-
PlayUtils.instance.getBillingResponseData(
|
|
461
|
+
PlayUtils.instance.getBillingResponseData(billingResultCode)
|
|
451
462
|
promise.safeReject(errorData[0], errorData[1])
|
|
452
463
|
}
|
|
453
464
|
}
|
|
@@ -455,16 +466,16 @@ class RNIapModule(
|
|
|
455
466
|
|
|
456
467
|
@ReactMethod
|
|
457
468
|
fun acknowledgePurchase(
|
|
458
|
-
token: String
|
|
469
|
+
token: String,
|
|
459
470
|
developerPayLoad: String?,
|
|
460
471
|
promise: Promise
|
|
461
472
|
) {
|
|
462
473
|
ensureConnection(
|
|
463
474
|
promise
|
|
464
|
-
) {
|
|
475
|
+
) { billingClient ->
|
|
465
476
|
val acknowledgePurchaseParams =
|
|
466
477
|
AcknowledgePurchaseParams.newBuilder().setPurchaseToken(
|
|
467
|
-
token
|
|
478
|
+
token
|
|
468
479
|
).build()
|
|
469
480
|
billingClient.acknowledgePurchase(
|
|
470
481
|
acknowledgePurchaseParams
|
|
@@ -485,14 +496,14 @@ class RNIapModule(
|
|
|
485
496
|
|
|
486
497
|
@ReactMethod
|
|
487
498
|
fun consumeProduct(
|
|
488
|
-
token: String
|
|
499
|
+
token: String,
|
|
489
500
|
developerPayLoad: String?,
|
|
490
501
|
promise: Promise
|
|
491
502
|
) {
|
|
492
|
-
val params = ConsumeParams.newBuilder().setPurchaseToken(token
|
|
503
|
+
val params = ConsumeParams.newBuilder().setPurchaseToken(token).build()
|
|
493
504
|
ensureConnection(
|
|
494
505
|
promise
|
|
495
|
-
) {
|
|
506
|
+
) { billingClient ->
|
|
496
507
|
billingClient.consumeAsync(
|
|
497
508
|
params
|
|
498
509
|
) { billingResult: BillingResult, purchaseToken: String? ->
|
|
@@ -576,7 +587,7 @@ class RNIapModule(
|
|
|
576
587
|
private fun sendUnconsumedPurchases(promise: Promise) {
|
|
577
588
|
ensureConnection(
|
|
578
589
|
promise
|
|
579
|
-
) {
|
|
590
|
+
) { billingClient ->
|
|
580
591
|
val types = arrayOf(BillingClient.SkuType.INAPP, BillingClient.SkuType.SUBS)
|
|
581
592
|
for (type in types) {
|
|
582
593
|
billingClient.queryPurchasesAsync(
|
|
@@ -607,9 +618,8 @@ class RNIapModule(
|
|
|
607
618
|
// Keep: Required for RN built-in Event Emitter Calls.
|
|
608
619
|
}
|
|
609
620
|
|
|
610
|
-
@
|
|
611
|
-
|
|
612
|
-
get() = reactApplicationContext.packageName
|
|
621
|
+
@ReactMethod
|
|
622
|
+
fun getPackageName(promise: Promise) = promise.resolve(reactApplicationContext.packageName)
|
|
613
623
|
|
|
614
624
|
private fun sendEvent(
|
|
615
625
|
reactContext: ReactContext,
|
|
@@ -631,7 +641,7 @@ class RNIapModule(
|
|
|
631
641
|
override fun onHostResume() {}
|
|
632
642
|
override fun onHostPause() {}
|
|
633
643
|
override fun onHostDestroy() {
|
|
634
|
-
|
|
644
|
+
billingClientCache?.endConnection()
|
|
635
645
|
}
|
|
636
646
|
}
|
|
637
647
|
reactContext.addLifecycleEventListener(lifecycleEventListener)
|
|
@@ -11,6 +11,7 @@ import com.facebook.react.bridge.Arguments
|
|
|
11
11
|
import com.facebook.react.bridge.Promise
|
|
12
12
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
13
|
import com.facebook.react.bridge.ReadableArray
|
|
14
|
+
import com.facebook.react.bridge.ReadableType
|
|
14
15
|
import com.facebook.react.bridge.WritableArray
|
|
15
16
|
import com.facebook.react.bridge.WritableMap
|
|
16
17
|
import com.google.android.gms.common.ConnectionResult
|
|
@@ -55,9 +56,13 @@ class RNIapModuleTest {
|
|
|
55
56
|
|
|
56
57
|
@Test
|
|
57
58
|
fun `initConnection Already connected should resolve to true`() {
|
|
59
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
60
|
+
module.initConnection(mockk())
|
|
61
|
+
|
|
58
62
|
every { billingClient.isReady } returns true
|
|
59
|
-
val promise = mockk<Promise>(relaxed = true)
|
|
60
63
|
|
|
64
|
+
val promise = mockk<Promise>(relaxed = true)
|
|
65
|
+
// Already connected
|
|
61
66
|
module.initConnection(promise)
|
|
62
67
|
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
63
68
|
verify { promise.resolve(true) }
|
|
@@ -112,8 +117,10 @@ class RNIapModuleTest {
|
|
|
112
117
|
|
|
113
118
|
@Test
|
|
114
119
|
fun `endConnection resolves`() {
|
|
120
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
115
121
|
val promise = mockk<Promise>(relaxed = true)
|
|
116
122
|
|
|
123
|
+
module.initConnection(mockk())
|
|
117
124
|
module.endConnection(promise)
|
|
118
125
|
|
|
119
126
|
verify { billingClient.endConnection() }
|
|
@@ -123,12 +130,14 @@ class RNIapModuleTest {
|
|
|
123
130
|
|
|
124
131
|
@Test
|
|
125
132
|
fun `flushFailedPurchasesCachedAsPending resolves to false if no pending purchases`() {
|
|
133
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
126
134
|
every { billingClient.isReady } returns true
|
|
127
135
|
val promise = mockk<Promise>(relaxed = true)
|
|
128
136
|
val listener = slot<PurchasesResponseListener>()
|
|
129
137
|
every { billingClient.queryPurchasesAsync(any(), capture(listener)) } answers {
|
|
130
138
|
listener.captured.onQueryPurchasesResponse(BillingResult.newBuilder().build(), listOf())
|
|
131
139
|
}
|
|
140
|
+
module.initConnection(mockk())
|
|
132
141
|
module.flushFailedPurchasesCachedAsPending(promise)
|
|
133
142
|
|
|
134
143
|
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
@@ -137,6 +146,7 @@ class RNIapModuleTest {
|
|
|
137
146
|
|
|
138
147
|
@Test
|
|
139
148
|
fun `flushFailedPurchasesCachedAsPending resolves to true if pending purchases`() {
|
|
149
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
140
150
|
every { billingClient.isReady } returns true
|
|
141
151
|
val promise = mockk<Promise>(relaxed = true)
|
|
142
152
|
val listener = slot<PurchasesResponseListener>()
|
|
@@ -161,7 +171,7 @@ class RNIapModuleTest {
|
|
|
161
171
|
""
|
|
162
172
|
)
|
|
163
173
|
}
|
|
164
|
-
|
|
174
|
+
module.initConnection(mockk())
|
|
165
175
|
module.flushFailedPurchasesCachedAsPending(promise)
|
|
166
176
|
|
|
167
177
|
verify(exactly = 0) { promise.reject(any(), any<String>()) }
|
|
@@ -173,12 +183,20 @@ class RNIapModuleTest {
|
|
|
173
183
|
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
174
184
|
val promise = mockk<Promise>(relaxed = true)
|
|
175
185
|
var isCallbackCalled = false
|
|
176
|
-
val callback = {
|
|
186
|
+
val callback = { _: BillingClient ->
|
|
177
187
|
isCallbackCalled = true
|
|
178
188
|
promise.resolve(true)
|
|
179
189
|
}
|
|
180
190
|
|
|
181
|
-
every { billingClient.isReady } returns
|
|
191
|
+
every { billingClient.isReady } returns true
|
|
192
|
+
val listener = slot<BillingClientStateListener>()
|
|
193
|
+
every { billingClient.startConnection(capture(listener)) } answers {
|
|
194
|
+
listener.captured.onBillingSetupFinished(
|
|
195
|
+
BillingResult.newBuilder().setResponseCode(BillingClient.BillingResponseCode.OK)
|
|
196
|
+
.build()
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
182
200
|
module.ensureConnection(promise, callback)
|
|
183
201
|
verify { promise.resolve(true) } // at least one pending transactions
|
|
184
202
|
assertTrue("Should call callback", isCallbackCalled)
|
|
@@ -186,6 +204,7 @@ class RNIapModuleTest {
|
|
|
186
204
|
|
|
187
205
|
@Test
|
|
188
206
|
fun getItemsByType() {
|
|
207
|
+
every { availability.isGooglePlayServicesAvailable(any()) } returns ConnectionResult.SUCCESS
|
|
189
208
|
every { billingClient.isReady } returns true
|
|
190
209
|
val promise = mockk<Promise>(relaxed = true)
|
|
191
210
|
val listener = slot<SkuDetailsResponseListener>()
|
|
@@ -219,6 +238,7 @@ class RNIapModuleTest {
|
|
|
219
238
|
val skus = mockk<ReadableArray>() {
|
|
220
239
|
every { size() } returns 1
|
|
221
240
|
every { getString(0) } returns "sku0"
|
|
241
|
+
every { getType(0) } returns ReadableType.String
|
|
222
242
|
}
|
|
223
243
|
mockkStatic(Arguments::class)
|
|
224
244
|
|
|
@@ -231,7 +251,7 @@ class RNIapModuleTest {
|
|
|
231
251
|
every { itemsArr.pushMap(any()) } answers {
|
|
232
252
|
itemsSize++
|
|
233
253
|
}
|
|
234
|
-
|
|
254
|
+
module.initConnection(mockk())
|
|
235
255
|
module.getItemsByType("subs", skus, promise)
|
|
236
256
|
verify { promise.resolve(any()) }
|
|
237
257
|
assertEquals(itemsSize, 1)
|
package/ios/RNIapQueue.swift
CHANGED
|
@@ -10,12 +10,13 @@ import StoreKit
|
|
|
10
10
|
|
|
11
11
|
// Temporarily stores payment information since it is sent by the OS before RN instantiates the RNModule
|
|
12
12
|
@objc(RNIapQueue)
|
|
13
|
-
class RNIapQueue: NSObject, SKPaymentTransactionObserver {
|
|
14
|
-
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
|
13
|
+
public class RNIapQueue: NSObject, SKPaymentTransactionObserver {
|
|
14
|
+
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
|
15
15
|
//No-op
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
@objc
|
|
19
|
+
public static let shared = RNIapQueue()
|
|
19
20
|
|
|
20
21
|
var queue: SKPaymentQueue? = nil;
|
|
21
22
|
var payment: SKPayment? = nil;
|
package/package.json
CHANGED
package/src/iap.js
CHANGED
|
@@ -400,10 +400,19 @@ export var acknowledgePurchaseAndroid = function (token, developerPayload) {
|
|
|
400
400
|
* @param {string} sku The product's SKU (on Android)
|
|
401
401
|
* @returns {Promise<void>}
|
|
402
402
|
*/
|
|
403
|
-
export var deepLinkToSubscriptionsAndroid = function (sku) {
|
|
404
|
-
|
|
405
|
-
return
|
|
406
|
-
|
|
403
|
+
export var deepLinkToSubscriptionsAndroid = function (sku) { return __awaiter(void 0, void 0, void 0, function () {
|
|
404
|
+
var _a, _b, _c;
|
|
405
|
+
return __generator(this, function (_d) {
|
|
406
|
+
switch (_d.label) {
|
|
407
|
+
case 0:
|
|
408
|
+
checkNativeAndroidAvailable();
|
|
409
|
+
_b = (_a = Linking).openURL;
|
|
410
|
+
_c = "https://play.google.com/store/account/subscriptions?package=".concat;
|
|
411
|
+
return [4 /*yield*/, RNIapModule.getPackageName()];
|
|
412
|
+
case 1: return [2 /*return*/, _b.apply(_a, [_c.apply("https://play.google.com/store/account/subscriptions?package=", [_d.sent(), "&sku="]).concat(sku)])];
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}); };
|
|
407
416
|
/**
|
|
408
417
|
* Should Add Store Payment (iOS only)
|
|
409
418
|
* Indicates the the App Store purchase should continue from the app instead of the App Store.
|