From 12d3aefb46e9ff2cd4b256f6bfb2e80c6d655d7a Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Tue, 29 Aug 2023 21:58:24 +0530 Subject: [PATCH 1/3] Error handling improvements --- .../flutter/sdk/CBExceptionExtensions.kt | 26 ++++ .../chargebee/flutter/sdk/CBNativeError.kt | 52 +++---- .../flutter/sdk/ChargebeeFlutterSdkPlugin.kt | 117 ++++++++++++---- ios/Classes/CBError+Extensions.swift | 63 +++++++++ ios/Classes/FlutterErrorHandler.swift | 132 +++++++++++++----- .../SwiftChargebeeFlutterSdkPlugin.swift | 8 +- 6 files changed, 305 insertions(+), 93 deletions(-) create mode 100644 android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt create mode 100644 ios/Classes/CBError+Extensions.swift diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt new file mode 100644 index 0000000..954de5b --- /dev/null +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt @@ -0,0 +1,26 @@ +package com.chargebee.flutter.sdk + +import com.chargebee.android.exceptions.CBException +import org.json.JSONException +import org.json.JSONObject + +fun CBException.messageUserInfo(): MutableMap { + val messageUserInfo = mutableMapOf() + try { + val jsonObject = JSONObject(this.message) + messageUserInfo["message"] = jsonObject.optString("message") + messageUserInfo["apiErrorCode"] = jsonObject.optString("api_error_code") + messageUserInfo["httpStatusCode"] = jsonObject.optInt("http_status_code") + } catch (e: JSONException) { + messageUserInfo["message"] = this.message + } + return messageUserInfo +} + +fun CBException.errorCode(): CBNativeError { + return when (this.httpStatusCode) { + 401 -> CBNativeError.INVALID_API_KEY + 404 -> CBNativeError.INVALID_SDK_CONFIGURATION + else -> CBNativeError.UNKNOWN + } +} diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt index e8a53e2..c31fb66 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt @@ -3,34 +3,38 @@ package com.chargebee.flutter.sdk import com.android.billingclient.api.BillingClient enum class CBNativeError(val code: Int) { - // Restore Error - SERVICE_TIMEOUT(2014), - FEATURE_NOT_SUPPORTED(2015), - SERVICE_UNAVAILABLE(2016), - DEVELOPER_ERROR(2017), - ERROR(2018), - SERVICE_DISCONNECTED(2019), - USER_CANCELED(2020), - BILLING_UNAVAILABLE(2021), - ITEM_UNAVAILABLE(2022), - ITEM_NOT_OWNED(2023), - ITEM_ALREADY_OWNED(2024), - UNKNOWN(0); + + UNKNOWN(0), + + // Chargebee Errors + INVALID_API_KEY(999), + INVALID_SDK_CONFIGURATION(1000), + INVALID_CATALOG_VERSION(1001), + + // Store Errors + INVALID_OFFER(2001), + INVALID_PURCHASE(2002), + INVALID_SANDBOX(2003), + NETWORK_ERROR(2004), + PAYMENT_FAILED(2005), + PAYMENT_NOT_ALLOWED(2006), + PRODUCT_NOT_AVAILABLE(2007), + PURCHASE_NOT_ALLOWED(2008), + PURCHASE_CANCELLED(2009), + STORE_PROBLEM(2010), + INVALID_RECEIPT(2011), + REQUEST_FAILED(2012), + PRODUCT_PURCHASED_ALREADY(2013); companion object { fun billingResponseCode(code: Int): CBNativeError { return when (code) { - BillingClient.BillingResponseCode.SERVICE_TIMEOUT -> SERVICE_TIMEOUT - BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> FEATURE_NOT_SUPPORTED - BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> SERVICE_UNAVAILABLE - BillingClient.BillingResponseCode.DEVELOPER_ERROR -> DEVELOPER_ERROR - BillingClient.BillingResponseCode.ERROR -> ERROR - BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> SERVICE_DISCONNECTED - BillingClient.BillingResponseCode.USER_CANCELED -> USER_CANCELED - BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> BILLING_UNAVAILABLE - BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> ITEM_UNAVAILABLE - BillingClient.BillingResponseCode.ITEM_NOT_OWNED -> ITEM_NOT_OWNED - BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> ITEM_ALREADY_OWNED + BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED, BillingClient.BillingResponseCode.BILLING_UNAVAILABLE, BillingClient.BillingResponseCode.ITEM_NOT_OWNED -> PURCHASE_NOT_ALLOWED + BillingClient.BillingResponseCode.ERROR, BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE, BillingClient.BillingResponseCode.SERVICE_DISCONNECTED, BillingClient.BillingResponseCode.SERVICE_TIMEOUT -> STORE_PROBLEM + BillingClient.BillingResponseCode.USER_CANCELED -> PURCHASE_CANCELLED + BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> PRODUCT_NOT_AVAILABLE + BillingClient.BillingResponseCode.DEVELOPER_ERROR -> INVALID_PURCHASE + BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> PRODUCT_PURCHASED_ALREADY else -> UNKNOWN } } diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt index 41c44f2..49a122c 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt @@ -134,7 +134,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar result.success(response.in_app_detail.status) } is ChargebeeResult.Error -> { - onError(it.exp, result) + onCBError( + "${it.exp.errorCode().code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -148,15 +151,25 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar object : CBCallback.ListProductsCallback> { override fun onSuccess(productDetails: ArrayList) { mSkuProductList.clear() - for (product in productDetails) { - val jsonMapString = Gson().toJson(product.toMap()) - mSkuProductList.add(jsonMapString) + if (productDetails.isEmpty()) { + val messageUserInfo = productNotAvailableError.messageUserInfo() + onCBError( + "${productNotAvailableError.httpStatusCode}", + messageUserInfo["message"] as String, + productNotAvailableError, + result + ) + } else { + for (product in productDetails) { + val jsonMapString = Gson().toJson(product.toMap()) + mSkuProductList.add(jsonMapString) + } + result.success(mSkuProductList) } - result.success(mSkuProductList) } override fun onError(error: CBException) { - onError(error, result) + onStoreError(error, result) } }) } @@ -195,8 +208,11 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar object : CBCallback.ListProductsCallback> { override fun onSuccess(productIDs: ArrayList) { if (productIDs.size == 0) { - onError( - CBException(ErrorDetail(GPErrorCode.ProductUnavailable.errorMsg)), + val messageUserInfo = productNotAvailableError.messageUserInfo() + onCBError( + "${productNotAvailableError.httpStatusCode}", + messageUserInfo["message"] as String, + productNotAvailableError, result ) return @@ -220,13 +236,13 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } override fun onError(error: CBException) { - onError(error, result) + onStoreError(error, result) } }) } override fun onError(error: CBException) { - onError(error, result) + onStoreError(error, result) } }) } @@ -237,6 +253,12 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar Chargebee.showManageSubscriptionsSettings(context = activity, productId = productId, packageName = packageName) } + val productNotAvailableError = CBException( + ErrorDetail( + message = GPErrorCode.ProductUnavailable.errorMsg, + httpStatusCode = CBNativeError.PRODUCT_NOT_AVAILABLE.code + ) + ) private fun purchaseNonSubscriptionProduct(args: Map, callback: Result) { val customer = CBCustomer( args["customerId"] as String, @@ -257,8 +279,11 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar object : CBCallback.ListProductsCallback> { override fun onSuccess(productIDs: ArrayList) { if (productIDs.size == 0) { - onError( - CBException(ErrorDetail(GPErrorCode.ProductUnavailable.errorMsg)), + val messageUserInfo = productNotAvailableError.messageUserInfo() + onCBError( + "${productNotAvailableError.httpStatusCode}", + messageUserInfo["message"] as String, + productNotAvailableError, callback ) return @@ -273,13 +298,13 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } override fun onError(error: CBException) { - onError(error, callback) + onStoreError(error, callback) } }) } override fun onError(error: CBException) { - onError(error, callback) + onStoreError(error, callback) } }) } @@ -304,7 +329,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar result.success(jsonString) } is ChargebeeResult.Error -> { - onError(it.exp, result) + onCBError( + "${it.exp.errorCode().code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -326,7 +354,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar result.success(jsonString) } is ChargebeeResult.Error -> { - onError(it.exp, result) + onCBError( + "${it.exp.errorCode().code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -347,7 +378,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar result.success(jsonString) } is ChargebeeResult.Error -> { - onError(it.exp, result) + onCBError( + "${it.exp.errorCode().code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -368,7 +402,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } } is CBProductIDResult.Error -> { - onError(it.exp, result) + onCBError( + "${CBNativeError.INVALID_CATALOG_VERSION.code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -385,7 +422,10 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } } is ChargebeeResult.Error -> { - onError(it.exp, result) + onCBError( + "${it.exp.errorCode().code}", + it.exp.messageUserInfo()["message"] as String, it.exp, result + ) } } } @@ -405,8 +445,11 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar object : CBCallback.ListProductsCallback> { override fun onSuccess(productIDs: ArrayList) { if (productIDs.size == 0) { - onError( - CBException(ErrorDetail(GPErrorCode.ProductUnavailable.errorMsg)), + val messageUserInfo = productNotAvailableError.messageUserInfo() + onCBError( + "${productNotAvailableError.httpStatusCode}", + messageUserInfo["message"] as String, + productNotAvailableError, result ) return @@ -429,13 +472,16 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } override fun onError(error: CBException) { - onError(error, result) + onCBError( + "${CBNativeError.INVALID_RECEIPT.code}", + error.messageUserInfo()["message"] as String, error, result + ) } }) } override fun onError(error: CBException) { - onError(error, result) + onStoreError(error, result) } }) } @@ -459,8 +505,11 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar object : CBCallback.ListProductsCallback> { override fun onSuccess(productIDs: ArrayList) { if (productIDs.size == 0) { - onError( - CBException(ErrorDetail(GPErrorCode.ProductUnavailable.errorMsg)), + val messageUserInfo = productNotAvailableError.messageUserInfo() + onCBError( + "${productNotAvailableError.httpStatusCode}", + messageUserInfo["message"] as String, + productNotAvailableError, callback ) return @@ -475,13 +524,16 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } override fun onError(error: CBException) { - onError(error, callback) + onCBError( + "${CBNativeError.INVALID_RECEIPT.code}", + error.messageUserInfo()["message"] as String, error, callback + ) } }) } override fun onError(error: CBException) { - onError(error, callback) + onStoreError(error, callback) } }) } @@ -507,13 +559,18 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar onDetachedFromActivity(); } - private fun onError(error: CBException, result: Result) { - error("${error.httpStatusCode}", error, result) + private fun onCBError(errorCode: String, errorMsg: String, error: CBException, result: Result) { + result.error( + errorCode, errorMsg, error.messageUserInfo() + ) } private fun onStoreError(error: CBException, result: Result) { val errorCode = error.httpStatusCode?.let { CBNativeError.billingResponseCode(it).code } - error("$errorCode", error, result) + ?: CBNativeError.UNKNOWN + result.error( + "$errorCode", error.messageUserInfo()["message"] as String, error.messageUserInfo() + ) } private fun error(errorCode: String, error: CBException, result: Result) { diff --git a/ios/Classes/CBError+Extensions.swift b/ios/Classes/CBError+Extensions.swift new file mode 100644 index 0000000..541053e --- /dev/null +++ b/ios/Classes/CBError+Extensions.swift @@ -0,0 +1,63 @@ +// +// CBError+Extensions.swift +// chargebee_flutter_sdk +// +// Created by Amutha C on 28/08/23. +// + +import Foundation +import Chargebee + +extension CBError { + + var userInfo: [String: Any] { + switch self { + case .invalidRequest(let response), .operationFailed(let response), + .paymentFailed(let response), .serverError(let response): + var userInfo: [String: Any] = [:] + userInfo["message"] = response.message + userInfo["type"] = response.type + userInfo["apiErrorCode"] = response.apiErrorCode + userInfo["param"] = response.param + userInfo["httpStatusCode"] = response.httpStatusCode + return userInfo + } + } +} + +extension CBPurchaseError { + var userInfo: [String: Any] { + var userInfo: [String: Any] = [:] + userInfo["message"] = self.errorDescription + return userInfo + } +} + +extension RestoreError { + var reactNativeError: CBNativeError { + switch self { + case .invalidReceiptData, .invalidReceiptURL: + return .invalidReceipt + case .noReceipt: + return .noReceipt + case .refreshReceiptFailed: + return .refreshReceiptFailed + case .restoreFailed: + return .restoreFailed + case .noProductsToRestore: + return .noProductToRestore + case .serviceError: + return .systemError + } + } + + var asNSError: NSError { + return NSError(domain: "RestoreError", code: self.reactNativeError.rawValue, userInfo: self.userInfo) + } + + var userInfo: [String: Any] { + var userInfo: [String: Any] = [:] + userInfo["message"] = self.errorDescription + return userInfo + } +} diff --git a/ios/Classes/FlutterErrorHandler.swift b/ios/Classes/FlutterErrorHandler.swift index e3e6016..0b185a1 100644 --- a/ios/Classes/FlutterErrorHandler.swift +++ b/ios/Classes/FlutterErrorHandler.swift @@ -22,34 +22,49 @@ extension FlutterError { } static func chargebeeError(_ error: CBError) -> FlutterError { - return FlutterError(code: error.httpStatusCode.description, - message: error.localizedDescription, - details: "Chargebee error") + return FlutterError(code: "\(errorCode(error: error).rawValue)", + message: error.errorDescription, + details: error.userInfo) } static func purchaseError(_ error: CBPurchaseError) -> FlutterError { - return FlutterError (code: error.skErrorCode.description, - message: error.localizedDescription, - details: "Request failed") + return FlutterError (code: "\(CBNativeError.errorCode(purchaseError: error).rawValue)", + message: error.errorDescription, + details: error.userInfo) } - static func productIdentifierError(_ description: String) -> FlutterError { - return FlutterError (code: "400", - message: description, - details: "Request Failed") + static func productIdentifierError(_ error: Error) -> FlutterError { + if let error = error as? CBPurchaseError { + return FlutterError (code: "\(CBNativeError.invalidCatalogVersion.rawValue)", + message: error.errorDescription, + details: error.userInfo) + } else { + return chargebeeError(error as! CBError) + } } - static func jsonSerializationError(_ description: String) -> FlutterError { - return FlutterError(code: "JSONSerialization", - message: description, - details: "JSON Serialization Error") - + static func jsonSerializationError(_ errorDescription: String) -> FlutterError { + return FlutterError(code: "\(CBNativeError.systemError.rawValue)", + message: errorDescription, + details: ["message": "\(errorDescription)"]) } static func restoreError(_ error: RestoreError) -> FlutterError { - return FlutterError (code: "\(CBNativeError.errorCode(restoreError: error).rawValue)", - message: error.localizedDescription, - details: "Restore Error") + let restoreError = error.asNSError + return FlutterError(code: "\(restoreError.code)", + message: error.errorDescription, + details: restoreError.userInfo) + } + + internal static func errorCode(error: CBError) -> CBNativeError { + switch error.httpStatusCode { + case 401: + return CBNativeError.invalidAPIKey + case 404: + return CBNativeError.invalidSdkConfiguration + default: + return CBNativeError.unknown + } } } @@ -83,7 +98,32 @@ extension CBPurchaseError { } } -enum CBNativeError: Int { +enum CBNativeError: Int, Error { + case unknown = 0 + + // MARK: Chargebee Errors + case invalidAPIKey = 999 + case invalidSdkConfiguration = 1000 + case invalidCatalogVersion = 1001 + case cannotMakePayments = 1002 + case noProductToRestore = 1003 + case invalidResource = 1004 + + // MARK: Store Errors + case invalidOffer = 2001 + case invalidPurchase = 2002 + case invalidSandbox = 2003 + case networkError = 2004 + case paymentFailed = 2005 + case paymentNotAllowed = 2006 + case productNotAvailable = 2007 + case purchaseNotAllowed = 2008 + case purchaseCancelled = 2009 + case storeProblem = 2010 + case invalidReceipt = 2011 + case requestFailed = 2012 + case productPurchasedAlready = 2013 + // MARK: Restore Error case noReceipt = 2014 case refreshReceiptFailed = 2015 @@ -92,25 +132,47 @@ enum CBNativeError: Int { case invalidReceiptData = 2018 case noProductsToRestore = 2019 case serviceError = 2020 + + // MARK: General Errors + case systemError = 3000 + } extension CBNativeError { - static func errorCode(restoreError: RestoreError) -> CBNativeError { - switch restoreError { - case .noReceipt: - return CBNativeError.noReceipt - case .refreshReceiptFailed: - return CBNativeError.refreshReceiptFailed - case .restoreFailed: - return CBNativeError.restoreFailed - case .invalidReceiptURL: - return CBNativeError.invalidReceiptURL - case .invalidReceiptData: - return CBNativeError.invalidReceiptData - case .noProductsToRestore: - return CBNativeError.noProductsToRestore - case .serviceError: - return CBNativeError.serviceError + static func errorCode(purchaseError: CBPurchaseError) -> CBNativeError { + switch purchaseError { + case .productIDNotFound, .productsNotFound, .productNotAvailable: + return CBNativeError.productNotAvailable + case .skRequestFailed: + return CBNativeError.requestFailed + case .cannotMakePayments: + return CBNativeError.cannotMakePayments + case .noProductToRestore: + return CBNativeError.noProductToRestore + case .invalidSDKKey: + return CBNativeError.invalidSdkConfiguration + case .invalidCustomerId: + return CBNativeError.invalidSdkConfiguration + case .invalidCatalogVersion: + return CBNativeError.invalidCatalogVersion + case .userCancelled: + return CBNativeError.purchaseCancelled + case .paymentFailed: + return CBNativeError.paymentFailed + case .invalidPurchase: + return CBNativeError.invalidPurchase + case .invalidClient, .privacyAcknowledgementRequired: + return CBNativeError.purchaseNotAllowed + case .networkConnectionFailed: + return CBNativeError.networkError + case .unknown: + return CBNativeError.unknown + case .paymentNotAllowed: + return CBNativeError.paymentNotAllowed + case .invalidOffer, .invalidPrice, .invalidPromoCode, .invalidPromoOffer: + return CBNativeError.invalidOffer + case .invalidSandbox: + return CBNativeError.invalidSandbox } } } \ No newline at end of file diff --git a/ios/Classes/SwiftChargebeeFlutterSdkPlugin.swift b/ios/Classes/SwiftChargebeeFlutterSdkPlugin.swift index ce336c3..a86c603 100644 --- a/ios/Classes/SwiftChargebeeFlutterSdkPlugin.swift +++ b/ios/Classes/SwiftChargebeeFlutterSdkPlugin.swift @@ -28,8 +28,8 @@ public class SwiftChargebeeFlutterSdkPlugin: NSObject, FlutterPlugin { _result(status.details.status!) case .error(let error): debugPrint("error : \(error)") - _result(FlutterError.chargebeeError(error as CBError)) - + _result(FlutterError.chargebeeError(error)) + } } @@ -177,10 +177,10 @@ public class SwiftChargebeeFlutterSdkPlugin: NSObject, FlutterPlugin { _result(jsonString) } }else { - _result(FlutterError.jsonSerializationError("Serialization Issue")) + _result(FlutterError.jsonSerializationError("JSON serialization error")) } case let .failure(error): - _result(FlutterError.productIdentifierError(error.localizedDescription)) + _result(FlutterError.productIdentifierError(error)) } } }) From da7007bacd92b190610a2d0d390db84b398add30 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Tue, 29 Aug 2023 22:42:40 +0530 Subject: [PATCH 2/3] Error handling improvements --- .../chargebee/flutter/sdk/CBNativeError.kt | 1 - .../flutter/sdk/ChargebeeFlutterSdkPlugin.kt | 10 ++--- ios/Classes/FlutterErrorHandler.swift | 44 +------------------ 3 files changed, 6 insertions(+), 49 deletions(-) diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt index c31fb66..6cfbf5e 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt @@ -7,7 +7,6 @@ enum class CBNativeError(val code: Int) { UNKNOWN(0), // Chargebee Errors - INVALID_API_KEY(999), INVALID_SDK_CONFIGURATION(1000), INVALID_CATALOG_VERSION(1001), diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt index 49a122c..0f88780 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt @@ -135,7 +135,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } is ChargebeeResult.Error -> { onCBError( - "${it.exp.errorCode().code}", + "${CBNativeError.INVALID_SDK_CONFIGURATION.code}", it.exp.messageUserInfo()["message"] as String, it.exp, result ) } @@ -330,7 +330,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } is ChargebeeResult.Error -> { onCBError( - "${it.exp.errorCode().code}", + "${CBNativeError.INVALID_SDK_CONFIGURATION.code}", it.exp.messageUserInfo()["message"] as String, it.exp, result ) } @@ -355,7 +355,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } is ChargebeeResult.Error -> { onCBError( - "${it.exp.errorCode().code}", + "${CBNativeError.INVALID_SDK_CONFIGURATION.code}", it.exp.messageUserInfo()["message"] as String, it.exp, result ) } @@ -379,7 +379,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } is ChargebeeResult.Error -> { onCBError( - "${it.exp.errorCode().code}", + "${CBNativeError.INVALID_SDK_CONFIGURATION.code}", it.exp.messageUserInfo()["message"] as String, it.exp, result ) } @@ -423,7 +423,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar } is ChargebeeResult.Error -> { onCBError( - "${it.exp.errorCode().code}", + "${CBNativeError.INVALID_SDK_CONFIGURATION.code}", it.exp.messageUserInfo()["message"] as String, it.exp, result ) } diff --git a/ios/Classes/FlutterErrorHandler.swift b/ios/Classes/FlutterErrorHandler.swift index 0b185a1..3091dc3 100644 --- a/ios/Classes/FlutterErrorHandler.swift +++ b/ios/Classes/FlutterErrorHandler.swift @@ -22,7 +22,7 @@ extension FlutterError { } static func chargebeeError(_ error: CBError) -> FlutterError { - return FlutterError(code: "\(errorCode(error: error).rawValue)", + return FlutterError(code: "\(CBNativeError.invalidSdkConfiguration.rawValue)", message: error.errorDescription, details: error.userInfo) } @@ -55,54 +55,12 @@ extension FlutterError { message: error.errorDescription, details: restoreError.userInfo) } - - internal static func errorCode(error: CBError) -> CBNativeError { - switch error.httpStatusCode { - case 401: - return CBNativeError.invalidAPIKey - case 404: - return CBNativeError.invalidSdkConfiguration - default: - return CBNativeError.unknown - } - } - -} - -extension CBPurchaseError { - public var skErrorCode: Int { - switch self { - case .unknown: return 0 - case .invalidClient: return 1 - case .userCancelled: return 2 - case .paymentFailed: return 3 - case .paymentNotAllowed: return 4 - case .productNotAvailable: return 5 - case .cannotMakePayments: return 6 - case .networkConnectionFailed: return 7 - case .productsNotFound: return 8 - case .privacyAcknowledgementRequired: return 9 - case .invalidOffer: return 11 - case .invalidPromoCode: return 12 - case .invalidPrice: return 14 - case .invalidPromoOffer: return 13 - case .invalidSandbox: return 8 - case .productIDNotFound: return 10 - case .skRequestFailed:return 15 - case .noProductToRestore:return 16 - case .invalidSDKKey:return 17 - case .invalidCustomerId:return 18 - case .invalidCatalogVersion:return 19 - case .invalidPurchase:return 20 - } - } } enum CBNativeError: Int, Error { case unknown = 0 // MARK: Chargebee Errors - case invalidAPIKey = 999 case invalidSdkConfiguration = 1000 case invalidCatalogVersion = 1001 case cannotMakePayments = 1002 From d073b0a0190c4b8c0eb0f9cc9d706bf4e9dd7347 Mon Sep 17 00:00:00 2001 From: cb-amutha Date: Fri, 1 Sep 2023 18:08:12 +0530 Subject: [PATCH 3/3] Added dart error handling and updated sdk version to support enhanced enum values to pass --- .../flutter/sdk/CBExceptionExtensions.kt | 10 +- .../chargebee/flutter/sdk/CBNativeError.kt | 9 +- .../flutter/sdk/ChargebeeFlutterSdkPlugin.kt | 46 ++++++- example/lib/main.dart | 116 ++++++++++++++---- ios/Classes/FlutterErrorHandler.swift | 5 +- lib/chargebee_flutter.dart | 1 + lib/src/chargebee_error.dart | 43 +++++++ pubspec.yaml | 2 +- 8 files changed, 188 insertions(+), 44 deletions(-) create mode 100644 lib/src/chargebee_error.dart diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt index 954de5b..4a7298f 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBExceptionExtensions.kt @@ -15,12 +15,4 @@ fun CBException.messageUserInfo(): MutableMap { messageUserInfo["message"] = this.message } return messageUserInfo -} - -fun CBException.errorCode(): CBNativeError { - return when (this.httpStatusCode) { - 401 -> CBNativeError.INVALID_API_KEY - 404 -> CBNativeError.INVALID_SDK_CONFIGURATION - else -> CBNativeError.UNKNOWN - } -} +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt index 6cfbf5e..a47a0d6 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/CBNativeError.kt @@ -9,6 +9,7 @@ enum class CBNativeError(val code: Int) { // Chargebee Errors INVALID_SDK_CONFIGURATION(1000), INVALID_CATALOG_VERSION(1001), + INVALID_RESOURCE(1003) // Store Errors INVALID_OFFER(2001), @@ -23,7 +24,13 @@ enum class CBNativeError(val code: Int) { STORE_PROBLEM(2010), INVALID_RECEIPT(2011), REQUEST_FAILED(2012), - PRODUCT_PURCHASED_ALREADY(2013); + PRODUCT_PURCHASED_ALREADY(2013), + + // MARK: Restore Error + NO_PRODUCTS_TO_RESTORE(2019), + SERVICE_ERROR(2020), + + SYSTEM_ERROR(3000); companion object { fun billingResponseCode(code: Int): CBNativeError { diff --git a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt index 0f88780..7bf130b 100644 --- a/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/chargebee/flutter/sdk/ChargebeeFlutterSdkPlugin.kt @@ -174,6 +174,13 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar }) } + val purchasesNotFoundToRestore = CBException( + ErrorDetail( + message = "Products not found to restore.", + httpStatusCode = CBNativeError.NO_PRODUCTS_TO_RESTORE.code + ) + ) + private fun restorePurchases(resultCallback: Result, queryParams: Map?) { val includeInactivePurchases = queryParams?.get("includeInactivePurchases") as Boolean CBPurchase.restorePurchases( @@ -181,10 +188,20 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar includeInactivePurchases, object : CBCallback.RestorePurchaseCallback { override fun onSuccess(result: List) { - val restoreSubscription = result.map { subscription -> - Gson().toJson(subscription.toMap()) + if (result.isNotEmpty()) { + val restoreSubscription = result.map { subscription -> + Gson().toJson(subscription.toMap()) + } + resultCallback.success(restoreSubscription) + } else { + val messageUserInfo = purchasesNotFoundToRestore.messageUserInfo() + onCBError( + "${purchasesNotFoundToRestore.httpStatusCode}", + messageUserInfo["message"] as String, + purchasesNotFoundToRestore, + resultCallback + ) } - resultCallback.success(restoreSubscription) } override fun onError(error: CBException) { @@ -259,6 +276,7 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar httpStatusCode = CBNativeError.PRODUCT_NOT_AVAILABLE.code ) ) + private fun purchaseNonSubscriptionProduct(args: Map, callback: Result) { val customer = CBCustomer( args["customerId"] as String, @@ -319,14 +337,30 @@ class ChargebeeFlutterSdkPlugin : FlutterPlugin, MethodCallHandler, ActivityAwar return Gson().toJson(subscriptionStatus) } + val subscriptionNotFound = CBException( + ErrorDetail( + message = "Subscription not found", + httpStatusCode = CBNativeError.INVALID_SDK_CONFIGURATION.code + ) + ) + private fun retrieveSubscriptions(queryParams: Map = mapOf(), result: Result) { Chargebee.retrieveSubscriptions(queryParams) { when (it) { is ChargebeeResult.Success -> { val listSubscriptions = (it.data as CBSubscription).list - val jsonString = Gson().toJson(listSubscriptions) - - result.success(jsonString) + if (listSubscriptions.isNotEmpty()){ + val jsonString = Gson().toJson(listSubscriptions) + result.success(jsonString) + } else { + val messageUserInfo = subscriptionNotFound.messageUserInfo() + onCBError( + "${subscriptionNotFound.httpStatusCode}", + messageUserInfo["message"] as String, + subscriptionNotFound, + result + ) + } } is ChargebeeResult.Error -> { onCBError( diff --git a/example/lib/main.dart b/example/lib/main.dart index 2524a54..2659134 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -214,8 +214,14 @@ class _MyHomePageState extends State { try { await Chargebee.configure(siteName, apiKey, iosSdkKey, androidSdkKey); } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } } } @@ -239,9 +245,14 @@ class _MyHomePageState extends State { _showDialog(context, 'Items not available to buy'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); - + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.productNotAvailable) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -272,12 +283,20 @@ class _MyHomePageState extends State { _showDialog(context, 'Product Ids not avilable in chargebee'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); - if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidCatalogVersion) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } } } @@ -295,8 +314,14 @@ class _MyHomePageState extends State { _showDialog(context, 'Subscription not found in Chargebee System'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -328,8 +353,14 @@ class _MyHomePageState extends State { _showDialog(context, 'Entitlements not found in system'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -361,8 +392,14 @@ class _MyHomePageState extends State { _showDialog(context, 'Plans not available in chargebee'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -396,9 +433,14 @@ class _MyHomePageState extends State { _showDialog(context, 'Items not available in chargebee'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); - + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.invalidSDKConfiguration) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -426,9 +468,17 @@ class _MyHomePageState extends State { _showDialog(context, 'Purchase not found to restore'); } } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); - + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.noProductToRestore) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else if (errorCode == ChargebeeError.noReceipt) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } if (mProgressBarUtil.isProgressBarShowing()) { mProgressBarUtil.hideProgressDialog(); } @@ -454,8 +504,17 @@ class _MyHomePageState extends State { final prefs = await SharedPreferences.getInstance(); prefs.remove('productId'); } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.productNotAvailable) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else if (errorCode == ChargebeeError.invalidReceipt) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } mProgressBarUtil.hideProgressDialog(); } } @@ -473,8 +532,17 @@ class _MyHomePageState extends State { productId, productType, customer); debugPrint('subscription result : $result'); } on PlatformException catch (e) { - debugPrint( - 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + final errorCode = ChargebeeErrorHelper.getErrorCode(e); + if (errorCode == ChargebeeError.productNotAvailable) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else if (errorCode == ChargebeeError.invalidReceipt) { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } else { + debugPrint( + 'Error Message: ${e.message}, Error Details: ${e.details}, Error Code: ${e.code}'); + } mProgressBarUtil.hideProgressDialog(); } } diff --git a/ios/Classes/FlutterErrorHandler.swift b/ios/Classes/FlutterErrorHandler.swift index 3091dc3..e60992b 100644 --- a/ios/Classes/FlutterErrorHandler.swift +++ b/ios/Classes/FlutterErrorHandler.swift @@ -64,8 +64,7 @@ enum CBNativeError: Int, Error { case invalidSdkConfiguration = 1000 case invalidCatalogVersion = 1001 case cannotMakePayments = 1002 - case noProductToRestore = 1003 - case invalidResource = 1004 + case invalidResource = 1003 // MARK: Store Errors case invalidOffer = 2001 @@ -88,7 +87,7 @@ enum CBNativeError: Int, Error { case restoreFailed = 2016 case invalidReceiptURL = 2017 case invalidReceiptData = 2018 - case noProductsToRestore = 2019 + case noProductToRestore = 2019 case serviceError = 2020 // MARK: General Errors diff --git a/lib/chargebee_flutter.dart b/lib/chargebee_flutter.dart index d4ada93..73b7458 100644 --- a/lib/chargebee_flutter.dart +++ b/lib/chargebee_flutter.dart @@ -1,4 +1,5 @@ export 'src/chargebee.dart'; +export 'src/chargebee_error.dart'; export 'src/models/customer.dart'; export 'src/models/item.dart'; export 'src/models/plan.dart'; diff --git a/lib/src/chargebee_error.dart b/lib/src/chargebee_error.dart new file mode 100644 index 0000000..ba2cf3e --- /dev/null +++ b/lib/src/chargebee_error.dart @@ -0,0 +1,43 @@ +import 'package:flutter/services.dart'; + +enum ChargebeeError { + unknown(0), + invalidSDKConfiguration(1000), + invalidCatalogVersion(1001), + cannotMakePayments(1002), + invalidResource(1003), + invalidOffer(2001), + invalidPurchase(2002), + invalidSandbox(2003), + networkError(2004), + paymentFailed(2005), + paymentNotAllowed(2006), + productNotAvailable(2007), + purchaseNotAllowed(2008), + purchaseCancelled(2009), + storeProblem(2010), + invalidReceipt(2011), + requestFailed(2012), + productPurchasedAlready(2013), + noReceipt(2014), + refreshReceiptFailed(2015), + restoreFailed(2016), + invalidReceiptURL(2017), + invalidReceiptData(2018), + noProductToRestore(2019), + serviceError(2020), + systemError(3000); + + final int errorCode; + const ChargebeeError(this.errorCode); +} + +class ChargebeeErrorHelper { + static ChargebeeError getErrorCode(PlatformException e) { + final errorCode = int.parse(e.code); + if (errorCode >= ChargebeeError.values.length) { + return ChargebeeError.unknown; + } + return ChargebeeError.values[errorCode]; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index f456124..5f7e5c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: 'https://chargebee.com' repository: 'https://github.com/chargebee/chargebee-flutter' environment: - sdk: ">=2.15.1 <3.0.0" + sdk: ">=2.17.1 <4.0.0" flutter: ">=2.8.1" dependencies: