react-native-iap 15.1.0 → 15.2.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.
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +171 -114
- package/android/src/main/java/com/margelo/nitro/iap/ProductQueryHelpers.kt +42 -0
- package/android/src/test/java/com/margelo/nitro/iap/ProductQueryHelpersTest.kt +140 -0
- package/ios/HybridRnIap.swift +72 -1
- package/lib/module/hooks/useIAP.js +11 -1
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/hooks/useWebhookEvents.js +113 -0
- package/lib/module/hooks/useWebhookEvents.js.map +1 -0
- package/lib/module/index.js +405 -131
- package/lib/module/index.js.map +1 -1
- package/lib/module/kit-api.js +161 -0
- package/lib/module/kit-api.js.map +1 -0
- package/lib/module/types.js +16 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/errorMapping.js +6 -0
- package/lib/module/utils/errorMapping.js.map +1 -1
- package/lib/module/webhook-client.js +164 -0
- package/lib/module/webhook-client.js.map +1 -0
- package/lib/typescript/plugin/src/withIAP.d.ts +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +172 -2
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useWebhookEvents.d.ts +55 -0
- package/lib/typescript/src/hooks/useWebhookEvents.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +283 -129
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/kit-api.d.ts +54 -0
- package/lib/typescript/src/kit-api.d.ts.map +1 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +24 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +320 -75
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts +3 -0
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/utils/errorMapping.d.ts +6 -0
- package/lib/typescript/src/utils/errorMapping.d.ts.map +1 -1
- package/lib/typescript/src/webhook-client.d.ts +82 -0
- package/lib/typescript/src/webhook-client.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroIap+autolinking.cmake +3 -0
- package/nitrogen/generated/android/c++/JAdvancedCommerceInfoIOS.hpp +118 -0
- package/nitrogen/generated/android/c++/JAdvancedCommerceItemDetailsIOS.hpp +62 -0
- package/nitrogen/generated/android/c++/JAdvancedCommerceItemIOS.hpp +78 -0
- package/nitrogen/generated/android/c++/JAdvancedCommerceRefundIOS.hpp +62 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +52 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +3 -0
- package/nitrogen/generated/android/c++/JPurchase.hpp +11 -0
- package/nitrogen/generated/android/c++/JPurchaseIOS.hpp +16 -1
- package/nitrogen/generated/android/c++/JRequestPurchaseResult.hpp +11 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AdvancedCommerceInfoIOS.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AdvancedCommerceInfoIOS.hpp +84 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AdvancedCommerceItemDetailsIOS.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_AdvancedCommerceItemDetailsIOS.hpp +74 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Array_AdvancedCommerceRefundIOS_.cpp +35 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Array_AdvancedCommerceRefundIOS_.hpp +84 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/AdvancedCommerceInfoIOS.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/AdvancedCommerceItemDetailsIOS.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/AdvancedCommerceItemIOS.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/AdvancedCommerceRefundIOS.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/PurchaseIOS.kt +5 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_AdvancedCommerceInfoIOS.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_AdvancedCommerceItemDetailsIOS.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_Array_AdvancedCommerceRefundIOS_.kt +53 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +166 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +12 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +32 -0
- package/nitrogen/generated/ios/swift/AdvancedCommerceInfoIOS.swift +294 -0
- package/nitrogen/generated/ios/swift/AdvancedCommerceItemDetailsIOS.swift +61 -0
- package/nitrogen/generated/ios/swift/AdvancedCommerceItemIOS.swift +141 -0
- package/nitrogen/generated/ios/swift/AdvancedCommerceRefundIOS.swift +61 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +3 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +57 -0
- package/nitrogen/generated/ios/swift/PurchaseIOS.swift +39 -2
- package/nitrogen/generated/ios/swift/Variant_NullType_AdvancedCommerceInfoIOS.swift +18 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_AdvancedCommerceItemDetailsIOS.swift +18 -0
- package/nitrogen/generated/ios/swift/Variant_NullType__AdvancedCommerceRefundIOS_.swift +18 -0
- package/nitrogen/generated/shared/c++/AdvancedCommerceInfoIOS.hpp +117 -0
- package/nitrogen/generated/shared/c++/AdvancedCommerceItemDetailsIOS.hpp +86 -0
- package/nitrogen/generated/shared/c++/AdvancedCommerceItemIOS.hpp +99 -0
- package/nitrogen/generated/shared/c++/AdvancedCommerceRefundIOS.hpp +86 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +3 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +3 -0
- package/nitrogen/generated/shared/c++/PurchaseIOS.hpp +9 -2
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/plugin/build/withIAP.d.ts +1 -1
- package/plugin/src/withIAP.ts +1 -1
- package/src/hooks/useIAP.ts +185 -2
- package/src/hooks/useWebhookEvents.ts +180 -0
- package/src/index.ts +440 -130
- package/src/kit-api.ts +225 -0
- package/src/specs/RnIap.nitro.ts +31 -0
- package/src/types.ts +330 -75
- package/src/utils/error.ts +3 -0
- package/src/utils/errorMapping.ts +12 -0
- package/src/webhook-client.ts +312 -0
|
@@ -10,7 +10,7 @@ import dev.hyo.openiap.FetchProductsResult
|
|
|
10
10
|
import dev.hyo.openiap.FetchProductsResultAll
|
|
11
11
|
import dev.hyo.openiap.FetchProductsResultProducts
|
|
12
12
|
import dev.hyo.openiap.FetchProductsResultSubscriptions
|
|
13
|
-
import dev.hyo.openiap.OpenIapError
|
|
13
|
+
import dev.hyo.openiap.OpenIapError
|
|
14
14
|
import dev.hyo.openiap.OpenIapModule
|
|
15
15
|
import dev.hyo.openiap.ProductAndroid
|
|
16
16
|
import dev.hyo.openiap.ProductQueryType
|
|
@@ -84,6 +84,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
84
84
|
private val promotedProductListenersIOS = mutableListOf<(NitroProduct) -> Unit>()
|
|
85
85
|
private val userChoiceBillingListenersAndroid = mutableListOf<(UserChoiceBillingDetails) -> Unit>()
|
|
86
86
|
private val developerProvidedBillingListenersAndroid = mutableListOf<(DeveloperProvidedBillingDetailsAndroid) -> Unit>()
|
|
87
|
+
private val subscriptionBillingIssueListeners = mutableListOf<(NitroPurchase) -> Unit>()
|
|
87
88
|
private var listenersAttached = false
|
|
88
89
|
private var isInitialized = false
|
|
89
90
|
private var initDeferred: CompletableDeferred<Boolean>? = null
|
|
@@ -133,7 +134,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
133
134
|
} catch (err: CancellationException) {
|
|
134
135
|
throw err
|
|
135
136
|
} catch (err: Throwable) {
|
|
136
|
-
val error =
|
|
137
|
+
val error = OpenIapError.InitConnection
|
|
137
138
|
val errorMessage = err.message ?: err.javaClass.name
|
|
138
139
|
RnIapLog.failure("initConnection.setActivity", err)
|
|
139
140
|
throw OpenIapException(
|
|
@@ -172,22 +173,14 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
172
173
|
}.onFailure { RnIapLog.failure("purchaseUpdatedListener", it) }
|
|
173
174
|
})
|
|
174
175
|
openIap.addPurchaseErrorListener(OpenIapPurchaseErrorListener { e ->
|
|
175
|
-
val code =
|
|
176
|
-
val message = e.message ?:
|
|
176
|
+
val code = OpenIapError.toCode(e)
|
|
177
|
+
val message = e.message ?: OpenIapError.defaultMessage(code)
|
|
177
178
|
runCatching {
|
|
178
179
|
RnIapLog.result(
|
|
179
180
|
"purchaseErrorListener",
|
|
180
181
|
mapOf("code" to code, "message" to message)
|
|
181
182
|
)
|
|
182
|
-
sendPurchaseError(
|
|
183
|
-
NitroPurchaseResult(
|
|
184
|
-
responseCode = -1.0,
|
|
185
|
-
debugMessage = null,
|
|
186
|
-
code = code,
|
|
187
|
-
message = message,
|
|
188
|
-
purchaseToken = null
|
|
189
|
-
)
|
|
190
|
-
)
|
|
183
|
+
sendPurchaseError(toErrorResult(e))
|
|
191
184
|
}.onFailure { RnIapLog.failure("purchaseErrorListener", it) }
|
|
192
185
|
})
|
|
193
186
|
openIap.addUserChoiceBillingListener(OpenIapUserChoiceBillingListener { details ->
|
|
@@ -222,7 +215,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
222
215
|
throw err
|
|
223
216
|
} catch (err: Throwable) {
|
|
224
217
|
listenersAttached = false
|
|
225
|
-
val error =
|
|
218
|
+
val error = OpenIapError.InitConnection
|
|
226
219
|
val errorMessage = err.message ?: err.javaClass.name
|
|
227
220
|
RnIapLog.failure("initConnection.listeners", err)
|
|
228
221
|
val wrapped = OpenIapException(
|
|
@@ -266,7 +259,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
266
259
|
openIap.initConnection(openIapConfig)
|
|
267
260
|
}
|
|
268
261
|
} catch (err: Throwable) {
|
|
269
|
-
val error =
|
|
262
|
+
val error = OpenIapError.InitConnection
|
|
270
263
|
RnIapLog.failure("initConnection.native", err)
|
|
271
264
|
throw OpenIapException(
|
|
272
265
|
toErrorJson(
|
|
@@ -277,7 +270,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
277
270
|
)
|
|
278
271
|
}
|
|
279
272
|
if (!ok) {
|
|
280
|
-
val error =
|
|
273
|
+
val error = OpenIapError.InitConnection
|
|
281
274
|
RnIapLog.failure("initConnection.native", Exception(error.message))
|
|
282
275
|
throw OpenIapException(
|
|
283
276
|
toErrorJson(
|
|
@@ -314,6 +307,8 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
314
307
|
promotedProductListenersIOS.clear()
|
|
315
308
|
synchronized(userChoiceBillingListenersAndroid) { userChoiceBillingListenersAndroid.clear() }
|
|
316
309
|
synchronized(developerProvidedBillingListenersAndroid) { developerProvidedBillingListenersAndroid.clear() }
|
|
310
|
+
synchronized(subscriptionBillingIssueListeners) { subscriptionBillingIssueListeners.clear() }
|
|
311
|
+
detachSubscriptionBillingIssueIfNeeded()
|
|
317
312
|
initDeferred = null
|
|
318
313
|
RnIapLog.result("endConnection", true)
|
|
319
314
|
true
|
|
@@ -332,7 +327,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
332
327
|
)
|
|
333
328
|
|
|
334
329
|
if (skus.isEmpty()) {
|
|
335
|
-
throw OpenIapException(toErrorJson(
|
|
330
|
+
throw OpenIapException(toErrorJson(OpenIapError.EmptySkuList))
|
|
336
331
|
}
|
|
337
332
|
|
|
338
333
|
ensureConnection()
|
|
@@ -340,46 +335,46 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
340
335
|
val queryType = parseProductQueryType(type)
|
|
341
336
|
val skusList = skus.toList()
|
|
342
337
|
|
|
343
|
-
val products: List<ProductCommon> =
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
338
|
+
val products: List<ProductCommon> = try {
|
|
339
|
+
when (queryType) {
|
|
340
|
+
ProductQueryType.All -> {
|
|
341
|
+
collectAllQueryProducts(
|
|
342
|
+
skusList = skusList,
|
|
343
|
+
fetchKind = { kind ->
|
|
344
|
+
RnIapLog.payload(
|
|
345
|
+
"fetchProducts.native",
|
|
346
|
+
mapOf("skus" to skusList, "type" to kind.rawValue)
|
|
347
|
+
)
|
|
348
|
+
val fetched = openIap.fetchProducts(ProductRequest(skusList, kind)).productsOrEmpty()
|
|
349
|
+
RnIapLog.result(
|
|
350
|
+
"fetchProducts.native",
|
|
351
|
+
fetched.map { mapOf("id" to it.id, "type" to it.type.rawValue) }
|
|
352
|
+
)
|
|
353
|
+
fetched
|
|
354
|
+
},
|
|
355
|
+
onFailure = { kind, error ->
|
|
356
|
+
RnIapLog.failure("fetchProducts.native[${kind.rawValue}]", error)
|
|
357
|
+
},
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
else -> {
|
|
349
361
|
RnIapLog.payload(
|
|
350
362
|
"fetchProducts.native",
|
|
351
|
-
mapOf("skus" to skusList, "type" to
|
|
363
|
+
mapOf("skus" to skusList, "type" to queryType.rawValue)
|
|
352
364
|
)
|
|
353
|
-
val fetched = openIap.fetchProducts(ProductRequest(skusList,
|
|
365
|
+
val fetched = openIap.fetchProducts(ProductRequest(skusList, queryType)).productsOrEmpty()
|
|
354
366
|
RnIapLog.result(
|
|
355
367
|
"fetchProducts.native",
|
|
356
368
|
fetched.map { mapOf("id" to it.id, "type" to it.type.rawValue) }
|
|
357
369
|
)
|
|
358
370
|
|
|
359
|
-
//
|
|
360
|
-
fetched.
|
|
361
|
-
|
|
362
|
-
}
|
|
371
|
+
// Preserve input order for non-All queries
|
|
372
|
+
val byId = fetched.associateBy { it.id }
|
|
373
|
+
skusList.mapNotNull { byId[it] }
|
|
363
374
|
}
|
|
364
|
-
|
|
365
|
-
// Return products in the same order as input skusList
|
|
366
|
-
skusList.mapNotNull { byId[it] }
|
|
367
|
-
}
|
|
368
|
-
else -> {
|
|
369
|
-
RnIapLog.payload(
|
|
370
|
-
"fetchProducts.native",
|
|
371
|
-
mapOf("skus" to skusList, "type" to queryType.rawValue)
|
|
372
|
-
)
|
|
373
|
-
val fetched = openIap.fetchProducts(ProductRequest(skusList, queryType)).productsOrEmpty()
|
|
374
|
-
RnIapLog.result(
|
|
375
|
-
"fetchProducts.native",
|
|
376
|
-
fetched.map { mapOf("id" to it.id, "type" to it.type.rawValue) }
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
// Preserve input order for non-All queries
|
|
380
|
-
val byId = fetched.associateBy { it.id }
|
|
381
|
-
skusList.mapNotNull { byId[it] }
|
|
382
375
|
}
|
|
376
|
+
} catch (e: OpenIapError) {
|
|
377
|
+
throw OpenIapException(toErrorJson(e))
|
|
383
378
|
}
|
|
384
379
|
|
|
385
380
|
products.forEach { p -> productTypeBySku[p.id] = p.type.rawValue }
|
|
@@ -411,13 +406,13 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
411
406
|
|
|
412
407
|
if (androidRequest == null) {
|
|
413
408
|
RnIapLog.warn("requestPurchase called without android payload")
|
|
414
|
-
sendPurchaseError(toErrorResult(
|
|
409
|
+
sendPurchaseError(toErrorResult(OpenIapError.DeveloperError()))
|
|
415
410
|
return@async defaultResult
|
|
416
411
|
}
|
|
417
412
|
|
|
418
413
|
if (androidRequest.skus.isEmpty()) {
|
|
419
414
|
RnIapLog.warn("requestPurchase received empty SKU list")
|
|
420
|
-
sendPurchaseError(toErrorResult(
|
|
415
|
+
sendPurchaseError(toErrorResult(OpenIapError.EmptySkuList))
|
|
421
416
|
return@async defaultResult
|
|
422
417
|
}
|
|
423
418
|
|
|
@@ -432,7 +427,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
432
427
|
|
|
433
428
|
if (activity == null) {
|
|
434
429
|
RnIapLog.warn("requestPurchase: Activity is null - cannot start purchase flow")
|
|
435
|
-
sendPurchaseError(toErrorResult(
|
|
430
|
+
sendPurchaseError(toErrorResult(OpenIapError.MissingCurrentActivity))
|
|
436
431
|
return@async defaultResult
|
|
437
432
|
}
|
|
438
433
|
|
|
@@ -454,7 +449,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
454
449
|
}
|
|
455
450
|
fetched.firstOrNull()?.let { productTypeBySku[it.id] = it.type.rawValue }
|
|
456
451
|
if (!productTypeBySku.containsKey(sku)) {
|
|
457
|
-
sendPurchaseError(toErrorResult(
|
|
452
|
+
sendPurchaseError(toErrorResult(OpenIapError.SkuNotFound(sku)))
|
|
458
453
|
return@async defaultResult
|
|
459
454
|
}
|
|
460
455
|
}
|
|
@@ -549,7 +544,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
549
544
|
RnIapLog.failure("requestPurchase", e)
|
|
550
545
|
sendPurchaseError(
|
|
551
546
|
toErrorResult(
|
|
552
|
-
error =
|
|
547
|
+
error = OpenIapError.PurchaseFailed(),
|
|
553
548
|
debugMessage = e.message,
|
|
554
549
|
messageOverride = e.message
|
|
555
550
|
)
|
|
@@ -651,7 +646,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
651
646
|
nitroSubscriptions.toTypedArray()
|
|
652
647
|
} catch (e: Exception) {
|
|
653
648
|
RnIapLog.failure("getActiveSubscriptions", e)
|
|
654
|
-
val error =
|
|
649
|
+
val error = OpenIapError.ServiceUnavailable()
|
|
655
650
|
throw OpenIapException(
|
|
656
651
|
toErrorJson(
|
|
657
652
|
error = error,
|
|
@@ -678,7 +673,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
678
673
|
hasActive
|
|
679
674
|
} catch (e: Exception) {
|
|
680
675
|
RnIapLog.failure("hasActiveSubscriptions", e)
|
|
681
|
-
val error =
|
|
676
|
+
val error = OpenIapError.ServiceUnavailable()
|
|
682
677
|
throw OpenIapException(
|
|
683
678
|
toErrorJson(
|
|
684
679
|
error = error,
|
|
@@ -713,7 +708,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
713
708
|
NitroPurchaseResult(
|
|
714
709
|
responseCode = -1.0,
|
|
715
710
|
debugMessage = "Missing purchaseToken",
|
|
716
|
-
code =
|
|
711
|
+
code = OpenIapError.toCode(OpenIapError.DeveloperError()),
|
|
717
712
|
message = "Missing purchaseToken",
|
|
718
713
|
purchaseToken = null
|
|
719
714
|
)
|
|
@@ -724,12 +719,12 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
724
719
|
try {
|
|
725
720
|
ensureConnection()
|
|
726
721
|
} catch (e: Exception) {
|
|
727
|
-
val err =
|
|
722
|
+
val err = OpenIapError.InitConnection
|
|
728
723
|
return@async Variant_Boolean_NitroPurchaseResult.Second(
|
|
729
724
|
NitroPurchaseResult(
|
|
730
725
|
responseCode = -1.0,
|
|
731
726
|
debugMessage = e.message,
|
|
732
|
-
code =
|
|
727
|
+
code = OpenIapError.toCode(err),
|
|
733
728
|
message = e.message?.takeIf { it.isNotBlank() } ?: err.message,
|
|
734
729
|
purchaseToken = purchaseToken
|
|
735
730
|
)
|
|
@@ -754,13 +749,13 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
754
749
|
RnIapLog.result("finishTransaction", mapOf("success" to true))
|
|
755
750
|
result
|
|
756
751
|
} catch (e: Exception) {
|
|
757
|
-
val err =
|
|
752
|
+
val err = OpenIapError.BillingError()
|
|
758
753
|
RnIapLog.failure("finishTransaction", e)
|
|
759
754
|
Variant_Boolean_NitroPurchaseResult.Second(
|
|
760
755
|
NitroPurchaseResult(
|
|
761
756
|
responseCode = -1.0,
|
|
762
757
|
debugMessage = e.message,
|
|
763
|
-
code =
|
|
758
|
+
code = OpenIapError.toCode(err),
|
|
764
759
|
message = e.message?.takeIf { it.isNotBlank() } ?: err.message,
|
|
765
760
|
purchaseToken = null
|
|
766
761
|
)
|
|
@@ -1276,14 +1271,14 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1276
1271
|
// iOS-specific method - not supported on Android
|
|
1277
1272
|
override fun getStorefrontIOS(): Promise<String> {
|
|
1278
1273
|
return Promise.async {
|
|
1279
|
-
throw OpenIapException(toErrorJson(
|
|
1274
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1280
1275
|
}
|
|
1281
1276
|
}
|
|
1282
1277
|
|
|
1283
1278
|
// iOS-specific method - not supported on Android
|
|
1284
1279
|
override fun getAppTransactionIOS(): Promise<Variant_NullType_String> {
|
|
1285
1280
|
return Promise.async {
|
|
1286
|
-
throw OpenIapException(toErrorJson(
|
|
1281
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1287
1282
|
}
|
|
1288
1283
|
}
|
|
1289
1284
|
|
|
@@ -1370,7 +1365,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1370
1365
|
try {
|
|
1371
1366
|
// For Android, we need the google options to be provided (new platform-specific structure)
|
|
1372
1367
|
val nitroGoogleOptions = (params.google as? Variant_NullType_NitroReceiptValidationGoogleOptions.Second)?.value
|
|
1373
|
-
?: throw OpenIapException(toErrorJson(
|
|
1368
|
+
?: throw OpenIapException(toErrorJson(OpenIapError.DeveloperError(), debugMessage = "Missing required parameter: google options"))
|
|
1374
1369
|
|
|
1375
1370
|
// Validate required google fields
|
|
1376
1371
|
val validations = mapOf(
|
|
@@ -1381,7 +1376,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1381
1376
|
)
|
|
1382
1377
|
for ((name, value) in validations) {
|
|
1383
1378
|
if (value.isEmpty()) {
|
|
1384
|
-
throw OpenIapException(toErrorJson(
|
|
1379
|
+
throw OpenIapException(toErrorJson(OpenIapError.DeveloperError(), debugMessage = "Missing or empty required parameter: $name"))
|
|
1385
1380
|
}
|
|
1386
1381
|
}
|
|
1387
1382
|
|
|
@@ -1409,7 +1404,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1409
1404
|
|
|
1410
1405
|
// Cast to Android result type (on Android, verifyPurchase returns VerifyPurchaseResultAndroid)
|
|
1411
1406
|
val androidResult = verifyResult as? VerifyPurchaseResultAndroid
|
|
1412
|
-
?: throw OpenIapException(toErrorJson(
|
|
1407
|
+
?: throw OpenIapException(toErrorJson(OpenIapError.InvalidPurchaseVerification, debugMessage = "Unexpected result type from verifyPurchase"))
|
|
1413
1408
|
|
|
1414
1409
|
// Convert OpenIAP result to Nitro result
|
|
1415
1410
|
val result = NitroReceiptValidationResultAndroid(
|
|
@@ -1441,7 +1436,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1441
1436
|
} catch (e: Exception) {
|
|
1442
1437
|
RnIapLog.failure("validateReceipt", e)
|
|
1443
1438
|
val debugMessage = e.message
|
|
1444
|
-
val error =
|
|
1439
|
+
val error = OpenIapError.InvalidPurchaseVerification
|
|
1445
1440
|
throw OpenIapException(
|
|
1446
1441
|
toErrorJson(
|
|
1447
1442
|
error = error,
|
|
@@ -1506,7 +1501,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1506
1501
|
)
|
|
1507
1502
|
} catch (e: Exception) {
|
|
1508
1503
|
RnIapLog.failure("verifyPurchaseWithProvider", e)
|
|
1509
|
-
val error =
|
|
1504
|
+
val error = OpenIapError.VerificationFailed
|
|
1510
1505
|
throw OpenIapException(
|
|
1511
1506
|
toErrorJson(
|
|
1512
1507
|
error = error,
|
|
@@ -1521,31 +1516,37 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1521
1516
|
// iOS-specific methods - Not applicable on Android, return appropriate defaults
|
|
1522
1517
|
override fun subscriptionStatusIOS(sku: String): Promise<Variant_NullType_Array_NitroSubscriptionStatus_> {
|
|
1523
1518
|
return Promise.async {
|
|
1524
|
-
throw OpenIapException(toErrorJson(
|
|
1519
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1525
1520
|
}
|
|
1526
1521
|
}
|
|
1527
1522
|
|
|
1528
1523
|
override fun currentEntitlementIOS(sku: String): Promise<Variant_NullType_NitroPurchase> {
|
|
1529
1524
|
return Promise.async {
|
|
1530
|
-
throw OpenIapException(toErrorJson(
|
|
1525
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1531
1526
|
}
|
|
1532
1527
|
}
|
|
1533
1528
|
|
|
1534
1529
|
override fun latestTransactionIOS(sku: String): Promise<Variant_NullType_NitroPurchase> {
|
|
1535
1530
|
return Promise.async {
|
|
1536
|
-
throw OpenIapException(toErrorJson(
|
|
1531
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1537
1532
|
}
|
|
1538
1533
|
}
|
|
1539
1534
|
|
|
1540
1535
|
override fun getPendingTransactionsIOS(): Promise<Array<NitroPurchase>> {
|
|
1541
1536
|
return Promise.async {
|
|
1542
|
-
throw OpenIapException(toErrorJson(
|
|
1537
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1543
1538
|
}
|
|
1544
1539
|
}
|
|
1545
1540
|
|
|
1541
|
+
override fun getAllTransactionsIOS(): Promise<Array<NitroPurchase>> {
|
|
1542
|
+
return Promise.async {
|
|
1543
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1546
1547
|
override fun syncIOS(): Promise<Boolean> {
|
|
1547
1548
|
return Promise.async {
|
|
1548
|
-
throw OpenIapException(toErrorJson(
|
|
1549
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1549
1550
|
}
|
|
1550
1551
|
}
|
|
1551
1552
|
|
|
@@ -1553,37 +1554,37 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1553
1554
|
|
|
1554
1555
|
override fun isEligibleForIntroOfferIOS(groupID: String): Promise<Boolean> {
|
|
1555
1556
|
return Promise.async {
|
|
1556
|
-
throw OpenIapException(toErrorJson(
|
|
1557
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1557
1558
|
}
|
|
1558
1559
|
}
|
|
1559
1560
|
|
|
1560
1561
|
override fun getReceiptDataIOS(): Promise<String> {
|
|
1561
1562
|
return Promise.async {
|
|
1562
|
-
throw OpenIapException(toErrorJson(
|
|
1563
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1563
1564
|
}
|
|
1564
1565
|
}
|
|
1565
1566
|
|
|
1566
1567
|
override fun getReceiptIOS(): Promise<String> {
|
|
1567
1568
|
return Promise.async {
|
|
1568
|
-
throw OpenIapException(toErrorJson(
|
|
1569
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1569
1570
|
}
|
|
1570
1571
|
}
|
|
1571
1572
|
|
|
1572
1573
|
override fun requestReceiptRefreshIOS(): Promise<String> {
|
|
1573
1574
|
return Promise.async {
|
|
1574
|
-
throw OpenIapException(toErrorJson(
|
|
1575
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1575
1576
|
}
|
|
1576
1577
|
}
|
|
1577
1578
|
|
|
1578
1579
|
override fun isTransactionVerifiedIOS(sku: String): Promise<Boolean> {
|
|
1579
1580
|
return Promise.async {
|
|
1580
|
-
throw OpenIapException(toErrorJson(
|
|
1581
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1581
1582
|
}
|
|
1582
1583
|
}
|
|
1583
1584
|
|
|
1584
1585
|
override fun getTransactionJwsIOS(sku: String): Promise<Variant_NullType_String> {
|
|
1585
1586
|
return Promise.async {
|
|
1586
|
-
throw OpenIapException(toErrorJson(
|
|
1587
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1587
1588
|
}
|
|
1588
1589
|
}
|
|
1589
1590
|
|
|
@@ -1613,7 +1614,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1613
1614
|
RnIapLog.payload("showAlternativeBillingDialogAndroid", null)
|
|
1614
1615
|
try {
|
|
1615
1616
|
val activity = context.currentActivity
|
|
1616
|
-
?: throw OpenIapException(toErrorJson(
|
|
1617
|
+
?: throw OpenIapException(toErrorJson(OpenIapError.DeveloperError(), debugMessage = "Activity not available"))
|
|
1617
1618
|
|
|
1618
1619
|
val userAccepted = withContext(Dispatchers.Main) {
|
|
1619
1620
|
openIap.setActivity(activity)
|
|
@@ -1685,6 +1686,57 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1685
1686
|
snapshot.forEach { it(details) }
|
|
1686
1687
|
}
|
|
1687
1688
|
|
|
1689
|
+
// -------------------------------------------------------------------------
|
|
1690
|
+
// Subscription billing-issue listener (cross-platform event)
|
|
1691
|
+
// Source: Play Billing 8.1+ Purchase.isSuspended detection inside openiap-google.
|
|
1692
|
+
// -------------------------------------------------------------------------
|
|
1693
|
+
|
|
1694
|
+
@Volatile
|
|
1695
|
+
private var subscriptionBillingIssueAttached = false
|
|
1696
|
+
private val subscriptionBillingIssueAttachLock = Any()
|
|
1697
|
+
private var subscriptionBillingIssueNativeListener: dev.hyo.openiap.listener.OpenIapSubscriptionBillingIssueListener? = null
|
|
1698
|
+
|
|
1699
|
+
override fun addSubscriptionBillingIssueListener(listener: (purchase: NitroPurchase) -> Unit) {
|
|
1700
|
+
synchronized(subscriptionBillingIssueListeners) {
|
|
1701
|
+
subscriptionBillingIssueListeners.add(listener)
|
|
1702
|
+
}
|
|
1703
|
+
attachSubscriptionBillingIssueIfNeeded()
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
override fun removeSubscriptionBillingIssueListener(listener: (purchase: NitroPurchase) -> Unit) {
|
|
1707
|
+
synchronized(subscriptionBillingIssueListeners) {
|
|
1708
|
+
subscriptionBillingIssueListeners.remove(listener)
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
private fun attachSubscriptionBillingIssueIfNeeded() {
|
|
1713
|
+
synchronized(subscriptionBillingIssueAttachLock) {
|
|
1714
|
+
if (subscriptionBillingIssueAttached) return
|
|
1715
|
+
val nativeListener = dev.hyo.openiap.listener.OpenIapSubscriptionBillingIssueListener { purchase ->
|
|
1716
|
+
runCatching {
|
|
1717
|
+
val nitro = convertToNitroPurchase(purchase)
|
|
1718
|
+
val snapshot = synchronized(subscriptionBillingIssueListeners) {
|
|
1719
|
+
ArrayList(subscriptionBillingIssueListeners)
|
|
1720
|
+
}
|
|
1721
|
+
snapshot.forEach { it(nitro) }
|
|
1722
|
+
}.onFailure { RnIapLog.failure("subscriptionBillingIssueListener", it) }
|
|
1723
|
+
}
|
|
1724
|
+
openIap.addSubscriptionBillingIssueListener(nativeListener)
|
|
1725
|
+
subscriptionBillingIssueNativeListener = nativeListener
|
|
1726
|
+
subscriptionBillingIssueAttached = true
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
private fun detachSubscriptionBillingIssueIfNeeded() {
|
|
1731
|
+
synchronized(subscriptionBillingIssueAttachLock) {
|
|
1732
|
+
subscriptionBillingIssueNativeListener?.let {
|
|
1733
|
+
openIap.removeSubscriptionBillingIssueListener(it)
|
|
1734
|
+
}
|
|
1735
|
+
subscriptionBillingIssueNativeListener = null
|
|
1736
|
+
subscriptionBillingIssueAttached = false
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1688
1740
|
// -------------------------------------------------------------------------
|
|
1689
1741
|
// Billing Programs API (Android 8.2.0+)
|
|
1690
1742
|
// -------------------------------------------------------------------------
|
|
@@ -1759,7 +1811,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1759
1811
|
|
|
1760
1812
|
val activity = withContext(Dispatchers.Main) {
|
|
1761
1813
|
runCatching { context.currentActivity }.getOrNull()
|
|
1762
|
-
} ?: throw OpenIapException(toErrorJson(
|
|
1814
|
+
} ?: throw OpenIapException(toErrorJson(OpenIapError.DeveloperError(), debugMessage = "Activity not available"))
|
|
1763
1815
|
|
|
1764
1816
|
val openIapParams = OpenIapLaunchExternalLinkParams(
|
|
1765
1817
|
billingProgram = mapBillingProgram(params.billingProgram),
|
|
@@ -1814,96 +1866,98 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1814
1866
|
|
|
1815
1867
|
override fun canPresentExternalPurchaseNoticeIOS(): Promise<Boolean> {
|
|
1816
1868
|
return Promise.async {
|
|
1817
|
-
throw OpenIapException(toErrorJson(
|
|
1869
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1818
1870
|
}
|
|
1819
1871
|
}
|
|
1820
1872
|
|
|
1821
1873
|
override fun presentExternalPurchaseNoticeSheetIOS(): Promise<ExternalPurchaseNoticeResultIOS> {
|
|
1822
1874
|
return Promise.async {
|
|
1823
|
-
throw OpenIapException(toErrorJson(
|
|
1875
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1824
1876
|
}
|
|
1825
1877
|
}
|
|
1826
1878
|
|
|
1827
1879
|
override fun presentExternalPurchaseLinkIOS(url: String): Promise<ExternalPurchaseLinkResultIOS> {
|
|
1828
1880
|
return Promise.async {
|
|
1829
|
-
throw OpenIapException(toErrorJson(
|
|
1881
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1830
1882
|
}
|
|
1831
1883
|
}
|
|
1832
1884
|
|
|
1833
1885
|
// ExternalPurchaseCustomLink (iOS 18.1+) - iOS only stubs
|
|
1834
1886
|
override fun isEligibleForExternalPurchaseCustomLinkIOS(): Promise<Boolean> {
|
|
1835
1887
|
return Promise.async {
|
|
1836
|
-
throw OpenIapException(toErrorJson(
|
|
1888
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1837
1889
|
}
|
|
1838
1890
|
}
|
|
1839
1891
|
|
|
1840
1892
|
override fun getExternalPurchaseCustomLinkTokenIOS(tokenType: ExternalPurchaseCustomLinkTokenTypeIOS): Promise<ExternalPurchaseCustomLinkTokenResultIOS> {
|
|
1841
1893
|
return Promise.async {
|
|
1842
|
-
throw OpenIapException(toErrorJson(
|
|
1894
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1843
1895
|
}
|
|
1844
1896
|
}
|
|
1845
1897
|
|
|
1846
1898
|
override fun showExternalPurchaseCustomLinkNoticeIOS(noticeType: ExternalPurchaseCustomLinkNoticeTypeIOS): Promise<ExternalPurchaseCustomLinkNoticeResultIOS> {
|
|
1847
1899
|
return Promise.async {
|
|
1848
|
-
throw OpenIapException(toErrorJson(
|
|
1900
|
+
throw OpenIapException(toErrorJson(OpenIapError.FeatureNotSupported()))
|
|
1849
1901
|
}
|
|
1850
1902
|
}
|
|
1851
1903
|
|
|
1852
1904
|
// ---------------------------------------------------------------------
|
|
1853
1905
|
// OpenIAP error helpers: unify error codes/messages from library
|
|
1854
1906
|
// ---------------------------------------------------------------------
|
|
1855
|
-
private fun parseOpenIapError(err: Throwable):
|
|
1856
|
-
// Try to extract
|
|
1907
|
+
private fun parseOpenIapError(err: Throwable): OpenIapError {
|
|
1908
|
+
// Try to extract OpenIapError from the exception chain
|
|
1857
1909
|
var cause: Throwable? = err
|
|
1858
1910
|
while (cause != null) {
|
|
1859
1911
|
val message = cause.message ?: ""
|
|
1860
1912
|
// Check if message contains OpenIAP error patterns
|
|
1861
1913
|
when {
|
|
1862
1914
|
message.contains("not prepared", ignoreCase = true) ||
|
|
1863
|
-
message.contains("not initialized", ignoreCase = true) -> return
|
|
1915
|
+
message.contains("not initialized", ignoreCase = true) -> return OpenIapError.NotPrepared
|
|
1864
1916
|
message.contains("developer error", ignoreCase = true) ||
|
|
1865
|
-
message.contains("activity not available", ignoreCase = true) -> return
|
|
1866
|
-
message.contains("network", ignoreCase = true) -> return
|
|
1917
|
+
message.contains("activity not available", ignoreCase = true) -> return OpenIapError.DeveloperError()
|
|
1918
|
+
message.contains("network", ignoreCase = true) -> return OpenIapError.NetworkError
|
|
1867
1919
|
message.contains("service unavailable", ignoreCase = true) ||
|
|
1868
|
-
message.contains("billing unavailable", ignoreCase = true) -> return
|
|
1920
|
+
message.contains("billing unavailable", ignoreCase = true) -> return OpenIapError.ServiceUnavailable()
|
|
1869
1921
|
}
|
|
1870
1922
|
cause = cause.cause
|
|
1871
1923
|
}
|
|
1872
1924
|
// Default to ServiceUnavailable if we can't determine the error type
|
|
1873
|
-
return
|
|
1925
|
+
return OpenIapError.ServiceUnavailable()
|
|
1874
1926
|
}
|
|
1875
1927
|
|
|
1876
1928
|
private fun toErrorJson(
|
|
1877
|
-
error:
|
|
1929
|
+
error: OpenIapError,
|
|
1878
1930
|
productId: String? = null,
|
|
1879
1931
|
debugMessage: String? = null,
|
|
1880
1932
|
messageOverride: String? = null
|
|
1881
1933
|
): String {
|
|
1882
|
-
val code =
|
|
1934
|
+
val code = OpenIapError.Companion.toCode(error)
|
|
1883
1935
|
val message = messageOverride?.takeIf { it.isNotBlank() }
|
|
1884
1936
|
?: error.message?.takeIf { it.isNotBlank() }
|
|
1885
|
-
?:
|
|
1886
|
-
|
|
1887
|
-
val
|
|
1937
|
+
?: OpenIapError.Companion.defaultMessage(code)
|
|
1938
|
+
val diagnostics = error.toJSON()
|
|
1939
|
+
val responseCode = (diagnostics["responseCode"] as? Number)?.toInt()
|
|
1940
|
+
val productIds = diagnostics["productIds"] as? List<*>
|
|
1941
|
+
val productType = diagnostics["productType"] as? String
|
|
1942
|
+
val isEmptyProductList = diagnostics["isEmptyProductList"] as? Boolean
|
|
1943
|
+
|
|
1944
|
+
val errorMap = mutableMapOf<String, Any?>(
|
|
1888
1945
|
"code" to code,
|
|
1889
1946
|
"message" to message
|
|
1890
1947
|
)
|
|
1891
1948
|
|
|
1892
|
-
errorMap["responseCode"] = -1
|
|
1893
|
-
debugMessage
|
|
1949
|
+
errorMap["responseCode"] = responseCode ?: -1
|
|
1950
|
+
debugMessage
|
|
1951
|
+
?.let { errorMap["debugMessage"] = it }
|
|
1952
|
+
?: (diagnostics["debugMessage"] as? String)?.let { errorMap["debugMessage"] = it }
|
|
1953
|
+
?: error.message?.let { errorMap["debugMessage"] = it }
|
|
1894
1954
|
productId?.let { errorMap["productId"] = it }
|
|
1955
|
+
if (!productIds.isNullOrEmpty()) errorMap["productIds"] = productIds
|
|
1956
|
+
productType?.let { errorMap["productType"] = it }
|
|
1957
|
+
isEmptyProductList?.let { errorMap["isEmptyProductList"] = it }
|
|
1895
1958
|
|
|
1896
1959
|
return try {
|
|
1897
|
-
|
|
1898
|
-
val valueStr = when (value) {
|
|
1899
|
-
is String -> "\"${value.replace("\"", "\\\"")}\""
|
|
1900
|
-
is Number -> value.toString()
|
|
1901
|
-
is Boolean -> value.toString()
|
|
1902
|
-
else -> "\"$value\""
|
|
1903
|
-
}
|
|
1904
|
-
"\"$key\":$valueStr"
|
|
1905
|
-
}
|
|
1906
|
-
"{${jsonPairs.joinToString(",")}}"
|
|
1960
|
+
JSONObject(errorMap).toString()
|
|
1907
1961
|
} catch (e: Exception) {
|
|
1908
1962
|
"$code: $message"
|
|
1909
1963
|
}
|
|
@@ -1957,18 +2011,21 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1957
2011
|
}
|
|
1958
2012
|
|
|
1959
2013
|
private fun toErrorResult(
|
|
1960
|
-
error:
|
|
2014
|
+
error: OpenIapError,
|
|
1961
2015
|
productId: String? = null,
|
|
1962
2016
|
debugMessage: String? = null,
|
|
1963
2017
|
messageOverride: String? = null
|
|
1964
2018
|
): NitroPurchaseResult {
|
|
1965
|
-
val code =
|
|
2019
|
+
val code = OpenIapError.Companion.toCode(error)
|
|
1966
2020
|
val message = messageOverride?.takeIf { it.isNotBlank() }
|
|
1967
2021
|
?: error.message?.takeIf { it.isNotBlank() }
|
|
1968
|
-
?:
|
|
2022
|
+
?: OpenIapError.Companion.defaultMessage(code)
|
|
2023
|
+
val diagnostics = error.toJSON()
|
|
2024
|
+
val responseCode = (diagnostics["responseCode"] as? Number)?.toDouble()
|
|
2025
|
+
val diagnosticMessage = diagnostics["debugMessage"] as? String
|
|
1969
2026
|
return NitroPurchaseResult(
|
|
1970
|
-
responseCode = -1.0,
|
|
1971
|
-
debugMessage = debugMessage ?: error.message,
|
|
2027
|
+
responseCode = responseCode ?: -1.0,
|
|
2028
|
+
debugMessage = debugMessage ?: diagnosticMessage ?: error.message,
|
|
1972
2029
|
code = code,
|
|
1973
2030
|
message = message,
|
|
1974
2031
|
purchaseToken = null
|