diff --git a/.gitignore b/.gitignore index eb5751cfb..02923dd7b 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ lint/reports/ # Sandbox stuff _sandbox + +# Since Kotlin 2.0.0 +.kotlin/ diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 148fdd246..6d0ee1c2a 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8fd7fd87e..dfe216554 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,9 @@ buildscript { ext { - androidGradlePluginVersion = '8.4.1' - kotlinVersion = '1.9.24' - kspVersion = '1.9.24-1.0.20' + androidGradlePluginVersion = '8.4.2' + kotlinVersion = '2.0.0' + kspVersion = '2.0.0-1.0.22' dokkaVersion = '1.9.20' androidxNavigationVersion = '2.7.7' nexusPublishPluginVersion = '2.0.0' @@ -18,6 +18,7 @@ plugins { id 'com.android.application' version "$androidGradlePluginVersion" apply false id 'com.android.library' version "$androidGradlePluginVersion" apply false id 'org.jetbrains.kotlin.android' version "$kotlinVersion" apply false + id 'org.jetbrains.kotlin.plugin.compose' version "$kotlinVersion" apply false id 'org.jetbrains.dokka' version "$dokkaVersion" apply true id 'com.google.devtools.ksp' version "$kspVersion" apply false id 'io.github.gradle-nexus.publish-plugin' version "$nexusPublishPluginVersion" apply true @@ -36,7 +37,7 @@ ext { publishVersion = file('version.resolved').getText().trim() androidxCoreVersion = '1.13.1' - androidxAppCompatVersion = '1.6.1' + androidxAppCompatVersion = '1.7.0' androidxConstraintLayoutVersion = '2.1.4' androidxActivityVersion = '1.9.0' androidxFragmentVersion = '1.7.1' @@ -46,11 +47,10 @@ ext { androidxBrowserVersion = '1.8.0' androidxComposeBOMVersion = '2024.05.00' - androidxComposeCompilerVersion = '1.5.14' materialVersion = '1.12.0' - gmsWalletVersion = '19.3.0' + gmsWalletVersion = '19.4.0' kotlinxCoroutinesPlayServicesVersion = '1.8.1' retrofitVersion = '2.11.0' @@ -59,7 +59,7 @@ ext { okioVersion = '3.9.0' coilVersion = '2.6.0' commonMarkVersion = '0.22.0' - libphonenumberVersion = '8.13.37' + libphonenumberVersion = '8.13.38' checkout3dsSdkVersion = '3.2.2' adyen3dsSdkVersion = '2.2.15' diff --git a/checkout-3ds/build.gradle b/checkout-3ds/build.gradle index 255120470..bae7ef0bd 100644 --- a/checkout-3ds/build.gradle +++ b/checkout-3ds/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' @@ -51,10 +53,6 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = '17' - freeCompilerArgs += '-opt-in=com.processout.sdk.core.annotation.ProcessOutInternalApi' - } publishing { singleVariant("productionRelease") { @@ -66,6 +64,13 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + optIn.add("com.processout.sdk.core.annotation.ProcessOutInternalApi") + } +} + @SuppressWarnings('GrMethodMayBeStatic') def setBuildConfig(buildType) { buildType.buildConfigField("String", "LIBRARY_NAME", "\"ProcessOut Android SDK - Checkout 3DS\"") diff --git a/example/build.gradle b/example/build.gradle index b7abc4600..fad795f47 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' @@ -52,10 +54,13 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = '17' - freeCompilerArgs += '-opt-in=com.processout.sdk.core.annotation.ProcessOutInternalApi' - freeCompilerArgs += '-opt-in=com.processout.sdk.ui.core.annotation.ProcessOutInternalApi' +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + optIn.add("com.processout.sdk.core.annotation.ProcessOutInternalApi") + optIn.add("com.processout.sdk.ui.core.annotation.ProcessOutInternalApi") } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1f3e86b56..8dda536a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Oct 05 19:43:19 EEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/sdk/build.gradle b/sdk/build.gradle index 808f4f837..bd07057e7 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' @@ -63,10 +65,6 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = '17' - freeCompilerArgs += '-opt-in=com.processout.sdk.core.annotation.ProcessOutInternalApi' - } testOptions { unitTests { @@ -88,6 +86,13 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + optIn.add("com.processout.sdk.core.annotation.ProcessOutInternalApi") + } +} + @SuppressWarnings('GrMethodMayBeStatic') def setBuildConfig(buildType) { buildType.buildConfigField("String", "LIBRARY_NAME", "\"ProcessOut Android SDK\"") diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOut.kt b/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOut.kt index f1ca4f1c2..698a24fb1 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOut.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOut.kt @@ -9,6 +9,7 @@ import com.processout.sdk.api.dispatcher.POEventDispatchers import com.processout.sdk.api.dispatcher.PONativeAlternativePaymentMethodEventDispatcher import com.processout.sdk.api.dispatcher.napm.PODefaultNativeAlternativePaymentMethodEventDispatcher import com.processout.sdk.api.network.ApiConstants +import com.processout.sdk.api.preferences.Preferences import com.processout.sdk.api.repository.POCardsRepository import com.processout.sdk.api.repository.POGatewayConfigurationsRepository import com.processout.sdk.api.service.POAlternativePaymentMethodsService @@ -58,6 +59,9 @@ class ProcessOut private constructor( apiGraph.serviceGraph.browserCapabilitiesService } + /** Dispatchers that allows to handle events during various payment flows. */ + val dispatchers: POEventDispatchers by lazy { DefaultEventDispatchers } + /** Dispatcher that allows to handle events during native alternative payments. */ @Deprecated( message = "Use replacement property.", @@ -67,9 +71,6 @@ class ProcessOut private constructor( PODefaultNativeAlternativePaymentMethodEventDispatcher } - /** Dispatchers that allows to handle events during various payment flows. */ - val dispatchers: POEventDispatchers by lazy { DefaultEventDispatchers } - /** * Entry point to ProcessOut Android SDK. * Provides configuration and access to services. @@ -103,26 +104,27 @@ class ProcessOut private constructor( * the configuration applies only on first invocation and all subsequent calls are ignored. * When set to _true_, the existing instances will be reconfigured. */ - fun configure(configuration: ProcessOutConfiguration, force: Boolean = false) { + fun configure( + configuration: ProcessOutConfiguration, + force: Boolean = false + ) { if (isConfigured) { - if (force) { - with(instance.apiGraph) { - contextGraph.configuration = configuration - POLogger.clear() - if (configuration.debug) { - POLogger.add(serviceGraph.systemLoggerService) - POLogger.info("Applied new ProcessOut configuration.") - } - } - } else { + if (!force) { POLogger.info("ProcessOut is already configured.") + return } + with(instance.apiGraph) { + contextGraph.configuration = configuration + configureLogger(configuration, serviceGraph) + } + POLogger.info("Applied new ProcessOut configuration.") } else { val contextGraph = DefaultContextGraph( configuration = configuration ) val networkGraph = DefaultNetworkGraph( contextGraph = contextGraph, + preferences = Preferences(contextGraph), baseUrl = ApiConstants.BASE_URL, sdkVersion = VERSION ) @@ -145,10 +147,21 @@ class ProcessOut private constructor( instance = lazy { ProcessOut(apiGraph) }.value legacyInstance = lazy { ProcessOutLegacyAccessor.configure(contextGraph) }.value - if (configuration.debug) { - POLogger.add(serviceGraph.systemLoggerService) - POLogger.info("ProcessOut configuration is complete.") - } + configureLogger(configuration, serviceGraph) + POLogger.info("ProcessOut configuration is complete.") + } + } + + private fun configureLogger( + configuration: ProcessOutConfiguration, + serviceGraph: ServiceGraph + ) { + POLogger.clear() + if (configuration.debug) { + POLogger.add(serviceGraph.systemLoggerService) + } + if (configuration.enableTelemetry) { + POLogger.add(serviceGraph.telemetryService) } } } diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOutConfiguration.kt b/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOutConfiguration.kt index a7d8f4333..1e792bd6a 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOutConfiguration.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/ProcessOutConfiguration.kt @@ -3,6 +3,7 @@ package com.processout.sdk.api import android.app.Application import androidx.annotation.VisibleForTesting import com.processout.sdk.core.annotation.ProcessOutInternalApi +import java.util.UUID /** * Defines ProcessOut configuration. @@ -10,12 +11,30 @@ import com.processout.sdk.core.annotation.ProcessOutInternalApi * @param[application] Instance of the [Application]. * @param[projectId] Project ID. * @param[debug] Enables debug mode. Default value is _false_. __Note:__ debug logs may contain sensitive data. + * @param[enableTelemetry] Enables sending telemetry data to ProcessOut. Default value is _false_. + * @param[applicationInformation] Application information that helps ProcessOut to troubleshoot potential issues. */ data class ProcessOutConfiguration( val application: Application, val projectId: String, - val debug: Boolean = false + val debug: Boolean = false, + val enableTelemetry: Boolean = false, + val applicationInformation: ApplicationInformation? = null ) { + + /** + * Application information that helps ProcessOut to troubleshoot potential issues. + * + * @param[name] Application name. + * @param[version] Application version. + */ + data class ApplicationInformation( + val name: String? = null, + val version: String? = null + ) + + internal val sessionId = UUID.randomUUID().toString() + /** * __Warning:__ Intended to be used only for testing purposes. * Storing private key inside application is extremely dangerous and is highly discouraged. diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/DeviceData.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/DeviceData.kt index cc6c5611b..268a15eff 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/DeviceData.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/DeviceData.kt @@ -1,5 +1,6 @@ package com.processout.sdk.api.model.request +import android.os.Build import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -8,10 +9,14 @@ internal data class DeviceData( @Json(name = "app_language") val appLanguage: String, @Json(name = "app_screen_width") - val appScreenWidth: Int, + val screenWidth: Int, @Json(name = "app_screen_height") - val appScreenHeight: Int, + val screenHeight: Int, @Json(name = "app_timezone_offset") - val appTimeZoneOffset: Int, - val channel: String = "android" + val timeZoneOffset: Int, + val channel: String = "android", + @Json(ignore = true) + val model: String = "${Build.MANUFACTURER} ${Build.MODEL}", + @Json(ignore = true) + val systemApiLevel: String = Build.VERSION.SDK_INT.toString() ) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/LogRequest.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/LogRequest.kt deleted file mode 100644 index 6d17f781f..000000000 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/LogRequest.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.processout.sdk.api.model.request - -import com.squareup.moshi.JsonClass -import java.util.Date - -@JsonClass(generateAdapter = true) -internal data class LogRequest( - val level: String, - val tag: String, - val message: String, - val timestamp: Date, - val attributes: Map -) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/TelemetryRequest.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/TelemetryRequest.kt new file mode 100644 index 000000000..6e6ac5c9c --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/TelemetryRequest.kt @@ -0,0 +1,58 @@ +package com.processout.sdk.api.model.request + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import java.util.Date + +@JsonClass(generateAdapter = true) +internal data class TelemetryRequest( + val events: List, + val metadata: Metadata +) { + + /** + * @param[timestamp] RFC3339 encoded timestamp. + * @param[level] Event level: debug, info, warn, error. + */ + @JsonClass(generateAdapter = true) + data class Event( + val timestamp: Date, + val level: String, + val message: String, + @Json(name = "gateway_configuration_id") + val gatewayConfigurationId: String?, + @Json(name = "customer_id") + val customerId: String?, + @Json(name = "customer_token_id") + val customerTokenId: String?, + @Json(name = "card_id") + val cardId: String?, + @Json(name = "invoice_id") + val invoiceId: String?, + val attributes: Map + ) + + @JsonClass(generateAdapter = true) + data class Metadata( + val application: ApplicationMetadata, + val device: DeviceMetadata + ) + + @JsonClass(generateAdapter = true) + data class ApplicationMetadata( + val name: String?, + val version: String? + ) + + /** + * @param[language] Default locale of the client device. + * @param[timeZone] UTC offset of the device time zone. + */ + @JsonClass(generateAdapter = true) + data class DeviceMetadata( + val language: String, + val model: String, + @Json(name = "time_zone") + val timeZone: Int + ) +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/TelemetryResponse.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/TelemetryResponse.kt new file mode 100644 index 000000000..98b72b445 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/TelemetryResponse.kt @@ -0,0 +1,8 @@ +package com.processout.sdk.api.model.response + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class TelemetryResponse( + val success: Boolean +) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/LogsApi.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/LogsApi.kt deleted file mode 100644 index b5e10aed9..000000000 --- a/sdk/src/main/kotlin/com/processout/sdk/api/network/LogsApi.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.processout.sdk.api.network - -import com.processout.sdk.api.model.request.LogRequest -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.POST - -internal interface LogsApi { - - @POST("/logs") - suspend fun send(@Body request: LogRequest): Response -} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/TelemetryApi.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/TelemetryApi.kt new file mode 100644 index 000000000..ae350489d --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/network/TelemetryApi.kt @@ -0,0 +1,13 @@ +package com.processout.sdk.api.network + +import com.processout.sdk.api.model.request.TelemetryRequest +import com.processout.sdk.api.model.response.TelemetryResponse +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +internal interface TelemetryApi { + + @POST("/telemetry") + suspend fun send(@Body request: TelemetryRequest): Response +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/UserAgentInterceptor.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/UserAgentInterceptor.kt index 9c516093b..1fd50491d 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/UserAgentInterceptor.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/network/interceptor/UserAgentInterceptor.kt @@ -1,5 +1,6 @@ package com.processout.sdk.api.network.interceptor +import com.processout.sdk.api.preferences.Preferences import com.processout.sdk.core.locale.currentSdkLocale import com.processout.sdk.di.ContextGraph import okhttp3.Interceptor @@ -10,6 +11,7 @@ import java.util.UUID internal class UserAgentInterceptor( private val contextGraph: ContextGraph, + private val preferences: Preferences, private val sdkVersion: String ) : Interceptor { @@ -24,6 +26,11 @@ internal class UserAgentInterceptor( .header("Idempotency-Key", UUID.randomUUID().toString()) .header("User-Agent", userAgentComponents.joinToString(separator = "/")) .header("Accept-Language", contextGraph.configuration.application.currentSdkLocale().toLanguageTag()) + .header("Session-Id", contextGraph.configuration.sessionId) + .header("Installation-Id", preferences.installationId) + .header("Device-System-Name", contextGraph.deviceData.channel) + .header("Device-System-Version", contextGraph.deviceData.systemApiLevel) + .header("Product-Version", sdkVersion) .build() return chain.proceed(userAgentRequest) } diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/preferences/Preferences.kt b/sdk/src/main/kotlin/com/processout/sdk/api/preferences/Preferences.kt new file mode 100644 index 000000000..237bb3d5f --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/preferences/Preferences.kt @@ -0,0 +1,38 @@ +package com.processout.sdk.api.preferences + +import android.content.Context +import androidx.core.content.edit +import com.processout.sdk.BuildConfig +import com.processout.sdk.di.ContextGraph +import kotlinx.coroutines.* +import java.util.UUID + +internal class Preferences( + contextGraph: ContextGraph, + scope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob()), + workDispatcher: CoroutineDispatcher = Dispatchers.IO +) { + + private companion object { + const val FILENAME = "${BuildConfig.LIBRARY_PACKAGE_NAME}.preferences" + const val KEY_INSTALLATION_ID = "InstallationId" + } + + private val sharedPreferences = contextGraph.configuration.application + .getSharedPreferences(FILENAME, Context.MODE_PRIVATE) + + var installationId = String() + private set + + init { + scope.launch(workDispatcher) { + installationId = sharedPreferences.getString(KEY_INSTALLATION_ID, String()) ?: String() + if (installationId.isEmpty()) { + installationId = UUID.randomUUID().toString() + sharedPreferences.edit(commit = true) { + putString(KEY_INSTALLATION_ID, installationId) + } + } + } + } +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultLogsRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultLogsRepository.kt deleted file mode 100644 index da908ee43..000000000 --- a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultLogsRepository.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.processout.sdk.api.repository - -import com.processout.sdk.api.model.request.LogRequest -import com.processout.sdk.api.network.LogsApi -import kotlinx.coroutines.launch - -internal class DefaultLogsRepository( - failureMapper: ApiFailureMapper, - private val api: LogsApi -) : BaseRepository(failureMapper), LogsRepository { - - override fun send(request: LogRequest) { - repositoryScope.launch { - apiCall { api.send(request) } - } - } -} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultTelemetryRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultTelemetryRepository.kt new file mode 100644 index 000000000..ca69b1ad9 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultTelemetryRepository.kt @@ -0,0 +1,13 @@ +package com.processout.sdk.api.repository + +import com.processout.sdk.api.model.request.TelemetryRequest +import com.processout.sdk.api.network.TelemetryApi + +internal class DefaultTelemetryRepository( + failureMapper: ApiFailureMapper, + private val api: TelemetryApi +) : BaseRepository(failureMapper), TelemetryRepository { + + override suspend fun send(request: TelemetryRequest) = + apiCall { api.send(request) } +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/LogsRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/LogsRepository.kt deleted file mode 100644 index 4463f33b4..000000000 --- a/sdk/src/main/kotlin/com/processout/sdk/api/repository/LogsRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.processout.sdk.api.repository - -import com.processout.sdk.api.model.request.LogRequest - -internal interface LogsRepository { - - fun send(request: LogRequest) -} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/TelemetryRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/TelemetryRepository.kt new file mode 100644 index 000000000..2aeacd232 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/repository/TelemetryRepository.kt @@ -0,0 +1,10 @@ +package com.processout.sdk.api.repository + +import com.processout.sdk.api.model.request.TelemetryRequest +import com.processout.sdk.api.model.response.TelemetryResponse +import com.processout.sdk.core.ProcessOutResult + +internal interface TelemetryRepository { + + suspend fun send(request: TelemetryRequest): ProcessOutResult +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/service/RemoteLoggerService.kt b/sdk/src/main/kotlin/com/processout/sdk/api/service/RemoteLoggerService.kt deleted file mode 100644 index 6694b9897..000000000 --- a/sdk/src/main/kotlin/com/processout/sdk/api/service/RemoteLoggerService.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.processout.sdk.api.service - -import com.processout.sdk.api.model.request.LogRequest -import com.processout.sdk.api.repository.LogsRepository -import com.processout.sdk.core.logger.BaseLoggerService -import com.processout.sdk.core.logger.LogEvent -import com.processout.sdk.core.logger.POLogLevel - -internal class RemoteLoggerService( - minimumLevel: POLogLevel, - private val repository: LogsRepository -) : BaseLoggerService(minimumLevel) { - - companion object { - private const val ATTRIBUTE_LINE = "Line" - } - - override fun log(event: LogEvent) { - repository.send(event.toRequest()) - } - - private fun LogEvent.toRequest() = LogRequest( - level = level.name.lowercase(), - tag = simpleClassName, - message = message, - timestamp = timestamp, - attributes = mutableMapOf( - ATTRIBUTE_LINE to lineNumber.toString() - ).apply { attributes?.let { putAll(it) } } - ) -} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/service/TelemetryService.kt b/sdk/src/main/kotlin/com/processout/sdk/api/service/TelemetryService.kt new file mode 100644 index 000000000..368775f47 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/service/TelemetryService.kt @@ -0,0 +1,79 @@ +package com.processout.sdk.api.service + +import com.processout.sdk.api.ProcessOutConfiguration.ApplicationInformation +import com.processout.sdk.api.model.request.DeviceData +import com.processout.sdk.api.model.request.TelemetryRequest +import com.processout.sdk.api.model.request.TelemetryRequest.* +import com.processout.sdk.api.repository.TelemetryRepository +import com.processout.sdk.core.logger.BaseLoggerService +import com.processout.sdk.core.logger.LogEvent +import com.processout.sdk.core.logger.POLogAttribute.CARD_ID +import com.processout.sdk.core.logger.POLogAttribute.CUSTOMER_ID +import com.processout.sdk.core.logger.POLogAttribute.CUSTOMER_TOKEN_ID +import com.processout.sdk.core.logger.POLogAttribute.FILE +import com.processout.sdk.core.logger.POLogAttribute.GATEWAY_CONFIGURATION_ID +import com.processout.sdk.core.logger.POLogAttribute.INVOICE_ID +import com.processout.sdk.core.logger.POLogAttribute.LINE +import com.processout.sdk.core.logger.POLogLevel +import com.processout.sdk.di.ContextGraph +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +internal class TelemetryService( + minimumLevel: POLogLevel, + private val scope: CoroutineScope, + private val repository: TelemetryRepository, + private val contextGraph: ContextGraph +) : BaseLoggerService(minimumLevel) { + + override fun log(event: LogEvent) { + scope.launch { + repository.send( + event.toRequest( + deviceData = contextGraph.deviceData, + appInfo = contextGraph.configuration.applicationInformation + ) + ) + } + } + + private fun LogEvent.toRequest( + deviceData: DeviceData, + appInfo: ApplicationInformation?, + ): TelemetryRequest { + val additionalAttributes = mutableMapOf( + FILE to simpleClassName, + LINE to lineNumber.toString() + ) + val standaloneAttributes = listOf(GATEWAY_CONFIGURATION_ID, CUSTOMER_ID, CUSTOMER_TOKEN_ID, CARD_ID, INVOICE_ID) + attributes?.filterKeys { !standaloneAttributes.contains(it) }?.let { + additionalAttributes.putAll(it) + } + return TelemetryRequest( + events = listOf( + Event( + timestamp = timestamp, + level = level.name.lowercase(), + message = message, + gatewayConfigurationId = attributes?.get(GATEWAY_CONFIGURATION_ID), + customerId = attributes?.get(CUSTOMER_ID), + customerTokenId = attributes?.get(CUSTOMER_TOKEN_ID), + cardId = attributes?.get(CARD_ID), + invoiceId = attributes?.get(INVOICE_ID), + attributes = additionalAttributes + ) + ), + metadata = Metadata( + application = ApplicationMetadata( + name = appInfo?.name, + version = appInfo?.version + ), + device = DeviceMetadata( + language = deviceData.appLanguage, + model = deviceData.model, + timeZone = deviceData.timeZoneOffset + ) + ) + ) + } +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/core/logger/BaseLoggerService.kt b/sdk/src/main/kotlin/com/processout/sdk/core/logger/BaseLoggerService.kt index f9428d763..d20264130 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/core/logger/BaseLoggerService.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/core/logger/BaseLoggerService.kt @@ -20,8 +20,8 @@ internal abstract class BaseLoggerService( val formattedMessage = if (args.isNotEmpty()) message.format(*args) else message val stackTraceElement = Throwable().stackTrace.find { element -> - // Find first element in the stack trace that do not belongs to logger package. - // This element refers to the class that actually invoked logging. + // Find first element in the stack trace that do not belong to logger package. + // This element refers to the class that actually invoked the logging. loggerPackageName?.let { element.className.startsWith(it).not() } ?: false diff --git a/sdk/src/main/kotlin/com/processout/sdk/core/logger/POLogAttribute.kt b/sdk/src/main/kotlin/com/processout/sdk/core/logger/POLogAttribute.kt new file mode 100644 index 000000000..0b40fdc30 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/core/logger/POLogAttribute.kt @@ -0,0 +1,19 @@ +package com.processout.sdk.core.logger + +import com.processout.sdk.core.annotation.ProcessOutInternalApi + +/** @suppress */ +@ProcessOutInternalApi +object POLogAttribute { + // Generic + const val FILE = "File" + const val LINE = "Line" + + // Specific + const val IIN = "IIN" + const val CARD_ID = "CardId" + const val INVOICE_ID = "InvoiceId" + const val CUSTOMER_ID = "CustomerId" + const val CUSTOMER_TOKEN_ID = "CustomerTokenId" + const val GATEWAY_CONFIGURATION_ID = "GatewayConfigurationId" +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/di/ContextGraph.kt b/sdk/src/main/kotlin/com/processout/sdk/di/ContextGraph.kt index 5e122ea98..7e2e39701 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/di/ContextGraph.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/di/ContextGraph.kt @@ -35,9 +35,9 @@ internal class DefaultContextGraph( } return DeviceData( appLanguage = configuration.application.currentAppLocale().toLanguageTag(), - appScreenWidth = screenSize.width, - appScreenHeight = screenSize.height, - appTimeZoneOffset = timeZoneOffset + screenWidth = screenSize.width, + screenHeight = screenSize.height, + timeZoneOffset = timeZoneOffset ) } diff --git a/sdk/src/main/kotlin/com/processout/sdk/di/NetworkGraph.kt b/sdk/src/main/kotlin/com/processout/sdk/di/NetworkGraph.kt index 988010254..d4ec40d13 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/di/NetworkGraph.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/di/NetworkGraph.kt @@ -3,6 +3,7 @@ package com.processout.sdk.di import com.processout.sdk.api.network.* import com.processout.sdk.api.network.interceptor.BasicAuthInterceptor import com.processout.sdk.api.network.interceptor.UserAgentInterceptor +import com.processout.sdk.api.preferences.Preferences import com.processout.sdk.core.logger.POLogger import com.squareup.moshi.Moshi import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter @@ -19,11 +20,12 @@ internal interface NetworkGraph { val invoicesApi: InvoicesApi val cardsApi: CardsApi val customerTokensApi: CustomerTokensApi - val logsApi: LogsApi + val telemetryApi: TelemetryApi } internal class DefaultNetworkGraph( contextGraph: ContextGraph, + preferences: Preferences, baseUrl: String, sdkVersion: String ) : NetworkGraph { @@ -34,7 +36,7 @@ internal class DefaultNetworkGraph( .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .addInterceptor(BasicAuthInterceptor(contextGraph)) - .addInterceptor(UserAgentInterceptor(contextGraph, sdkVersion)) + .addInterceptor(UserAgentInterceptor(contextGraph, preferences, sdkVersion)) .addInterceptor(HttpLoggingInterceptor { message -> if (contextGraph.configuration.debug) { POLogger.debug(message) @@ -73,7 +75,7 @@ internal class DefaultNetworkGraph( retrofit.create(CustomerTokensApi::class.java) } - override val logsApi: LogsApi by lazy { - retrofit.create(LogsApi::class.java) + override val telemetryApi: TelemetryApi by lazy { + retrofit.create(TelemetryApi::class.java) } } diff --git a/sdk/src/main/kotlin/com/processout/sdk/di/RepositoryGraph.kt b/sdk/src/main/kotlin/com/processout/sdk/di/RepositoryGraph.kt index 922e3d3a1..c1c69b9e0 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/di/RepositoryGraph.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/di/RepositoryGraph.kt @@ -8,7 +8,7 @@ internal interface RepositoryGraph { val invoicesRepository: InvoicesRepository val cardsRepository: POCardsRepository val customerTokensRepository: CustomerTokensRepository - val logsRepository: LogsRepository + val telemetryRepository: TelemetryRepository } internal class DefaultRepositoryGraph( @@ -35,7 +35,7 @@ internal class DefaultRepositoryGraph( DefaultCustomerTokensRepository(failureMapper, networkGraph.customerTokensApi, contextGraph) } - override val logsRepository: LogsRepository by lazy { - DefaultLogsRepository(failureMapper, networkGraph.logsApi) + override val telemetryRepository: TelemetryRepository by lazy { + DefaultTelemetryRepository(failureMapper, networkGraph.telemetryApi) } } diff --git a/sdk/src/main/kotlin/com/processout/sdk/di/ServiceGraph.kt b/sdk/src/main/kotlin/com/processout/sdk/di/ServiceGraph.kt index ff2c4643e..b9bb103a3 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/di/ServiceGraph.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/di/ServiceGraph.kt @@ -14,7 +14,7 @@ internal interface ServiceGraph { val alternativePaymentMethodsService: POAlternativePaymentMethodsService val browserCapabilitiesService: POBrowserCapabilitiesService val systemLoggerService: POLoggerService - val remoteLoggerService: POLoggerService + val telemetryService: POLoggerService } internal class DefaultServiceGraph( @@ -59,7 +59,12 @@ internal class DefaultServiceGraph( SystemLoggerService(minimumLevel = POLogLevel.DEBUG) } - override val remoteLoggerService: POLoggerService by lazy { - RemoteLoggerService(minimumLevel = POLogLevel.WARN, repositoryGraph.logsRepository) + override val telemetryService: POLoggerService by lazy { + TelemetryService( + minimumLevel = POLogLevel.WARN, + scope = mainCoroutineScope, + repository = repositoryGraph.telemetryRepository, + contextGraph = contextGraph + ) } } diff --git a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt index 898ab8855..cde6eeea9 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/ui/nativeapm/NativeAlternativePaymentMethodViewModel.kt @@ -32,6 +32,7 @@ import com.processout.sdk.api.service.POInvoicesService import com.processout.sdk.core.POFailure import com.processout.sdk.core.POFailure.Code.* import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.core.logger.POLogAttribute import com.processout.sdk.core.logger.POLogger import com.processout.sdk.core.retry.PORetryStrategy import com.processout.sdk.core.retry.PORetryStrategy.Exponential @@ -85,8 +86,8 @@ internal class NativeAlternativePaymentMethodViewModel( ), options = options.validate(), logAttributes = mapOf( - LOG_ATTRIBUTE_INVOICE_ID to invoiceId, - LOG_ATTRIBUTE_GATEWAY_CONFIGURATION_ID to gatewayConfigurationId + POLogAttribute.INVOICE_ID to invoiceId, + POLogAttribute.GATEWAY_CONFIGURATION_ID to gatewayConfigurationId ) ) } as T @@ -98,11 +99,6 @@ internal class NativeAlternativePaymentMethodViewModel( ) } - companion object { - private const val LOG_ATTRIBUTE_INVOICE_ID = "InvoiceId" - private const val LOG_ATTRIBUTE_GATEWAY_CONFIGURATION_ID = "GatewayConfigurationId" - } - private val _uiState = MutableStateFlow(Loading) val uiState = _uiState.asStateFlow() diff --git a/sdk/src/test/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt similarity index 95% rename from sdk/src/test/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt index 80e3454f9..e36123b30 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/AlternativePaymentMethodsServiceTests.kt @@ -6,9 +6,9 @@ import com.processout.sdk.api.model.request.POAlternativePaymentMethodRequest import com.processout.sdk.api.model.response.POAlternativePaymentMethodResponse import com.processout.sdk.api.network.ApiConstants import com.processout.sdk.api.service.POAlternativePaymentMethodsService -import com.processout.sdk.config.SetupRule -import com.processout.sdk.config.TestApplication -import com.processout.sdk.config.assertFailure +import com.processout.sdk.configuration.TestApplication +import com.processout.sdk.configuration.TestSetupRule +import com.processout.sdk.configuration.assertFailure import com.processout.sdk.core.onSuccess import org.junit.Before import org.junit.Rule @@ -23,7 +23,7 @@ class AlternativePaymentMethodsServiceTests { @Rule @JvmField - val setupRule = SetupRule() + val setupRule = TestSetupRule() private lateinit var apmService: POAlternativePaymentMethodsService diff --git a/sdk/src/test/kotlin/com/processout/sdk/CardsRepositoryTests.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/CardsRepositoryTests.kt similarity index 93% rename from sdk/src/test/kotlin/com/processout/sdk/CardsRepositoryTests.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/CardsRepositoryTests.kt index 3c66e9498..e41ea581e 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/CardsRepositoryTests.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/CardsRepositoryTests.kt @@ -5,9 +5,9 @@ import com.processout.sdk.api.model.request.POCardTokenizationRequest import com.processout.sdk.api.model.request.POCardUpdateCVCRequest import com.processout.sdk.api.model.request.POCardUpdateRequest import com.processout.sdk.api.repository.POCardsRepository -import com.processout.sdk.config.SetupRule -import com.processout.sdk.config.TestApplication -import com.processout.sdk.config.assertFailure +import com.processout.sdk.configuration.TestApplication +import com.processout.sdk.configuration.TestSetupRule +import com.processout.sdk.configuration.assertFailure import com.processout.sdk.core.onSuccess import kotlinx.coroutines.runBlocking import org.junit.Before @@ -23,7 +23,7 @@ class CardsRepositoryTests { @Rule @JvmField - val setupRule = SetupRule() + val setupRule = TestSetupRule() private lateinit var cards: POCardsRepository diff --git a/sdk/src/test/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt similarity index 95% rename from sdk/src/test/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt index d89931ba6..ba8de68bb 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/CustomerTokensRepositoryTests.kt @@ -5,9 +5,9 @@ import com.processout.sdk.api.model.request.* import com.processout.sdk.api.repository.CustomerTokensRepository import com.processout.sdk.api.repository.InvoicesRepository import com.processout.sdk.api.repository.POCardsRepository -import com.processout.sdk.config.SetupRule -import com.processout.sdk.config.TestApplication -import com.processout.sdk.config.assertFailure +import com.processout.sdk.configuration.TestApplication +import com.processout.sdk.configuration.TestSetupRule +import com.processout.sdk.configuration.assertFailure import com.processout.sdk.core.onSuccess import kotlinx.coroutines.runBlocking import org.junit.Before @@ -23,7 +23,7 @@ class CustomerTokensRepositoryTests { @Rule @JvmField - val setupRule = SetupRule() + val setupRule = TestSetupRule() private lateinit var customerTokens: CustomerTokensRepository private lateinit var invoices: InvoicesRepository diff --git a/sdk/src/test/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt similarity index 83% rename from sdk/src/test/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt index 4ee83a2b5..3754947a8 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/GatewayConfigurationsRepositoryTests.kt @@ -4,10 +4,10 @@ import com.processout.sdk.api.ProcessOut import com.processout.sdk.api.model.request.POAllGatewayConfigurationsRequest import com.processout.sdk.api.model.request.POGatewayConfigurationRequest import com.processout.sdk.api.repository.POGatewayConfigurationsRepository -import com.processout.sdk.config.PROCESSOUT_GATEWAY_CONFIGURATION_ID -import com.processout.sdk.config.SetupRule -import com.processout.sdk.config.TestApplication -import com.processout.sdk.config.assertFailure +import com.processout.sdk.configuration.PROCESSOUT_GATEWAY_CONFIGURATION_ID +import com.processout.sdk.configuration.TestApplication +import com.processout.sdk.configuration.TestSetupRule +import com.processout.sdk.configuration.assertFailure import kotlinx.coroutines.runBlocking import org.junit.Before import org.junit.Rule @@ -22,7 +22,7 @@ class GatewayConfigurationsRepositoryTests { @Rule @JvmField - val setupRule = SetupRule() + val setupRule = TestSetupRule() private lateinit var gatewayConfigurations: POGatewayConfigurationsRepository diff --git a/sdk/src/test/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt similarity index 89% rename from sdk/src/test/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt index 9213fcae0..75fc4431c 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/InvoicesRepositoryTests.kt @@ -1,14 +1,17 @@ package com.processout.sdk import com.processout.sdk.api.ProcessOut -import com.processout.sdk.api.model.request.* +import com.processout.sdk.api.model.request.POCardTokenizationRequest +import com.processout.sdk.api.model.request.POCreateInvoiceRequest +import com.processout.sdk.api.model.request.POInvoiceAuthorizationRequest +import com.processout.sdk.api.model.request.PONativeAlternativePaymentMethodRequest import com.processout.sdk.api.model.response.CustomerAction import com.processout.sdk.api.repository.InvoicesRepository import com.processout.sdk.api.repository.POCardsRepository -import com.processout.sdk.config.PROCESSOUT_GATEWAY_CONFIGURATION_ID -import com.processout.sdk.config.SetupRule -import com.processout.sdk.config.TestApplication -import com.processout.sdk.config.assertFailure +import com.processout.sdk.configuration.PROCESSOUT_GATEWAY_CONFIGURATION_ID +import com.processout.sdk.configuration.TestApplication +import com.processout.sdk.configuration.TestSetupRule +import com.processout.sdk.configuration.assertFailure import com.processout.sdk.core.POFailure import com.processout.sdk.core.onFailure import com.processout.sdk.core.onSuccess @@ -27,7 +30,7 @@ class InvoicesRepositoryTests { @Rule @JvmField - val setupRule = SetupRule() + val setupRule = TestSetupRule() private lateinit var invoices: InvoicesRepository private lateinit var cards: POCardsRepository diff --git a/sdk/src/test/kotlin/com/processout/sdk/config/TestApplication.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestApplication.kt similarity index 63% rename from sdk/src/test/kotlin/com/processout/sdk/config/TestApplication.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestApplication.kt index 9f560d4a6..c6ea28755 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/config/TestApplication.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestApplication.kt @@ -1,4 +1,4 @@ -package com.processout.sdk.config +package com.processout.sdk.configuration import android.app.Application diff --git a/sdk/src/test/kotlin/com/processout/sdk/config/TestConfiguration.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConfiguration.kt similarity index 87% rename from sdk/src/test/kotlin/com/processout/sdk/config/TestConfiguration.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConfiguration.kt index 76542a3b1..5220f602b 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/config/TestConfiguration.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConfiguration.kt @@ -1,4 +1,6 @@ -package com.processout.sdk.config +@file:Suppress("RestrictedApi") + +package com.processout.sdk.configuration import com.processout.sdk.BuildConfig import com.processout.sdk.api.ProcessOut diff --git a/sdk/src/test/kotlin/com/processout/sdk/config/TestConstants.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConstants.kt similarity index 71% rename from sdk/src/test/kotlin/com/processout/sdk/config/TestConstants.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConstants.kt index 837fd3bf1..f41739150 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/config/TestConstants.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestConstants.kt @@ -1,3 +1,3 @@ -package com.processout.sdk.config +package com.processout.sdk.configuration internal const val PROCESSOUT_GATEWAY_CONFIGURATION_ID = "gway_conf_VJEp8Y6ZCqiiwkSa3JioJrwdVM3bVgJd" diff --git a/sdk/src/test/kotlin/com/processout/sdk/config/TestExtensions.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestExtensions.kt similarity index 89% rename from sdk/src/test/kotlin/com/processout/sdk/config/TestExtensions.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestExtensions.kt index c7d21b5c5..6c03a1f55 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/config/TestExtensions.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestExtensions.kt @@ -1,4 +1,4 @@ -package com.processout.sdk.config +package com.processout.sdk.configuration import com.processout.sdk.core.ProcessOutResult import com.processout.sdk.core.onFailure diff --git a/sdk/src/test/kotlin/com/processout/sdk/config/SetupRule.kt b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestSetupRule.kt similarity index 86% rename from sdk/src/test/kotlin/com/processout/sdk/config/SetupRule.kt rename to sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestSetupRule.kt index cb6d66826..252f95dbb 100644 --- a/sdk/src/test/kotlin/com/processout/sdk/config/SetupRule.kt +++ b/sdk/src/testProductionDebug/kotlin/com/processout/sdk/configuration/TestSetupRule.kt @@ -1,11 +1,11 @@ -package com.processout.sdk.config +package com.processout.sdk.configuration import androidx.test.core.app.ApplicationProvider import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement -class SetupRule : TestRule { +class TestSetupRule : TestRule { override fun apply(base: Statement, description: Description) = object : Statement() { diff --git a/ui-core/build.gradle b/ui-core/build.gradle index 139cd060c..4634128d2 100644 --- a/ui-core/build.gradle +++ b/ui-core/build.gradle @@ -1,6 +1,9 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.compose' id 'kotlin-parcelize' } @@ -47,24 +50,11 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = '17' - freeCompilerArgs += '-opt-in=com.processout.sdk.ui.core.annotation.ProcessOutInternalApi' - if (project.findProperty("composeCompilerReports") == "true") { - freeCompilerArgs += [ - "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + - project.buildDir.absolutePath + "/compose" - ] - } - } buildFeatures { buildConfig true compose true } - composeOptions { - kotlinCompilerExtensionVersion = rootProject.ext.androidxComposeCompilerVersion - } publishing { singleVariant("productionRelease") { @@ -74,6 +64,17 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + optIn.add("com.processout.sdk.ui.core.annotation.ProcessOutInternalApi") + } +} + +composeCompiler { + reportsDestination = layout.buildDirectory.dir("compose") +} + @SuppressWarnings('GrMethodMayBeStatic') def setBuildConfig(buildType) { buildType.buildConfigField("String", "LIBRARY_NAME", "\"ProcessOut Android SDK - UI Core\"") diff --git a/ui/build.gradle b/ui/build.gradle index 06c58be10..f37cc7709 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -1,6 +1,9 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.compose' id 'org.jetbrains.dokka' id 'kotlin-parcelize' id 'com.google.devtools.ksp' @@ -49,25 +52,11 @@ android { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = '17' - freeCompilerArgs += '-opt-in=com.processout.sdk.core.annotation.ProcessOutInternalApi' - freeCompilerArgs += '-opt-in=com.processout.sdk.ui.core.annotation.ProcessOutInternalApi' - if (project.findProperty("composeCompilerReports") == "true") { - freeCompilerArgs += [ - "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + - project.buildDir.absolutePath + "/compose" - ] - } - } buildFeatures { buildConfig true compose true } - composeOptions { - kotlinCompilerExtensionVersion = rootProject.ext.androidxComposeCompilerVersion - } publishing { singleVariant("productionRelease") { @@ -79,6 +68,18 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + optIn.add("com.processout.sdk.core.annotation.ProcessOutInternalApi") + optIn.add("com.processout.sdk.ui.core.annotation.ProcessOutInternalApi") + } +} + +composeCompiler { + reportsDestination = layout.buildDirectory.dir("compose") +} + @SuppressWarnings('GrMethodMayBeStatic') def setBuildConfig(buildType) { buildType.buildConfigField("String", "LIBRARY_NAME", "\"ProcessOut Android SDK - UI\"") diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationInteractor.kt index e5e4cee27..f297be1cf 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationInteractor.kt @@ -19,6 +19,7 @@ import com.processout.sdk.core.POFailure.Code.Generic import com.processout.sdk.core.POFailure.GenericCode.* import com.processout.sdk.core.ProcessOutResult import com.processout.sdk.core.getOrNull +import com.processout.sdk.core.logger.POLogAttribute import com.processout.sdk.core.logger.POLogger import com.processout.sdk.core.onFailure import com.processout.sdk.core.onSuccess @@ -53,8 +54,6 @@ internal class CardTokenizationInteractor( private companion object { const val IIN_LENGTH = 6 const val EXPIRATION_DATE_PART_LENGTH = 2 - const val LOG_ATTRIBUTE_IIN = "IIN" - const val LOG_ATTRIBUTE_CARD_ID = "CardId" } private data class Expiration( @@ -260,7 +259,7 @@ internal class CardTokenizationInteractor( .onFailure { POLogger.info( message = "Failed to fetch issuer information: %s", it, - attributes = mapOf(LOG_ATTRIBUTE_IIN to iin) + attributes = mapOf(POLogAttribute.IIN to iin) ) }.getOrNull() @@ -533,7 +532,7 @@ internal class CardTokenizationInteractor( _state.update { it.copy(tokenizedCard = card) } POLogger.info( message = "Card tokenized successfully.", - attributes = mapOf(LOG_ATTRIBUTE_CARD_ID to card.id) + attributes = mapOf(POLogAttribute.CARD_ID to card.id) ) dispatch(DidTokenize(card)) if (eventDispatcher.subscribedForProcessTokenizedCard()) { @@ -541,7 +540,7 @@ internal class CardTokenizationInteractor( } else { POLogger.info( message = "Completed successfully.", - attributes = mapOf(LOG_ATTRIBUTE_CARD_ID to card.id) + attributes = mapOf(POLogAttribute.CARD_ID to card.id) ) _completion.update { Success(card) } } @@ -561,7 +560,7 @@ internal class CardTokenizationInteractor( eventDispatcher.processTokenizedCard(card) POLogger.info( message = "Requested to process tokenized card.", - attributes = mapOf(LOG_ATTRIBUTE_CARD_ID to card.id) + attributes = mapOf(POLogAttribute.CARD_ID to card.id) ) } } @@ -573,7 +572,7 @@ internal class CardTokenizationInteractor( _state.value.tokenizedCard?.let { card -> POLogger.info( message = "Completed successfully.", - attributes = mapOf(LOG_ATTRIBUTE_CARD_ID to card.id) + attributes = mapOf(POLogAttribute.CARD_ID to card.id) ) _completion.update { Success(card) } }.orElse { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt index c0ac277ba..3967a46e9 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt @@ -21,6 +21,7 @@ import com.processout.sdk.core.POFailure.Code.Cancelled import com.processout.sdk.core.POFailure.Code.Generic import com.processout.sdk.core.POFailure.GenericCode.* import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.core.logger.POLogAttribute import com.processout.sdk.core.logger.POLogger import com.processout.sdk.core.onFailure import com.processout.sdk.core.onSuccess @@ -59,14 +60,10 @@ internal class CardUpdateViewModel( options = options, cardsRepository = ProcessOut.instance.cards, eventDispatcher = PODefaultCardUpdateEventDispatcher, - logAttributes = mapOf(LOG_ATTRIBUTE_CARD_ID to cardId) + logAttributes = mapOf(POLogAttribute.CARD_ID to cardId) ) as T } - private companion object { - const val LOG_ATTRIBUTE_CARD_ID = "CardId" - } - private object CardFieldId { const val NUMBER = "card-number" const val CVC = "card-cvc" diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt index 03e6bef95..b547cbcd0 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt @@ -61,8 +61,6 @@ internal class NativeAlternativePaymentInteractor( companion object { const val SUCCESS_DELAY_MS = 3000L - const val LOG_ATTRIBUTE_INVOICE_ID = "InvoiceId" - const val LOG_ATTRIBUTE_GATEWAY_CONFIGURATION_ID = "GatewayConfigurationId" } private val _completion = MutableStateFlow(Awaiting) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index 9565c3058..91ccab6c5 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -15,12 +15,11 @@ import com.processout.sdk.api.dispatcher.napm.PODefaultNativeAlternativePaymentM import com.processout.sdk.api.model.response.PONativeAlternativePaymentMethodParameter.ParameterType import com.processout.sdk.api.model.response.PONativeAlternativePaymentMethodParameter.ParameterType.* import com.processout.sdk.api.model.response.PONativeAlternativePaymentMethodTransactionDetails.Invoice +import com.processout.sdk.core.logger.POLogAttribute import com.processout.sdk.core.retry.PORetryStrategy.Exponential import com.processout.sdk.ui.core.state.POActionState import com.processout.sdk.ui.core.state.POActionState.Confirmation import com.processout.sdk.ui.core.state.POImmutableList -import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractor.Companion.LOG_ATTRIBUTE_GATEWAY_CONFIGURATION_ID -import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractor.Companion.LOG_ATTRIBUTE_INVOICE_ID import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.* import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Field.* import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Options @@ -67,8 +66,8 @@ internal class NativeAlternativePaymentViewModel( ), eventDispatcher = PODefaultNativeAlternativePaymentMethodEventDispatcher, logAttributes = mapOf( - LOG_ATTRIBUTE_INVOICE_ID to invoiceId, - LOG_ATTRIBUTE_GATEWAY_CONFIGURATION_ID to gatewayConfigurationId + POLogAttribute.INVOICE_ID to invoiceId, + POLogAttribute.GATEWAY_CONFIGURATION_ID to gatewayConfigurationId ) ) ) as T