diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/AdyenApiResponse.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/AdyenApiResponse.kt new file mode 100644 index 0000000000..dfded7e75f --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/AdyenApiResponse.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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 21/2/2025. + */ + +package com.adyen.checkout.core.internal.data.api + +import androidx.annotation.RestrictTo + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +data class AdyenApiResponse( + val path: String, + val statusCode: Int, + val headers: Map, + val body: String, +) diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClient.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClient.kt index 653585360b..f28a244e19 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClient.kt +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClient.kt @@ -17,12 +17,12 @@ interface HttpClient { path: String, queryParameters: Map = emptyMap(), headers: Map = emptyMap() - ): ByteArray + ): AdyenApiResponse suspend fun post( path: String, jsonBody: String, queryParameters: Map = emptyMap(), headers: Map = emptyMap() - ): ByteArray + ): AdyenApiResponse } diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClientExt.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClientExt.kt index 327f2eac43..ba92d540a6 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClientExt.kt +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/HttpClientExt.kt @@ -27,12 +27,11 @@ suspend fun HttpClient.get( ): T { adyenLog(AdyenLogLevel.DEBUG) { "GET - $path" } - val result = runAndLogHttpException { get(path, queryParameters) } - val resultJson = result.toJSONObject() + val response = runAndLogHttpException { get(path, queryParameters) } - adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } + logResponse(response) - return responseSerializer.deserialize(resultJson) + return responseSerializer.deserialize(response.body.toJSONObject()) } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -43,12 +42,11 @@ suspend fun HttpClient.getList( ): List { adyenLog(AdyenLogLevel.DEBUG) { "GET - $path" } - val result = runAndLogHttpException { get(path, queryParameters) } - val resultJson = JSONArray(String(result, Charsets.UTF_8)) + val response = runAndLogHttpException { get(path, queryParameters) } - adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } + logResponse(response) - return ModelUtils.deserializeOptList(resultJson, responseSerializer).orEmpty() + return ModelUtils.deserializeOptList(JSONArray(response.body), responseSerializer).orEmpty() } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -65,12 +63,11 @@ suspend fun HttpClient.post( adyenLog(AdyenLogLevel.VERBOSE) { "request - ${requestJson.toStringPretty()}" } - val result = runAndLogHttpException { post(path, requestJson.toString(), queryParameters) } - val resultJson = result.toJSONObject() + val response = runAndLogHttpException { post(path, requestJson.toString(), queryParameters) } - adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } + logResponse(response) - return responseSerializer.deserialize(resultJson) + return responseSerializer.deserialize(response.body.toJSONObject()) } private inline fun T.runAndLogHttpException(block: T.() -> R): R { @@ -90,10 +87,19 @@ private fun HttpException.getLogMessage(): String { } } -private fun ByteArray.toJSONObject(): JSONObject { +private fun String.toJSONObject(): JSONObject { return if (isEmpty()) { JSONObject() } else { - JSONObject(String(this, Charsets.UTF_8)) + JSONObject(this) } } + +private fun Any.logResponse(response: AdyenApiResponse) { + adyenLog(AdyenLogLevel.VERBOSE) { "response - ${response.statusCode} .../${response.path}" } + response.headers.forEach { (key, value) -> + adyenLog(AdyenLogLevel.VERBOSE) { "$key: $value" } + } + adyenLog(AdyenLogLevel.VERBOSE) { response.body.toJSONObject().toStringPretty() } + adyenLog(AdyenLogLevel.VERBOSE) { "response - END" } +} diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/OkHttpClient.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/OkHttpClient.kt index 3a9c9e6381..ca333cbef0 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/OkHttpClient.kt +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/api/OkHttpClient.kt @@ -34,14 +34,14 @@ internal class OkHttpClient( path: String, queryParameters: Map, headers: Map - ): ByteArray { + ): AdyenApiResponse { val request = Request.Builder() .headers(headers.combineToHeaders()) .url(buildURL(path, queryParameters)) .get() .build() - return executeRequest(request) + return executeRequest(request, path) } override suspend fun post( @@ -49,14 +49,14 @@ internal class OkHttpClient( jsonBody: String, queryParameters: Map, headers: Map - ): ByteArray { + ): AdyenApiResponse { val request = Request.Builder() .headers(headers.combineToHeaders()) .url(buildURL(path, queryParameters)) .post(jsonBody.toRequestBody(MEDIA_TYPE_JSON)) .build() - return executeRequest(request) + return executeRequest(request, path) } private fun buildURL(path: String, queryParameters: Map): String { @@ -70,7 +70,7 @@ internal class OkHttpClient( return builder.toString() } - private fun executeRequest(request: Request): ByteArray { + private fun executeRequest(request: Request, path: String): AdyenApiResponse { val call = client.newCall(request) try { @@ -81,7 +81,12 @@ internal class OkHttpClient( ?.bytes() ?: ByteArray(0) response.body?.close() - return bytes + return AdyenApiResponse( + path = path, + statusCode = response.code, + headers = response.headers.toMap(), + body = String(bytes, Charsets.UTF_8), + ) } else { val exception = response.getHttpException() response.body?.close() diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt index f926f6b5b4..626d247fbf 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt @@ -1,6 +1,7 @@ package com.adyen.checkout.components.core.internal.data.api import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest +import com.adyen.checkout.core.internal.data.api.AdyenApiResponse import com.adyen.checkout.core.internal.data.api.HttpClient import com.adyen.checkout.core.internal.data.model.EmptyResponse import com.adyen.checkout.test.LoggingExtension @@ -37,11 +38,11 @@ internal class AnalyticsServiceTest( platform = "android", info = emptyList(), logs = emptyList(), - errors = emptyList() + errors = emptyList(), ) val checkoutAttemptId = "testtest" whenever(httpClient.post(eq("v3/analytics/$checkoutAttemptId"), any(), any(), any())) - .doReturn(ByteArray(0)) + .doReturn(AdyenApiResponse("", 0, emptyMap(), "")) val response = analyticsService.sendEvents(request, checkoutAttemptId, TEST_CLIENT_KEY)