From f99a25cdf86f2a63307126efc59c0a664a1e2dfa Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 8 Oct 2024 10:40:44 +0200 Subject: [PATCH 1/2] Add lint rule to prevent the use of JSONObject's optString, optBoolean, optInt, optLong and optDouble These functions are non nullable and return arbitrary default values which could cause unexpected bugs. We should use our internal extension functions that return null instead COAND-1003 --- .../adyen/checkout/lint/JSONOptFunctions.kt | 73 +++++++ .../adyen/checkout/lint/LintIssueRegistry.kt | 1 + .../checkout/lint/JSONOptFunctionsTest.kt | 193 ++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 lint/src/main/java/com/adyen/checkout/lint/JSONOptFunctions.kt create mode 100644 lint/src/test/java/com/adyen/checkout/lint/JSONOptFunctionsTest.kt diff --git a/lint/src/main/java/com/adyen/checkout/lint/JSONOptFunctions.kt b/lint/src/main/java/com/adyen/checkout/lint/JSONOptFunctions.kt new file mode 100644 index 0000000000..de8ca2af51 --- /dev/null +++ b/lint/src/main/java/com/adyen/checkout/lint/JSONOptFunctions.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by oscars on 14/8/2024. + */ + +package com.adyen.checkout.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression + +internal val JSON_OPT_FUNCTIONS_ISSUE = Issue.create( + id = "JSONOptFunctions", + briefDescription = "JSONObject \"opt\" functions should not be used directly", + explanation = """ + JSONObject's optString, optBoolean, optInt, optLong and optDouble functions are non nullable and return + arbitrary default values which could cause unexpected bugs. Use an internal extension function that returns + null instead. + """.trimIndent().replace(Regex("(\n*)\n"), "$1"), + implementation = Implementation(JSONOptFunctionsDetector::class.java, Scope.JAVA_FILE_SCOPE), + category = Category.CUSTOM_LINT_CHECKS, + priority = 5, + severity = Severity.ERROR, + androidSpecific = true, +) + +internal class JSONOptFunctionsDetector : Detector(), Detector.UastScanner { + + override fun getApplicableMethodNames(): List = listOf( + "optString", "optBoolean", "optInt", "optLong", "optDouble", + ) + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (!context.evaluator.isMemberInClass(method, "org.json.JSONObject")) return + val methodName = node.methodIdentifier?.name.orEmpty() + val replacement = when (methodName) { + "optString" -> "getStringOrNull" + "optBoolean" -> "getBooleanOrNull" + "optInt" -> "getIntOrNull" + "optLong" -> "getLongOrNull" + "optDouble" -> "getDoubleOrNull" + else -> return + } + context.report( + JSON_OPT_FUNCTIONS_ISSUE, + node, + context.getLocation(node.methodIdentifier), + "JSONObject.$methodName should not be used directly. Use an internal extension function instead.", + fix() + .alternatives( + // this replacement does not compile when 2 arguments are passed to the opt functions instead of 1. + // however, it's easy for the developer to fix it manually + fix() + .replace() + .all() + .with(replacement) + .imports("com.adyen.checkout.core.internal.data.model.$replacement") + .reformat(true) + .shortenNames() + .build(), + ), + ) + } +} diff --git a/lint/src/main/java/com/adyen/checkout/lint/LintIssueRegistry.kt b/lint/src/main/java/com/adyen/checkout/lint/LintIssueRegistry.kt index 664187418c..7597f92cf5 100644 --- a/lint/src/main/java/com/adyen/checkout/lint/LintIssueRegistry.kt +++ b/lint/src/main/java/com/adyen/checkout/lint/LintIssueRegistry.kt @@ -22,5 +22,6 @@ internal class LintIssueRegistry : IssueRegistry() { NOT_ADYEN_LOG_ISSUE, OBJECT_IN_PUBLIC_SEALED_CLASS_ISSUE, TEXT_IN_LAYOUT_XML_ISSUE, + JSON_OPT_FUNCTIONS_ISSUE, ) } diff --git a/lint/src/test/java/com/adyen/checkout/lint/JSONOptFunctionsTest.kt b/lint/src/test/java/com/adyen/checkout/lint/JSONOptFunctionsTest.kt new file mode 100644 index 0000000000..eb9142d568 --- /dev/null +++ b/lint/src/test/java/com/adyen/checkout/lint/JSONOptFunctionsTest.kt @@ -0,0 +1,193 @@ +package com.adyen.checkout.lint + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest.java +import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin +import com.android.tools.lint.checks.infrastructure.TestLintTask.lint +import org.junit.Test + +class JSONOptFunctionsTest { + + @Test + fun whenJSONObjectOptFunctionsAreUsed_thenIssueIsDetected() { + lint() + .files( + JSON_OBJECT_STUB, + kotlin( + """ + package test + + import org.json.JSONObject + + class SomeClass { + fun someFun(jsonObject: JSONObject) { + val stringWithoutFallback = jsonObject.optString("key") + val stringWithFallback = jsonObject.optString("key", "fallback") + val intWithoutFallback = jsonObject.optInt("key") + val intWithFallback = jsonObject.optInt("key", 1) + val doubleWithoutFallback = jsonObject.optDouble("key") + val doubleWithFallback = jsonObject.optDouble("key", 1.0) + val longWithoutFallback = jsonObject.optLong("key") + val longWithFallback = jsonObject.optLong("key", 1L) + val booleanWithoutFallback = jsonObject.optBoolean("key") + val booleanWithFallback = jsonObject.optBoolean("key", true) + } + } + """.trimIndent(), + ), + ) + .issues(JSON_OPT_FUNCTIONS_ISSUE) + .allowMissingSdk() + .run() + .expect( + """ +src/test/SomeClass.kt:7: Error: JSONObject.optString should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val stringWithoutFallback = jsonObject.optString("key") + ~~~~~~~~~ +src/test/SomeClass.kt:8: Error: JSONObject.optString should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val stringWithFallback = jsonObject.optString("key", "fallback") + ~~~~~~~~~ +src/test/SomeClass.kt:9: Error: JSONObject.optInt should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val intWithoutFallback = jsonObject.optInt("key") + ~~~~~~ +src/test/SomeClass.kt:10: Error: JSONObject.optInt should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val intWithFallback = jsonObject.optInt("key", 1) + ~~~~~~ +src/test/SomeClass.kt:11: Error: JSONObject.optDouble should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val doubleWithoutFallback = jsonObject.optDouble("key") + ~~~~~~~~~ +src/test/SomeClass.kt:12: Error: JSONObject.optDouble should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val doubleWithFallback = jsonObject.optDouble("key", 1.0) + ~~~~~~~~~ +src/test/SomeClass.kt:13: Error: JSONObject.optLong should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val longWithoutFallback = jsonObject.optLong("key") + ~~~~~~~ +src/test/SomeClass.kt:14: Error: JSONObject.optLong should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val longWithFallback = jsonObject.optLong("key", 1L) + ~~~~~~~ +src/test/SomeClass.kt:15: Error: JSONObject.optBoolean should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val booleanWithoutFallback = jsonObject.optBoolean("key") + ~~~~~~~~~~ +src/test/SomeClass.kt:16: Error: JSONObject.optBoolean should not be used directly. Use an internal extension function instead. [JSONOptFunctions] + val booleanWithFallback = jsonObject.optBoolean("key", true) + ~~~~~~~~~~ +10 errors, 0 warnings + """.trimIndent(), + ) + .expectFixDiffs( + """ +Fix for src/test/SomeClass.kt line 7: Replace with getStringOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getStringOrNull +@@ -7 +8 +- val stringWithoutFallback = jsonObject.optString("key") ++ val stringWithoutFallback = jsonObject.getStringOrNull("key") +Fix for src/test/SomeClass.kt line 8: Replace with getStringOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getStringOrNull +@@ -8 +9 +- val stringWithFallback = jsonObject.optString("key", "fallback") ++ val stringWithFallback = jsonObject.getStringOrNull("key", "fallback") +Fix for src/test/SomeClass.kt line 9: Replace with getIntOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getIntOrNull +@@ -9 +10 +- val intWithoutFallback = jsonObject.optInt("key") ++ val intWithoutFallback = jsonObject.getIntOrNull("key") +Fix for src/test/SomeClass.kt line 10: Replace with getIntOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getIntOrNull +@@ -10 +11 +- val intWithFallback = jsonObject.optInt("key", 1) ++ val intWithFallback = jsonObject.getIntOrNull("key", 1) +Fix for src/test/SomeClass.kt line 11: Replace with getDoubleOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getDoubleOrNull +@@ -11 +12 +- val doubleWithoutFallback = jsonObject.optDouble("key") ++ val doubleWithoutFallback = jsonObject.getDoubleOrNull("key") +Fix for src/test/SomeClass.kt line 12: Replace with getDoubleOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getDoubleOrNull +@@ -12 +13 +- val doubleWithFallback = jsonObject.optDouble("key", 1.0) ++ val doubleWithFallback = jsonObject.getDoubleOrNull("key", 1.0) +Fix for src/test/SomeClass.kt line 13: Replace with getLongOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getLongOrNull +@@ -13 +14 +- val longWithoutFallback = jsonObject.optLong("key") ++ val longWithoutFallback = jsonObject.getLongOrNull("key") +Fix for src/test/SomeClass.kt line 14: Replace with getLongOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getLongOrNull +@@ -14 +15 +- val longWithFallback = jsonObject.optLong("key", 1L) ++ val longWithFallback = jsonObject.getLongOrNull("key", 1L) +Fix for src/test/SomeClass.kt line 15: Replace with getBooleanOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getBooleanOrNull +@@ -15 +16 +- val booleanWithoutFallback = jsonObject.optBoolean("key") ++ val booleanWithoutFallback = jsonObject.getBooleanOrNull("key") +Fix for src/test/SomeClass.kt line 16: Replace with getBooleanOrNull: +@@ -3 +3 ++ import com.adyen.checkout.core.internal.data.model.getBooleanOrNull +@@ -16 +17 +- val booleanWithFallback = jsonObject.optBoolean("key", true) ++ val booleanWithFallback = jsonObject.getBooleanOrNull("key", true) + """.trimIndent(), + ) + } + + companion object { + + private val JSON_OBJECT_STUB = java( + """ + package org.json; + + public class JSONObject { + + @NonNull public String optString(@Nullable String name) { + return "stub"; + } + + @NonNull public String optString(@Nullable String name, @NonNull String fallback) { + return "stub"; + } + + public int optInt(@Nullable String name) { + return 0; + } + + public int optInt(@Nullable String name, int fallback) { + return 0; + } + + public long optLong(@Nullable String name) { + return 0L; + } + + public long optLong(@Nullable String name, long fallback) { + return 0L; + } + + public double optDouble(@Nullable String name) { + return Double.NaN; + } + + public double optDouble(@Nullable String name, double fallback) { + return Double.NaN; + } + + public boolean optBoolean(@Nullable String name) { + return false; + } + + public boolean optBoolean(@Nullable String name, boolean fallback) { + return false; + } + } + """.trimIndent(), + ) + } +} From feeffe5418e0a0290974e6039d4e67e183f9fd4a Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 8 Oct 2024 10:42:16 +0200 Subject: [PATCH 2/2] Replace JSONObject's opt functions with nullable extensions The opt functions are non nullable and return arbitrary default values which could cause unexpected bugs COAND-1003 --- .../internal/data/model/ErrorResponseBody.kt | 10 ++++---- .../adyen/checkout/components/core/Amount.kt | 3 ++- .../checkout/components/core/Installments.kt | 3 ++- .../adyen/checkout/components/core/Issuer.kt | 3 ++- .../checkout/components/core/OrderRequest.kt | 5 ++-- .../checkout/components/core/OrderResponse.kt | 9 ++++--- .../components/core/PaymentComponentData.kt | 16 ++++++------ .../checkout/components/core/action/Action.kt | 6 ++--- .../components/core/action/TwintSdkData.kt | 3 ++- .../internal/data/model/PublicKeyResponse.kt | 3 ++- .../googlepay/BillingAddressParameters.kt | 3 ++- .../googlepay/ShippingAddressParameters.kt | 3 ++- .../internal/data/model/CardParameters.kt | 13 +++++----- .../data/model/IsReadyToPayRequestModel.kt | 13 ++++++---- .../data/model/PaymentDataRequestModel.kt | 25 +++++++++++-------- .../googlepay/internal/util/GooglePayUtils.kt | 2 +- .../internal/ui/DefaultRedirectDelegate.kt | 3 ++- .../checkout/sessions/core/SessionModel.kt | 5 ++-- .../core/SessionSetupConfiguration.kt | 2 +- .../core/SessionSetupInstallmentOptions.kt | 3 ++- .../sessions/core/SessionSetupResponse.kt | 11 ++++---- .../data/model/SessionBalanceRequest.kt | 3 ++- .../data/model/SessionBalanceResponse.kt | 3 ++- .../data/model/SessionCancelOrderRequest.kt | 3 ++- .../data/model/SessionCancelOrderResponse.kt | 5 ++-- .../data/model/SessionDetailsRequest.kt | 5 ++-- .../data/model/SessionDetailsResponse.kt | 9 ++++--- .../data/model/SessionDisableTokenRequest.kt | 5 ++-- .../data/model/SessionDisableTokenResponse.kt | 3 ++- .../data/model/SessionOrderRequest.kt | 3 ++- .../data/model/SessionOrderResponse.kt | 7 +++--- .../data/model/SessionPaymentsRequest.kt | 3 ++- .../data/model/SessionPaymentsResponse.kt | 9 ++++--- .../data/model/SessionSetupRequest.kt | 3 ++- 34 files changed, 119 insertions(+), 86 deletions(-) diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/ErrorResponseBody.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/ErrorResponseBody.kt index ece6673222..93dfeda372 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/ErrorResponseBody.kt +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/ErrorResponseBody.kt @@ -51,11 +51,11 @@ data class ErrorResponseBody( override fun deserialize(jsonObject: JSONObject): ErrorResponseBody { return try { ErrorResponseBody( - status = jsonObject.optInt(STATUS), - errorCode = jsonObject.optString(ERROR_CODE), - message = jsonObject.optString(MESSAGE), - errorType = jsonObject.optString(ERROR_TYPE), - pspReference = jsonObject.optString(PSP_REFERENCE), + status = jsonObject.getIntOrNull(STATUS), + errorCode = jsonObject.getStringOrNull(ERROR_CODE), + message = jsonObject.getStringOrNull(MESSAGE), + errorType = jsonObject.getStringOrNull(ERROR_TYPE), + pspReference = jsonObject.getStringOrNull(PSP_REFERENCE), ) } catch (e: JSONException) { throw ModelSerializationException(ErrorResponseBody::class.java, e) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/Amount.kt b/components-core/src/main/java/com/adyen/checkout/components/core/Amount.kt index f92adf12c4..5dc27282ed 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/Amount.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/Amount.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core import com.adyen.checkout.components.core.internal.util.EMPTY_VALUE import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getLongOrNull import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException @@ -41,7 +42,7 @@ data class Amount( override fun deserialize(jsonObject: JSONObject): Amount { return Amount( currency = jsonObject.getStringOrNull(CURRENCY), - value = jsonObject.optLong(VALUE, EMPTY_VALUE), + value = jsonObject.getLongOrNull(VALUE) ?: EMPTY_VALUE, ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/Installments.kt b/components-core/src/main/java/com/adyen/checkout/components/core/Installments.kt index e3428b4034..a70e87ebf1 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/Installments.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/Installments.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getIntOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -41,7 +42,7 @@ data class Installments( return try { Installments( plan = jsonObject.getString(PLAN), - value = jsonObject.optInt(VALUE, 1) + value = jsonObject.getIntOrNull(VALUE) ?: 1, ) } catch (e: JSONException) { throw ModelSerializationException(Installments::class.java, e) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/Issuer.kt b/components-core/src/main/java/com/adyen/checkout/components/core/Issuer.kt index 57161575fc..1f323099fb 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/Issuer.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/Issuer.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException @@ -44,7 +45,7 @@ data class Issuer( return Issuer( id = jsonObject.getStringOrNull(ID), name = jsonObject.getStringOrNull(NAME), - isDisabled = jsonObject.optBoolean(DISABLED, false), + isDisabled = jsonObject.getBooleanOrNull(DISABLED) ?: false, ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/OrderRequest.kt b/components-core/src/main/java/com/adyen/checkout/components/core/OrderRequest.kt index 734e739014..c78d7c8224 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/OrderRequest.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/OrderRequest.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -38,8 +39,8 @@ data class OrderRequest constructor( override fun deserialize(jsonObject: JSONObject): OrderRequest { return OrderRequest( - pspReference = jsonObject.optString(PSP_REFERENCE, ""), - orderData = jsonObject.optString(ORDER_DATA, "") + pspReference = jsonObject.getStringOrNull(PSP_REFERENCE).orEmpty(), + orderData = jsonObject.getStringOrNull(ORDER_DATA).orEmpty(), ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/OrderResponse.kt b/components-core/src/main/java/com/adyen/checkout/components/core/OrderResponse.kt index 7eb2b8131c..d92b40ec85 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/OrderResponse.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/OrderResponse.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -38,7 +39,7 @@ data class OrderResponse( putOpt(AMOUNT, ModelUtils.serializeOpt(modelObject.amount, Amount.SERIALIZER)) putOpt( REMAINING_AMOUNT, - ModelUtils.serializeOpt(modelObject.remainingAmount, Amount.SERIALIZER) + ModelUtils.serializeOpt(modelObject.remainingAmount, Amount.SERIALIZER), ) } catch (e: JSONException) { throw ModelSerializationException(OrderResponse::class.java, e) @@ -48,12 +49,12 @@ data class OrderResponse( override fun deserialize(jsonObject: JSONObject): OrderResponse { return OrderResponse( - pspReference = jsonObject.optString(PSP_REFERENCE, ""), - orderData = jsonObject.optString(ORDER_DATA, ""), + pspReference = jsonObject.getStringOrNull(PSP_REFERENCE).orEmpty(), + orderData = jsonObject.getStringOrNull(ORDER_DATA).orEmpty(), amount = ModelUtils.deserializeOpt(jsonObject.optJSONObject(AMOUNT), Amount.SERIALIZER), remainingAmount = ModelUtils.deserializeOpt( jsonObject.optJSONObject(REMAINING_AMOUNT), - Amount.SERIALIZER + Amount.SERIALIZER, ), ) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentComponentData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentComponentData.kt index aeb4b7a57a..ba6bd96a6a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentComponentData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentComponentData.kt @@ -12,6 +12,8 @@ import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOpt import com.adyen.checkout.core.internal.data.model.ModelUtils.serializeOpt +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -89,17 +91,17 @@ data class PaymentComponentData( ), order = deserializeOpt(jsonObject.optJSONObject(ORDER), OrderRequest.SERIALIZER), amount = deserializeOpt(jsonObject.optJSONObject(AMOUNT), Amount.SERIALIZER), - storePaymentMethod = jsonObject.optBoolean(STORE_PAYMENT_METHOD), - shopperReference = jsonObject.optString(SHOPPER_REFERENCE), + storePaymentMethod = jsonObject.getBooleanOrNull(STORE_PAYMENT_METHOD), + shopperReference = jsonObject.getStringOrNull(SHOPPER_REFERENCE), billingAddress = deserializeOpt(jsonObject.optJSONObject(BILLING_ADDRESS), Address.SERIALIZER), deliveryAddress = deserializeOpt(jsonObject.optJSONObject(DELIVERY_ADDRESS), Address.SERIALIZER), shopperName = deserializeOpt(jsonObject.optJSONObject(SHOPPER_NAME), ShopperName.SERIALIZER), - telephoneNumber = jsonObject.optString(TELEPHONE_NUMBER), - shopperEmail = jsonObject.optString(SHOPPER_EMAIL), - dateOfBirth = jsonObject.optString(DATE_OF_BIRTH), - socialSecurityNumber = jsonObject.optString(SOCIAL_SECURITY_NUMBER), + telephoneNumber = jsonObject.getStringOrNull(TELEPHONE_NUMBER), + shopperEmail = jsonObject.getStringOrNull(SHOPPER_EMAIL), + dateOfBirth = jsonObject.getStringOrNull(DATE_OF_BIRTH), + socialSecurityNumber = jsonObject.getStringOrNull(SOCIAL_SECURITY_NUMBER), installments = deserializeOpt(jsonObject.optJSONObject(INSTALLMENTS), Installments.SERIALIZER), - supportNativeRedirect = jsonObject.optBoolean(SUPPORT_NATIVE_REDIRECT), + supportNativeRedirect = jsonObject.getBooleanOrNull(SUPPORT_NATIVE_REDIRECT), ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/action/Action.kt b/components-core/src/main/java/com/adyen/checkout/components/core/action/Action.kt index 05e025517a..6d79d16f3b 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/action/Action.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/action/Action.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core.action import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import org.json.JSONObject /** @@ -41,10 +42,7 @@ abstract class Action : ModelObject() { } override fun deserialize(jsonObject: JSONObject): Action { - val actionType = jsonObject.optString(TYPE) - if (actionType.isEmpty()) { - throw CheckoutException("Action type not found") - } + val actionType = jsonObject.getStringOrNull(TYPE) ?: throw CheckoutException("Action type not found") val serializer = getChildSerializer(actionType) return serializer.deserialize(jsonObject) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt index b62db89c2d..547f0a6435 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core.action import com.adyen.checkout.core.exception.ModelSerializationException +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -41,7 +42,7 @@ data class TwintSdkData( return try { TwintSdkData( token = jsonObject.getString(TOKEN), - isStored = jsonObject.optBoolean(IS_STORED), + isStored = jsonObject.getBooleanOrNull(IS_STORED) ?: false, ) } catch (e: JSONException) { throw ModelSerializationException(TwintSdkData::class.java, e) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/PublicKeyResponse.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/PublicKeyResponse.kt index 775e7d766f..8884c44a59 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/PublicKeyResponse.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/PublicKeyResponse.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.data.model import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -38,7 +39,7 @@ internal data class PublicKeyResponse( override fun deserialize(jsonObject: JSONObject): PublicKeyResponse { return try { PublicKeyResponse( - publicKey = jsonObject.optString(PUBLIC_KEY) + publicKey = jsonObject.getStringOrNull(PUBLIC_KEY).orEmpty(), ) } catch (e: JSONException) { throw ModelSerializationException(PublicKeyResponse::class.java, e) diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/BillingAddressParameters.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/BillingAddressParameters.kt index 03ef5e2662..1673aae4de 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/BillingAddressParameters.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/BillingAddressParameters.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.googlepay import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException @@ -49,7 +50,7 @@ data class BillingAddressParameters( override fun deserialize(jsonObject: JSONObject) = BillingAddressParameters( format = jsonObject.getStringOrNull(FORMAT), - isPhoneNumberRequired = jsonObject.optBoolean(PHONE_NUMBER_REQUIRED), + isPhoneNumberRequired = jsonObject.getBooleanOrNull(PHONE_NUMBER_REQUIRED) ?: false, ) } } diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/ShippingAddressParameters.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/ShippingAddressParameters.kt index 133d6f2b5a..04722a1b7a 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/ShippingAddressParameters.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/ShippingAddressParameters.kt @@ -11,6 +11,7 @@ import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.JsonUtils.parseOptStringList import com.adyen.checkout.core.internal.data.model.JsonUtils.serializeOptStringList import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -50,7 +51,7 @@ data class ShippingAddressParameters( override fun deserialize(jsonObject: JSONObject) = ShippingAddressParameters( allowedCountryCodes = parseOptStringList(jsonObject.optJSONArray(ALLOWED_COUNTRY_CODES)), - isPhoneNumberRequired = jsonObject.optBoolean(PHONE_NUMBER_REQUIRED), + isPhoneNumberRequired = jsonObject.getBooleanOrNull(PHONE_NUMBER_REQUIRED) ?: false, ) } } diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/CardParameters.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/CardParameters.kt index cf72f8d6c2..629fb15a84 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/CardParameters.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/CardParameters.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.core.internal.data.model.JsonUtils.serializeOptStringL import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOpt import com.adyen.checkout.core.internal.data.model.ModelUtils.serializeOpt +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull import com.adyen.checkout.googlepay.BillingAddressParameters import kotlinx.parcelize.Parcelize import org.json.JSONException @@ -51,7 +52,7 @@ internal data class CardParameters( putOpt(BILLING_ADDRESS_REQUIRED, modelObject.isBillingAddressRequired) putOpt( BILLING_ADDRESS_PARAMETERS, - serializeOpt(modelObject.billingAddressParameters, BillingAddressParameters.SERIALIZER) + serializeOpt(modelObject.billingAddressParameters, BillingAddressParameters.SERIALIZER), ) } } catch (e: JSONException) { @@ -62,13 +63,13 @@ internal data class CardParameters( override fun deserialize(jsonObject: JSONObject) = CardParameters( allowedAuthMethods = parseOptStringList(jsonObject.optJSONArray(ALLOWED_AUTH_METHODS)), allowedCardNetworks = parseOptStringList(jsonObject.optJSONArray(ALLOWED_CARD_NETWORKS)), - isAllowPrepaidCards = jsonObject.optBoolean(ALLOW_PREPAID_CARDS), - isAllowCreditCards = jsonObject.optBoolean(ALLOW_CREDIT_CARDS), - isAssuranceDetailsRequired = jsonObject.optBoolean(ASSURANCE_DETAILS_REQUIRED), - isBillingAddressRequired = jsonObject.optBoolean(BILLING_ADDRESS_REQUIRED), + isAllowPrepaidCards = jsonObject.getBooleanOrNull(ALLOW_PREPAID_CARDS) ?: false, + isAllowCreditCards = jsonObject.getBooleanOrNull(ALLOW_CREDIT_CARDS), + isAssuranceDetailsRequired = jsonObject.getBooleanOrNull(ASSURANCE_DETAILS_REQUIRED), + isBillingAddressRequired = jsonObject.getBooleanOrNull(BILLING_ADDRESS_REQUIRED) ?: false, billingAddressParameters = deserializeOpt( jsonObject.optJSONObject(BILLING_ADDRESS_PARAMETERS), - BillingAddressParameters.SERIALIZER + BillingAddressParameters.SERIALIZER, ), ) } diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/IsReadyToPayRequestModel.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/IsReadyToPayRequestModel.kt index b383e211ad..c1f7bdc573 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/IsReadyToPayRequestModel.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/IsReadyToPayRequestModel.kt @@ -11,6 +11,8 @@ import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOptList import com.adyen.checkout.core.internal.data.model.ModelUtils.serializeOptList +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull +import com.adyen.checkout.core.internal.data.model.getIntOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -38,7 +40,7 @@ internal data class IsReadyToPayRequestModel( putOpt(API_VERSION_MINOR, modelObject.apiVersionMinor) putOpt( ALLOWED_PAYMENT_METHODS, - serializeOptList(modelObject.allowedPaymentMethods, GooglePayPaymentMethodModel.SERIALIZER) + serializeOptList(modelObject.allowedPaymentMethods, GooglePayPaymentMethodModel.SERIALIZER), ) putOpt(EXISTING_PAYMENT_METHOD_REQUIRED, modelObject.isExistingPaymentMethodRequired) } @@ -48,13 +50,14 @@ internal data class IsReadyToPayRequestModel( } override fun deserialize(jsonObject: JSONObject) = IsReadyToPayRequestModel( - apiVersion = jsonObject.optInt(API_VERSION), - apiVersionMinor = jsonObject.optInt(API_VERSION_MINOR), + apiVersion = jsonObject.getIntOrNull(API_VERSION) ?: 0, + apiVersionMinor = jsonObject.getIntOrNull(API_VERSION_MINOR) ?: 0, allowedPaymentMethods = deserializeOptList( jsonObject.optJSONArray(ALLOWED_PAYMENT_METHODS), - GooglePayPaymentMethodModel.SERIALIZER + GooglePayPaymentMethodModel.SERIALIZER, ), - isExistingPaymentMethodRequired = jsonObject.optBoolean(EXISTING_PAYMENT_METHOD_REQUIRED) + isExistingPaymentMethodRequired = jsonObject.getBooleanOrNull(EXISTING_PAYMENT_METHOD_REQUIRED) + ?: false, ) } } diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/PaymentDataRequestModel.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/PaymentDataRequestModel.kt index 08b74268e7..acaa06eb12 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/PaymentDataRequestModel.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/data/model/PaymentDataRequestModel.kt @@ -13,6 +13,8 @@ import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOpt import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOptList import com.adyen.checkout.core.internal.data.model.ModelUtils.serializeOpt import com.adyen.checkout.core.internal.data.model.ModelUtils.serializeOptList +import com.adyen.checkout.core.internal.data.model.getBooleanOrNull +import com.adyen.checkout.core.internal.data.model.getIntOrNull import com.adyen.checkout.googlepay.MerchantInfo import com.adyen.checkout.googlepay.ShippingAddressParameters import kotlinx.parcelize.Parcelize @@ -51,17 +53,17 @@ internal data class PaymentDataRequestModel( putOpt(MERCHANT_INFO, serializeOpt(modelObject.merchantInfo, MerchantInfo.SERIALIZER)) putOpt( ALLOWED_PAYMENT_METHODS, - serializeOptList(modelObject.allowedPaymentMethods, GooglePayPaymentMethodModel.SERIALIZER) + serializeOptList(modelObject.allowedPaymentMethods, GooglePayPaymentMethodModel.SERIALIZER), ) putOpt( TRANSACTION_INFO, - serializeOpt(modelObject.transactionInfo, TransactionInfoModel.SERIALIZER) + serializeOpt(modelObject.transactionInfo, TransactionInfoModel.SERIALIZER), ) putOpt(EMAIL_REQUIRED, modelObject.isEmailRequired) putOpt(SHIPPING_ADDRESS_REQUIRED, modelObject.isShippingAddressRequired) putOpt( SHIPPING_ADDRESS_PARAMETERS, - serializeOpt(modelObject.shippingAddressParameters, ShippingAddressParameters.SERIALIZER) + serializeOpt(modelObject.shippingAddressParameters, ShippingAddressParameters.SERIALIZER), ) } } catch (e: JSONException) { @@ -71,25 +73,26 @@ internal data class PaymentDataRequestModel( override fun deserialize(jsonObject: JSONObject): PaymentDataRequestModel { val paymentDataRequestModel = PaymentDataRequestModel() - paymentDataRequestModel.apiVersion = jsonObject.optInt(API_VERSION) - paymentDataRequestModel.apiVersionMinor = jsonObject.optInt(API_VERSION_MINOR) + paymentDataRequestModel.apiVersion = jsonObject.getIntOrNull(API_VERSION) ?: 0 + paymentDataRequestModel.apiVersionMinor = jsonObject.getIntOrNull(API_VERSION_MINOR) ?: 0 paymentDataRequestModel.merchantInfo = deserializeOpt( jsonObject.optJSONObject(MERCHANT_INFO), - MerchantInfo.SERIALIZER + MerchantInfo.SERIALIZER, ) paymentDataRequestModel.allowedPaymentMethods = deserializeOptList( jsonObject.optJSONArray(ALLOWED_PAYMENT_METHODS), - GooglePayPaymentMethodModel.SERIALIZER + GooglePayPaymentMethodModel.SERIALIZER, ) paymentDataRequestModel.transactionInfo = deserializeOpt( jsonObject.optJSONObject(TRANSACTION_INFO), - TransactionInfoModel.SERIALIZER + TransactionInfoModel.SERIALIZER, ) - paymentDataRequestModel.isEmailRequired = jsonObject.optBoolean(EMAIL_REQUIRED) - paymentDataRequestModel.isShippingAddressRequired = jsonObject.optBoolean(SHIPPING_ADDRESS_REQUIRED) + paymentDataRequestModel.isEmailRequired = jsonObject.getBooleanOrNull(EMAIL_REQUIRED) ?: false + paymentDataRequestModel.isShippingAddressRequired = + jsonObject.getBooleanOrNull(SHIPPING_ADDRESS_REQUIRED) ?: false paymentDataRequestModel.shippingAddressParameters = deserializeOpt( jsonObject.optJSONObject(SHIPPING_ADDRESS_PARAMETERS), - ShippingAddressParameters.SERIALIZER + ShippingAddressParameters.SERIALIZER, ) return paymentDataRequestModel } diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtils.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtils.kt index 64eccaa025..990cc45d51 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtils.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtils.kt @@ -143,7 +143,7 @@ internal object GooglePayUtils { val tokenizationDataJson = paymentMethodDataJson.getJSONObject(TOKENIZATION_DATA) googlePayToken = tokenizationDataJson.getString(TOKEN) val infoJson = paymentMethodDataJson.optJSONObject(INFO) - if (infoJson != null && infoJson.has(CARD_NETWORK)) { + if (infoJson != null && !infoJson.isNull(CARD_NETWORK)) { googlePayCardNetwork = infoJson.getString(CARD_NETWORK) } } catch (e: JSONException) { diff --git a/redirect/src/main/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegate.kt b/redirect/src/main/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegate.kt index c9b438fbd3..de0baabdc1 100644 --- a/redirect/src/main/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegate.kt +++ b/redirect/src/main/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegate.kt @@ -31,6 +31,7 @@ import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.core.exception.HttpException import com.adyen.checkout.core.exception.ModelSerializationException +import com.adyen.checkout.core.internal.data.model.getStringOrNull import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.redirect.internal.data.api.NativeRedirectService import com.adyen.checkout.redirect.internal.data.model.NativeRedirectRequest @@ -171,7 +172,7 @@ constructor( coroutineScope.launch { val request = NativeRedirectRequest( redirectData = nativeRedirectData, - returnQueryString = details.optString(RETURN_URL_QUERY_STRING_PARAMETER), + returnQueryString = details.getStringOrNull(RETURN_URL_QUERY_STRING_PARAMETER).orEmpty(), ) try { val response = nativeRedirectService.makeNativeRedirect(request, componentParams.clientKey) diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionModel.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionModel.kt index 18a85beb1e..50eb5b0bdb 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionModel.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionModel.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.sessions.core import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -43,8 +44,8 @@ data class SessionModel( override fun deserialize(jsonObject: JSONObject): SessionModel { return SessionModel( - id = jsonObject.optString(ID), - sessionData = jsonObject.optString(SESSION_DATA) + id = jsonObject.getStringOrNull(ID).orEmpty(), + sessionData = jsonObject.getStringOrNull(SESSION_DATA) ) } } diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupConfiguration.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupConfiguration.kt index 74cfa2f6be..f985ec6f23 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupConfiguration.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupConfiguration.kt @@ -52,7 +52,7 @@ data class SessionSetupConfiguration( return try { SessionSetupConfiguration( enableStoreDetails = jsonObject.getBooleanOrNull(ENABLE_STORE_DETAILS), - showInstallmentAmount = jsonObject.optBoolean(SHOW_INSTALLMENT_AMOUNT), + showInstallmentAmount = jsonObject.getBooleanOrNull(SHOW_INSTALLMENT_AMOUNT) ?: false, installmentOptions = jsonObject.optJSONObject(INSTALLMENT_OPTIONS) ?.jsonToMap(SessionSetupInstallmentOptions.SERIALIZER), showRemovePaymentMethodButton = jsonObject.getBooleanOrNull(SHOW_REMOVE_PAYMENT_METHOD_BUTTON), diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupInstallmentOptions.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupInstallmentOptions.kt index e7f197b4d9..69f1589663 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupInstallmentOptions.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupInstallmentOptions.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.sessions.core import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.JsonUtils import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getIntOrNull import com.adyen.checkout.core.internal.data.model.optIntList import com.adyen.checkout.core.internal.data.model.optStringList import kotlinx.parcelize.Parcelize @@ -48,7 +49,7 @@ data class SessionSetupInstallmentOptions( return try { SessionSetupInstallmentOptions( plans = jsonObject.optStringList(PLANS).orEmpty(), - preselectedValue = jsonObject.optInt(PRESELECTED_VALUE), + preselectedValue = jsonObject.getIntOrNull(PRESELECTED_VALUE), values = jsonObject.optIntList(VALUES) ) } catch (e: JSONException) { diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupResponse.kt index ed32a60092..2fad56cee6 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/SessionSetupResponse.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -70,20 +71,20 @@ data class SessionSetupResponse( override fun deserialize(jsonObject: JSONObject): SessionSetupResponse { return try { SessionSetupResponse( - id = jsonObject.optString(ID), - sessionData = jsonObject.optString(SESSION_DATA), + id = jsonObject.getStringOrNull(ID).orEmpty(), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), amount = ModelUtils.deserializeOpt(jsonObject.optJSONObject(AMOUNT), Amount.SERIALIZER), - expiresAt = jsonObject.optString(EXPIRES_AT), + expiresAt = jsonObject.getStringOrNull(EXPIRES_AT).orEmpty(), paymentMethodsApiResponse = ModelUtils.deserializeOpt( jsonObject.optJSONObject(PAYMENT_METHODS), PaymentMethodsApiResponse.SERIALIZER ), - returnUrl = jsonObject.optString(RETURN_URL), + returnUrl = jsonObject.getStringOrNull(RETURN_URL), configuration = ModelUtils.deserializeOpt( jsonObject.optJSONObject(CONFIGURATION), SessionSetupConfiguration.SERIALIZER ), - shopperLocale = jsonObject.optString(SHOPPER_LOCALE), + shopperLocale = jsonObject.getStringOrNull(SHOPPER_LOCALE), ) } catch (e: JSONException) { throw ModelSerializationException(SessionSetupResponse::class.java, e) diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceRequest.kt index 2b27cc96be..3652db5235 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceRequest.kt @@ -14,6 +14,7 @@ import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -57,7 +58,7 @@ data class SessionBalanceRequest( override fun deserialize(jsonObject: JSONObject): SessionBalanceRequest { return try { SessionBalanceRequest( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), paymentMethod = ModelUtils.deserializeOpt( jsonObject.optJSONObject(PAYMENT_METHOD), PaymentMethodDetails.SERIALIZER diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceResponse.kt index d64bd628b3..0a54d9e078 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionBalanceResponse.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -49,7 +50,7 @@ data class SessionBalanceResponse( override fun deserialize(jsonObject: JSONObject): SessionBalanceResponse { return SessionBalanceResponse( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), balance = ModelUtils.deserializeOpt(jsonObject.optJSONObject(BALANCE), Amount.SERIALIZER) ?: throw CheckoutException("Balance not found"), transactionLimit = ModelUtils.deserializeOpt( diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderRequest.kt index 32bf12a9bc..55199dddae 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderRequest.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -44,7 +45,7 @@ data class SessionCancelOrderRequest( override fun deserialize(jsonObject: JSONObject): SessionCancelOrderRequest { return try { SessionCancelOrderRequest( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), order = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ORDER), OrderRequest.SERIALIZER) ) } catch (e: JSONException) { diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderResponse.kt index 25cb081912..639c643dce 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionCancelOrderResponse.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -40,8 +41,8 @@ data class SessionCancelOrderResponse( override fun deserialize(jsonObject: JSONObject): SessionCancelOrderResponse { return SessionCancelOrderResponse( - sessionData = jsonObject.optString(SESSION_DATA), - status = jsonObject.optString(STATUS) + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + status = jsonObject.getStringOrNull(STATUS) ) } } diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsRequest.kt index 868e8b86a7..453b63a17d 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsRequest.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import com.adyen.checkout.core.internal.util.JSONObjectParceler import kotlinx.parcelize.Parcelize import kotlinx.parcelize.WriteWith @@ -47,8 +48,8 @@ data class SessionDetailsRequest( override fun deserialize(jsonObject: JSONObject): SessionDetailsRequest { return try { SessionDetailsRequest( - sessionData = jsonObject.optString(SESSION_DATA), - paymentData = jsonObject.optString(PAYMENT_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + paymentData = jsonObject.getStringOrNull(PAYMENT_DATA), details = jsonObject.optJSONObject(DETAILS) ) } catch (e: JSONException) { diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsResponse.kt index 35c742e86e..e87790414a 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDetailsResponse.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -55,11 +56,11 @@ data class SessionDetailsResponse( override fun deserialize(jsonObject: JSONObject): SessionDetailsResponse { return SessionDetailsResponse( - sessionData = jsonObject.optString(SESSION_DATA), - status = jsonObject.optString(STATUS), - resultCode = jsonObject.optString(RESULT_CODE), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + status = jsonObject.getStringOrNull(STATUS), + resultCode = jsonObject.getStringOrNull(RESULT_CODE), action = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ACTION), Action.SERIALIZER), - sessionResult = jsonObject.optString(SESSION_RESULT), + sessionResult = jsonObject.getStringOrNull(SESSION_RESULT), order = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ORDER), OrderResponse.SERIALIZER), ) } diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenRequest.kt index cce3a5b34b..235be2540a 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenRequest.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -41,8 +42,8 @@ data class SessionDisableTokenRequest( override fun deserialize(jsonObject: JSONObject): SessionDisableTokenRequest { return try { SessionDisableTokenRequest( - sessionData = jsonObject.optString(SESSION_DATA), - storedPaymentMethodId = jsonObject.optString(STORED_PAYMENT_METHOD_ID), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + storedPaymentMethodId = jsonObject.getStringOrNull(STORED_PAYMENT_METHOD_ID).orEmpty(), ) } catch (e: JSONException) { throw ModelSerializationException(SessionDisableTokenRequest::class.java, e) diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenResponse.kt index e0fe3c37d1..51e3bcc845 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionDisableTokenResponse.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -38,7 +39,7 @@ data class SessionDisableTokenResponse( override fun deserialize(jsonObject: JSONObject): SessionDisableTokenResponse { return try { SessionDisableTokenResponse( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), ) } catch (e: JSONException) { throw ModelSerializationException(SessionDisableTokenResponse::class.java, e) diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderRequest.kt index c6d04dc87e..46c3993525 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderRequest.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -39,7 +40,7 @@ data class SessionOrderRequest( override fun deserialize(jsonObject: JSONObject): SessionOrderRequest { return try { SessionOrderRequest( - sessionData = jsonObject.optString(SESSION_DATA) + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), ) } catch (e: JSONException) { throw ModelSerializationException(SessionOrderRequest::class.java, e) diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderResponse.kt index 63f7f0ca49..08913c2b24 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionOrderResponse.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.sessions.core.internal.data.model import androidx.annotation.RestrictTo import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -43,9 +44,9 @@ data class SessionOrderResponse( override fun deserialize(jsonObject: JSONObject): SessionOrderResponse { return SessionOrderResponse( - sessionData = jsonObject.optString(SESSION_DATA), - orderData = jsonObject.optString(ORDER_DATA), - pspReference = jsonObject.optString(PSP_REFERENCE) + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + orderData = jsonObject.getStringOrNull(ORDER_DATA).orEmpty(), + pspReference = jsonObject.getStringOrNull(PSP_REFERENCE).orEmpty(), ) } } diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsRequest.kt index 40bf1b5a22..ecabdfa241 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsRequest.kt @@ -14,6 +14,7 @@ import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -45,7 +46,7 @@ data class SessionPaymentsRequest( override fun deserialize(jsonObject: JSONObject): SessionPaymentsRequest { return try { SessionPaymentsRequest( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), paymentComponentData = ModelUtils.deserializeOpt( jsonObject, PaymentComponentData.SERIALIZER diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsResponse.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsResponse.kt index 88ea349dd2..5422b4fff7 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsResponse.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionPaymentsResponse.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -55,12 +56,12 @@ data class SessionPaymentsResponse( override fun deserialize(jsonObject: JSONObject): SessionPaymentsResponse { return SessionPaymentsResponse( - sessionData = jsonObject.optString(SESSION_DATA), - status = jsonObject.optString(STATUS), - resultCode = jsonObject.optString(RESULT_CODE), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), + status = jsonObject.getStringOrNull(STATUS), + resultCode = jsonObject.getStringOrNull(RESULT_CODE), action = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ACTION), Action.SERIALIZER), order = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ORDER), OrderResponse.SERIALIZER), - sessionResult = jsonObject.optString(SESSION_RESULT), + sessionResult = jsonObject.getStringOrNull(SESSION_RESULT), ) } } diff --git a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionSetupRequest.kt b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionSetupRequest.kt index 0cfcc692cb..b92aaea3b5 100644 --- a/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionSetupRequest.kt +++ b/sessions-core/src/main/java/com/adyen/checkout/sessions/core/internal/data/model/SessionSetupRequest.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.core.exception.ModelSerializationException import com.adyen.checkout.core.internal.data.model.ModelObject import com.adyen.checkout.core.internal.data.model.ModelUtils +import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject @@ -44,7 +45,7 @@ data class SessionSetupRequest( override fun deserialize(jsonObject: JSONObject): SessionSetupRequest { return try { SessionSetupRequest( - sessionData = jsonObject.optString(SESSION_DATA), + sessionData = jsonObject.getStringOrNull(SESSION_DATA).orEmpty(), order = ModelUtils.deserializeOpt(jsonObject.optJSONObject(ORDER), OrderRequest.SERIALIZER) ) } catch (e: JSONException) {