expo-iap 2.7.12 → 2.7.13
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.
|
@@ -408,10 +408,11 @@ class ExpoIapModule :
|
|
|
408
408
|
if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
|
|
409
409
|
val errorData = PlayUtils.getBillingResponseData(billingResult.responseCode)
|
|
410
410
|
var errorMessage = billingResult.debugMessage ?: errorData.message
|
|
411
|
+
var subResponseCode: Int? = null
|
|
411
412
|
|
|
412
413
|
// Check for sub-response codes (v8.0.0+)
|
|
413
414
|
try {
|
|
414
|
-
|
|
415
|
+
subResponseCode = billingResult.javaClass.getMethod("getSubResponseCode").invoke(billingResult) as? Int
|
|
415
416
|
if (subResponseCode != null && subResponseCode != 0) {
|
|
416
417
|
if (subResponseCode == 1) { // PAYMENT_DECLINED_DUE_TO_INSUFFICIENT_FUNDS
|
|
417
418
|
errorMessage = "$errorMessage (Payment declined due to insufficient funds)"
|
|
@@ -423,6 +424,32 @@ class ExpoIapModule :
|
|
|
423
424
|
// Method doesn't exist in older versions, ignore
|
|
424
425
|
}
|
|
425
426
|
|
|
427
|
+
// Send error event to match iOS behavior
|
|
428
|
+
val errorMap = mutableMapOf<String, Any?>(
|
|
429
|
+
"responseCode" to billingResult.responseCode,
|
|
430
|
+
"debugMessage" to billingResult.debugMessage,
|
|
431
|
+
"code" to errorData.code,
|
|
432
|
+
"message" to errorMessage
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
// Add product ID if available
|
|
436
|
+
if (skuArr.isNotEmpty()) {
|
|
437
|
+
errorMap["productId"] = skuArr.first()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Add sub-response code if available
|
|
441
|
+
subResponseCode?.let {
|
|
442
|
+
if (it != 0) {
|
|
443
|
+
errorMap["subResponseCode"] = it
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
sendEvent(IapEvent.PURCHASE_ERROR, errorMap.toMap())
|
|
449
|
+
} catch (e: Exception) {
|
|
450
|
+
Log.e(TAG, "Failed to send PURCHASE_ERROR event: ${e.message}")
|
|
451
|
+
}
|
|
452
|
+
|
|
426
453
|
promise.reject(errorData.code, errorMessage, null)
|
|
427
454
|
return@ensureConnection
|
|
428
455
|
}
|
package/ios/ExpoIapModule.swift
CHANGED
|
@@ -241,7 +241,7 @@ public class ExpoIapModule: Module {
|
|
|
241
241
|
"ERROR_CODES": IapErrorCode.toDictionary()
|
|
242
242
|
])
|
|
243
243
|
|
|
244
|
-
Events(IapEvent.PurchaseUpdated, IapEvent.PurchaseError)
|
|
244
|
+
Events(IapEvent.PurchaseUpdated, IapEvent.PurchaseError, IapEvent.PromotedProductIOS)
|
|
245
245
|
|
|
246
246
|
OnStartObserving {
|
|
247
247
|
self.hasListeners = true
|
|
@@ -494,6 +494,14 @@ public class ExpoIapModule: Module {
|
|
|
494
494
|
options.insert(.appAccountToken(appAccountUUID))
|
|
495
495
|
}
|
|
496
496
|
guard let windowScene = await self.currentWindowScene() else {
|
|
497
|
+
let errorData = [
|
|
498
|
+
"responseCode": IapErrorCode.serviceError,
|
|
499
|
+
"debugMessage": "Could not find window scene",
|
|
500
|
+
"code": IapErrorCode.serviceError,
|
|
501
|
+
"message": "Could not find window scene",
|
|
502
|
+
"productId": sku,
|
|
503
|
+
]
|
|
504
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
497
505
|
throw Exception(name: "ExpoIapModule", description: "Could not find window scene", code: IapErrorCode.serviceError)
|
|
498
506
|
}
|
|
499
507
|
let result: Product.PurchaseResult
|
|
@@ -537,19 +545,105 @@ public class ExpoIapModule: Module {
|
|
|
537
545
|
return serialized
|
|
538
546
|
}
|
|
539
547
|
case .userCancelled:
|
|
548
|
+
let errorData = [
|
|
549
|
+
"responseCode": IapErrorCode.userCancelled,
|
|
550
|
+
"debugMessage": "User cancelled the purchase",
|
|
551
|
+
"code": IapErrorCode.userCancelled,
|
|
552
|
+
"message": "User cancelled the purchase",
|
|
553
|
+
"productId": sku,
|
|
554
|
+
]
|
|
555
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
540
556
|
throw Exception(name: "ExpoIapModule", description: "User cancelled the purchase", code: IapErrorCode.userCancelled)
|
|
541
557
|
case .pending:
|
|
558
|
+
let errorData = [
|
|
559
|
+
"responseCode": IapErrorCode.deferredPayment,
|
|
560
|
+
"debugMessage": "The payment was deferred",
|
|
561
|
+
"code": IapErrorCode.deferredPayment,
|
|
562
|
+
"message": "The payment was deferred",
|
|
563
|
+
"productId": sku,
|
|
564
|
+
]
|
|
565
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
542
566
|
throw Exception(name: "ExpoIapModule", description: "The payment was deferred", code: IapErrorCode.deferredPayment)
|
|
543
567
|
@unknown default:
|
|
568
|
+
let errorData = [
|
|
569
|
+
"responseCode": IapErrorCode.unknown,
|
|
570
|
+
"debugMessage": "Unknown purchase result",
|
|
571
|
+
"code": IapErrorCode.unknown,
|
|
572
|
+
"message": "Unknown purchase result",
|
|
573
|
+
"productId": sku,
|
|
574
|
+
]
|
|
575
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
544
576
|
throw Exception(name: "ExpoIapModule", description: "Unknown purchase result", code: IapErrorCode.unknown)
|
|
545
577
|
}
|
|
546
578
|
} catch {
|
|
547
579
|
if error is Exception {
|
|
548
580
|
throw error
|
|
549
581
|
}
|
|
550
|
-
|
|
582
|
+
|
|
583
|
+
// Map StoreKit errors to proper error codes
|
|
584
|
+
var errorCode = IapErrorCode.purchaseError
|
|
585
|
+
var errorMessage = error.localizedDescription
|
|
586
|
+
|
|
587
|
+
// Check for specific StoreKit error types
|
|
588
|
+
if let nsError = error as NSError? {
|
|
589
|
+
switch nsError.domain {
|
|
590
|
+
case "SKErrorDomain":
|
|
591
|
+
// Handle SKError codes
|
|
592
|
+
switch nsError.code {
|
|
593
|
+
case 0: // SKError.unknown
|
|
594
|
+
errorCode = IapErrorCode.unknown
|
|
595
|
+
case 1: // SKError.clientInvalid
|
|
596
|
+
errorCode = IapErrorCode.serviceError
|
|
597
|
+
case 2: // SKError.paymentCancelled
|
|
598
|
+
errorCode = IapErrorCode.userCancelled
|
|
599
|
+
errorMessage = "User cancelled the purchase"
|
|
600
|
+
case 3: // SKError.paymentInvalid
|
|
601
|
+
errorCode = IapErrorCode.userError
|
|
602
|
+
case 4: // SKError.paymentNotAllowed
|
|
603
|
+
errorCode = IapErrorCode.userError
|
|
604
|
+
errorMessage = "Payment not allowed"
|
|
605
|
+
case 5: // SKError.storeProductNotAvailable
|
|
606
|
+
errorCode = IapErrorCode.itemUnavailable
|
|
607
|
+
case 6: // SKError.cloudServicePermissionDenied
|
|
608
|
+
errorCode = IapErrorCode.serviceError
|
|
609
|
+
case 7: // SKError.cloudServiceNetworkConnectionFailed
|
|
610
|
+
errorCode = IapErrorCode.networkError
|
|
611
|
+
case 8: // SKError.cloudServiceRevoked
|
|
612
|
+
errorCode = IapErrorCode.serviceError
|
|
613
|
+
default:
|
|
614
|
+
errorCode = IapErrorCode.purchaseError
|
|
615
|
+
}
|
|
616
|
+
case "NSURLErrorDomain":
|
|
617
|
+
errorCode = IapErrorCode.networkError
|
|
618
|
+
errorMessage = "Network error: \(error.localizedDescription)"
|
|
619
|
+
default:
|
|
620
|
+
errorCode = IapErrorCode.purchaseError
|
|
621
|
+
}
|
|
622
|
+
} else if error.localizedDescription.lowercased().contains("network") {
|
|
623
|
+
errorCode = IapErrorCode.networkError
|
|
624
|
+
} else if error.localizedDescription.lowercased().contains("cancelled") {
|
|
625
|
+
errorCode = IapErrorCode.userCancelled
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
let errorData = [
|
|
629
|
+
"responseCode": errorCode,
|
|
630
|
+
"debugMessage": "Purchase failed: \(error.localizedDescription)",
|
|
631
|
+
"code": errorCode,
|
|
632
|
+
"message": errorMessage,
|
|
633
|
+
"productId": sku,
|
|
634
|
+
]
|
|
635
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
636
|
+
throw Exception(name: "ExpoIapModule", description: errorMessage, code: errorCode)
|
|
551
637
|
}
|
|
552
638
|
} else {
|
|
639
|
+
let errorData = [
|
|
640
|
+
"responseCode": IapErrorCode.itemUnavailable,
|
|
641
|
+
"debugMessage": "Invalid product ID",
|
|
642
|
+
"code": IapErrorCode.itemUnavailable,
|
|
643
|
+
"message": "Invalid product ID",
|
|
644
|
+
"productId": sku,
|
|
645
|
+
]
|
|
646
|
+
self.sendEvent(IapEvent.PurchaseError, errorData)
|
|
553
647
|
throw Exception(name: "ExpoIapModule", description: "Invalid product ID", code: IapErrorCode.itemUnavailable)
|
|
554
648
|
}
|
|
555
649
|
}
|