From fcb703a1e86876cb6a3e6df83c8ae791a2a14bf1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 12:57:27 +0000 Subject: [PATCH 001/272] Update hilt_version to v2.51 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 111 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index db49c46394..c43349bdb6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -23,7 +23,7 @@ ext { kotlin_version = '1.9.22' detekt_gradle_plugin_version = "1.23.5" dokka_version = "1.9.10" - hilt_version = "2.50" + hilt_version = "2.51" compose_compiler_version = '1.5.8' // Code quality diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 8dbd49e9bd..c5c5738ba8 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2990,6 +2990,14 @@ + + + + + + + + @@ -2998,6 +3006,14 @@ + + + + + + + + @@ -3006,6 +3022,14 @@ + + + + + + + + @@ -3014,6 +3038,14 @@ + + + + + + + + @@ -3022,6 +3054,14 @@ + + + + + + + + @@ -3030,6 +3070,14 @@ + + + + + + + + @@ -3038,6 +3086,14 @@ + + + + + + + + @@ -3046,11 +3102,24 @@ + + + + + + + + + + + + + @@ -3091,6 +3160,14 @@ + + + + + + + + @@ -3132,6 +3209,11 @@ + + + + + @@ -3184,6 +3266,14 @@ + + + + + + + + @@ -3216,6 +3306,14 @@ + + + + + + + + @@ -3241,6 +3339,11 @@ + + + + + @@ -4695,6 +4798,14 @@ + + + + + + + + From 5ed847104829c4bf363a25e71edf59c89e8c0f9a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:47:25 +0000 Subject: [PATCH 002/272] Update dependency org.bitbucket.b_c:jose4j to v0.9.6 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index c43349bdb6..7170b245c4 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -71,7 +71,7 @@ ext { arch_core_testing_version = "2.2.0" espresso_version = "3.5.0" json_version = '20231013' - jose4j_version = '0.9.4' + jose4j_version = '0.9.6' junit_jupiter_version = "5.9.1" mockito_kotlin_version = "4.1.0" mockito_version = "4.9.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index c5c5738ba8..360e0e51c2 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4729,6 +4729,14 @@ + + + + + + + + From 19e72bd2741984402318e2c9b2fd23ff8381c034 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:59:59 +0000 Subject: [PATCH 003/272] Update dependency androidx.compose:compose-bom to v2024.02.02 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 308 +++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 7170b245c4..963969eea0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -45,7 +45,7 @@ ext { // Compose Dependencies compose_activity_version = '1.8.2' - compose_bom_version = '2024.02.00' + compose_bom_version = '2024.02.02' compose_hilt_version = '1.2.0' compose_viewmodel_version = '2.7.0' diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 360e0e51c2..8010d085fb 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -375,6 +375,11 @@ + + + + + @@ -383,6 +388,14 @@ + + + + + + + + @@ -391,6 +404,14 @@ + + + + + + + + @@ -399,6 +420,14 @@ + + + + + + + + @@ -407,6 +436,14 @@ + + + + + + + + @@ -423,6 +460,14 @@ + + + + + + + + @@ -431,6 +476,14 @@ + + + + + + + + @@ -439,6 +492,14 @@ + + + + + + + + @@ -447,11 +508,24 @@ + + + + + + + + + + + + + @@ -460,6 +534,14 @@ + + + + + + + + @@ -468,6 +550,14 @@ + + + + + + + + @@ -476,6 +566,14 @@ + + + + + + + + @@ -484,6 +582,14 @@ + + + + + + + + @@ -492,6 +598,14 @@ + + + + + + + + @@ -500,6 +614,14 @@ + + + + + + + + @@ -508,6 +630,14 @@ + + + + + + + + @@ -521,6 +651,14 @@ + + + + + + + + @@ -529,6 +667,14 @@ + + + + + + + + @@ -537,6 +683,14 @@ + + + + + + + + @@ -545,6 +699,14 @@ + + + + + + + + @@ -553,6 +715,14 @@ + + + + + + + + @@ -561,6 +731,14 @@ + + + + + + + + @@ -569,6 +747,14 @@ + + + + + + + + @@ -577,6 +763,14 @@ + + + + + + + + @@ -585,6 +779,14 @@ + + + + + + + + @@ -593,6 +795,14 @@ + + + + + + + + @@ -601,6 +811,14 @@ + + + + + + + + @@ -609,6 +827,14 @@ + + + + + + + + @@ -617,11 +843,24 @@ + + + + + + + + + + + + + @@ -630,11 +869,24 @@ + + + + + + + + + + + + + @@ -643,6 +895,14 @@ + + + + + + + + @@ -651,6 +911,14 @@ + + + + + + + + @@ -659,6 +927,14 @@ + + + + + + + + @@ -667,6 +943,14 @@ + + + + + + + + @@ -675,6 +959,14 @@ + + + + + + + + @@ -683,6 +975,14 @@ + + + + + + + + @@ -691,6 +991,14 @@ + + + + + + + + From 393de8bce7cfb849f44f55e45ad58300f79d2634 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 08:22:32 +0000 Subject: [PATCH 004/272] Update dependency com.google.pay.button:compose-pay-button to v0.1.4 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 963969eea0..b4a858bc7d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -54,7 +54,7 @@ ext { // External Dependencies cash_app_pay_version = '2.3.0' - google_pay_compose_button_version = '0.1.3' + google_pay_compose_button_version = '0.1.4' okhttp_version = "4.12.0" play_services_wallet_version = '19.2.1' wechat_pay_version = "6.8.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 8010d085fb..235137f980 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3150,6 +3150,14 @@ + + + + + + + + @@ -3158,6 +3166,14 @@ + + + + + + + + @@ -3182,6 +3198,14 @@ + + + + + + + + @@ -3190,6 +3214,14 @@ + + + + + + + + @@ -3705,6 +3737,14 @@ + + + + + + + + From 32d4b8ddee9b48750d52b7df3ad4202e6a507f8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 08:49:04 +0000 Subject: [PATCH 005/272] Update dependency gradle to v8.6 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930b..a80b22ce5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85bee..7101f8e467 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 84fceef6e6b3c3996b52184644076fea7be46581 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 09:18:31 +0000 Subject: [PATCH 006/272] Update dependency com.pinterest.ktlint:ktlint-cli to v1.2.1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 243 +++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index b4a858bc7d..3e14d011e8 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -29,7 +29,7 @@ ext { // Code quality detekt_version = "1.23.5" jacoco_version = '0.8.11' - ktlint_version = '1.0.1' + ktlint_version = '1.2.1' sonarqube_version = '4.4.1.3373' // Android Dependencies diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 235137f980..9a1b2de681 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2196,6 +2196,14 @@ + + + + + + + + @@ -2204,6 +2212,14 @@ + + + + + + + + @@ -2212,6 +2228,11 @@ + + + + + @@ -3121,6 +3142,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3824,6 +3884,14 @@ + + + + + + + + @@ -3832,6 +3900,14 @@ + + + + + + + + @@ -3840,6 +3916,14 @@ + + + + + + + + @@ -3848,6 +3932,14 @@ + + + + + + + + @@ -3856,6 +3948,14 @@ + + + + + + + + @@ -3864,6 +3964,14 @@ + + + + + + + + @@ -3872,6 +3980,14 @@ + + + + + + + + @@ -3880,6 +3996,14 @@ + + + + + + + + @@ -3888,6 +4012,14 @@ + + + + + + + + @@ -3896,6 +4028,14 @@ + + + + + + + + @@ -3912,6 +4052,14 @@ + + + + + + + + @@ -3928,6 +4076,14 @@ + + + + + + + + @@ -3936,6 +4092,14 @@ + + + + + + + + @@ -3952,6 +4116,14 @@ + + + + + + + + @@ -3968,6 +4140,14 @@ + + + + + + + + @@ -4368,6 +4548,11 @@ + + + + + @@ -4376,6 +4561,14 @@ + + + + + + + + @@ -4434,6 +4627,14 @@ + + + + + + + + @@ -4804,6 +5005,14 @@ + + + + + + + + @@ -4888,6 +5097,14 @@ + + + + + + + + @@ -5465,6 +5682,11 @@ + + + + + @@ -5473,6 +5695,14 @@ + + + + + + + + @@ -6846,6 +7076,14 @@ + + + + + + + + @@ -6866,6 +7104,11 @@ + + + + + From df456dcc2a7cda589dde328829a6027cebcf3386 Mon Sep 17 00:00:00 2001 From: josephj Date: Fri, 12 Apr 2024 13:14:23 +0200 Subject: [PATCH 007/272] Make backing property _viewFlow in DefaultBacsDirectDebitDelegate private --- .../bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt | 4 +--- .../bacs/internal/DefaultBacsDirectDebitDelegateTest.kt | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 3d0c507ff0..9a1b0155dc 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -59,9 +59,7 @@ internal class DefaultBacsDirectDebitDelegate( override val uiStateFlow: Flow = submitHandler.uiStateFlow override val uiEventFlow: Flow = submitHandler.uiEventFlow - @VisibleForTesting - @Suppress("VariableNaming", "PropertyName") - internal val _viewFlow: MutableStateFlow = MutableStateFlow(BacsComponentViewType.INPUT) + private val _viewFlow: MutableStateFlow = MutableStateFlow(BacsComponentViewType.INPUT) override val viewFlow: Flow = _viewFlow override fun initialize(coroutineScope: CoroutineScope) { diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index 92bc7cf4ba..b980860d8c 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -277,7 +277,6 @@ internal class DefaultBacsDirectDebitDelegateTest( @Test fun `with INPUT parameter while current mode is also INPUT, then no value should be emitted`() = runTest { delegate.updateInputData { mode = BacsDirectDebitMode.INPUT } - delegate._viewFlow.value = BacsComponentViewType.INPUT delegate.viewFlow.test { awaitItem() @@ -292,7 +291,6 @@ internal class DefaultBacsDirectDebitDelegateTest( fun `with CONFIRMATION parameter while current mode is also CONFIRMATION, then no value should be emitted`() = runTest { delegate.updateInputData { mode = BacsDirectDebitMode.CONFIRMATION } - delegate._viewFlow.value = BacsComponentViewType.CONFIRMATION delegate.viewFlow.test { awaitItem() @@ -306,7 +304,6 @@ internal class DefaultBacsDirectDebitDelegateTest( @Test fun `with INPUT parameter while current mode is CONFIRMATION, then INPUT should be emitted`() = runTest { delegate.updateInputData { mode = BacsDirectDebitMode.CONFIRMATION } - delegate._viewFlow.value = BacsComponentViewType.CONFIRMATION delegate.viewFlow.test { awaitItem() @@ -329,7 +326,6 @@ internal class DefaultBacsDirectDebitDelegateTest( isAccountConsentChecked = true mode = BacsDirectDebitMode.INPUT } - delegate._viewFlow.value = BacsComponentViewType.INPUT delegate.viewFlow.test { awaitItem() @@ -344,7 +340,6 @@ internal class DefaultBacsDirectDebitDelegateTest( fun `with CONFIRMATION parameter while current mode is INPUT and input data is invalid, then no value should be emitted`() = runTest { delegate.updateInputData { mode = BacsDirectDebitMode.INPUT } - delegate._viewFlow.value = BacsComponentViewType.INPUT delegate.viewFlow.test { awaitItem() From bac6525691cc4e6ac56e4ebec090a0c630f3341d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:38:57 +0000 Subject: [PATCH 008/272] Update dependency com.google.android.gms:play-services-wallet to v19.3.0 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 3e14d011e8..9b41cd7078 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -56,7 +56,7 @@ ext { cash_app_pay_version = '2.3.0' google_pay_compose_button_version = '0.1.4' okhttp_version = "4.12.0" - play_services_wallet_version = '19.2.1' + play_services_wallet_version = '19.3.0' wechat_pay_version = "6.8.0" // Example app diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 9a1b2de681..214cfab503 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -1426,6 +1426,14 @@ + + + + + + + + From d986d23dcec8f7d5e7a3248eaf05e277ed176350 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:55:34 +0000 Subject: [PATCH 009/272] Update dependency androidx.test.ext:junit-ktx to v1.1.5 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 9b41cd7078..f9ad46f349 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -76,7 +76,7 @@ ext { mockito_kotlin_version = "4.1.0" mockito_version = "4.9.0" robolectric_version = "4.11.1" - test_ext_version = "1.1.4" + test_ext_version = "1.1.5" test_rules_version = "1.5.0" turbine_version = "0.12.1" uiautomator_version = "2.2.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 214cfab503..36e857a2f8 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2052,6 +2052,14 @@ + + + + + + + + @@ -2060,6 +2068,14 @@ + + + + + + + + From 9197625d32b3de8c32181b83515092398d45de92 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:29:15 +0000 Subject: [PATCH 010/272] Update espresso_version to v3.5.1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index f9ad46f349..bac684b40b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -69,7 +69,7 @@ ext { // Tests arch_core_testing_version = "2.2.0" - espresso_version = "3.5.0" + espresso_version = "3.5.1" json_version = '20231013' jose4j_version = '0.9.6' junit_jupiter_version = "5.9.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 36e857a2f8..250d0aa83a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2004,6 +2004,14 @@ + + + + + + + + @@ -2012,6 +2020,14 @@ + + + + + + + + @@ -2020,6 +2036,14 @@ + + + + + + + + @@ -2044,6 +2068,14 @@ + + + + + + + + From 1453af3ba2d06b4a74acbdc0086fb93659897232 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:27:53 +0000 Subject: [PATCH 011/272] Update kotlin --- dependencies.gradle | 6 +- gradle/verification-metadata.xml | 343 +++++++++++++++++++++++++++++++ 2 files changed, 346 insertions(+), 3 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index bac684b40b..2481ee8c8a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -20,11 +20,11 @@ ext { // Build Script android_gradle_plugin_version = '8.3.1' - kotlin_version = '1.9.22' + kotlin_version = '1.9.23' detekt_gradle_plugin_version = "1.23.5" dokka_version = "1.9.10" hilt_version = "2.51" - compose_compiler_version = '1.5.8' + compose_compiler_version = '1.5.11' // Code quality detekt_version = "1.23.5" @@ -36,7 +36,7 @@ ext { annotation_version = "1.7.1" appcompat_version = "1.6.1" browser_version = "1.7.0" - coroutines_version = "1.6.4" + coroutines_version = "1.8.0" fragment_version = "1.6.2" lifecycle_version = "2.5.1" material_version = "1.11.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 250d0aa83a..f5bdb1b3e4 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -444,6 +444,14 @@ + + + + + + + + @@ -5884,6 +5892,14 @@ + + + + + + + + @@ -5892,6 +5908,14 @@ + + + + + + + + @@ -5900,6 +5924,14 @@ + + + + + + + + @@ -5918,6 +5950,14 @@ + + + + + + + + @@ -5926,6 +5966,14 @@ + + + + + + + + @@ -5934,6 +5982,14 @@ + + + + + + + + @@ -5950,6 +6006,14 @@ + + + + + + + + @@ -5958,6 +6022,14 @@ + + + + + + + + @@ -5966,6 +6038,14 @@ + + + + + + + + @@ -5982,6 +6062,14 @@ + + + + + + + + @@ -5990,6 +6078,14 @@ + + + + + + + + @@ -5998,6 +6094,14 @@ + + + + + + + + @@ -6006,6 +6110,14 @@ + + + + + + + + @@ -6014,6 +6126,14 @@ + + + + + + + + @@ -6022,6 +6142,14 @@ + + + + + + + + @@ -6030,6 +6158,14 @@ + + + + + + + + @@ -6038,6 +6174,14 @@ + + + + + + + + @@ -6046,6 +6190,14 @@ + + + + + + + + @@ -6054,6 +6206,14 @@ + + + + + + + + @@ -6062,6 +6222,14 @@ + + + + + + + + @@ -6070,6 +6238,14 @@ + + + + + + + + @@ -6078,6 +6254,14 @@ + + + + + + + + @@ -6086,6 +6270,14 @@ + + + + + + + + @@ -6155,6 +6347,14 @@ + + + + + + + + @@ -6163,6 +6363,14 @@ + + + + + + + + @@ -6171,6 +6379,14 @@ + + + + + + + + @@ -6179,6 +6395,14 @@ + + + + + + + + @@ -6187,6 +6411,14 @@ + + + + + + + + @@ -6219,6 +6451,14 @@ + + + + + + + + @@ -6230,6 +6470,17 @@ + + + + + + + + + + + @@ -6282,11 +6533,21 @@ + + + + + + + + + + @@ -6437,6 +6698,14 @@ + + + + + + + + @@ -6445,6 +6714,14 @@ + + + + + + + + @@ -6453,11 +6730,24 @@ + + + + + + + + + + + + + @@ -6482,6 +6772,14 @@ + + + + + + + + @@ -6506,6 +6804,14 @@ + + + + + + + + @@ -6526,6 +6832,11 @@ + + + + + @@ -6555,6 +6866,14 @@ + + + + + + + + @@ -6587,6 +6906,14 @@ + + + + + + + + @@ -6600,6 +6927,14 @@ + + + + + + + + @@ -6616,6 +6951,14 @@ + + + + + + + + From 367448bf4d0574fbfe3128971162b3142428f5ec Mon Sep 17 00:00:00 2001 From: josephj Date: Mon, 22 Apr 2024 15:44:03 +0200 Subject: [PATCH 012/272] Fix failing unit test --- .../qrcode/internal/ui/DefaultQRCodeDelegateTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 819a4c7ff9..7cbcd6d0b9 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -43,6 +43,7 @@ import com.adyen.checkout.ui.core.internal.test.TestRedirectHandler import com.adyen.checkout.ui.core.internal.util.ImageSaver import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals @@ -58,6 +59,7 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -458,7 +460,9 @@ internal class DefaultQRCodeDelegateTest( @Test fun `when refreshStatus is called, then status for statusRepository gets refreshed`() = runTest { - val statusRepository = mock() + val statusRepository = mock { + on { poll(any(), any()) } doReturn flowOf() + } val paymentData = "Payment Data" val delegate = createDelegate( statusRepository = statusRepository, From 83c9440fa1ac47f5e39683f928273efae198ad19 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:25:59 +0000 Subject: [PATCH 013/272] Update lifecycle_version to v2.7.0 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 2481ee8c8a..cca096a441 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -38,7 +38,7 @@ ext { browser_version = "1.7.0" coroutines_version = "1.8.0" fragment_version = "1.6.2" - lifecycle_version = "2.5.1" + lifecycle_version = "2.7.0" material_version = "1.11.0" recyclerview_version = "1.3.2" constraintlayout_version = '2.1.4' From 2ac42c75fb797e3bf1d2e3e7d1dfe686dda02b37 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:44:44 +0000 Subject: [PATCH 014/272] Update dependency org.mockito.kotlin:mockito-kotlin to v5 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index cca096a441..d1a0a92b39 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -73,7 +73,7 @@ ext { json_version = '20231013' jose4j_version = '0.9.6' junit_jupiter_version = "5.9.1" - mockito_kotlin_version = "4.1.0" + mockito_kotlin_version = "5.2.1" mockito_version = "4.9.0" robolectric_version = "4.11.1" test_ext_version = "1.1.5" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f5bdb1b3e4..f271aed4eb 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5130,6 +5130,14 @@ + + + + + + + + @@ -5138,6 +5146,14 @@ + + + + + + + + @@ -5151,6 +5167,11 @@ + + + + + @@ -7170,6 +7191,14 @@ + + + + + + + + @@ -7194,6 +7223,14 @@ + + + + + + + + From 4ff8799afd65ee4c3c544f0194800a69b401a78b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 22:41:32 +0000 Subject: [PATCH 015/272] Update retrofit2_version to v2.10.0 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index d1a0a92b39..b54ccef5c0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -65,7 +65,7 @@ ext { moshi_kotlin_adapter_version = '1.14.0' okhttp_logging_version = "4.12.0" preference_version = "1.2.1" - retrofit2_version = '2.9.0' + retrofit2_version = '2.10.0' // Tests arch_core_testing_version = "2.2.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f271aed4eb..2b33afe5f0 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4401,6 +4401,14 @@ + + + + + + + + @@ -4479,6 +4487,14 @@ + + + + + + + + @@ -4487,6 +4503,22 @@ + + + + + + + + + + + + + + + + @@ -4495,6 +4527,14 @@ + + + + + + + + @@ -6607,6 +6647,9 @@ + + + @@ -6683,6 +6726,9 @@ + + + From de5f325807b350ab640548224e87f7e5a19acd56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 06:50:26 +0000 Subject: [PATCH 016/272] Update dependency org.json:json to v20240303 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index b54ccef5c0..7eb7f2d646 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -70,7 +70,7 @@ ext { // Tests arch_core_testing_version = "2.2.0" espresso_version = "3.5.1" - json_version = '20231013' + json_version = '20240303' jose4j_version = '0.9.6' junit_jupiter_version = "5.9.1" mockito_kotlin_version = "5.2.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 2b33afe5f0..3a3f661cc8 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -7120,6 +7120,14 @@ + + + + + + + + From 28909de0f55c92cd3089bb98b801b62f00f48bb5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:08:44 +0000 Subject: [PATCH 017/272] Update junit5 monorepo to v5.10.2 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 7eb7f2d646..39f3928d86 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -72,7 +72,7 @@ ext { espresso_version = "3.5.1" json_version = '20240303' jose4j_version = '0.9.6' - junit_jupiter_version = "5.9.1" + junit_jupiter_version = "5.10.2" mockito_kotlin_version = "5.2.1" mockito_version = "4.9.0" robolectric_version = "4.11.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 3a3f661cc8..985af194df 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -7144,6 +7144,14 @@ + + + + + + + + @@ -7168,6 +7176,14 @@ + + + + + + + + @@ -7176,6 +7192,14 @@ + + + + + + + + @@ -7184,6 +7208,14 @@ + + + + + + + + @@ -7192,6 +7224,14 @@ + + + + + + + + @@ -7200,6 +7240,14 @@ + + + + + + + + @@ -7208,6 +7256,14 @@ + + + + + + + + @@ -7306,6 +7362,14 @@ + + + + + + + + From 25d27b6ed1677550595df231ef72c05483fca1e7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:33:35 +0000 Subject: [PATCH 018/272] Update mockito monorepo to v5 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 39f3928d86..2e200c9e5b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -74,7 +74,7 @@ ext { jose4j_version = '0.9.6' junit_jupiter_version = "5.10.2" mockito_kotlin_version = "5.2.1" - mockito_version = "4.9.0" + mockito_version = "5.11.0" robolectric_version = "4.11.1" test_ext_version = "1.1.5" test_rules_version = "1.5.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 985af194df..6d9966db6a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5170,6 +5170,14 @@ + + + + + + + + @@ -5186,6 +5194,14 @@ + + + + + + + + @@ -5202,11 +5218,24 @@ + + + + + + + + + + + + + @@ -7288,6 +7317,14 @@ + + + + + + + + @@ -7301,6 +7338,14 @@ + + + + + + + + @@ -7325,6 +7370,14 @@ + + + + + + + + From 4476e2d1deffe555f6ad76d4b37d51da9d394f15 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 23 Apr 2024 10:36:28 +0200 Subject: [PATCH 019/272] Remove mockito-inline mockito-inline is now the default for mockito, so it's no longer needed. --- dependencies.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 2e200c9e5b..58eef23700 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -163,7 +163,6 @@ ext { ], mockito : [ "org.mockito:mockito-junit-jupiter:$mockito_version", - "org.mockito:mockito-inline:$mockito_version", "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version" ], mockitoAndroid : [ From 375e0afe471816f9b951a2deca04dc05d02b84fd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:35:26 +0000 Subject: [PATCH 020/272] Update dependency com.squareup.moshi:moshi-adapters to v1.15.1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 58eef23700..6dc22496ac 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -61,7 +61,7 @@ ext { // Example app leak_canary_version = '2.13' - moshi_adapters_version = '1.14.0' + moshi_adapters_version = '1.15.1' moshi_kotlin_adapter_version = '1.14.0' okhttp_logging_version = "4.12.0" preference_version = "1.2.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6d9966db6a..b1ae7ce8d2 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4422,6 +4422,14 @@ + + + + + + + + From e366977e11a2a56e6df72ce33314db66845f2164 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:00:56 +0000 Subject: [PATCH 021/272] Update dependency com.squareup.moshi:moshi-kotlin to v1.15.1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 6dc22496ac..1f328b3b10 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -62,7 +62,7 @@ ext { // Example app leak_canary_version = '2.13' moshi_adapters_version = '1.15.1' - moshi_kotlin_adapter_version = '1.14.0' + moshi_kotlin_adapter_version = '1.15.1' okhttp_logging_version = "4.12.0" preference_version = "1.2.1" retrofit2_version = '2.10.0' diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b1ae7ce8d2..20c23d5c41 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -4446,6 +4446,14 @@ + + + + + + + + @@ -6397,6 +6405,14 @@ + + + + + + + + From bd9f298e6463fbb6df701c7a8f2f82ed41adcfbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:13:44 +0000 Subject: [PATCH 022/272] Update dependency com.google.pay.button:compose-pay-button to v1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 1f328b3b10..a9119878a7 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -54,7 +54,7 @@ ext { // External Dependencies cash_app_pay_version = '2.3.0' - google_pay_compose_button_version = '0.1.4' + google_pay_compose_button_version = '1.0.0' okhttp_version = "4.12.0" play_services_wallet_version = '19.3.0' wechat_pay_version = "6.8.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 20c23d5c41..2b29a44fc3 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -3869,6 +3869,14 @@ + + + + + + + + From 0b5935a20d1f9d2b94b6eec0b1db6e9bcb81551e Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 15 Apr 2024 16:39:45 +0200 Subject: [PATCH 023/272] Use correct coroutine scope in CardComponentDialogFragment COAND-869 --- .../internal/ui/CardComponentDialogFragment.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/CardComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/CardComponentDialogFragment.kt index b5d1b0801b..891965b3f2 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/CardComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/CardComponentDialogFragment.kt @@ -12,7 +12,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.lifecycleScope import com.adyen.checkout.card.CardComponent import com.adyen.checkout.components.core.AddressLookupCallback import com.adyen.checkout.components.core.LookupAddress @@ -56,13 +56,13 @@ internal class CardComponentDialogFragment : BaseComponentDialogFragment(), Addr binding.cardView.requestFocus() } - dropInViewModel.addressLookupOptionsFlow.onEach { - cardComponent.updateAddressLookupOptions(it) - }.launchIn(dropInViewModel.viewModelScope) + dropInViewModel.addressLookupOptionsFlow + .onEach { cardComponent.updateAddressLookupOptions(it) } + .launchIn(viewLifecycleOwner.lifecycleScope) - dropInViewModel.addressLookupCompleteFlow.onEach { - cardComponent.setAddressLookupResult(it) - }.launchIn(dropInViewModel.viewModelScope) + dropInViewModel.addressLookupCompleteFlow + .onEach { cardComponent.setAddressLookupResult(it) } + .launchIn(viewLifecycleOwner.lifecycleScope) } override fun onQueryChanged(query: String) { From 5609b01b817dc0c9cfe5b0b0435f92af43ab7511 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 15 Apr 2024 16:43:24 +0200 Subject: [PATCH 024/272] Properly create ViewModel in PaymentMethodListDialogFragment Also removed confusing ViewModel creation extension methods. COAND-869 --- .../ui/PaymentMethodListDialogFragment.kt | 28 +++++----- .../PreselectedStoredPaymentMethodFragment.kt | 16 +++--- .../dropin/internal/util/DropInExt.kt | 53 ------------------- 3 files changed, 25 insertions(+), 72 deletions(-) delete mode 100644 drop-in/src/main/java/com/adyen/checkout/dropin/internal/util/DropInExt.kt diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt index a8d19604a0..0f10906f9f 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt @@ -15,11 +15,13 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.core.view.children +import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent +import com.adyen.checkout.components.core.internal.util.viewModelFactory import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.dropin.R @@ -31,7 +33,6 @@ import com.adyen.checkout.dropin.internal.ui.model.PaymentMethodModel import com.adyen.checkout.dropin.internal.ui.model.StoredACHDirectDebitModel import com.adyen.checkout.dropin.internal.ui.model.StoredCardModel import com.adyen.checkout.dropin.internal.ui.model.StoredPaymentMethodModel -import com.adyen.checkout.dropin.internal.util.getViewModel import com.adyen.checkout.ui.core.internal.ui.view.AdyenSwipeToRevealLayout import com.adyen.checkout.ui.core.internal.util.PayButtonFormatter import kotlinx.coroutines.flow.launchIn @@ -46,7 +47,19 @@ internal class PaymentMethodListDialogFragment : private var _binding: FragmentPaymentMethodsListBinding? = null private val binding: FragmentPaymentMethodsListBinding get() = requireNotNull(_binding) - private lateinit var paymentMethodsListViewModel: PaymentMethodsListViewModel + private val paymentMethodsListViewModel: PaymentMethodsListViewModel by viewModels { + viewModelFactory { + PaymentMethodsListViewModel( + application = requireActivity().application, + paymentMethods = dropInViewModel.getPaymentMethods(), + storedPaymentMethods = dropInViewModel.getStoredPaymentMethods(), + order = dropInViewModel.currentOrder, + checkoutConfiguration = dropInViewModel.checkoutConfiguration, + dropInParams = dropInViewModel.dropInParams, + dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), + ) + } + } private var paymentMethodAdapter: PaymentMethodAdapter? = null private var component: PaymentComponent? = null @@ -58,17 +71,6 @@ internal class PaymentMethodListDialogFragment : override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { adyenLog(AdyenLogLevel.DEBUG) { "onCreateView" } - paymentMethodsListViewModel = getViewModel { - PaymentMethodsListViewModel( - application = requireActivity().application, - paymentMethods = dropInViewModel.getPaymentMethods(), - storedPaymentMethods = dropInViewModel.getStoredPaymentMethods(), - order = dropInViewModel.currentOrder, - checkoutConfiguration = dropInViewModel.checkoutConfiguration, - dropInParams = dropInViewModel.dropInParams, - dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), - ) - } _binding = FragmentPaymentMethodsListBinding.inflate(inflater, container, false) return binding.root } diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt index ee7cb32148..f5c2e37c07 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt @@ -14,11 +14,13 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible +import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.adyen.checkout.components.core.ComponentError import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent import com.adyen.checkout.components.core.internal.util.DateUtils +import com.adyen.checkout.components.core.internal.util.viewModelFactory import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException @@ -31,7 +33,6 @@ import com.adyen.checkout.dropin.internal.ui.model.StoredACHDirectDebitModel import com.adyen.checkout.dropin.internal.ui.model.StoredCardModel import com.adyen.checkout.dropin.internal.ui.model.StoredPaymentMethodModel import com.adyen.checkout.dropin.internal.util.arguments -import com.adyen.checkout.dropin.internal.util.viewModelsFactory import com.adyen.checkout.ui.core.internal.ui.loadLogo import com.adyen.checkout.ui.core.internal.util.PayButtonFormatter import kotlinx.coroutines.flow.launchIn @@ -40,11 +41,14 @@ import kotlinx.coroutines.flow.onEach @Suppress("TooManyFunctions") internal class PreselectedStoredPaymentMethodFragment : DropInBottomSheetDialogFragment() { - private val storedPaymentViewModel: PreselectedStoredPaymentViewModel by viewModelsFactory { - PreselectedStoredPaymentViewModel( - storedPaymentMethod, - dropInViewModel.dropInParams, - ) + private val storedPaymentViewModel: PreselectedStoredPaymentViewModel by viewModels { + viewModelFactory { + PreselectedStoredPaymentViewModel( + storedPaymentMethod, + dropInViewModel.dropInParams, + ) + } + } private var _binding: FragmentStoredPaymentMethodBinding? = null diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/util/DropInExt.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/util/DropInExt.kt deleted file mode 100644 index 8e3a68c68f..0000000000 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/util/DropInExt.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by caiof on 1/12/2020. - */ - -package com.adyen.checkout.dropin.internal.util - -import androidx.activity.viewModels -import androidx.annotation.MainThread -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.adyen.checkout.components.core.internal.util.viewModelFactory - -@MainThread -internal inline fun AppCompatActivity.getViewModel( - crossinline factoryProducer: () -> ViewModelT -): ViewModelT { - return ViewModelProvider(this, viewModelFactory(factoryProducer))[ViewModelT::class.java] -} - -@MainThread -internal inline fun Fragment.getViewModel( - crossinline f: () -> ViewModelT -): ViewModelT { - return ViewModelProvider(this, viewModelFactory(f))[ViewModelT::class.java] -} - -@MainThread -internal inline fun Fragment.getActivityViewModel( - crossinline f: () -> ViewModelT -): ViewModelT { - return ViewModelProvider(requireActivity(), viewModelFactory(f))[ViewModelT::class.java] -} - -@MainThread -internal inline fun AppCompatActivity.viewModelsFactory( - crossinline factoryProducer: () -> VM -): Lazy { - return viewModels { viewModelFactory(factoryProducer) } -} - -@MainThread -internal inline fun Fragment.viewModelsFactory( - crossinline factoryProducer: () -> VM -): Lazy { - return viewModels { viewModelFactory(factoryProducer) } -} From aea5fd2866b37794f9fbf268afd90fc044f69c25 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 15 Apr 2024 17:17:54 +0200 Subject: [PATCH 025/272] Remove component reference in PreselectedStoredPaymentMethodFragment COAND-869 --- .../internal/ui/PreselectedStoredPaymentMethodFragment.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt index f5c2e37c07..fadba39f1c 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt @@ -18,7 +18,6 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.adyen.checkout.components.core.ComponentError import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.PaymentComponent import com.adyen.checkout.components.core.internal.util.DateUtils import com.adyen.checkout.components.core.internal.util.viewModelFactory import com.adyen.checkout.core.AdyenLogLevel @@ -48,13 +47,11 @@ internal class PreselectedStoredPaymentMethodFragment : DropInBottomSheetDialogF dropInViewModel.dropInParams, ) } - } private var _binding: FragmentStoredPaymentMethodBinding? = null private val binding: FragmentStoredPaymentMethodBinding get() = requireNotNull(_binding) private val storedPaymentMethod: StoredPaymentMethod by arguments(STORED_PAYMENT_KEY) - private lateinit var component: PaymentComponent override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { if (storedPaymentMethod.type.isNullOrEmpty()) { @@ -77,7 +74,7 @@ internal class PreselectedStoredPaymentMethodFragment : DropInBottomSheetDialogF private fun loadComponent() { try { - component = getComponentFor( + getComponentFor( fragment = this, storedPaymentMethod = storedPaymentMethod, checkoutConfiguration = dropInViewModel.checkoutConfiguration, From 5632d8a383304ed218e16fc85fc292271b5ae173 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 15 Apr 2024 17:20:17 +0200 Subject: [PATCH 026/272] Use correct coroutine scope in PaymentMethodListDialogFragment COAND-869 --- .../dropin/internal/ui/PaymentMethodListDialogFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt index 0f10906f9f..955fd2fb14 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt @@ -98,7 +98,8 @@ internal class PaymentMethodListDialogFragment : .onEach { paymentMethods -> adyenLog(AdyenLogLevel.DEBUG) { "paymentMethods changed" } paymentMethodAdapter?.submitList(paymentMethods) - }.launchIn(lifecycleScope) + } + .launchIn(viewLifecycleOwner.lifecycleScope) paymentMethodsListViewModel .eventsFlow @@ -132,7 +133,7 @@ internal class PaymentMethodListDialogFragment : } } } - .launchIn(lifecycleScope) + .launchIn(viewLifecycleOwner.lifecycleScope) } override fun onDestroyView() { From c088e6b77496fce45d49672446b72971ce857ca8 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 16 Apr 2024 09:25:58 +0200 Subject: [PATCH 027/272] Cleanup reference to Protocol in DropInBottomSheetDialogFragment COAND-869 --- .../internal/ui/DropInBottomSheetDialogFragment.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInBottomSheetDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInBottomSheetDialogFragment.kt index 1913fc218d..6271f01e01 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInBottomSheetDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInBottomSheetDialogFragment.kt @@ -30,7 +30,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment internal abstract class DropInBottomSheetDialogFragment : BottomSheetDialogFragment() { - lateinit var protocol: Protocol + private var _protocol: Protocol? = null + protected val protocol: Protocol get() = requireNotNull(_protocol) private var dialogInitViewState: Int = BottomSheetBehavior.STATE_COLLAPSED protected val dropInViewModel: DropInViewModel by activityViewModels { DropInViewModelFactory(requireActivity()) } @@ -43,7 +44,7 @@ internal abstract class DropInBottomSheetDialogFragment : BottomSheetDialogFragm super.onAttach(context) require(activity is Protocol) - protocol = activity as Protocol + _protocol = activity as Protocol } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -88,6 +89,11 @@ internal abstract class DropInBottomSheetDialogFragment : BottomSheetDialogFragm protocol.terminateDropIn() } + override fun onDetach() { + _protocol = null + super.onDetach() + } + /** * Interface for Drop-in fragments to interact with the main Activity */ From bb4d781549322e434c62a4197b0f304beadb1497 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 16 Apr 2024 13:35:09 +0200 Subject: [PATCH 028/272] Fix build warning COAND-869 --- .../com/adyen/checkout/test/extensions/ViewModelExtensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-core/src/main/java/com/adyen/checkout/test/extensions/ViewModelExtensions.kt b/test-core/src/main/java/com/adyen/checkout/test/extensions/ViewModelExtensions.kt index 6897897300..2a2631fb18 100644 --- a/test-core/src/main/java/com/adyen/checkout/test/extensions/ViewModelExtensions.kt +++ b/test-core/src/main/java/com/adyen/checkout/test/extensions/ViewModelExtensions.kt @@ -18,9 +18,9 @@ import androidx.lifecycle.ViewModel */ @RestrictTo(RestrictTo.Scope.TESTS) fun ViewModel.invokeOnCleared() { - var clazz = javaClass as Class + var clazz: Class<*> = javaClass while (clazz.declaredMethods.toList().none { it.name == "onCleared" }) { - clazz = clazz.superclass as Class + clazz = clazz.superclass } with(clazz.getDeclaredMethod("onCleared")) { isAccessible = true From cea44216e11ba069894904eb55c1873984c756ba Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 17 Apr 2024 13:16:45 +0200 Subject: [PATCH 029/272] Bind BaseDropInService to DropInActivity lifecycle COAND-869 --- .../internal/service/BaseDropInService.kt | 34 ++----------------- .../dropin/internal/ui/DropInActivity.kt | 7 ++-- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/service/BaseDropInService.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/service/BaseDropInService.kt index cfd44ddf3e..27fc2b8f69 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/service/BaseDropInService.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/service/BaseDropInService.kt @@ -167,22 +167,7 @@ constructor() : Service(), CoroutineScope, BaseDropInServiceInterface, BaseDropI private const val INTENT_EXTRA_ADDITIONAL_DATA = "ADDITIONAL_DATA" - internal fun startService( - context: Context, - connection: ServiceConnection, - merchantService: ComponentName, - additionalData: Bundle?, - ): Boolean { - adyenLog(AdyenLogLevel.DEBUG) { "startService - ${context::class.simpleName}" } - val intent = Intent().apply { - component = merchantService - } - adyenLog(AdyenLogLevel.DEBUG) { "merchant service: ${merchantService.className}" } - context.startService(intent) - return bindService(context, connection, merchantService, additionalData) - } - - private fun bindService( + internal fun bindService( context: Context, connection: ServiceConnection, merchantService: ComponentName, @@ -196,22 +181,7 @@ constructor() : Service(), CoroutineScope, BaseDropInServiceInterface, BaseDropI return context.bindService(intent, connection, Context.BIND_AUTO_CREATE) } - internal fun stopService( - context: Context, - merchantService: ComponentName, - connection: ServiceConnection, - ) { - unbindService(context, connection) - - adyenLog(AdyenLogLevel.DEBUG) { "stopService - ${context::class.simpleName}" } - - val intent = Intent().apply { - component = merchantService - } - context.stopService(intent) - } - - private fun unbindService(context: Context, connection: ServiceConnection) { + internal fun unbindService(context: Context, connection: ServiceConnection) { adyenLog(AdyenLogLevel.DEBUG) { "unbindService - ${context::class.simpleName}" } context.unbindService(connection) } diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt index 15dec810c6..e123eece40 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt @@ -132,6 +132,7 @@ internal class DropInActivity : override fun onServiceDisconnected(className: ComponentName) { adyenLog(AdyenLogLevel.DEBUG) { "onServiceDisconnected" } + serviceBound = false dropInService = null } } @@ -214,7 +215,7 @@ internal class DropInActivity : } private fun startDropInService() { - val bound = BaseDropInService.startService( + val bound = BaseDropInService.bindService( context = this, connection = serviceConnection, merchantService = dropInViewModel.serviceComponentName, @@ -237,9 +238,8 @@ internal class DropInActivity : private fun stopDropInService() { if (serviceBound) { - BaseDropInService.stopService( + BaseDropInService.unbindService( context = this, - merchantService = dropInViewModel.serviceComponentName, connection = serviceConnection, ) serviceBound = false @@ -295,6 +295,7 @@ internal class DropInActivity : override fun onDestroy() { adyenLog(AdyenLogLevel.VERBOSE) { "onDestroy" } + stopDropInService() super.onDestroy() } From e0abbc8ebf5f2e77cd436c0cc32caf94de513770 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 17 Apr 2024 14:02:14 +0200 Subject: [PATCH 030/272] Add release note COAND-869 --- RELEASE_NOTES.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3574118366..940d99ce26 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,17 +9,4 @@ [//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object) ## Fixed -- For Drop-in and Components, when `?android:attr/textColor` is not defined in your own theme, the Card Component no longer crashes. -- The `onAdditionalDetails` event is now triggered only once. Previously, the event was triggered multiple times in some edge cases. -- The build output no longer contains warnings about multiple substitutions specified in non-positional format in string resources. -- For the Card Component, we fixed localization issues that occurred when using the Address Lookup functionality. -- Overriding some of the XML styles without specifying a parent style no longer causes a build error. - -## Removed -- You can no longer use functions like `CheckoutConfiguration.getCardConfiguration()` or `CheckoutConfiguration.getDropInConfiguration()` to get configurations from the `CheckoutConfiguration` object. When starting Drop-in or Components, pass the full `CheckoutConfiguration` object. - -## Changed -- Dependency versions: - | Name | Version | - |--------------------------------------------------------------------------------------------------------|-------------------------------| - | [Android Gradle plugin](https://developer.android.com/build/releases/gradle-plugin) | **8.3.1** | +- Fixed a memory leak in `DropInActivity`. From c7e3c49e36af25cc15e447e383cf0142371fb797 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 13:37:10 +0000 Subject: [PATCH 031/272] Update dependency app.cash.turbine:turbine to v1 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index a9119878a7..4a19888e52 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -78,7 +78,7 @@ ext { robolectric_version = "4.11.1" test_ext_version = "1.1.5" test_rules_version = "1.5.0" - turbine_version = "0.12.1" + turbine_version = "1.1.0" uiautomator_version = "2.2.0" libraries = [ diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 2b29a44fc3..0fc9f673ad 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2252,6 +2252,14 @@ + + + + + + + + @@ -2260,6 +2268,14 @@ + + + + + + + + From 5ec8cde6041466bd92709cd6686133c10fde686b Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 23 Apr 2024 15:41:56 +0200 Subject: [PATCH 032/272] Fix turbine tests failing with v1 upgrade --- .../internal/ui/DefaultCardDelegateTest.kt | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index d5cb26abb3..0fd637df4e 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -10,7 +10,6 @@ package com.adyen.checkout.card.internal.ui import androidx.annotation.StringRes import app.cash.turbine.test -import app.cash.turbine.testIn import com.adyen.checkout.card.AddressConfiguration import com.adyen.checkout.card.CardBrand import com.adyen.checkout.card.CardComponentState @@ -59,6 +58,7 @@ import com.adyen.checkout.cse.internal.BaseGenericEncryptor import com.adyen.checkout.cse.internal.test.TestCardEncryptor import com.adyen.checkout.cse.internal.test.TestGenericEncryptor import com.adyen.checkout.test.TestDispatcherExtension +import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.data.api.AddressRepository import com.adyen.checkout.ui.core.internal.test.TestAddressRepository import com.adyen.checkout.ui.core.internal.ui.AddressFormUIState @@ -70,6 +70,7 @@ import com.adyen.checkout.ui.core.internal.ui.model.AddressParams import com.adyen.checkout.ui.core.internal.util.AddressFormUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -141,22 +142,22 @@ internal class DefaultCardDelegateTest( inner class InputDataChangedTest { @Test fun `address configuration is none, then countries and states should not be fetched`() = runTest { - val countriesFlow = addressRepository.countriesFlow.testIn(this) - val statesFlow = addressRepository.statesFlow.testIn(this) + val countriesFlow = addressRepository.countriesFlow.test(testScheduler) + val statesFlow = addressRepository.statesFlow.test(testScheduler) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - countriesFlow.expectNoEvents() - statesFlow.expectNoEvents() + assert(countriesFlow.values.isEmpty()) + assert(statesFlow.values.isEmpty()) - countriesFlow.cancelAndIgnoreRemainingEvents() - statesFlow.cancelAndIgnoreRemainingEvents() + countriesFlow.cancel() + statesFlow.cancel() } @Test fun `address configuration is postal code, then countries and states should not be fetched`() = runTest { - val countriesFlow = addressRepository.countriesFlow.testIn(this) - val statesFlow = addressRepository.statesFlow.testIn(this) + val countriesFlow = addressRepository.countriesFlow.test(testScheduler) + val statesFlow = addressRepository.statesFlow.test(testScheduler) delegate = createCardDelegate( configuration = createCheckoutConfiguration { setAddressConfiguration(AddressConfiguration.PostalCode()) @@ -165,17 +166,17 @@ internal class DefaultCardDelegateTest( delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - countriesFlow.expectNoEvents() - statesFlow.expectNoEvents() + assert(countriesFlow.values.isEmpty()) + assert(statesFlow.values.isEmpty()) - countriesFlow.cancelAndIgnoreRemainingEvents() - statesFlow.cancelAndIgnoreRemainingEvents() + countriesFlow.cancel() + statesFlow.cancel() } @Test fun `address repository returns error, then countries should be emitted empty`() = runTest { - val countriesFlow = addressRepository.countriesFlow.testIn(this) - val statesFlow = addressRepository.statesFlow.testIn(this) + val countriesFlow = addressRepository.countriesFlow.test(testScheduler) + val statesFlow = addressRepository.statesFlow.test(testScheduler) addressRepository.shouldReturnError = true delegate = createCardDelegate( @@ -185,18 +186,18 @@ internal class DefaultCardDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - assertTrue(countriesFlow.awaitItem().isEmpty()) - statesFlow.expectNoEvents() + assertTrue(countriesFlow.latestValue.isEmpty()) + assert(statesFlow.values.isEmpty()) - countriesFlow.cancelAndIgnoreRemainingEvents() - statesFlow.cancelAndIgnoreRemainingEvents() + countriesFlow.cancel() + statesFlow.cancel() } @Test fun `address configuration is full address with default country, then countries and states should be emitted`() = runTest { - val countriesFlow = addressRepository.countriesFlow.testIn(this) - val statesFlow = addressRepository.statesFlow.testIn(this) + val countriesFlow = addressRepository.countriesFlow.test(testScheduler) + val statesFlow = addressRepository.statesFlow.test(testScheduler) delegate = createCardDelegate( configuration = createCheckoutConfiguration { @@ -205,18 +206,18 @@ internal class DefaultCardDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - assertEquals(TestAddressRepository.COUNTRIES, countriesFlow.awaitItem()) - assertEquals(TestAddressRepository.STATES, statesFlow.awaitItem()) + assertEquals(TestAddressRepository.COUNTRIES, countriesFlow.latestValue) + assertEquals(TestAddressRepository.STATES, statesFlow.latestValue) - countriesFlow.cancelAndIgnoreRemainingEvents() - statesFlow.cancelAndIgnoreRemainingEvents() + countriesFlow.cancel() + statesFlow.cancel() } @Test fun `address configuration is full address without default country, then only countries should be emitted`() = runTest { - val countriesFlow = addressRepository.countriesFlow.testIn(this) - val statesFlow = addressRepository.statesFlow.testIn(this) + val countriesFlow = addressRepository.countriesFlow.test(testScheduler) + val statesFlow = addressRepository.statesFlow.test(testScheduler) delegate = createCardDelegate( configuration = createCheckoutConfiguration(shopperLocale = Locale.CANADA) { @@ -225,11 +226,11 @@ internal class DefaultCardDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - assertEquals(TestAddressRepository.COUNTRIES, countriesFlow.awaitItem()) - statesFlow.expectNoEvents() + assertEquals(TestAddressRepository.COUNTRIES, countriesFlow.latestValue) + assert(statesFlow.values.isEmpty()) - countriesFlow.cancelAndIgnoreRemainingEvents() - statesFlow.cancelAndIgnoreRemainingEvents() + countriesFlow.cancel() + statesFlow.cancel() } @Test From 2022d88067d174adef52596c09456659e757cc50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:29:52 +0000 Subject: [PATCH 033/272] Update ts-graphviz/setup-graphviz action to v2 --- .github/workflows/generate_dependency_graph.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate_dependency_graph.yml b/.github/workflows/generate_dependency_graph.yml index 724419dc54..e6f6f43ff4 100644 --- a/.github/workflows/generate_dependency_graph.yml +++ b/.github/workflows/generate_dependency_graph.yml @@ -20,7 +20,7 @@ jobs: cache: 'gradle' - name: Setup Graphviz - uses: ts-graphviz/setup-graphviz@v1 + uses: ts-graphviz/setup-graphviz@v2 - name: Generate Dependency Graph run: ./gradlew dependencyGraph From 6720521f782d1b02d093852964d1f9030c2ecfb1 Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 23 Apr 2024 17:58:45 +0200 Subject: [PATCH 034/272] Fix action components leaking callbacks through DefaultActionComponentEventHandler --- .../internal/provider/Adyen3DS2ComponentProvider.kt | 6 ++++-- .../internal/provider/GenericActionComponentProvider.kt | 6 ++++-- .../await/internal/provider/AwaitComponentProvider.kt | 6 ++++-- .../components/core/internal/ActionComponentEventHandler.kt | 3 ++- .../core/internal/DefaultActionComponentEventHandler.kt | 6 ++---- .../qrcode/internal/provider/QRCodeComponentProvider.kt | 6 ++++-- .../redirect/internal/provider/RedirectComponentProvider.kt | 6 ++++-- .../voucher/internal/provider/VoucherComponentProvider.kt | 6 ++++-- .../internal/provider/WeChatPayActionComponentProvider.kt | 6 ++++-- 9 files changed, 32 insertions(+), 19 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt index dc48488c83..63633597fc 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt @@ -66,12 +66,14 @@ constructor( Adyen3DS2Component( delegate = adyen3DS2Delegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, threeDS2Factory)[key, Adyen3DS2Component::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt index 9dfc7f7ef2..31c72334f9 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt @@ -62,12 +62,14 @@ constructor( val genericActionDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) GenericActionComponent( genericActionDelegate = genericActionDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, genericActionFactory)[key, GenericActionComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt index 41ec02bb66..c96bc5e10a 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt @@ -62,11 +62,13 @@ constructor( val awaitDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) AwaitComponent( awaitDelegate, - DefaultActionComponentEventHandler(callback), + DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, awaitFactory)[key, AwaitComponent::class.java].also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ActionComponentEventHandler.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ActionComponentEventHandler.kt index a5124d077e..e8f1e4d0d9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ActionComponentEventHandler.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ActionComponentEventHandler.kt @@ -9,8 +9,9 @@ package com.adyen.checkout.components.core.internal import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.ActionComponentCallback @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) interface ActionComponentEventHandler { - fun onActionComponentEvent(event: ActionComponentEvent) + fun onActionComponentEvent(event: ActionComponentEvent, actionComponentCallback: ActionComponentCallback) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/DefaultActionComponentEventHandler.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/DefaultActionComponentEventHandler.kt index 6f56eb60ee..15fedb50d4 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/DefaultActionComponentEventHandler.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/DefaultActionComponentEventHandler.kt @@ -14,11 +14,9 @@ import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class DefaultActionComponentEventHandler( - private val actionComponentCallback: ActionComponentCallback -) : ActionComponentEventHandler { +class DefaultActionComponentEventHandler : ActionComponentEventHandler { - override fun onActionComponentEvent(event: ActionComponentEvent) { + override fun onActionComponentEvent(event: ActionComponentEvent, actionComponentCallback: ActionComponentCallback) { adyenLog(AdyenLogLevel.VERBOSE) { "Event received $event" } when (event) { is ActionComponentEvent.ActionDetails -> actionComponentCallback.onAdditionalDetails(event.data) diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt index 787954eef8..0038093ca1 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt @@ -61,12 +61,14 @@ constructor( val qrCodeDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) QRCodeComponent( delegate = qrCodeDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, qrCodeFactory)[key, QRCodeComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt index 07a742fc9d..f0cac0d3af 100644 --- a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt +++ b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt @@ -59,12 +59,14 @@ constructor( val redirectDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) RedirectComponent( delegate = redirectDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, redirectFactory)[key, RedirectComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt index bc9f857239..78aa301a2e 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt @@ -57,12 +57,14 @@ constructor( val voucherDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) VoucherComponent( delegate = voucherDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, voucherFactory)[key, VoucherComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt index a56ddfa77e..8b199b49ef 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt @@ -59,13 +59,15 @@ constructor( val weChatDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) WeChatPayActionComponent( delegate = weChatDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, weChatFactory)[key, WeChatPayActionComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } From df7b3465738be9129b9d69cdc3c81efeb9c7b0a7 Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 23 Apr 2024 18:17:07 +0200 Subject: [PATCH 035/272] Update release notes --- RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 940d99ce26..ce08fe74af 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,4 +9,4 @@ [//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object) ## Fixed -- Fixed a memory leak in `DropInActivity`. +- Fixed various memory leaks. From c49dd19a17e2788752127c804f3e9d00097edb71 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 24 Apr 2024 13:05:54 +0200 Subject: [PATCH 036/272] Replace flags with ISO codes COAND-849 --- .../core/internal/util/CountryUtils.kt | 483 +++++++++--------- .../econtext/internal/ui/view/EContextView.kt | 9 +- .../mbway/internal/ui/view/MbWayView.kt | 1 - .../ui/core/internal/ui/CountryViewHolder.kt | 4 +- .../ui/core/internal/ui/model/CountryModel.kt | 3 +- ui-core/src/main/res/layout/country_view.xml | 4 +- 6 files changed, 250 insertions(+), 254 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt index e9c9fbbf46..a146dc16ac 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt @@ -41,247 +41,247 @@ object CountryUtils { } private val countries = listOf( - CountryInfo(isoCode = "AF", callingCode = "+93", emoji = "🇦🇫"), - CountryInfo(isoCode = "AL", callingCode = "+355", emoji = "🇦🇱"), - CountryInfo(isoCode = "DZ", callingCode = "+213", emoji = "🇩🇿"), - CountryInfo(isoCode = "AS", callingCode = "+1684", emoji = "🇦🇸"), - CountryInfo(isoCode = "AD", callingCode = "+376", emoji = "🇦🇩"), - CountryInfo(isoCode = "AO", callingCode = "+244", emoji = "🇦🇴"), - CountryInfo(isoCode = "AI", callingCode = "+1264", emoji = "🇦🇮"), - CountryInfo(isoCode = "AG", callingCode = "+1268", emoji = "🇦🇬"), - CountryInfo(isoCode = "AR", callingCode = "+54", emoji = "🇦🇷"), - CountryInfo(isoCode = "AM", callingCode = "+374", emoji = "🇦🇲"), - CountryInfo(isoCode = "AW", callingCode = "+297", emoji = "🇦🇼"), - CountryInfo(isoCode = "AU", callingCode = "+61", emoji = "🇦🇺"), - CountryInfo(isoCode = "AT", callingCode = "+43", emoji = "🇦🇹"), - CountryInfo(isoCode = "AZ", callingCode = "+994", emoji = "🇦🇿"), - CountryInfo(isoCode = "BS", callingCode = "+1242", emoji = "🇧🇸"), - CountryInfo(isoCode = "BH", callingCode = "+973", emoji = "🇧🇭"), - CountryInfo(isoCode = "BD", callingCode = "+880", emoji = "🇧🇩"), - CountryInfo(isoCode = "BB", callingCode = "+1246", emoji = "🇧🇧"), - CountryInfo(isoCode = "BY", callingCode = "+375", emoji = "🇧🇾"), - CountryInfo(isoCode = "BE", callingCode = "+32", emoji = "🇧🇪"), - CountryInfo(isoCode = "BZ", callingCode = "+501", emoji = "🇧🇿"), - CountryInfo(isoCode = "BJ", callingCode = "+229", emoji = "🇧🇯"), - CountryInfo(isoCode = "BM", callingCode = "+1441", emoji = "🇧🇲"), - CountryInfo(isoCode = "BT", callingCode = "+975", emoji = "🇧🇹"), - CountryInfo(isoCode = "BA", callingCode = "+387", emoji = "🇧🇦"), - CountryInfo(isoCode = "BW", callingCode = "+267", emoji = "🇧🇼"), - CountryInfo(isoCode = "BR", callingCode = "+55", emoji = "🇧🇷"), - CountryInfo(isoCode = "IO", callingCode = "+246", emoji = "🇮🇴"), - CountryInfo(isoCode = "BG", callingCode = "+359", emoji = "🇧🇬"), - CountryInfo(isoCode = "BF", callingCode = "+226", emoji = "🇧🇫"), - CountryInfo(isoCode = "BI", callingCode = "+257", emoji = "🇧🇮"), - CountryInfo(isoCode = "KH", callingCode = "+855", emoji = "🇰🇭"), - CountryInfo(isoCode = "CM", callingCode = "+237", emoji = "🇨🇲"), - CountryInfo(isoCode = "CA", callingCode = "+1", emoji = "🇨🇦"), - CountryInfo(isoCode = "CV", callingCode = "+238", emoji = "🇨🇻"), - CountryInfo(isoCode = "KY", callingCode = "+345", emoji = "🇰🇾"), - CountryInfo(isoCode = "CF", callingCode = "+236", emoji = "🇨🇫"), - CountryInfo(isoCode = "TD", callingCode = "+235", emoji = "🇹🇩"), - CountryInfo(isoCode = "CL", callingCode = "+56", emoji = "🇨🇱"), - CountryInfo(isoCode = "CN", callingCode = "+86", emoji = "🇨🇳"), - CountryInfo(isoCode = "CX", callingCode = "+61", emoji = "🇨🇽"), - CountryInfo(isoCode = "CO", callingCode = "+57", emoji = "🇨🇴"), - CountryInfo(isoCode = "KM", callingCode = "+269", emoji = "🇰🇲"), - CountryInfo(isoCode = "CG", callingCode = "+242", emoji = "🇨🇬"), - CountryInfo(isoCode = "CK", callingCode = "+682", emoji = "🇨🇰"), - CountryInfo(isoCode = "CR", callingCode = "+506", emoji = "🇨🇷"), - CountryInfo(isoCode = "HR", callingCode = "+385", emoji = "🇭🇷"), - CountryInfo(isoCode = "CU", callingCode = "+53", emoji = "🇨🇺"), - CountryInfo(isoCode = "CY", callingCode = "+537", emoji = "🇨🇾"), - CountryInfo(isoCode = "CZ", callingCode = "+420", emoji = "🇨🇿"), - CountryInfo(isoCode = "DK", callingCode = "+45", emoji = "🇩🇰"), - CountryInfo(isoCode = "DJ", callingCode = "+253", emoji = "🇩🇯"), - CountryInfo(isoCode = "DM", callingCode = "+1767", emoji = "🇩🇲"), - CountryInfo(isoCode = "DO", callingCode = "+1849", emoji = "🇩🇴"), - CountryInfo(isoCode = "EC", callingCode = "+593", emoji = "🇪🇨"), - CountryInfo(isoCode = "EG", callingCode = "+20", emoji = "🇪🇬"), - CountryInfo(isoCode = "SV", callingCode = "+503", emoji = "🇸🇻"), - CountryInfo(isoCode = "GQ", callingCode = "+240", emoji = "🇬🇶"), - CountryInfo(isoCode = "ER", callingCode = "+291", emoji = "🇪🇷"), - CountryInfo(isoCode = "EE", callingCode = "+372", emoji = "🇪🇪"), - CountryInfo(isoCode = "ET", callingCode = "+251", emoji = "🇪🇹"), - CountryInfo(isoCode = "FO", callingCode = "+298", emoji = "🇫🇴"), - CountryInfo(isoCode = "FJ", callingCode = "+679", emoji = "🇫🇯"), - CountryInfo(isoCode = "FI", callingCode = "+358", emoji = "🇫🇮"), - CountryInfo(isoCode = "FR", callingCode = "+33", emoji = "🇫🇷"), - CountryInfo(isoCode = "GF", callingCode = "+594", emoji = "🇬🇫"), - CountryInfo(isoCode = "PF", callingCode = "+689", emoji = "🇵🇫"), - CountryInfo(isoCode = "GA", callingCode = "+241", emoji = "🇬🇦"), - CountryInfo(isoCode = "GM", callingCode = "+220", emoji = "🇬🇲"), - CountryInfo(isoCode = "GE", callingCode = "+995", emoji = "🇬🇪"), - CountryInfo(isoCode = "DE", callingCode = "+49", emoji = "🇩🇪"), - CountryInfo(isoCode = "GH", callingCode = "+233", emoji = "🇬🇭"), - CountryInfo(isoCode = "GI", callingCode = "+350", emoji = "🇬🇮"), - CountryInfo(isoCode = "GR", callingCode = "+30", emoji = "🇬🇷"), - CountryInfo(isoCode = "GL", callingCode = "+299", emoji = "🇬🇱"), - CountryInfo(isoCode = "GD", callingCode = "+1473", emoji = "🇬🇩"), - CountryInfo(isoCode = "GP", callingCode = "+590", emoji = "🇬🇵"), - CountryInfo(isoCode = "GU", callingCode = "+1671", emoji = "🇬🇺"), - CountryInfo(isoCode = "GT", callingCode = "+502", emoji = "🇬🇹"), - CountryInfo(isoCode = "GN", callingCode = "+224", emoji = "🇬🇳"), - CountryInfo(isoCode = "GW", callingCode = "+245", emoji = "🇬🇼"), - CountryInfo(isoCode = "GY", callingCode = "+595", emoji = "🇬🇾"), - CountryInfo(isoCode = "HT", callingCode = "+509", emoji = "🇭🇹"), - CountryInfo(isoCode = "HN", callingCode = "+504", emoji = "🇭🇳"), - CountryInfo(isoCode = "HU", callingCode = "+36", emoji = "🇭🇺"), - CountryInfo(isoCode = "IS", callingCode = "+354", emoji = "🇮🇸"), - CountryInfo(isoCode = "IN", callingCode = "+91", emoji = "🇮🇳"), - CountryInfo(isoCode = "ID", callingCode = "+62", emoji = "🇮🇩"), - CountryInfo(isoCode = "IQ", callingCode = "+964", emoji = "🇮🇶"), - CountryInfo(isoCode = "IE", callingCode = "+353", emoji = "🇮🇪"), - CountryInfo(isoCode = "IL", callingCode = "+972", emoji = "🇮🇱"), - CountryInfo(isoCode = "IT", callingCode = "+39", emoji = "🇮🇹"), - CountryInfo(isoCode = "JM", callingCode = "+1876", emoji = "🇯🇲"), - CountryInfo(isoCode = "JP", callingCode = "+81", emoji = "🇯🇵"), - CountryInfo(isoCode = "JO", callingCode = "+962", emoji = "🇯🇴"), - CountryInfo(isoCode = "KZ", callingCode = "+77", emoji = "🇰🇿"), - CountryInfo(isoCode = "KE", callingCode = "+254", emoji = "🇰🇪"), - CountryInfo(isoCode = "KI", callingCode = "+686", emoji = "🇰🇮"), - CountryInfo(isoCode = "KW", callingCode = "+965", emoji = "🇰🇼"), - CountryInfo(isoCode = "KG", callingCode = "+996", emoji = "🇰🇬"), - CountryInfo(isoCode = "LV", callingCode = "+371", emoji = "🇱🇻"), - CountryInfo(isoCode = "LB", callingCode = "+961", emoji = "🇱🇧"), - CountryInfo(isoCode = "LS", callingCode = "+266", emoji = "🇱🇸"), - CountryInfo(isoCode = "LR", callingCode = "+231", emoji = "🇱🇷"), - CountryInfo(isoCode = "LI", callingCode = "+423", emoji = "🇱🇮"), - CountryInfo(isoCode = "LT", callingCode = "+370", emoji = "🇱🇹"), - CountryInfo(isoCode = "LU", callingCode = "+352", emoji = "🇱🇺"), - CountryInfo(isoCode = "MG", callingCode = "+261", emoji = "🇲🇬"), - CountryInfo(isoCode = "MW", callingCode = "+265", emoji = "🇲🇼"), - CountryInfo(isoCode = "MY", callingCode = "+60", emoji = "🇲🇾"), - CountryInfo(isoCode = "MV", callingCode = "+960", emoji = "🇲🇻"), - CountryInfo(isoCode = "ML", callingCode = "+223", emoji = "🇲🇱"), - CountryInfo(isoCode = "MT", callingCode = "+356", emoji = "🇲🇹"), - CountryInfo(isoCode = "MH", callingCode = "+692", emoji = "🇲🇭"), - CountryInfo(isoCode = "MQ", callingCode = "+596", emoji = "🇲🇶"), - CountryInfo(isoCode = "MR", callingCode = "+222", emoji = "🇲🇷"), - CountryInfo(isoCode = "MU", callingCode = "+230", emoji = "🇲🇺"), - CountryInfo(isoCode = "YT", callingCode = "+262", emoji = "🇾🇹"), - CountryInfo(isoCode = "MX", callingCode = "+52", emoji = "🇲🇽"), - CountryInfo(isoCode = "MC", callingCode = "+377", emoji = "🇲🇨"), - CountryInfo(isoCode = "MN", callingCode = "+976", emoji = "🇲🇳"), - CountryInfo(isoCode = "ME", callingCode = "+382", emoji = "🇲🇪"), - CountryInfo(isoCode = "MS", callingCode = "+1664", emoji = "🇲🇸"), - CountryInfo(isoCode = "MA", callingCode = "+212", emoji = "🇲🇦"), - CountryInfo(isoCode = "MM", callingCode = "+95", emoji = "🇲🇲"), - CountryInfo(isoCode = "NA", callingCode = "+264", emoji = "🇳🇦"), - CountryInfo(isoCode = "NR", callingCode = "+674", emoji = "🇳🇷"), - CountryInfo(isoCode = "NP", callingCode = "+977", emoji = "🇳🇵"), - CountryInfo(isoCode = "NL", callingCode = "+31", emoji = "🇳🇱"), - CountryInfo(isoCode = "AN", callingCode = "+599", emoji = "🇦🇳"), - CountryInfo(isoCode = "NC", callingCode = "+687", emoji = "🇳🇨"), - CountryInfo(isoCode = "NZ", callingCode = "+64", emoji = "🇳🇿"), - CountryInfo(isoCode = "NI", callingCode = "+505", emoji = "🇳🇮"), - CountryInfo(isoCode = "NE", callingCode = "+227", emoji = "🇳🇪"), - CountryInfo(isoCode = "NG", callingCode = "+234", emoji = "🇳🇬"), - CountryInfo(isoCode = "NU", callingCode = "+683", emoji = "🇳🇺"), - CountryInfo(isoCode = "NF", callingCode = "+672", emoji = "🇳🇫"), - CountryInfo(isoCode = "MP", callingCode = "+1670", emoji = "🇲🇵"), - CountryInfo(isoCode = "NO", callingCode = "+47", emoji = "🇳🇴"), - CountryInfo(isoCode = "OM", callingCode = "+968", emoji = "🇴🇲"), - CountryInfo(isoCode = "PK", callingCode = "+92", emoji = "🇵🇰"), - CountryInfo(isoCode = "PW", callingCode = "+680", emoji = "🇵🇼"), - CountryInfo(isoCode = "PA", callingCode = "+507", emoji = "🇵🇦"), - CountryInfo(isoCode = "PG", callingCode = "+675", emoji = "🇵🇬"), - CountryInfo(isoCode = "PY", callingCode = "+595", emoji = "🇵🇾"), - CountryInfo(isoCode = "PE", callingCode = "+51", emoji = "🇵🇪"), - CountryInfo(isoCode = "PH", callingCode = "+63", emoji = "🇵🇭"), - CountryInfo(isoCode = "PL", callingCode = "+48", emoji = "🇵🇱"), - CountryInfo(isoCode = "PT", callingCode = "+351", emoji = "🇵🇹"), - CountryInfo(isoCode = "PR", callingCode = "+1939", emoji = "🇵🇷"), - CountryInfo(isoCode = "QA", callingCode = "+974", emoji = "🇶🇦"), - CountryInfo(isoCode = "RO", callingCode = "+40", emoji = "🇷🇴"), - CountryInfo(isoCode = "RW", callingCode = "+250", emoji = "🇷🇼"), - CountryInfo(isoCode = "WS", callingCode = "+685", emoji = "🇼🇸"), - CountryInfo(isoCode = "SM", callingCode = "+378", emoji = "🇸🇲"), - CountryInfo(isoCode = "SA", callingCode = "+966", emoji = "🇸🇦"), - CountryInfo(isoCode = "SN", callingCode = "+221", emoji = "🇸🇳"), - CountryInfo(isoCode = "RS", callingCode = "+381", emoji = "🇷🇸"), - CountryInfo(isoCode = "SC", callingCode = "+248", emoji = "🇸🇨"), - CountryInfo(isoCode = "SL", callingCode = "+232", emoji = "🇸🇱"), - CountryInfo(isoCode = "SG", callingCode = "+65", emoji = "🇸🇬"), - CountryInfo(isoCode = "SK", callingCode = "+421", emoji = "🇸🇰"), - CountryInfo(isoCode = "SI", callingCode = "+386", emoji = "🇸🇮"), - CountryInfo(isoCode = "SB", callingCode = "+677", emoji = "🇸🇧"), - CountryInfo(isoCode = "ZA", callingCode = "+27", emoji = "🇿🇦"), - CountryInfo(isoCode = "GS", callingCode = "+500", emoji = "🇬🇸"), - CountryInfo(isoCode = "ES", callingCode = "+34", emoji = "🇪🇸"), - CountryInfo(isoCode = "LK", callingCode = "+94", emoji = "🇱🇰"), - CountryInfo(isoCode = "SD", callingCode = "+249", emoji = "🇸🇩"), - CountryInfo(isoCode = "SR", callingCode = "+597", emoji = "🇸🇷"), - CountryInfo(isoCode = "SZ", callingCode = "+268", emoji = "🇸🇿"), - CountryInfo(isoCode = "SE", callingCode = "+46", emoji = "🇸🇪"), - CountryInfo(isoCode = "CH", callingCode = "+41", emoji = "🇨🇭"), - CountryInfo(isoCode = "TJ", callingCode = "+992", emoji = "🇹🇯"), - CountryInfo(isoCode = "TH", callingCode = "+66", emoji = "🇹🇭"), - CountryInfo(isoCode = "TG", callingCode = "+228", emoji = "🇹🇬"), - CountryInfo(isoCode = "TK", callingCode = "+690", emoji = "🇹🇰"), - CountryInfo(isoCode = "TO", callingCode = "+676", emoji = "🇹🇴"), - CountryInfo(isoCode = "TT", callingCode = "+1868", emoji = "🇹🇹"), - CountryInfo(isoCode = "TN", callingCode = "+216", emoji = "🇹🇳"), - CountryInfo(isoCode = "TR", callingCode = "+90", emoji = "🇹🇷"), - CountryInfo(isoCode = "TM", callingCode = "+993", emoji = "🇹🇲"), - CountryInfo(isoCode = "TC", callingCode = "+1649", emoji = "🇹🇨"), - CountryInfo(isoCode = "TV", callingCode = "+688", emoji = "🇹🇻"), - CountryInfo(isoCode = "UG", callingCode = "+256", emoji = "🇺🇬"), - CountryInfo(isoCode = "UA", callingCode = "+380", emoji = "🇺🇦"), - CountryInfo(isoCode = "AE", callingCode = "+971", emoji = "🇦🇪"), - CountryInfo(isoCode = "GB", callingCode = "+44", emoji = "🇬🇧"), - CountryInfo(isoCode = "US", callingCode = "+1", emoji = "🇺🇸"), - CountryInfo(isoCode = "UY", callingCode = "+598", emoji = "🇺🇾"), - CountryInfo(isoCode = "UZ", callingCode = "+998", emoji = "🇺🇿"), - CountryInfo(isoCode = "VU", callingCode = "+678", emoji = "🇻🇺"), - CountryInfo(isoCode = "WF", callingCode = "+681", emoji = "🇼🇫"), - CountryInfo(isoCode = "YE", callingCode = "+967", emoji = "🇾🇪"), - CountryInfo(isoCode = "ZM", callingCode = "+260", emoji = "🇿🇲"), - CountryInfo(isoCode = "ZW", callingCode = "+263", emoji = "🇿🇼"), - CountryInfo(isoCode = "AX", callingCode = "+358", emoji = "🇦🇽"), - CountryInfo(isoCode = "AQ", callingCode = "+672", emoji = "🇦🇶"), - CountryInfo(isoCode = "BO", callingCode = "+591", emoji = "🇧🇴"), - CountryInfo(isoCode = "BN", callingCode = "+673", emoji = "🇧🇳"), - CountryInfo(isoCode = "CC", callingCode = "+61", emoji = "🇨🇨"), - CountryInfo(isoCode = "CD", callingCode = "+243", emoji = "🇨🇩"), - CountryInfo(isoCode = "CI", callingCode = "+225", emoji = "🇨🇮"), - CountryInfo(isoCode = "FK", callingCode = "+500", emoji = "🇫🇰"), - CountryInfo(isoCode = "GG", callingCode = "+44", emoji = "🇬🇬"), - CountryInfo(isoCode = "VA", callingCode = "+379", emoji = "🇻🇦"), - CountryInfo(isoCode = "HK", callingCode = "+852", emoji = "🇭🇰"), - CountryInfo(isoCode = "IR", callingCode = "+98", emoji = "🇮🇷"), - CountryInfo(isoCode = "IM", callingCode = "+44", emoji = "🇮🇲"), - CountryInfo(isoCode = "JE", callingCode = "+44", emoji = "🇯🇪"), - CountryInfo(isoCode = "KP", callingCode = "+850", emoji = "🇰🇵"), - CountryInfo(isoCode = "KR", callingCode = "+82", emoji = "🇰🇷"), - CountryInfo(isoCode = "LA", callingCode = "+856", emoji = "🇱🇦"), - CountryInfo(isoCode = "LY", callingCode = "+218", emoji = "🇱🇾"), - CountryInfo(isoCode = "MO", callingCode = "+853", emoji = "🇲🇴"), - CountryInfo(isoCode = "MK", callingCode = "+389", emoji = "🇲🇰"), - CountryInfo(isoCode = "FM", callingCode = "+691", emoji = "🇫🇲"), - CountryInfo(isoCode = "MD", callingCode = "+373", emoji = "🇲🇩"), - CountryInfo(isoCode = "MZ", callingCode = "+258", emoji = "🇲🇿"), - CountryInfo(isoCode = "PS", callingCode = "+970", emoji = "🇵🇸"), - CountryInfo(isoCode = "PN", callingCode = "+872", emoji = "🇵🇳"), - CountryInfo(isoCode = "RE", callingCode = "+262", emoji = "🇷🇪"), - CountryInfo(isoCode = "RU", callingCode = "+7", emoji = "🇷🇺"), - CountryInfo(isoCode = "BL", callingCode = "+590", emoji = "🇧🇱"), - CountryInfo(isoCode = "SH", callingCode = "+290", emoji = "🇸🇭"), - CountryInfo(isoCode = "KN", callingCode = "+1869", emoji = "🇰🇳"), - CountryInfo(isoCode = "LC", callingCode = "+1758", emoji = "🇱🇨"), - CountryInfo(isoCode = "MF", callingCode = "+590", emoji = "🇲🇫"), - CountryInfo(isoCode = "PM", callingCode = "+508", emoji = "🇵🇲"), - CountryInfo(isoCode = "VC", callingCode = "+1784", emoji = "🇻🇨"), - CountryInfo(isoCode = "ST", callingCode = "+239", emoji = "🇸🇹"), - CountryInfo(isoCode = "SO", callingCode = "+252", emoji = "🇸🇴"), - CountryInfo(isoCode = "SJ", callingCode = "+47", emoji = "🇸🇯"), - CountryInfo(isoCode = "SY", callingCode = "+963", emoji = "🇸🇾"), - CountryInfo(isoCode = "TW", callingCode = "+886", emoji = "🇹🇼"), - CountryInfo(isoCode = "TZ", callingCode = "+255", emoji = "🇹🇿"), - CountryInfo(isoCode = "TL", callingCode = "+670", emoji = "🇹🇱"), - CountryInfo(isoCode = "VE", callingCode = "+58", emoji = "🇻🇪"), - CountryInfo(isoCode = "VN", callingCode = "+84", emoji = "🇻🇳"), - CountryInfo(isoCode = "VG", callingCode = "+1284", emoji = "🇻🇬"), - CountryInfo(isoCode = "VI", callingCode = "+1340", emoji = "🇻🇮") + CountryInfo(isoCode = "AF", callingCode = "+93"), + CountryInfo(isoCode = "AL", callingCode = "+355"), + CountryInfo(isoCode = "DZ", callingCode = "+213"), + CountryInfo(isoCode = "AS", callingCode = "+1684"), + CountryInfo(isoCode = "AD", callingCode = "+376"), + CountryInfo(isoCode = "AO", callingCode = "+244"), + CountryInfo(isoCode = "AI", callingCode = "+1264"), + CountryInfo(isoCode = "AG", callingCode = "+1268"), + CountryInfo(isoCode = "AR", callingCode = "+54"), + CountryInfo(isoCode = "AM", callingCode = "+374"), + CountryInfo(isoCode = "AW", callingCode = "+297"), + CountryInfo(isoCode = "AU", callingCode = "+61"), + CountryInfo(isoCode = "AT", callingCode = "+43"), + CountryInfo(isoCode = "AZ", callingCode = "+994"), + CountryInfo(isoCode = "BS", callingCode = "+1242"), + CountryInfo(isoCode = "BH", callingCode = "+973"), + CountryInfo(isoCode = "BD", callingCode = "+880"), + CountryInfo(isoCode = "BB", callingCode = "+1246"), + CountryInfo(isoCode = "BY", callingCode = "+375"), + CountryInfo(isoCode = "BE", callingCode = "+32"), + CountryInfo(isoCode = "BZ", callingCode = "+501"), + CountryInfo(isoCode = "BJ", callingCode = "+229"), + CountryInfo(isoCode = "BM", callingCode = "+1441"), + CountryInfo(isoCode = "BT", callingCode = "+975"), + CountryInfo(isoCode = "BA", callingCode = "+387"), + CountryInfo(isoCode = "BW", callingCode = "+267"), + CountryInfo(isoCode = "BR", callingCode = "+55"), + CountryInfo(isoCode = "IO", callingCode = "+246"), + CountryInfo(isoCode = "BG", callingCode = "+359"), + CountryInfo(isoCode = "BF", callingCode = "+226"), + CountryInfo(isoCode = "BI", callingCode = "+257"), + CountryInfo(isoCode = "KH", callingCode = "+855"), + CountryInfo(isoCode = "CM", callingCode = "+237"), + CountryInfo(isoCode = "CA", callingCode = "+1"), + CountryInfo(isoCode = "CV", callingCode = "+238"), + CountryInfo(isoCode = "KY", callingCode = "+345"), + CountryInfo(isoCode = "CF", callingCode = "+236"), + CountryInfo(isoCode = "TD", callingCode = "+235"), + CountryInfo(isoCode = "CL", callingCode = "+56"), + CountryInfo(isoCode = "CN", callingCode = "+86"), + CountryInfo(isoCode = "CX", callingCode = "+61"), + CountryInfo(isoCode = "CO", callingCode = "+57"), + CountryInfo(isoCode = "KM", callingCode = "+269"), + CountryInfo(isoCode = "CG", callingCode = "+242"), + CountryInfo(isoCode = "CK", callingCode = "+682"), + CountryInfo(isoCode = "CR", callingCode = "+506"), + CountryInfo(isoCode = "HR", callingCode = "+385"), + CountryInfo(isoCode = "CU", callingCode = "+53"), + CountryInfo(isoCode = "CY", callingCode = "+537"), + CountryInfo(isoCode = "CZ", callingCode = "+420"), + CountryInfo(isoCode = "DK", callingCode = "+45"), + CountryInfo(isoCode = "DJ", callingCode = "+253"), + CountryInfo(isoCode = "DM", callingCode = "+1767"), + CountryInfo(isoCode = "DO", callingCode = "+1849"), + CountryInfo(isoCode = "EC", callingCode = "+593"), + CountryInfo(isoCode = "EG", callingCode = "+20"), + CountryInfo(isoCode = "SV", callingCode = "+503"), + CountryInfo(isoCode = "GQ", callingCode = "+240"), + CountryInfo(isoCode = "ER", callingCode = "+291"), + CountryInfo(isoCode = "EE", callingCode = "+372"), + CountryInfo(isoCode = "ET", callingCode = "+251"), + CountryInfo(isoCode = "FO", callingCode = "+298"), + CountryInfo(isoCode = "FJ", callingCode = "+679"), + CountryInfo(isoCode = "FI", callingCode = "+358"), + CountryInfo(isoCode = "FR", callingCode = "+33"), + CountryInfo(isoCode = "GF", callingCode = "+594"), + CountryInfo(isoCode = "PF", callingCode = "+689"), + CountryInfo(isoCode = "GA", callingCode = "+241"), + CountryInfo(isoCode = "GM", callingCode = "+220"), + CountryInfo(isoCode = "GE", callingCode = "+995"), + CountryInfo(isoCode = "DE", callingCode = "+49"), + CountryInfo(isoCode = "GH", callingCode = "+233"), + CountryInfo(isoCode = "GI", callingCode = "+350"), + CountryInfo(isoCode = "GR", callingCode = "+30"), + CountryInfo(isoCode = "GL", callingCode = "+299"), + CountryInfo(isoCode = "GD", callingCode = "+1473"), + CountryInfo(isoCode = "GP", callingCode = "+590"), + CountryInfo(isoCode = "GU", callingCode = "+1671"), + CountryInfo(isoCode = "GT", callingCode = "+502"), + CountryInfo(isoCode = "GN", callingCode = "+224"), + CountryInfo(isoCode = "GW", callingCode = "+245"), + CountryInfo(isoCode = "GY", callingCode = "+595"), + CountryInfo(isoCode = "HT", callingCode = "+509"), + CountryInfo(isoCode = "HN", callingCode = "+504"), + CountryInfo(isoCode = "HU", callingCode = "+36"), + CountryInfo(isoCode = "IS", callingCode = "+354"), + CountryInfo(isoCode = "IN", callingCode = "+91"), + CountryInfo(isoCode = "ID", callingCode = "+62"), + CountryInfo(isoCode = "IQ", callingCode = "+964"), + CountryInfo(isoCode = "IE", callingCode = "+353"), + CountryInfo(isoCode = "IL", callingCode = "+972"), + CountryInfo(isoCode = "IT", callingCode = "+39"), + CountryInfo(isoCode = "JM", callingCode = "+1876"), + CountryInfo(isoCode = "JP", callingCode = "+81"), + CountryInfo(isoCode = "JO", callingCode = "+962"), + CountryInfo(isoCode = "KZ", callingCode = "+77"), + CountryInfo(isoCode = "KE", callingCode = "+254"), + CountryInfo(isoCode = "KI", callingCode = "+686"), + CountryInfo(isoCode = "KW", callingCode = "+965"), + CountryInfo(isoCode = "KG", callingCode = "+996"), + CountryInfo(isoCode = "LV", callingCode = "+371"), + CountryInfo(isoCode = "LB", callingCode = "+961"), + CountryInfo(isoCode = "LS", callingCode = "+266"), + CountryInfo(isoCode = "LR", callingCode = "+231"), + CountryInfo(isoCode = "LI", callingCode = "+423"), + CountryInfo(isoCode = "LT", callingCode = "+370"), + CountryInfo(isoCode = "LU", callingCode = "+352"), + CountryInfo(isoCode = "MG", callingCode = "+261"), + CountryInfo(isoCode = "MW", callingCode = "+265"), + CountryInfo(isoCode = "MY", callingCode = "+60"), + CountryInfo(isoCode = "MV", callingCode = "+960"), + CountryInfo(isoCode = "ML", callingCode = "+223"), + CountryInfo(isoCode = "MT", callingCode = "+356"), + CountryInfo(isoCode = "MH", callingCode = "+692"), + CountryInfo(isoCode = "MQ", callingCode = "+596"), + CountryInfo(isoCode = "MR", callingCode = "+222"), + CountryInfo(isoCode = "MU", callingCode = "+230"), + CountryInfo(isoCode = "YT", callingCode = "+262"), + CountryInfo(isoCode = "MX", callingCode = "+52"), + CountryInfo(isoCode = "MC", callingCode = "+377"), + CountryInfo(isoCode = "MN", callingCode = "+976"), + CountryInfo(isoCode = "ME", callingCode = "+382"), + CountryInfo(isoCode = "MS", callingCode = "+1664"), + CountryInfo(isoCode = "MA", callingCode = "+212"), + CountryInfo(isoCode = "MM", callingCode = "+95"), + CountryInfo(isoCode = "NA", callingCode = "+264"), + CountryInfo(isoCode = "NR", callingCode = "+674"), + CountryInfo(isoCode = "NP", callingCode = "+977"), + CountryInfo(isoCode = "NL", callingCode = "+31"), + CountryInfo(isoCode = "AN", callingCode = "+599"), + CountryInfo(isoCode = "NC", callingCode = "+687"), + CountryInfo(isoCode = "NZ", callingCode = "+64"), + CountryInfo(isoCode = "NI", callingCode = "+505"), + CountryInfo(isoCode = "NE", callingCode = "+227"), + CountryInfo(isoCode = "NG", callingCode = "+234"), + CountryInfo(isoCode = "NU", callingCode = "+683"), + CountryInfo(isoCode = "NF", callingCode = "+672"), + CountryInfo(isoCode = "MP", callingCode = "+1670"), + CountryInfo(isoCode = "NO", callingCode = "+47"), + CountryInfo(isoCode = "OM", callingCode = "+968"), + CountryInfo(isoCode = "PK", callingCode = "+92"), + CountryInfo(isoCode = "PW", callingCode = "+680"), + CountryInfo(isoCode = "PA", callingCode = "+507"), + CountryInfo(isoCode = "PG", callingCode = "+675"), + CountryInfo(isoCode = "PY", callingCode = "+595"), + CountryInfo(isoCode = "PE", callingCode = "+51"), + CountryInfo(isoCode = "PH", callingCode = "+63"), + CountryInfo(isoCode = "PL", callingCode = "+48"), + CountryInfo(isoCode = "PT", callingCode = "+351"), + CountryInfo(isoCode = "PR", callingCode = "+1939"), + CountryInfo(isoCode = "QA", callingCode = "+974"), + CountryInfo(isoCode = "RO", callingCode = "+40"), + CountryInfo(isoCode = "RW", callingCode = "+250"), + CountryInfo(isoCode = "WS", callingCode = "+685"), + CountryInfo(isoCode = "SM", callingCode = "+378"), + CountryInfo(isoCode = "SA", callingCode = "+966"), + CountryInfo(isoCode = "SN", callingCode = "+221"), + CountryInfo(isoCode = "RS", callingCode = "+381"), + CountryInfo(isoCode = "SC", callingCode = "+248"), + CountryInfo(isoCode = "SL", callingCode = "+232"), + CountryInfo(isoCode = "SG", callingCode = "+65"), + CountryInfo(isoCode = "SK", callingCode = "+421"), + CountryInfo(isoCode = "SI", callingCode = "+386"), + CountryInfo(isoCode = "SB", callingCode = "+677"), + CountryInfo(isoCode = "ZA", callingCode = "+27"), + CountryInfo(isoCode = "GS", callingCode = "+500"), + CountryInfo(isoCode = "ES", callingCode = "+34"), + CountryInfo(isoCode = "LK", callingCode = "+94"), + CountryInfo(isoCode = "SD", callingCode = "+249"), + CountryInfo(isoCode = "SR", callingCode = "+597"), + CountryInfo(isoCode = "SZ", callingCode = "+268"), + CountryInfo(isoCode = "SE", callingCode = "+46"), + CountryInfo(isoCode = "CH", callingCode = "+41"), + CountryInfo(isoCode = "TJ", callingCode = "+992"), + CountryInfo(isoCode = "TH", callingCode = "+66"), + CountryInfo(isoCode = "TG", callingCode = "+228"), + CountryInfo(isoCode = "TK", callingCode = "+690"), + CountryInfo(isoCode = "TO", callingCode = "+676"), + CountryInfo(isoCode = "TT", callingCode = "+1868"), + CountryInfo(isoCode = "TN", callingCode = "+216"), + CountryInfo(isoCode = "TR", callingCode = "+90"), + CountryInfo(isoCode = "TM", callingCode = "+993"), + CountryInfo(isoCode = "TC", callingCode = "+1649"), + CountryInfo(isoCode = "TV", callingCode = "+688"), + CountryInfo(isoCode = "UG", callingCode = "+256"), + CountryInfo(isoCode = "UA", callingCode = "+380"), + CountryInfo(isoCode = "AE", callingCode = "+971"), + CountryInfo(isoCode = "GB", callingCode = "+44"), + CountryInfo(isoCode = "US", callingCode = "+1"), + CountryInfo(isoCode = "UY", callingCode = "+598"), + CountryInfo(isoCode = "UZ", callingCode = "+998"), + CountryInfo(isoCode = "VU", callingCode = "+678"), + CountryInfo(isoCode = "WF", callingCode = "+681"), + CountryInfo(isoCode = "YE", callingCode = "+967"), + CountryInfo(isoCode = "ZM", callingCode = "+260"), + CountryInfo(isoCode = "ZW", callingCode = "+263"), + CountryInfo(isoCode = "AX", callingCode = "+358"), + CountryInfo(isoCode = "AQ", callingCode = "+672"), + CountryInfo(isoCode = "BO", callingCode = "+591"), + CountryInfo(isoCode = "BN", callingCode = "+673"), + CountryInfo(isoCode = "CC", callingCode = "+61"), + CountryInfo(isoCode = "CD", callingCode = "+243"), + CountryInfo(isoCode = "CI", callingCode = "+225"), + CountryInfo(isoCode = "FK", callingCode = "+500"), + CountryInfo(isoCode = "GG", callingCode = "+44"), + CountryInfo(isoCode = "VA", callingCode = "+379"), + CountryInfo(isoCode = "HK", callingCode = "+852"), + CountryInfo(isoCode = "IR", callingCode = "+98"), + CountryInfo(isoCode = "IM", callingCode = "+44"), + CountryInfo(isoCode = "JE", callingCode = "+44"), + CountryInfo(isoCode = "KP", callingCode = "+850"), + CountryInfo(isoCode = "KR", callingCode = "+82"), + CountryInfo(isoCode = "LA", callingCode = "+856"), + CountryInfo(isoCode = "LY", callingCode = "+218"), + CountryInfo(isoCode = "MO", callingCode = "+853"), + CountryInfo(isoCode = "MK", callingCode = "+389"), + CountryInfo(isoCode = "FM", callingCode = "+691"), + CountryInfo(isoCode = "MD", callingCode = "+373"), + CountryInfo(isoCode = "MZ", callingCode = "+258"), + CountryInfo(isoCode = "PS", callingCode = "+970"), + CountryInfo(isoCode = "PN", callingCode = "+872"), + CountryInfo(isoCode = "RE", callingCode = "+262"), + CountryInfo(isoCode = "RU", callingCode = "+7"), + CountryInfo(isoCode = "BL", callingCode = "+590"), + CountryInfo(isoCode = "SH", callingCode = "+290"), + CountryInfo(isoCode = "KN", callingCode = "+1869"), + CountryInfo(isoCode = "LC", callingCode = "+1758"), + CountryInfo(isoCode = "MF", callingCode = "+590"), + CountryInfo(isoCode = "PM", callingCode = "+508"), + CountryInfo(isoCode = "VC", callingCode = "+1784"), + CountryInfo(isoCode = "ST", callingCode = "+239"), + CountryInfo(isoCode = "SO", callingCode = "+252"), + CountryInfo(isoCode = "SJ", callingCode = "+47"), + CountryInfo(isoCode = "SY", callingCode = "+963"), + CountryInfo(isoCode = "TW", callingCode = "+886"), + CountryInfo(isoCode = "TZ", callingCode = "+255"), + CountryInfo(isoCode = "TL", callingCode = "+670"), + CountryInfo(isoCode = "VE", callingCode = "+58"), + CountryInfo(isoCode = "VN", callingCode = "+84"), + CountryInfo(isoCode = "VG", callingCode = "+1284"), + CountryInfo(isoCode = "VI", callingCode = "+1340") ) } @@ -292,5 +292,4 @@ object CountryUtils { data class CountryInfo( val isoCode: String, val callingCode: String, - val emoji: String ) diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt index 9cb4c47d41..617b064ce3 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt @@ -104,19 +104,19 @@ internal class EContextView @JvmOverloads constructor( private fun initLocalizedStrings(localizedContext: Context) { binding.textInputLayoutFirstName.setLocalizedHintFromStyle( R.style.AdyenCheckout_EContext_FirstNameInput, - localizedContext + localizedContext, ) binding.textInputLayoutLastName.setLocalizedHintFromStyle( R.style.AdyenCheckout_EContext_LastNameInput, - localizedContext + localizedContext, ) binding.textInputLayoutMobileNumber.setLocalizedHintFromStyle( R.style.AdyenCheckout_EContext_PhoneNumberInput, - localizedContext + localizedContext, ) binding.textInputLayoutEmailAddress.setLocalizedHintFromStyle( R.style.AdyenCheckout_EContext_ShopperEmailInput, - localizedContext + localizedContext, ) } @@ -163,7 +163,6 @@ internal class EContextView @JvmOverloads constructor( isoCode = it.isoCode, countryName = CountryUtils.getCountryName(it.isoCode, delegate.componentParams.shopperLocale), callingCode = it.callingCode, - emoji = it.emoji ) } countryAdapter = CountryAdapter(context, localizedContext).apply { diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index 96a3bd898b..33d3fead31 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -122,7 +122,6 @@ internal class MbWayView @JvmOverloads constructor( isoCode = it.isoCode, countryName = CountryUtils.getCountryName(it.isoCode, delegate.componentParams.shopperLocale), callingCode = it.callingCode, - emoji = it.emoji, ) } } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt index 7717f8f462..d800c9b8aa 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt @@ -17,11 +17,11 @@ internal class CountryViewHolder(private val binding: CountryViewBinding) : Recy fun bindItem(country: CountryModel) { with(binding) { - textViewFlag.text = country.emoji + textViewCountryCode.text = country.isoCode textViewCountry.text = root.context.getString( R.string.checkout_country_name_format, country.countryName, - country.callingCode + country.callingCode, ) } } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/model/CountryModel.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/model/CountryModel.kt index 869fef5cef..815033b45d 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/model/CountryModel.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/model/CountryModel.kt @@ -15,9 +15,8 @@ data class CountryModel( val isoCode: String, val countryName: String, val callingCode: String, - val emoji: String ) { fun toShortString(): String { - return "$emoji $callingCode" + return "$isoCode $callingCode" } } diff --git a/ui-core/src/main/res/layout/country_view.xml b/ui-core/src/main/res/layout/country_view.xml index cf188f51a4..073dde3d45 100644 --- a/ui-core/src/main/res/layout/country_view.xml +++ b/ui-core/src/main/res/layout/country_view.xml @@ -14,13 +14,13 @@ android:padding="@dimen/standard_three_quarters_margin"> + tools:text="NL" /> Date: Wed, 24 Apr 2024 13:17:10 +0200 Subject: [PATCH 037/272] Update related strings COAND-849 --- example-app/src/main/res/values/strings.xml | 4 ++-- ui-core/src/main/res/values-ar/strings.xml | 4 ++-- ui-core/src/main/res/values-cs-rCZ/strings.xml | 4 ++-- ui-core/src/main/res/values-da-rDK/strings.xml | 4 ++-- ui-core/src/main/res/values-de-rDE/strings.xml | 4 ++-- ui-core/src/main/res/values-el-rGR/strings.xml | 4 ++-- ui-core/src/main/res/values-es-rES/strings.xml | 4 ++-- ui-core/src/main/res/values-fi-rFI/strings.xml | 4 ++-- ui-core/src/main/res/values-fr-rFR/strings.xml | 4 ++-- ui-core/src/main/res/values-hr-rHR/strings.xml | 4 ++-- ui-core/src/main/res/values-hu-rHU/strings.xml | 4 ++-- ui-core/src/main/res/values-it-rIT/strings.xml | 4 ++-- ui-core/src/main/res/values-ja-rJP/strings.xml | 4 ++-- ui-core/src/main/res/values-ko-rKR/strings.xml | 4 ++-- ui-core/src/main/res/values-nb-rNO/strings.xml | 4 ++-- ui-core/src/main/res/values-nl-rNL/strings.xml | 4 ++-- ui-core/src/main/res/values-pl-rPL/strings.xml | 4 ++-- ui-core/src/main/res/values-pt-rBR/strings.xml | 4 ++-- ui-core/src/main/res/values-pt-rPT/strings.xml | 4 ++-- ui-core/src/main/res/values-ro-rRO/strings.xml | 4 ++-- ui-core/src/main/res/values-ru-rRU/strings.xml | 4 ++-- ui-core/src/main/res/values-sk-rSK/strings.xml | 4 ++-- ui-core/src/main/res/values-sl-rSI/strings.xml | 4 ++-- ui-core/src/main/res/values-sv-rSE/strings.xml | 4 ++-- ui-core/src/main/res/values-zh-rTW/strings.xml | 4 ++-- ui-core/src/main/res/values/strings.xml | 4 ++-- 26 files changed, 52 insertions(+), 52 deletions(-) diff --git a/example-app/src/main/res/values/strings.xml b/example-app/src/main/res/values/strings.xml index a3bfde5856..a790cc154d 100644 --- a/example-app/src/main/res/values/strings.xml +++ b/example-app/src/main/res/values/strings.xml @@ -13,7 +13,7 @@ Gift Card Gift Card - Your country code must be %s + Your country/region code must be %s Your currency code must be %s Google Pay is unavailable on this device @@ -45,7 +45,7 @@ threeds_mode 3D Secure shopper_country - Country + Country/region shopper_locale Shopper Locale shopper_email diff --git a/ui-core/src/main/res/values-ar/strings.xml b/ui-core/src/main/res/values-ar/strings.xml index 29c36247bd..923fdc7d5d 100644 --- a/ui-core/src/main/res/values-ar/strings.xml +++ b/ui-core/src/main/res/values-ar/strings.xml @@ -28,7 +28,7 @@ رقم المنزل الشقة / الجناح المدينة - البلد + البلد/المنطقة الولاية الرمز البريدي الرمز البريدي @@ -40,7 +40,7 @@ رقم المنزل (اختياري) الشقة / الجناح (اختياري) المدينة (اختياري) - البلد (اختياري) + البلد/المنطقة (اختياري) الولاية (اختياري) الرمز البريدي (اختياري) الرمز البريدي (اختياري) diff --git a/ui-core/src/main/res/values-cs-rCZ/strings.xml b/ui-core/src/main/res/values-cs-rCZ/strings.xml index c38348c57c..6fc2124657 100644 --- a/ui-core/src/main/res/values-cs-rCZ/strings.xml +++ b/ui-core/src/main/res/values-cs-rCZ/strings.xml @@ -28,7 +28,7 @@ Číslo popisné Byt Město - Země + Země/region Stát PSČ PSČ @@ -40,7 +40,7 @@ Číslo popisné (nepovinné) Byt (nepovinné) Město (nepovinné) - Země (nepovinné) + Země/region (nepovinné) Stát (nepovinné) Poštovní směrovací číslo (nepovinné) PSČ (nepovinné) diff --git a/ui-core/src/main/res/values-da-rDK/strings.xml b/ui-core/src/main/res/values-da-rDK/strings.xml index 32d5fe0038..6c7e7ea9e4 100644 --- a/ui-core/src/main/res/values-da-rDK/strings.xml +++ b/ui-core/src/main/res/values-da-rDK/strings.xml @@ -28,7 +28,7 @@ Husnummer Lejlighed/suite By - Land + Land/region Stat Postnummer Postnummer @@ -40,7 +40,7 @@ Husnummer (valgfrit) Lejlighed/suite (valgfrit) By (valgfrit) - Land (valgfrit) + Land/region (valgfrit) Stat (valgfrit) Postnummer (valgfrit) Postnummer (valgfrit) diff --git a/ui-core/src/main/res/values-de-rDE/strings.xml b/ui-core/src/main/res/values-de-rDE/strings.xml index 50cf511ac9..a3e2dfc03a 100644 --- a/ui-core/src/main/res/values-de-rDE/strings.xml +++ b/ui-core/src/main/res/values-de-rDE/strings.xml @@ -28,7 +28,7 @@ Hausnummer Wohnung/Geschoss Stadt - Land + Land/Region Bundesstaat Postleitzahl PLZ @@ -40,7 +40,7 @@ Hausnummer (optional) Wohnung/Geschoss (optional) Stadt (optional) - Land (optional) + Land/Region (optional) Bundesstaat (optional) Postleitzahl (optional) PLZ (optional) diff --git a/ui-core/src/main/res/values-el-rGR/strings.xml b/ui-core/src/main/res/values-el-rGR/strings.xml index 07a0503a77..c427f4a4ec 100644 --- a/ui-core/src/main/res/values-el-rGR/strings.xml +++ b/ui-core/src/main/res/values-el-rGR/strings.xml @@ -28,7 +28,7 @@ Αριθμός οικίας Διαμέρισμα/Γραφείο Πόλη - Χώρα + Χώρα/Περιοχή Πολιτεία Ταχυδρομικός κωδικός Ταχυδρομικός κώδικας @@ -40,7 +40,7 @@ Αριθμός οικίας (προαιρετικό) Διαμέρισμα/Γραφείο (προαιρετικό) Πόλη (προαιρετικό) - Χώρα (προαιρετικό) + Χώρα/Περιοχή (προαιρετικό) Πολιτεία (προαιρετικό) Ταχυδρομικός κωδικός (προαιρετικό) Ταχυδρομικός κωδικός (προαιρετικό) diff --git a/ui-core/src/main/res/values-es-rES/strings.xml b/ui-core/src/main/res/values-es-rES/strings.xml index 189cbb4d2d..42c909f03d 100644 --- a/ui-core/src/main/res/values-es-rES/strings.xml +++ b/ui-core/src/main/res/values-es-rES/strings.xml @@ -28,7 +28,7 @@ Número de vivienda Apartamento/suite Ciudad - País + País o región Estado Código postal Código postal @@ -40,7 +40,7 @@ Número de vivienda (opcional) Apartamento/suite (opcional) Ciudad (opcional) - País (opcional) + País o región (opcional) Estado (opcional) Código postal (opcional) Código postal (opcional) diff --git a/ui-core/src/main/res/values-fi-rFI/strings.xml b/ui-core/src/main/res/values-fi-rFI/strings.xml index b336f5b5bc..0671c5c685 100644 --- a/ui-core/src/main/res/values-fi-rFI/strings.xml +++ b/ui-core/src/main/res/values-fi-rFI/strings.xml @@ -28,7 +28,7 @@ Talon numero Huoneisto / sviitti Kaupunki - Maa + Maa/alue Osavaltio Postinumero Postinumero @@ -40,7 +40,7 @@ Talon numero (valinnainen) Huoneisto / sviitti (valinnainen) Kaupunki (valinnainen) - Maa (valinnainen) + Maa/alue (valinnainen) Osavaltio (valinnainen) Postinumero (valinnainen) Postinumero (valinnainen) diff --git a/ui-core/src/main/res/values-fr-rFR/strings.xml b/ui-core/src/main/res/values-fr-rFR/strings.xml index 6b39c805c3..722f22a03d 100644 --- a/ui-core/src/main/res/values-fr-rFR/strings.xml +++ b/ui-core/src/main/res/values-fr-rFR/strings.xml @@ -28,7 +28,7 @@ Numéro de rue Appartement Ville - Pays + Pays/Région État Code postal Code postal @@ -40,7 +40,7 @@ Numéro de rue (facultatif) Appartement (facultatif) Ville (facultatif) - Pays (facultatif) + Pays/Région (facultatif) État (facultatif) Code postal (facultatif) Code postal (facultatif) diff --git a/ui-core/src/main/res/values-hr-rHR/strings.xml b/ui-core/src/main/res/values-hr-rHR/strings.xml index 62e0f17962..0c3a72fda8 100644 --- a/ui-core/src/main/res/values-hr-rHR/strings.xml +++ b/ui-core/src/main/res/values-hr-rHR/strings.xml @@ -28,7 +28,7 @@ Kućni broj Stan/apartman Grad - Zemlja + Zemlja/regija Savezna država Poštanski broj Poštanski broj @@ -40,7 +40,7 @@ Kućni broj (neobavezno) Stan/apartman (neobavezno) Grad (nije obvezno) - Zemlja (neobavezno) + Zemlja/regija (neobavezno) Savezna država (neobavezno) Poštanski broj (nije obvezno) Poštanski broj (neobavezno) diff --git a/ui-core/src/main/res/values-hu-rHU/strings.xml b/ui-core/src/main/res/values-hu-rHU/strings.xml index 3af44abe0f..6b182fa479 100644 --- a/ui-core/src/main/res/values-hu-rHU/strings.xml +++ b/ui-core/src/main/res/values-hu-rHU/strings.xml @@ -28,7 +28,7 @@ Házszám Lakás/ajtószám Város - Ország + Ország/régió Állam Irányítószám Irányítószám @@ -40,7 +40,7 @@ Házszám (nem kötelező) Lakás/ajtószám (nem kötelező) Város (nem kötelező) - Ország (nem kötelező) + Ország/régió (nem kötelező) Állam (nem kötelező) Irányítószám (nem kötelező) Irányítószám (nem kötelező) diff --git a/ui-core/src/main/res/values-it-rIT/strings.xml b/ui-core/src/main/res/values-it-rIT/strings.xml index f71fb9fdc0..438034ec69 100644 --- a/ui-core/src/main/res/values-it-rIT/strings.xml +++ b/ui-core/src/main/res/values-it-rIT/strings.xml @@ -28,7 +28,7 @@ Numero civico Appartamento/suite Città - Paese + Paese/Regione Stato Codice postale CAP @@ -40,7 +40,7 @@ Numero civico (facoltativo) Appartamento / Suite (facoltativo) Città (facoltativo) - Paese (facoltativo) + Paese/Regione (facoltativo) Stato (facoltativo) Codice postale (facoltativo) CAP (facoltativo) diff --git a/ui-core/src/main/res/values-ja-rJP/strings.xml b/ui-core/src/main/res/values-ja-rJP/strings.xml index 7183bb2aaa..294819a5bd 100644 --- a/ui-core/src/main/res/values-ja-rJP/strings.xml +++ b/ui-core/src/main/res/values-ja-rJP/strings.xml @@ -28,7 +28,7 @@ 部屋番号 アパート名/部屋名 市区 - + 国/地域 都道府県 郵便番号 郵便番号 @@ -40,7 +40,7 @@ 部屋番号 (任意) アパート名/部屋名 (任意) 市区町村 (任意) - 国 (任意) + 国/地域 (任意) 都道府県 (任意) 郵便番号 (任意) 郵便番号 (任意) diff --git a/ui-core/src/main/res/values-ko-rKR/strings.xml b/ui-core/src/main/res/values-ko-rKR/strings.xml index 60d1d4437a..ebe601e514 100644 --- a/ui-core/src/main/res/values-ko-rKR/strings.xml +++ b/ui-core/src/main/res/values-ko-rKR/strings.xml @@ -28,7 +28,7 @@ 집 전화번호 아파트/건물 - 국가 + 국가/지역 우편번호 우편번호 @@ -40,7 +40,7 @@ 집 전화번호(선택 사항) 아파트/건물(선택 사항) 도시(선택 사항) - 국가(선택 사항) + 국가/지역(선택 사항) 주(선택 사항) 우편번호(선택 사항) 우편 번호(선택 사항) diff --git a/ui-core/src/main/res/values-nb-rNO/strings.xml b/ui-core/src/main/res/values-nb-rNO/strings.xml index ace064b24b..73fecbce5c 100644 --- a/ui-core/src/main/res/values-nb-rNO/strings.xml +++ b/ui-core/src/main/res/values-nb-rNO/strings.xml @@ -28,7 +28,7 @@ Husnummer Leilighet/suite Poststed - Land + Land/region Delstat Postnummer Postnummer @@ -40,7 +40,7 @@ Husnummer (valgfritt) Leilighet/suite (valgfritt) Poststed (valgfritt) - Land (valgfritt) + Land/region (valgfritt) Delstat (valgfritt) Postnummer (valgfritt) Postnummer (valgfritt) diff --git a/ui-core/src/main/res/values-nl-rNL/strings.xml b/ui-core/src/main/res/values-nl-rNL/strings.xml index c9fbb25dec..fc576a1557 100644 --- a/ui-core/src/main/res/values-nl-rNL/strings.xml +++ b/ui-core/src/main/res/values-nl-rNL/strings.xml @@ -28,7 +28,7 @@ Huisnummer Appartement/Suite Stad - Land + Land/regio Staat Postcode Postcode @@ -40,7 +40,7 @@ Huisnummer (optioneel) Appartement/Suite (optioneel) Stad (optioneel) - Land (optioneel) + Land/regio (optioneel) Staat (optioneel) Postcode (optioneel) Postcode (optioneel) diff --git a/ui-core/src/main/res/values-pl-rPL/strings.xml b/ui-core/src/main/res/values-pl-rPL/strings.xml index 05b97cec49..64fde1a5f7 100644 --- a/ui-core/src/main/res/values-pl-rPL/strings.xml +++ b/ui-core/src/main/res/values-pl-rPL/strings.xml @@ -28,7 +28,7 @@ Numer domu i mieszkania Numer domu/mieszkania Miasto - Kraj + Kraj/Region Stan Kod pocztowy Kod pocztowy @@ -40,7 +40,7 @@ Numer domu i mieszkania (opcjonalnie) Numer domu/mieszkania (opcjonalnie) Miasto (opcjonalnie) - Kraj (opcjonalnie) + Kraj/Region (opcjonalnie) Stan (opcjonalnie) Kod pocztowy (opcjonalnie) Kod pocztowy (opcjonalnie) diff --git a/ui-core/src/main/res/values-pt-rBR/strings.xml b/ui-core/src/main/res/values-pt-rBR/strings.xml index eababec81d..05e89f2c5a 100644 --- a/ui-core/src/main/res/values-pt-rBR/strings.xml +++ b/ui-core/src/main/res/values-pt-rBR/strings.xml @@ -28,7 +28,7 @@ Número da casa Apartamento/Conjunto Cidade - País + País/região Estado CEP Código postal @@ -40,7 +40,7 @@ Número da casa (opcional) Apartamento/Conjunto (opcional) Cidade (opcional) - País (opcional) + País/região (opcional) Estado (opcional) Código postal (opcional) Código postal (opcional) diff --git a/ui-core/src/main/res/values-pt-rPT/strings.xml b/ui-core/src/main/res/values-pt-rPT/strings.xml index 110e10a076..0dbce79f0c 100644 --- a/ui-core/src/main/res/values-pt-rPT/strings.xml +++ b/ui-core/src/main/res/values-pt-rPT/strings.xml @@ -28,7 +28,7 @@ Número de porta Apartamento/Suite Cidade - País + País/região Estado Código postal Código postal @@ -40,7 +40,7 @@ Número de porta (opcional) Apartamento/Suite (opcional) Cidade (opcional) - País (opcional) + País/região (opcional) Estado (opcional) Código postal (opcional) Código postal (opcional) diff --git a/ui-core/src/main/res/values-ro-rRO/strings.xml b/ui-core/src/main/res/values-ro-rRO/strings.xml index 852b0b2c11..061d869cb8 100644 --- a/ui-core/src/main/res/values-ro-rRO/strings.xml +++ b/ui-core/src/main/res/values-ro-rRO/strings.xml @@ -28,7 +28,7 @@ Număr Apartament Oraș - Țară + Țară/regiune Stat Cod poștal Cod poștal @@ -40,7 +40,7 @@ Număr (opțional) Apartament(opțional) Oraș (opțional) - Țară (opțional) + Țară/regiune (opțional) Stat (opțional) Cod poștal (opțional) Cod poștal (opțional) diff --git a/ui-core/src/main/res/values-ru-rRU/strings.xml b/ui-core/src/main/res/values-ru-rRU/strings.xml index 2cbda30d55..07d07ba731 100644 --- a/ui-core/src/main/res/values-ru-rRU/strings.xml +++ b/ui-core/src/main/res/values-ru-rRU/strings.xml @@ -28,7 +28,7 @@ Номер дома Квартира / помещение Город - Страна + Страна/регион Штат Почтовый индекс Почтовый индекс @@ -40,7 +40,7 @@ Номер дома (необязательно) Квартира / помещение (необязательно) Город (необязательно) - Страна (необязательно) + Страна/регион (необязательно) Штат (необязательно) Почтовый индекс (необязательно) Почтовый индекс (необязательно) diff --git a/ui-core/src/main/res/values-sk-rSK/strings.xml b/ui-core/src/main/res/values-sk-rSK/strings.xml index c6f80e1df9..3df4e0432a 100644 --- a/ui-core/src/main/res/values-sk-rSK/strings.xml +++ b/ui-core/src/main/res/values-sk-rSK/strings.xml @@ -28,7 +28,7 @@ Číslo domu Byt/apartmán Mesto - Krajina + Krajina/región Štát PSČ PSČ @@ -40,7 +40,7 @@ Číslo domu (nepovinné) Byt/apartmán (voliteľné) Mesto (nepovinné) - Krajina (voliteľné) + Krajina/región (voliteľné) Štát (nepovinné) Poštové smerovacie číslo (nepovinné) PSČ (nepovinné) diff --git a/ui-core/src/main/res/values-sl-rSI/strings.xml b/ui-core/src/main/res/values-sl-rSI/strings.xml index 9700bb2e5c..f83f6d01a0 100644 --- a/ui-core/src/main/res/values-sl-rSI/strings.xml +++ b/ui-core/src/main/res/values-sl-rSI/strings.xml @@ -28,7 +28,7 @@ Hišna številka Št. apartmaja/stanovanja Mesto - Država + Država/regija Država Poštna številka Poštna številka @@ -40,7 +40,7 @@ Hišna številka (neobvezno) Št. apartmaja/stanovanja (neobvezno) Kraj (neobvezno) - Država (neobvezno) + Država/regija (neobvezno) Država (neobvezno) Poštna številka (neobvezno) Poštna številka (neobvezno) diff --git a/ui-core/src/main/res/values-sv-rSE/strings.xml b/ui-core/src/main/res/values-sv-rSE/strings.xml index 5e64df2f3b..5ce440ab6c 100644 --- a/ui-core/src/main/res/values-sv-rSE/strings.xml +++ b/ui-core/src/main/res/values-sv-rSE/strings.xml @@ -28,7 +28,7 @@ Husnummer Lägenhetsnummer Stad - Land + Land/region Delstat Postnummer Postnummer @@ -40,7 +40,7 @@ Husnummer (valfritt) Lägenhetsnummer (valfritt) Ort (valfritt) - Land (valfritt) + Land/region (valfritt) Delstat (valfritt) Postnummer (valfritt) Postnummer (valfritt) diff --git a/ui-core/src/main/res/values-zh-rTW/strings.xml b/ui-core/src/main/res/values-zh-rTW/strings.xml index 1aa118699b..5e6d50ecc2 100644 --- a/ui-core/src/main/res/values-zh-rTW/strings.xml +++ b/ui-core/src/main/res/values-zh-rTW/strings.xml @@ -28,7 +28,7 @@ 門牌號 公寓/套房 城市 - 國家/地區 + 國家/地區 郵遞區號 郵遞區號 @@ -40,7 +40,7 @@ 門牌號(選用) 公寓/套房(選用) 城市(選用) - 國家/地區(選用) + 國家/地區(選用) 州(選用) 郵遞區號(選用) 郵遞區號(選用) diff --git a/ui-core/src/main/res/values/strings.xml b/ui-core/src/main/res/values/strings.xml index ebd8d3f7b0..e54764ef77 100644 --- a/ui-core/src/main/res/values/strings.xml +++ b/ui-core/src/main/res/values/strings.xml @@ -28,7 +28,7 @@ House number Apartment / Suite City - Country + Country/Region State Postal code Zip code @@ -40,7 +40,7 @@ House number (optional) Apartment / Suite (optional) City (optional) - Country (optional) + Country/Region (optional) State (optional) Postal code (optional) Zip code (optional) From 6b37f214d856e167a96f6411f0db885d9b92fbfd Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 24 Apr 2024 13:38:55 +0200 Subject: [PATCH 038/272] Add release notes COAND-849 --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ce08fe74af..c6928a2b86 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,3 +10,7 @@ ## Fixed - Fixed various memory leaks. + +## Changed +- Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). +- Strings containing "country" are changed to "country/region". From 9ee42a711a55a8c6021a1378e0d87fb1a8d72b5f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 24 Apr 2024 15:49:28 +0200 Subject: [PATCH 039/272] Add unit tests for CountryUtils Also sort the list of countries based on alphabetical order. COAND-849 --- .../core/internal/util/CountryUtils.kt | 298 +++++++++--------- .../core/internal/util/CountryUtilsTest.kt | 45 +++ 2 files changed, 195 insertions(+), 148 deletions(-) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/util/CountryUtilsTest.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt index a146dc16ac..0da4fb4a63 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/CountryUtils.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core.internal.util import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting import java.util.Locale @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -40,248 +41,249 @@ object CountryUtils { return countryLocale.getDisplayCountry(locale) } - private val countries = listOf( + @VisibleForTesting + internal val countries = listOf( + CountryInfo(isoCode = "AD", callingCode = "+376"), + CountryInfo(isoCode = "AE", callingCode = "+971"), CountryInfo(isoCode = "AF", callingCode = "+93"), + CountryInfo(isoCode = "AG", callingCode = "+1268"), + CountryInfo(isoCode = "AI", callingCode = "+1264"), CountryInfo(isoCode = "AL", callingCode = "+355"), - CountryInfo(isoCode = "DZ", callingCode = "+213"), - CountryInfo(isoCode = "AS", callingCode = "+1684"), - CountryInfo(isoCode = "AD", callingCode = "+376"), + CountryInfo(isoCode = "AM", callingCode = "+374"), + CountryInfo(isoCode = "AN", callingCode = "+599"), CountryInfo(isoCode = "AO", callingCode = "+244"), - CountryInfo(isoCode = "AI", callingCode = "+1264"), - CountryInfo(isoCode = "AG", callingCode = "+1268"), + CountryInfo(isoCode = "AQ", callingCode = "+672"), CountryInfo(isoCode = "AR", callingCode = "+54"), - CountryInfo(isoCode = "AM", callingCode = "+374"), - CountryInfo(isoCode = "AW", callingCode = "+297"), - CountryInfo(isoCode = "AU", callingCode = "+61"), + CountryInfo(isoCode = "AS", callingCode = "+1684"), CountryInfo(isoCode = "AT", callingCode = "+43"), + CountryInfo(isoCode = "AU", callingCode = "+61"), + CountryInfo(isoCode = "AW", callingCode = "+297"), + CountryInfo(isoCode = "AX", callingCode = "+358"), CountryInfo(isoCode = "AZ", callingCode = "+994"), - CountryInfo(isoCode = "BS", callingCode = "+1242"), - CountryInfo(isoCode = "BH", callingCode = "+973"), - CountryInfo(isoCode = "BD", callingCode = "+880"), + CountryInfo(isoCode = "BA", callingCode = "+387"), CountryInfo(isoCode = "BB", callingCode = "+1246"), - CountryInfo(isoCode = "BY", callingCode = "+375"), + CountryInfo(isoCode = "BD", callingCode = "+880"), CountryInfo(isoCode = "BE", callingCode = "+32"), - CountryInfo(isoCode = "BZ", callingCode = "+501"), + CountryInfo(isoCode = "BF", callingCode = "+226"), + CountryInfo(isoCode = "BG", callingCode = "+359"), + CountryInfo(isoCode = "BH", callingCode = "+973"), + CountryInfo(isoCode = "BI", callingCode = "+257"), CountryInfo(isoCode = "BJ", callingCode = "+229"), + CountryInfo(isoCode = "BL", callingCode = "+590"), CountryInfo(isoCode = "BM", callingCode = "+1441"), + CountryInfo(isoCode = "BN", callingCode = "+673"), + CountryInfo(isoCode = "BO", callingCode = "+591"), + CountryInfo(isoCode = "BR", callingCode = "+55"), + CountryInfo(isoCode = "BS", callingCode = "+1242"), CountryInfo(isoCode = "BT", callingCode = "+975"), - CountryInfo(isoCode = "BA", callingCode = "+387"), CountryInfo(isoCode = "BW", callingCode = "+267"), - CountryInfo(isoCode = "BR", callingCode = "+55"), - CountryInfo(isoCode = "IO", callingCode = "+246"), - CountryInfo(isoCode = "BG", callingCode = "+359"), - CountryInfo(isoCode = "BF", callingCode = "+226"), - CountryInfo(isoCode = "BI", callingCode = "+257"), - CountryInfo(isoCode = "KH", callingCode = "+855"), - CountryInfo(isoCode = "CM", callingCode = "+237"), + CountryInfo(isoCode = "BY", callingCode = "+375"), + CountryInfo(isoCode = "BZ", callingCode = "+501"), CountryInfo(isoCode = "CA", callingCode = "+1"), - CountryInfo(isoCode = "CV", callingCode = "+238"), - CountryInfo(isoCode = "KY", callingCode = "+345"), + CountryInfo(isoCode = "CC", callingCode = "+61"), + CountryInfo(isoCode = "CD", callingCode = "+243"), CountryInfo(isoCode = "CF", callingCode = "+236"), - CountryInfo(isoCode = "TD", callingCode = "+235"), + CountryInfo(isoCode = "CG", callingCode = "+242"), + CountryInfo(isoCode = "CH", callingCode = "+41"), + CountryInfo(isoCode = "CI", callingCode = "+225"), + CountryInfo(isoCode = "CK", callingCode = "+682"), CountryInfo(isoCode = "CL", callingCode = "+56"), + CountryInfo(isoCode = "CM", callingCode = "+237"), CountryInfo(isoCode = "CN", callingCode = "+86"), - CountryInfo(isoCode = "CX", callingCode = "+61"), CountryInfo(isoCode = "CO", callingCode = "+57"), - CountryInfo(isoCode = "KM", callingCode = "+269"), - CountryInfo(isoCode = "CG", callingCode = "+242"), - CountryInfo(isoCode = "CK", callingCode = "+682"), CountryInfo(isoCode = "CR", callingCode = "+506"), - CountryInfo(isoCode = "HR", callingCode = "+385"), CountryInfo(isoCode = "CU", callingCode = "+53"), + CountryInfo(isoCode = "CV", callingCode = "+238"), + CountryInfo(isoCode = "CX", callingCode = "+61"), CountryInfo(isoCode = "CY", callingCode = "+537"), CountryInfo(isoCode = "CZ", callingCode = "+420"), - CountryInfo(isoCode = "DK", callingCode = "+45"), + CountryInfo(isoCode = "DE", callingCode = "+49"), CountryInfo(isoCode = "DJ", callingCode = "+253"), + CountryInfo(isoCode = "DK", callingCode = "+45"), CountryInfo(isoCode = "DM", callingCode = "+1767"), CountryInfo(isoCode = "DO", callingCode = "+1849"), + CountryInfo(isoCode = "DZ", callingCode = "+213"), CountryInfo(isoCode = "EC", callingCode = "+593"), + CountryInfo(isoCode = "EE", callingCode = "+372"), CountryInfo(isoCode = "EG", callingCode = "+20"), - CountryInfo(isoCode = "SV", callingCode = "+503"), - CountryInfo(isoCode = "GQ", callingCode = "+240"), CountryInfo(isoCode = "ER", callingCode = "+291"), - CountryInfo(isoCode = "EE", callingCode = "+372"), + CountryInfo(isoCode = "ES", callingCode = "+34"), CountryInfo(isoCode = "ET", callingCode = "+251"), - CountryInfo(isoCode = "FO", callingCode = "+298"), - CountryInfo(isoCode = "FJ", callingCode = "+679"), CountryInfo(isoCode = "FI", callingCode = "+358"), + CountryInfo(isoCode = "FJ", callingCode = "+679"), + CountryInfo(isoCode = "FK", callingCode = "+500"), + CountryInfo(isoCode = "FM", callingCode = "+691"), + CountryInfo(isoCode = "FO", callingCode = "+298"), CountryInfo(isoCode = "FR", callingCode = "+33"), - CountryInfo(isoCode = "GF", callingCode = "+594"), - CountryInfo(isoCode = "PF", callingCode = "+689"), CountryInfo(isoCode = "GA", callingCode = "+241"), - CountryInfo(isoCode = "GM", callingCode = "+220"), + CountryInfo(isoCode = "GB", callingCode = "+44"), + CountryInfo(isoCode = "GD", callingCode = "+1473"), CountryInfo(isoCode = "GE", callingCode = "+995"), - CountryInfo(isoCode = "DE", callingCode = "+49"), + CountryInfo(isoCode = "GF", callingCode = "+594"), + CountryInfo(isoCode = "GG", callingCode = "+44"), CountryInfo(isoCode = "GH", callingCode = "+233"), CountryInfo(isoCode = "GI", callingCode = "+350"), - CountryInfo(isoCode = "GR", callingCode = "+30"), CountryInfo(isoCode = "GL", callingCode = "+299"), - CountryInfo(isoCode = "GD", callingCode = "+1473"), + CountryInfo(isoCode = "GM", callingCode = "+220"), + CountryInfo(isoCode = "GN", callingCode = "+224"), CountryInfo(isoCode = "GP", callingCode = "+590"), - CountryInfo(isoCode = "GU", callingCode = "+1671"), + CountryInfo(isoCode = "GQ", callingCode = "+240"), + CountryInfo(isoCode = "GR", callingCode = "+30"), + CountryInfo(isoCode = "GS", callingCode = "+500"), CountryInfo(isoCode = "GT", callingCode = "+502"), - CountryInfo(isoCode = "GN", callingCode = "+224"), + CountryInfo(isoCode = "GU", callingCode = "+1671"), CountryInfo(isoCode = "GW", callingCode = "+245"), CountryInfo(isoCode = "GY", callingCode = "+595"), - CountryInfo(isoCode = "HT", callingCode = "+509"), + CountryInfo(isoCode = "HK", callingCode = "+852"), CountryInfo(isoCode = "HN", callingCode = "+504"), + CountryInfo(isoCode = "HR", callingCode = "+385"), + CountryInfo(isoCode = "HT", callingCode = "+509"), CountryInfo(isoCode = "HU", callingCode = "+36"), - CountryInfo(isoCode = "IS", callingCode = "+354"), - CountryInfo(isoCode = "IN", callingCode = "+91"), CountryInfo(isoCode = "ID", callingCode = "+62"), - CountryInfo(isoCode = "IQ", callingCode = "+964"), CountryInfo(isoCode = "IE", callingCode = "+353"), CountryInfo(isoCode = "IL", callingCode = "+972"), + CountryInfo(isoCode = "IM", callingCode = "+44"), + CountryInfo(isoCode = "IN", callingCode = "+91"), + CountryInfo(isoCode = "IO", callingCode = "+246"), + CountryInfo(isoCode = "IQ", callingCode = "+964"), + CountryInfo(isoCode = "IR", callingCode = "+98"), + CountryInfo(isoCode = "IS", callingCode = "+354"), CountryInfo(isoCode = "IT", callingCode = "+39"), + CountryInfo(isoCode = "JE", callingCode = "+44"), CountryInfo(isoCode = "JM", callingCode = "+1876"), - CountryInfo(isoCode = "JP", callingCode = "+81"), CountryInfo(isoCode = "JO", callingCode = "+962"), - CountryInfo(isoCode = "KZ", callingCode = "+77"), + CountryInfo(isoCode = "JP", callingCode = "+81"), CountryInfo(isoCode = "KE", callingCode = "+254"), + CountryInfo(isoCode = "KG", callingCode = "+996"), + CountryInfo(isoCode = "KH", callingCode = "+855"), CountryInfo(isoCode = "KI", callingCode = "+686"), + CountryInfo(isoCode = "KM", callingCode = "+269"), + CountryInfo(isoCode = "KN", callingCode = "+1869"), + CountryInfo(isoCode = "KP", callingCode = "+850"), + CountryInfo(isoCode = "KR", callingCode = "+82"), CountryInfo(isoCode = "KW", callingCode = "+965"), - CountryInfo(isoCode = "KG", callingCode = "+996"), - CountryInfo(isoCode = "LV", callingCode = "+371"), + CountryInfo(isoCode = "KY", callingCode = "+345"), + CountryInfo(isoCode = "KZ", callingCode = "+77"), + CountryInfo(isoCode = "LA", callingCode = "+856"), CountryInfo(isoCode = "LB", callingCode = "+961"), - CountryInfo(isoCode = "LS", callingCode = "+266"), - CountryInfo(isoCode = "LR", callingCode = "+231"), + CountryInfo(isoCode = "LC", callingCode = "+1758"), CountryInfo(isoCode = "LI", callingCode = "+423"), + CountryInfo(isoCode = "LK", callingCode = "+94"), + CountryInfo(isoCode = "LR", callingCode = "+231"), + CountryInfo(isoCode = "LS", callingCode = "+266"), CountryInfo(isoCode = "LT", callingCode = "+370"), CountryInfo(isoCode = "LU", callingCode = "+352"), + CountryInfo(isoCode = "LV", callingCode = "+371"), + CountryInfo(isoCode = "LY", callingCode = "+218"), + CountryInfo(isoCode = "MA", callingCode = "+212"), + CountryInfo(isoCode = "MC", callingCode = "+377"), + CountryInfo(isoCode = "MD", callingCode = "+373"), + CountryInfo(isoCode = "ME", callingCode = "+382"), + CountryInfo(isoCode = "MF", callingCode = "+590"), CountryInfo(isoCode = "MG", callingCode = "+261"), - CountryInfo(isoCode = "MW", callingCode = "+265"), - CountryInfo(isoCode = "MY", callingCode = "+60"), - CountryInfo(isoCode = "MV", callingCode = "+960"), - CountryInfo(isoCode = "ML", callingCode = "+223"), - CountryInfo(isoCode = "MT", callingCode = "+356"), CountryInfo(isoCode = "MH", callingCode = "+692"), + CountryInfo(isoCode = "MK", callingCode = "+389"), + CountryInfo(isoCode = "ML", callingCode = "+223"), + CountryInfo(isoCode = "MM", callingCode = "+95"), + CountryInfo(isoCode = "MN", callingCode = "+976"), + CountryInfo(isoCode = "MO", callingCode = "+853"), + CountryInfo(isoCode = "MP", callingCode = "+1670"), CountryInfo(isoCode = "MQ", callingCode = "+596"), CountryInfo(isoCode = "MR", callingCode = "+222"), + CountryInfo(isoCode = "MS", callingCode = "+1664"), + CountryInfo(isoCode = "MT", callingCode = "+356"), CountryInfo(isoCode = "MU", callingCode = "+230"), - CountryInfo(isoCode = "YT", callingCode = "+262"), + CountryInfo(isoCode = "MV", callingCode = "+960"), + CountryInfo(isoCode = "MW", callingCode = "+265"), CountryInfo(isoCode = "MX", callingCode = "+52"), - CountryInfo(isoCode = "MC", callingCode = "+377"), - CountryInfo(isoCode = "MN", callingCode = "+976"), - CountryInfo(isoCode = "ME", callingCode = "+382"), - CountryInfo(isoCode = "MS", callingCode = "+1664"), - CountryInfo(isoCode = "MA", callingCode = "+212"), - CountryInfo(isoCode = "MM", callingCode = "+95"), + CountryInfo(isoCode = "MY", callingCode = "+60"), + CountryInfo(isoCode = "MZ", callingCode = "+258"), CountryInfo(isoCode = "NA", callingCode = "+264"), - CountryInfo(isoCode = "NR", callingCode = "+674"), - CountryInfo(isoCode = "NP", callingCode = "+977"), - CountryInfo(isoCode = "NL", callingCode = "+31"), - CountryInfo(isoCode = "AN", callingCode = "+599"), CountryInfo(isoCode = "NC", callingCode = "+687"), - CountryInfo(isoCode = "NZ", callingCode = "+64"), - CountryInfo(isoCode = "NI", callingCode = "+505"), CountryInfo(isoCode = "NE", callingCode = "+227"), - CountryInfo(isoCode = "NG", callingCode = "+234"), - CountryInfo(isoCode = "NU", callingCode = "+683"), CountryInfo(isoCode = "NF", callingCode = "+672"), - CountryInfo(isoCode = "MP", callingCode = "+1670"), + CountryInfo(isoCode = "NG", callingCode = "+234"), + CountryInfo(isoCode = "NI", callingCode = "+505"), + CountryInfo(isoCode = "NL", callingCode = "+31"), CountryInfo(isoCode = "NO", callingCode = "+47"), + CountryInfo(isoCode = "NP", callingCode = "+977"), + CountryInfo(isoCode = "NR", callingCode = "+674"), + CountryInfo(isoCode = "NU", callingCode = "+683"), + CountryInfo(isoCode = "NZ", callingCode = "+64"), CountryInfo(isoCode = "OM", callingCode = "+968"), - CountryInfo(isoCode = "PK", callingCode = "+92"), - CountryInfo(isoCode = "PW", callingCode = "+680"), CountryInfo(isoCode = "PA", callingCode = "+507"), - CountryInfo(isoCode = "PG", callingCode = "+675"), - CountryInfo(isoCode = "PY", callingCode = "+595"), CountryInfo(isoCode = "PE", callingCode = "+51"), + CountryInfo(isoCode = "PF", callingCode = "+689"), + CountryInfo(isoCode = "PG", callingCode = "+675"), CountryInfo(isoCode = "PH", callingCode = "+63"), + CountryInfo(isoCode = "PK", callingCode = "+92"), CountryInfo(isoCode = "PL", callingCode = "+48"), - CountryInfo(isoCode = "PT", callingCode = "+351"), + CountryInfo(isoCode = "PM", callingCode = "+508"), + CountryInfo(isoCode = "PN", callingCode = "+872"), CountryInfo(isoCode = "PR", callingCode = "+1939"), + CountryInfo(isoCode = "PS", callingCode = "+970"), + CountryInfo(isoCode = "PT", callingCode = "+351"), + CountryInfo(isoCode = "PW", callingCode = "+680"), + CountryInfo(isoCode = "PY", callingCode = "+595"), CountryInfo(isoCode = "QA", callingCode = "+974"), + CountryInfo(isoCode = "RE", callingCode = "+262"), CountryInfo(isoCode = "RO", callingCode = "+40"), + CountryInfo(isoCode = "RS", callingCode = "+381"), + CountryInfo(isoCode = "RU", callingCode = "+7"), CountryInfo(isoCode = "RW", callingCode = "+250"), - CountryInfo(isoCode = "WS", callingCode = "+685"), - CountryInfo(isoCode = "SM", callingCode = "+378"), CountryInfo(isoCode = "SA", callingCode = "+966"), - CountryInfo(isoCode = "SN", callingCode = "+221"), - CountryInfo(isoCode = "RS", callingCode = "+381"), + CountryInfo(isoCode = "SB", callingCode = "+677"), CountryInfo(isoCode = "SC", callingCode = "+248"), - CountryInfo(isoCode = "SL", callingCode = "+232"), + CountryInfo(isoCode = "SD", callingCode = "+249"), + CountryInfo(isoCode = "SE", callingCode = "+46"), CountryInfo(isoCode = "SG", callingCode = "+65"), - CountryInfo(isoCode = "SK", callingCode = "+421"), + CountryInfo(isoCode = "SH", callingCode = "+290"), CountryInfo(isoCode = "SI", callingCode = "+386"), - CountryInfo(isoCode = "SB", callingCode = "+677"), - CountryInfo(isoCode = "ZA", callingCode = "+27"), - CountryInfo(isoCode = "GS", callingCode = "+500"), - CountryInfo(isoCode = "ES", callingCode = "+34"), - CountryInfo(isoCode = "LK", callingCode = "+94"), - CountryInfo(isoCode = "SD", callingCode = "+249"), + CountryInfo(isoCode = "SJ", callingCode = "+47"), + CountryInfo(isoCode = "SK", callingCode = "+421"), + CountryInfo(isoCode = "SL", callingCode = "+232"), + CountryInfo(isoCode = "SM", callingCode = "+378"), + CountryInfo(isoCode = "SN", callingCode = "+221"), + CountryInfo(isoCode = "SO", callingCode = "+252"), CountryInfo(isoCode = "SR", callingCode = "+597"), + CountryInfo(isoCode = "ST", callingCode = "+239"), + CountryInfo(isoCode = "SV", callingCode = "+503"), + CountryInfo(isoCode = "SY", callingCode = "+963"), CountryInfo(isoCode = "SZ", callingCode = "+268"), - CountryInfo(isoCode = "SE", callingCode = "+46"), - CountryInfo(isoCode = "CH", callingCode = "+41"), - CountryInfo(isoCode = "TJ", callingCode = "+992"), - CountryInfo(isoCode = "TH", callingCode = "+66"), + CountryInfo(isoCode = "TC", callingCode = "+1649"), + CountryInfo(isoCode = "TD", callingCode = "+235"), CountryInfo(isoCode = "TG", callingCode = "+228"), + CountryInfo(isoCode = "TH", callingCode = "+66"), + CountryInfo(isoCode = "TJ", callingCode = "+992"), CountryInfo(isoCode = "TK", callingCode = "+690"), - CountryInfo(isoCode = "TO", callingCode = "+676"), - CountryInfo(isoCode = "TT", callingCode = "+1868"), + CountryInfo(isoCode = "TL", callingCode = "+670"), + CountryInfo(isoCode = "TM", callingCode = "+993"), CountryInfo(isoCode = "TN", callingCode = "+216"), + CountryInfo(isoCode = "TO", callingCode = "+676"), CountryInfo(isoCode = "TR", callingCode = "+90"), - CountryInfo(isoCode = "TM", callingCode = "+993"), - CountryInfo(isoCode = "TC", callingCode = "+1649"), + CountryInfo(isoCode = "TT", callingCode = "+1868"), CountryInfo(isoCode = "TV", callingCode = "+688"), - CountryInfo(isoCode = "UG", callingCode = "+256"), + CountryInfo(isoCode = "TW", callingCode = "+886"), + CountryInfo(isoCode = "TZ", callingCode = "+255"), CountryInfo(isoCode = "UA", callingCode = "+380"), - CountryInfo(isoCode = "AE", callingCode = "+971"), - CountryInfo(isoCode = "GB", callingCode = "+44"), + CountryInfo(isoCode = "UG", callingCode = "+256"), CountryInfo(isoCode = "US", callingCode = "+1"), CountryInfo(isoCode = "UY", callingCode = "+598"), CountryInfo(isoCode = "UZ", callingCode = "+998"), + CountryInfo(isoCode = "VA", callingCode = "+379"), + CountryInfo(isoCode = "VC", callingCode = "+1784"), + CountryInfo(isoCode = "VE", callingCode = "+58"), + CountryInfo(isoCode = "VG", callingCode = "+1284"), + CountryInfo(isoCode = "VI", callingCode = "+1340"), + CountryInfo(isoCode = "VN", callingCode = "+84"), CountryInfo(isoCode = "VU", callingCode = "+678"), CountryInfo(isoCode = "WF", callingCode = "+681"), + CountryInfo(isoCode = "WS", callingCode = "+685"), CountryInfo(isoCode = "YE", callingCode = "+967"), + CountryInfo(isoCode = "YT", callingCode = "+262"), + CountryInfo(isoCode = "ZA", callingCode = "+27"), CountryInfo(isoCode = "ZM", callingCode = "+260"), CountryInfo(isoCode = "ZW", callingCode = "+263"), - CountryInfo(isoCode = "AX", callingCode = "+358"), - CountryInfo(isoCode = "AQ", callingCode = "+672"), - CountryInfo(isoCode = "BO", callingCode = "+591"), - CountryInfo(isoCode = "BN", callingCode = "+673"), - CountryInfo(isoCode = "CC", callingCode = "+61"), - CountryInfo(isoCode = "CD", callingCode = "+243"), - CountryInfo(isoCode = "CI", callingCode = "+225"), - CountryInfo(isoCode = "FK", callingCode = "+500"), - CountryInfo(isoCode = "GG", callingCode = "+44"), - CountryInfo(isoCode = "VA", callingCode = "+379"), - CountryInfo(isoCode = "HK", callingCode = "+852"), - CountryInfo(isoCode = "IR", callingCode = "+98"), - CountryInfo(isoCode = "IM", callingCode = "+44"), - CountryInfo(isoCode = "JE", callingCode = "+44"), - CountryInfo(isoCode = "KP", callingCode = "+850"), - CountryInfo(isoCode = "KR", callingCode = "+82"), - CountryInfo(isoCode = "LA", callingCode = "+856"), - CountryInfo(isoCode = "LY", callingCode = "+218"), - CountryInfo(isoCode = "MO", callingCode = "+853"), - CountryInfo(isoCode = "MK", callingCode = "+389"), - CountryInfo(isoCode = "FM", callingCode = "+691"), - CountryInfo(isoCode = "MD", callingCode = "+373"), - CountryInfo(isoCode = "MZ", callingCode = "+258"), - CountryInfo(isoCode = "PS", callingCode = "+970"), - CountryInfo(isoCode = "PN", callingCode = "+872"), - CountryInfo(isoCode = "RE", callingCode = "+262"), - CountryInfo(isoCode = "RU", callingCode = "+7"), - CountryInfo(isoCode = "BL", callingCode = "+590"), - CountryInfo(isoCode = "SH", callingCode = "+290"), - CountryInfo(isoCode = "KN", callingCode = "+1869"), - CountryInfo(isoCode = "LC", callingCode = "+1758"), - CountryInfo(isoCode = "MF", callingCode = "+590"), - CountryInfo(isoCode = "PM", callingCode = "+508"), - CountryInfo(isoCode = "VC", callingCode = "+1784"), - CountryInfo(isoCode = "ST", callingCode = "+239"), - CountryInfo(isoCode = "SO", callingCode = "+252"), - CountryInfo(isoCode = "SJ", callingCode = "+47"), - CountryInfo(isoCode = "SY", callingCode = "+963"), - CountryInfo(isoCode = "TW", callingCode = "+886"), - CountryInfo(isoCode = "TZ", callingCode = "+255"), - CountryInfo(isoCode = "TL", callingCode = "+670"), - CountryInfo(isoCode = "VE", callingCode = "+58"), - CountryInfo(isoCode = "VN", callingCode = "+84"), - CountryInfo(isoCode = "VG", callingCode = "+1284"), - CountryInfo(isoCode = "VI", callingCode = "+1340") ) } diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/util/CountryUtilsTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/util/CountryUtilsTest.kt new file mode 100644 index 0000000000..49f8189ecf --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/util/CountryUtilsTest.kt @@ -0,0 +1,45 @@ +package com.adyen.checkout.components.core.internal.util + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import java.util.Locale + +internal class CountryUtilsTest { + + @Nested + inner class GetCountriesTest { + + @Test + fun `when passing nothing, then all countries are returned`() { + val actual = CountryUtils.getCountries() + + assertEquals(CountryUtils.countries, actual) + } + + @Test + fun `when passing list of countries, then only specified countries are returned`() { + val specifiedCountries = listOf( + "NL", + "US", + "DE", + ) + val actual = CountryUtils.getCountries(specifiedCountries) + + val expected = listOf( + CountryInfo(isoCode = "DE", callingCode = "+49"), + CountryInfo(isoCode = "NL", callingCode = "+31"), + CountryInfo(isoCode = "US", callingCode = "+1"), + ) + assertEquals(expected, actual) + } + } + + @Test + fun `when getting country name, then it is returned localized`() { + val actual = CountryUtils.getCountryName("NL", Locale.US) + + val expected = "Netherlands" + assertEquals(expected, actual) + } +} From 7d247b1c3be5fcf1cd01e611585ef62cfcbf2f95 Mon Sep 17 00:00:00 2001 From: josephj Date: Wed, 24 Apr 2024 17:07:28 +0200 Subject: [PATCH 040/272] Make BcmcComponent constructor internal --- bcmc/src/main/java/com/adyen/checkout/bcmc/BcmcComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/BcmcComponent.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/BcmcComponent.kt index 7f1a786332..19fba8a926 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/BcmcComponent.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/BcmcComponent.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.internal.PaymentComponent /** * A [PaymentComponent] that supports the [PaymentMethodTypes.BCMC] payment method. */ -class BcmcComponent( +class BcmcComponent internal constructor( cardDelegate: CardDelegate, genericActionDelegate: GenericActionDelegate, actionHandlingComponent: DefaultActionHandlingComponent, From 3678868257f10b526fdd9102a0100306174b43e1 Mon Sep 17 00:00:00 2001 From: josephj Date: Wed, 24 Apr 2024 17:07:43 +0200 Subject: [PATCH 041/272] Restrict CardComponent constructor --- card/src/main/java/com/adyen/checkout/card/CardComponent.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/card/src/main/java/com/adyen/checkout/card/CardComponent.kt b/card/src/main/java/com/adyen/checkout/card/CardComponent.kt index d4c6aa6fec..024b0182a1 100644 --- a/card/src/main/java/com/adyen/checkout/card/CardComponent.kt +++ b/card/src/main/java/com/adyen/checkout/card/CardComponent.kt @@ -40,7 +40,9 @@ import kotlinx.coroutines.flow.Flow * A [PaymentComponent] that supports the [PaymentMethodTypes.SCHEME] payment method. */ @Suppress("TooManyFunctions") -open class CardComponent constructor( +open class CardComponent +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +constructor( private val cardDelegate: CardDelegate, private val genericActionDelegate: GenericActionDelegate, private val actionHandlingComponent: DefaultActionHandlingComponent, From d4962cd684656e1b593e3070dc5c113af89a6b88 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 24 Apr 2024 17:07:14 +0200 Subject: [PATCH 042/272] Hold a weak reference to the on redirect callback --- .../checkout/ui/core/internal/DefaultRedirectHandler.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/DefaultRedirectHandler.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/DefaultRedirectHandler.kt index d225ad10fa..8adeaafddb 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/DefaultRedirectHandler.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/DefaultRedirectHandler.kt @@ -23,11 +23,12 @@ import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.ui.core.internal.util.ThemeUtil import org.json.JSONException import org.json.JSONObject +import java.lang.ref.WeakReference @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class DefaultRedirectHandler : RedirectHandler { - private var onRedirectListener: (() -> Unit)? = null + private var onRedirectListener: WeakReference<(() -> Unit)>? = null override fun parseRedirectResult(data: Uri?): JSONObject { adyenLog(AdyenLogLevel.DEBUG) { "parseRedirectResult - $data" } @@ -72,7 +73,7 @@ class DefaultRedirectHandler : RedirectHandler { launchWithCustomTabs(context, uri) || launchBrowser(context, uri) ) { - onRedirectListener?.invoke() + onRedirectListener?.get()?.invoke() return } @@ -194,10 +195,11 @@ class DefaultRedirectHandler : RedirectHandler { } override fun setOnRedirectListener(listener: () -> Unit) { - onRedirectListener = listener + onRedirectListener = WeakReference(listener) } override fun removeOnRedirectListener() { + onRedirectListener?.clear() onRedirectListener = null } From c7339aaacaee531f9f4a94199b40ff3d5edcb57b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 24 Apr 2024 17:08:35 +0200 Subject: [PATCH 043/272] Set on redirect listener when delegate is available In some edge cases when setOnRedirectListener would be called after creation of a delegate it would not actually set the callback. Now it will. --- .../action/core/internal/ui/DefaultGenericActionDelegate.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 5a19fd0bb4..ebbdd57ed3 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -196,6 +196,11 @@ internal class DefaultGenericActionDelegate( } override fun setOnRedirectListener(listener: () -> Unit) { + _delegate?.let { delegate -> + if (delegate is RedirectableDelegate) { + delegate.setOnRedirectListener(listener) + } + } onRedirectListener = listener } From ba4a369da89cb332c8ffbb6fd9384a7ed9341426 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 25 Apr 2024 17:17:04 +0200 Subject: [PATCH 044/272] Add function to set initial log level This fixes a bug where drop-in would override the log level that a merchant has already set before. COAND-889 --- .../com/adyen/checkout/core/AdyenLogger.kt | 14 ++++++++++++- .../core/internal/util/AdyenLoggerTest.kt | 20 +++++++++++++++++++ .../java/com/adyen/checkout/dropin/DropIn.kt | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/AdyenLogger.kt b/checkout-core/src/main/java/com/adyen/checkout/core/AdyenLogger.kt index 81f2ea42b3..346d357720 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/AdyenLogger.kt +++ b/checkout-core/src/main/java/com/adyen/checkout/core/AdyenLogger.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.core import android.util.Log +import androidx.annotation.RestrictTo import com.adyen.checkout.core.internal.util.LogcatLogger import com.adyen.checkout.core.internal.util.Logger @@ -35,6 +36,8 @@ interface AdyenLogger { internal var logger: AdyenLogger = LogcatLogger() private set + private var didSetLogLevelManually = false + /** * Sets the minimum level to be logged. */ @@ -55,13 +58,14 @@ interface AdyenLogger { Log.ERROR -> AdyenLogLevel.ERROR else -> AdyenLogLevel.NONE } - logger.setLogLevel(mappedLevel) + setLogLevel(mappedLevel) } /** * Sets the minimum level to be logged. */ fun setLogLevel(level: AdyenLogLevel) { + didSetLogLevelManually = true logger.setLogLevel(level) } @@ -77,6 +81,14 @@ interface AdyenLogger { */ fun resetLogger() { this.logger = LogcatLogger() + didSetLogLevelManually = false + } + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + fun setInitialLogLevel(level: AdyenLogLevel) { + if (!didSetLogLevelManually) { + logger.setLogLevel(level) + } } } } diff --git a/checkout-core/src/test/java/com/adyen/checkout/core/internal/util/AdyenLoggerTest.kt b/checkout-core/src/test/java/com/adyen/checkout/core/internal/util/AdyenLoggerTest.kt index d84f6005be..a3957d190b 100644 --- a/checkout-core/src/test/java/com/adyen/checkout/core/internal/util/AdyenLoggerTest.kt +++ b/checkout-core/src/test/java/com/adyen/checkout/core/internal/util/AdyenLoggerTest.kt @@ -27,6 +27,7 @@ internal class AdyenLoggerTest { @BeforeEach fun setup() { + AdyenLogger.resetLogger() logger = TestLogger() } @@ -77,6 +78,25 @@ internal class AdyenLoggerTest { logger.assertLogNotCalled() } + @Test + fun `when log level is set manually, then setting the initial level has no effect`() { + AdyenLogger.setLogger(logger) + AdyenLogger.setLogLevel(AdyenLogLevel.ASSERT) + + AdyenLogger.setInitialLogLevel(AdyenLogLevel.ERROR) + + logger.assertLogLevel(AdyenLogLevel.ASSERT) + } + + @Test + fun `when log level is not set manually, then setting the initial level has effect`() { + AdyenLogger.setLogger(logger) + + AdyenLogger.setInitialLogLevel(AdyenLogLevel.ERROR) + + logger.assertLogLevel(AdyenLogLevel.ERROR) + } + @Suppress("DEPRECATION") @ParameterizedTest @MethodSource("logLevelSource") diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/DropIn.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/DropIn.kt index 3325718186..65d6c468be 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/DropIn.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/DropIn.kt @@ -256,6 +256,6 @@ object DropIn { } else { AdyenLogLevel.NONE } - AdyenLogger.setLogLevel(logLevel) + AdyenLogger.setInitialLogLevel(logLevel) } } From 7672cbc358dbc34093cebf9286ca649c380bf9c9 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 25 Apr 2024 17:23:20 +0200 Subject: [PATCH 045/272] Add release note COAND-889 --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c6928a2b86..f819cac7c7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,7 @@ ## Fixed - Fixed various memory leaks. +- Drop-in no longer overrides the log level in case of debug builds. ## Changed - Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). From 999dffecf812497c882bb6ff34f15bb0e124e35b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 12 Apr 2024 13:24:32 +0200 Subject: [PATCH 046/272] Update 3DS2 SDK to 2.2.17 COAND-886 --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 4a19888e52..4cca498057 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -50,7 +50,7 @@ ext { compose_viewmodel_version = '2.7.0' // Adyen Dependencies - adyen3ds2_version = "2.2.15" + adyen3ds2_version = "2.2.17" // External Dependencies cash_app_pay_version = '2.3.0' diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 0fc9f673ad..d0be6d1131 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2326,6 +2326,14 @@ + + + + + + + + @@ -5520,6 +5528,14 @@ + + + + + + + + @@ -6948,6 +6964,11 @@ + + + + + @@ -7137,6 +7158,11 @@ + + + + + @@ -7147,6 +7173,11 @@ + + + + + @@ -7163,6 +7194,14 @@ + + + + + + + + @@ -7173,6 +7212,11 @@ + + + + + @@ -7189,6 +7233,14 @@ + + + + + + + + From e66440028e0f2445a5757f0f380c99b4bf867481 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 12 Apr 2024 13:25:54 +0200 Subject: [PATCH 047/272] Make sure FingerprintToken parameters are not null The new version of the 3DS2 SDK doesn't accept these parameters to be nullable anymore. COAND-886 --- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index cc5295a438..2fdd0aaa33 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -210,7 +210,10 @@ internal class DefaultAdyen3DS2Delegate( return } - val configParameters = createAdyenConfigParameters(fingerprintToken) + val configParameters = createAdyenConfigParameters(fingerprintToken) ?: run { + exceptionChannel.trySend(ComponentException("Failed to create ConfigParameters.")) + return + } val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> adyenLog(AdyenLogLevel.ERROR, throwable) { "Unexpected uncaught 3DS2 Exception" } @@ -259,18 +262,27 @@ internal class DefaultAdyen3DS2Delegate( return FingerprintToken.SERIALIZER.deserialize(fingerprintJson) } + @Suppress("DestructuringDeclarationWithTooManyEntries") private fun createAdyenConfigParameters( fingerprintToken: FingerprintToken - ): ConfigParameters = AdyenConfigParameters.Builder( - // directoryServerId - fingerprintToken.directoryServerId, - // directoryServerPublicKey - fingerprintToken.directoryServerPublicKey, - // directoryServerRootCertificates - fingerprintToken.directoryServerRootCertificates, - ) - .deviceParameterBlockList(componentParams.deviceParameterBlockList) - .build() + ): ConfigParameters? { + val (directoryServerId, directoryServerPublicKey, directoryServerRootCertificates, _, _) = fingerprintToken + + if (directoryServerId == null || directoryServerPublicKey == null || directoryServerRootCertificates == null) { + adyenLog(AdyenLogLevel.DEBUG) { + "directoryServerId, directoryServerPublicKey or directoryServerRootCertificates is null." + } + return null + } + + return AdyenConfigParameters.Builder( + directoryServerId, + directoryServerPublicKey, + directoryServerRootCertificates, + ) + .deviceParameterBlockList(componentParams.deviceParameterBlockList) + .build() + } private fun createTransaction(fingerprintToken: FingerprintToken): Transaction? { if (fingerprintToken.threeDSMessageVersion == null) { From 6a26957c26bae73140dd4b8d05d23ed9153e700c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 12 Apr 2024 13:26:46 +0200 Subject: [PATCH 048/272] Propagate error details when SDK initialization fails COAND-886 --- .../adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index 2fdd0aaa33..3bf4974251 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -46,6 +46,7 @@ import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.threeds2.AuthenticationRequestParameters import com.adyen.threeds2.ChallengeResult import com.adyen.threeds2.ChallengeStatusHandler +import com.adyen.threeds2.InitializeResult import com.adyen.threeds2.ThreeDS2Service import com.adyen.threeds2.Transaction import com.adyen.threeds2.exception.InvalidInputException @@ -224,11 +225,13 @@ internal class DefaultAdyen3DS2Delegate( // This makes sure the 3DS2 SDK doesn't re-use any state from previous transactions closeTransaction() - try { - adyenLog(AdyenLogLevel.DEBUG) { "initialize 3DS2 SDK" } + adyenLog(AdyenLogLevel.DEBUG) { "initialize 3DS2 SDK" } + val initializeResult = threeDS2Service.initialize(activity, configParameters, null, componentParams.uiCustomization) - } catch (e: SDKRuntimeException) { - exceptionChannel.trySend(ComponentException("Failed to initialize 3DS2 SDK", e)) + + if (initializeResult is InitializeResult.Failure) { + val details = makeDetails(initializeResult.transactionStatus, initializeResult.additionalDetails) + emitDetails(details) return@launch } From 8bf7ac996f916eb366554f5c17c14821117e0309 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 12 Apr 2024 13:27:16 +0200 Subject: [PATCH 049/272] Propagate error details when transaction creation fails COAND-886 --- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index 3bf4974251..6dfd21d253 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -49,6 +49,7 @@ import com.adyen.threeds2.ChallengeStatusHandler import com.adyen.threeds2.InitializeResult import com.adyen.threeds2.ThreeDS2Service import com.adyen.threeds2.Transaction +import com.adyen.threeds2.TransactionResult import com.adyen.threeds2.exception.InvalidInputException import com.adyen.threeds2.exception.SDKNotInitializedException import com.adyen.threeds2.exception.SDKRuntimeException @@ -289,17 +290,22 @@ internal class DefaultAdyen3DS2Delegate( private fun createTransaction(fingerprintToken: FingerprintToken): Transaction? { if (fingerprintToken.threeDSMessageVersion == null) { - exceptionChannel.trySend( - ComponentException( - "Failed to create 3DS2 Transaction. Missing threeDSMessageVersion inside fingerprintToken.", - ), - ) + val error = "Failed to create 3DS2 Transaction. Missing threeDSMessageVersion inside fingerprintToken." + exceptionChannel.trySend(ComponentException(error)) return null } return try { adyenLog(AdyenLogLevel.DEBUG) { "create transaction" } - threeDS2Service.createTransaction(null, fingerprintToken.threeDSMessageVersion) + when (val result = threeDS2Service.createTransaction(null, fingerprintToken.threeDSMessageVersion)) { + is TransactionResult.Failure -> { + val details = makeDetails(result.transactionStatus, result.additionalDetails) + emitDetails(details) + null + } + + is TransactionResult.Success -> result.transaction + } } catch (e: SDKNotInitializedException) { exceptionChannel.trySend(ComponentException("Failed to create 3DS2 Transaction", e)) null From b4228482a2f514d76d5506eb9fa85ece7ccef4cb Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 26 Apr 2024 13:16:56 +0200 Subject: [PATCH 050/272] Fix unit tests COAND-886 --- .../data/model/Adyen3DS2Serializer.kt | 11 +- .../internal/data/model/ChallengeResult.kt | 6 +- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 2 +- .../adyen3ds2/Adyen3DS2ComponentTest.kt | 20 +- .../ui/DefaultAdyen3DS2DelegateTest.kt | 480 +++++++++--------- 5 files changed, 272 insertions(+), 247 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/Adyen3DS2Serializer.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/Adyen3DS2Serializer.kt index 498787104f..20d2110c1c 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/Adyen3DS2Serializer.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/Adyen3DS2Serializer.kt @@ -13,11 +13,6 @@ import org.json.JSONException import org.json.JSONObject internal class Adyen3DS2Serializer { - companion object { - private const val FINGERPRINT_DETAILS_KEY = "threeds2.fingerprint" - private const val CHALLENGE_DETAILS_KEY = "threeds2.challengeResult" - private const val THREEDS_RESULT_KEY = "threeDSResult" - } @Throws(ComponentException::class) fun createFingerprintDetails(encodedFingerprint: String): JSONObject { @@ -57,4 +52,10 @@ internal class Adyen3DS2Serializer { } return threeDsDetails } + + companion object { + private const val FINGERPRINT_DETAILS_KEY = "threeds2.fingerprint" + private const val CHALLENGE_DETAILS_KEY = "threeds2.challengeResult" + private const val THREEDS_RESULT_KEY = "threeDSResult" + } } diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/ChallengeResult.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/ChallengeResult.kt index 3a5a906a34..b9e17c4bca 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/ChallengeResult.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/data/model/ChallengeResult.kt @@ -7,9 +7,10 @@ */ package com.adyen.checkout.adyen3ds2.internal.data.model -import com.adyen.checkout.components.core.internal.util.AndroidBase64Encoder import org.json.JSONException import org.json.JSONObject +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi internal class ChallengeResult private constructor(val isAuthenticated: Boolean, val payload: String) { @@ -28,6 +29,7 @@ internal class ChallengeResult private constructor(val isAuthenticated: Boolean, * @return The filled object with the content needed for the details response. * @throws JSONException In case parsing fails. */ + @OptIn(ExperimentalEncodingApi::class) fun from( transactionStatus: String, errorDetails: String? = null, @@ -38,7 +40,7 @@ internal class ChallengeResult private constructor(val isAuthenticated: Boolean, jsonObject.put(KEY_TRANSACTION_STATUS, transactionStatus) jsonObject.putOpt(KEY_AUTHORISATION_TOKEN, authorisationToken) jsonObject.putOpt(KEY_SDK_ERROR, errorDetails) - val payload = AndroidBase64Encoder().encode(jsonObject.toString()) + val payload = Base64.encode(jsonObject.toString().toByteArray()) return ChallengeResult(isAuthenticated, payload) } } diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index 6dfd21d253..af2fe077a7 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -500,7 +500,7 @@ internal class DefaultAdyen3DS2Delegate( private fun cleanUp3DS2() { @Suppress("SwallowedException") try { - ThreeDS2Service.INSTANCE.cleanup(application) + threeDS2Service.cleanup(application) } catch (e: SDKNotInitializedException) { // Safe to ignore } diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/Adyen3DS2ComponentTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/Adyen3DS2ComponentTest.kt index daa9052fee..aebc8b5ca7 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/Adyen3DS2ComponentTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/Adyen3DS2ComponentTest.kt @@ -12,7 +12,6 @@ import android.app.Activity import android.content.Intent import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.viewModelScope -import app.cash.turbine.test import com.adyen.checkout.adyen3ds2.internal.ui.Adyen3DS2ComponentViewType import com.adyen.checkout.adyen3ds2.internal.ui.Adyen3DS2Delegate import com.adyen.checkout.components.core.action.Threeds2Action @@ -21,6 +20,7 @@ import com.adyen.checkout.components.core.internal.ActionComponentEventHandler import com.adyen.checkout.test.LoggingExtension import com.adyen.checkout.test.TestDispatcherExtension import com.adyen.checkout.test.extensions.invokeOnCleared +import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.test.TestComponentViewType import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest @@ -80,10 +80,9 @@ internal class Adyen3DS2ComponentTest( @Test fun `when component is initialized then view flow should match delegate view flow`() = runTest { - component.viewFlow.test { - assertEquals(Adyen3DS2ComponentViewType, awaitItem()) - expectNoEvents() - } + val viewFlow = component.viewFlow.test(testScheduler) + + assertEquals(Adyen3DS2ComponentViewType, viewFlow.latestValue) } @Test @@ -91,15 +90,12 @@ internal class Adyen3DS2ComponentTest( val delegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1) whenever(adyen3DS2Delegate.viewFlow) doReturn delegateViewFlow component = Adyen3DS2Component(adyen3DS2Delegate, actionComponentEventHandler) + val viewFlow = component.viewFlow.test(testScheduler) - component.viewFlow.test { - assertEquals(TestComponentViewType.VIEW_TYPE_1, awaitItem()) - - delegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2) - assertEquals(TestComponentViewType.VIEW_TYPE_2, awaitItem()) + assertEquals(TestComponentViewType.VIEW_TYPE_1, viewFlow.values[0]) - expectNoEvents() - } + delegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2) + assertEquals(TestComponentViewType.VIEW_TYPE_2, viewFlow.values[1]) } @Test diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt index 5b57a41917..9b9260aa09 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt @@ -10,9 +10,9 @@ package com.adyen.checkout.adyen3ds2.internal.ui import android.app.Activity import android.app.Application +import android.content.Context import android.content.Intent import androidx.lifecycle.SavedStateHandle -import app.cash.turbine.test import com.adyen.checkout.adyen3ds2.Authentication3DS2Exception import com.adyen.checkout.adyen3ds2.Cancelled3DS2Exception import com.adyen.checkout.adyen3ds2.internal.data.api.SubmitFingerprintRepository @@ -32,17 +32,23 @@ import com.adyen.checkout.components.core.internal.util.JavaBase64Encoder import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.test.TestDispatcherExtension +import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.test.TestRedirectHandler import com.adyen.threeds2.AuthenticationRequestParameters import com.adyen.threeds2.ChallengeResult import com.adyen.threeds2.ChallengeStatusHandler import com.adyen.threeds2.ChallengeStatusReceiver +import com.adyen.threeds2.InitializeResult import com.adyen.threeds2.ProgressDialog import com.adyen.threeds2.ThreeDS2Service import com.adyen.threeds2.Transaction +import com.adyen.threeds2.TransactionResult +import com.adyen.threeds2.Warning +import com.adyen.threeds2.customization.UiCustomization import com.adyen.threeds2.exception.InvalidInputException import com.adyen.threeds2.exception.SDKRuntimeException import com.adyen.threeds2.parameters.ChallengeParameters +import com.adyen.threeds2.parameters.ConfigParameters import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -50,6 +56,7 @@ import kotlinx.coroutines.test.runTest import org.json.JSONException import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -62,6 +69,7 @@ import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import java.io.IOException import java.util.Locale @@ -70,8 +78,6 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultAdyen3DS2DelegateTest( @Mock private val submitFingerprintRepository: SubmitFingerprintRepository, - @Mock private val adyen3DS2Serializer: Adyen3DS2Serializer, - @Mock private val threeDS2Service: ThreeDS2Service, ) { private lateinit var redirectHandler: TestRedirectHandler @@ -80,12 +86,20 @@ internal class DefaultAdyen3DS2DelegateTest( private val base64Encoder = JavaBase64Encoder() + private val threeDS2Service: TestThreeDS2Service = TestThreeDS2Service() + @BeforeEach fun setup() { redirectHandler = TestRedirectHandler() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) + delegate = createDelegate() + } + + private fun createDelegate( + adyen3DS2Serializer: Adyen3DS2Serializer = Adyen3DS2Serializer() + ): DefaultAdyen3DS2Delegate { val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) - delegate = DefaultAdyen3DS2Delegate( + return DefaultAdyen3DS2Delegate( observerRepository = ActionObserverRepository(), savedStateHandle = SavedStateHandle(), componentParams = Adyen3DS2ComponentParamsMapper(CommonComponentParamsMapper()) @@ -110,45 +124,41 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `Threeds2FingerprintAction and token is null, then an exception is thrown`() = runTest { delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(Threeds2FingerprintAction(token = null), Activity()) + delegate.handleAction(Threeds2FingerprintAction(token = null), Activity()) - assertTrue(awaitItem() is ComponentException) - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test fun `Threeds2ChallengeAction and token is null, then an exception is thrown`() = runTest { delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(Threeds2ChallengeAction(token = null), Activity()) + delegate.handleAction(Threeds2ChallengeAction(token = null), Activity()) - assertTrue(awaitItem() is ComponentException) - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test fun `Threeds2Action and token is null, then an exception is thrown`() = runTest { delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(Threeds2Action(token = null), Activity()) + delegate.handleAction(Threeds2Action(token = null), Activity()) - assertTrue(awaitItem() is ComponentException) - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test fun `Threeds2Action and sub type is null, then an exception is thrown`() = runTest { delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(Threeds2Action(token = "sometoken", subtype = null), Activity()) + delegate.handleAction(Threeds2Action(token = "sometoken", subtype = null), Activity()) - assertTrue(awaitItem() is ComponentException) - } + assertTrue(exceptionFlow.latestValue is ComponentException) } } @@ -159,65 +169,78 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `fingerprint is malformed, then an exception is thrown`() = runTest { delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - val encodedJson = base64Encoder.encode("{incorrectJson}") - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = base64Encoder.encode("{incorrectJson}") + delegate.identifyShopper(Activity(), encodedJson, false) - assertTrue(awaitItem() is ComponentException) - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test fun `3ds2 sdk throws an exception while initializing, then an exception emitted`() = runTest { - val error = SDKRuntimeException("test", "test", null) - whenever(threeDS2Service.initialize(any(), any(), anyOrNull(), anyOrNull())) doAnswer { - throw error - } - delegate.initialize(this) + val error = InvalidInputException("test", null) + threeDS2Service.initializeError = error + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - val encodedJson = base64Encoder.encode( - """ - { - "directoryServerId":"id", - "directoryServerPublicKey":"key" - } - """.trimIndent(), - ) - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) - assertEquals(error, awaitItem().cause) - } + assertEquals(error, exceptionFlow.latestValue.cause) + } + + @Test + fun `3ds2 sdk returns an initialization error, then details are emitted`() = runTest { + val transStatus = "X" + val additionalDetails = "mockAdditionalDetails" + threeDS2Service.initializeResult = InitializeResult.Failure(transStatus, additionalDetails) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) + + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) + + // We don't care about the encoded value in this test, we just want to know if details are there + assertNotNull(detailsFlow.latestValue.details) } @Test fun `creating 3ds2 transaction fails, then an exception emitted`() = runTest { val error = SDKRuntimeException("test", "test", null) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doAnswer { - throw error - } - delegate.initialize(this) + threeDS2Service.createTransactionError = error + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) - assertEquals(error, awaitItem().cause) - } + assertEquals(error, exceptionFlow.latestValue.cause) + } + + @Test + fun `creating 3ds2 transaction return transaction error, then details are emitted`() = runTest { + threeDS2Service.transactionResult = TransactionResult.Failure("X", "mockDetails") + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) + + // We don't care about the encoded value in this test, we just want to know if details are there + assertNotNull(detailsFlow.latestValue.details) } @Test fun `transaction parameters are null, then an exception emitted`() = runTest { - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn TestTransaction() delegate.initialize(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) - assertTrue(awaitItem() is ComponentException) - } + assertEquals("Failed to retrieve 3DS2 authentication parameters", exceptionFlow.latestValue.message) } @Test @@ -230,23 +253,22 @@ internal class DefaultAdyen3DS2DelegateTest( sdkEphemeralPublicKey = "{}", messageVersion = "messageVersion", ) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn TestTransaction(authReqParams) + threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) val submitFingerprintResult = SubmitFingerprintResult.Completed(JSONObject()) whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.success(submitFingerprintResult) + val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.initialize(this) - delegate.detailsFlow.test { - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) - delegate.identifyShopper(Activity(), encodedJson, true) + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, true) - val expected = ActionComponentData( - paymentData = null, - details = submitFingerprintResult.details, - ) - assertEquals(expected, awaitItem()) - } + val expected = ActionComponentData( + paymentData = null, + details = submitFingerprintResult.details, + ) + assertEquals(expected, detailsFlow.latestValue) } @Test @@ -260,7 +282,7 @@ internal class DefaultAdyen3DS2DelegateTest( sdkEphemeralPublicKey = "{}", messageVersion = "messageVersion", ) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn TestTransaction(authReqParams) + threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) val submitFingerprintResult = SubmitFingerprintResult.Redirect(RedirectAction()) whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.success(submitFingerprintResult) @@ -283,18 +305,17 @@ internal class DefaultAdyen3DS2DelegateTest( sdkEphemeralPublicKey = "{}", messageVersion = "messageVersion", ) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn TestTransaction(authReqParams) + threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) val error = IOException("test") whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.failure(error) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.initialize(this) - delegate.exceptionFlow.test { - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) - delegate.identifyShopper(Activity(), encodedJson, true) - assertEquals(error, awaitItem().cause) - } + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, true) + assertEquals(error, exceptionFlow.latestValue.cause) } @Test @@ -307,22 +328,14 @@ internal class DefaultAdyen3DS2DelegateTest( sdkEphemeralPublicKey = "{}", messageVersion = "messageVersion", ) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn TestTransaction(authReqParams) - val fingerprintDetails = JSONObject("{\"finger\":\"print\"}") - whenever(adyen3DS2Serializer.createFingerprintDetails(any())) doReturn fingerprintDetails - + threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) + val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.initialize(this) - delegate.detailsFlow.test { - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + delegate.identifyShopper(Activity(), encodedJson, false) - val expected = ActionComponentData( - paymentData = null, - details = fingerprintDetails, - ) - assertEquals(expected, awaitItem()) - } + assertNotNull(detailsFlow.latestValue.details) } } @@ -332,29 +345,28 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `transaction is null, then an exception is emitted`() = runTest { - delegate.exceptionFlow.test { - delegate.challengeShopper(Activity(), "token") + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + delegate.challengeShopper(mock(), "token") - assertTrue(awaitItem() is Authentication3DS2Exception) - } + assertTrue(exceptionFlow.latestValue is Authentication3DS2Exception) } @Test fun `token can't be decoded, then an exception is emitted`() = runTest { initializeTransaction(this) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.challengeShopper(Activity(), base64Encoder.encode("token")) + delegate.challengeShopper(Activity(), base64Encoder.encode("token")) - assertTrue(awaitItem().cause is JSONException) - } + assertTrue(exceptionFlow.latestValue.cause is JSONException) } @Test fun `everything is good, then challenge should be executed`() = runTest { val transaction = initializeTransaction(this) - delegate.challengeShopper(Activity(), base64Encoder.encode("{}")) + // We need to set the messageVersion to workaround an error in the 3DS2 SDK + delegate.challengeShopper(Activity(), base64Encoder.encode("{\"messageVersion\":\"2.1.0\"}")) transaction.assertDoChallengeCalled() } @@ -365,11 +377,12 @@ internal class DefaultAdyen3DS2DelegateTest( shouldThrowError = true } - delegate.exceptionFlow.test { - delegate.challengeShopper(Activity(), base64Encoder.encode("{}")) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - assertTrue(awaitItem().cause is InvalidInputException) - } + // We need to set the messageVersion to workaround an error in the 3DS2 SDK + delegate.challengeShopper(Activity(), base64Encoder.encode("{\"messageVersion\":\"2.1.0\"}")) + + assertTrue(exceptionFlow.latestValue.cause is InvalidInputException) } private fun initializeTransaction(scope: CoroutineScope): TestTransaction { @@ -379,10 +392,10 @@ internal class DefaultAdyen3DS2DelegateTest( sdkAppID = "sdkAppID", sdkReferenceNumber = "sdkReferenceNumber", sdkEphemeralPublicKey = "{}", - messageVersion = "messageVersion", + messageVersion = "2.1.0", ) val transaction = TestTransaction(authReqParams) - whenever(threeDS2Service.createTransaction(anyOrNull(), any())) doReturn transaction + threeDS2Service.transactionResult = TransactionResult.Success(transaction) delegate.initialize(scope) @@ -399,27 +412,26 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `result is parsed, then details are emitted`() = runTest { - delegate.detailsFlow.test { - delegate.handleIntent(Intent()) + val detailsFlow = delegate.detailsFlow.test(testScheduler) - val expected = ActionComponentData( - paymentData = null, - details = TestRedirectHandler.REDIRECT_RESULT, - ) - assertEquals(expected, awaitItem()) - } + delegate.handleIntent(Intent()) + + val expected = ActionComponentData( + paymentData = null, + details = TestRedirectHandler.REDIRECT_RESULT, + ) + assertEquals(expected, detailsFlow.latestValue) } @Test fun `parsing fails, then an exception is emitted`() = runTest { val error = ComponentException("yes") redirectHandler.exception = error + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleIntent(Intent()) + delegate.handleIntent(Intent()) - assertEquals(error, awaitItem()) - } + assertEquals(error, exceptionFlow.latestValue) } } @@ -429,148 +441,83 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `completed, then details are emitted`() = runTest { - val details = JSONObject("{}") - whenever( - adyen3DS2Serializer.createChallengeDetails( + val details = + JSONObject("{\"threeds2.challengeResult\":\"eyJ0cmFuc1N0YXR1cyI6InRyYW5zYWN0aW9uU3RhdHVzIn0=\"}") + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + delegate.onCompletion( + result = ChallengeResult.Completed( transactionStatus = "transactionStatus", ), - ) doReturn details - - delegate.detailsFlow.test { - delegate.onCompletion( - result = ChallengeResult.Completed( - transactionStatus = "transactionStatus", - ), - ) + ) - val expected = ActionComponentData( - paymentData = null, - details = details, - ) - assertEquals(expected, awaitItem()) - } + val expected = ActionComponentData( + paymentData = null, + details = details, + ) + assertEquals(expected.details.toString(), detailsFlow.latestValue.details.toString()) } @Test fun `completed and creating details fails, then an error is emitted`() = runTest { val error = ComponentException("test") - whenever( - adyen3DS2Serializer.createChallengeDetails( + // We have to mock the serializer in order to throw an exception + val adyen3DS2Serializer: Adyen3DS2Serializer = mock() + whenever(adyen3DS2Serializer.createChallengeDetails(transactionStatus = "transactionStatus")) doAnswer { + throw error + } + delegate = createDelegate(adyen3DS2Serializer) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + + delegate.onCompletion( + result = ChallengeResult.Completed( transactionStatus = "transactionStatus", ), - ) doAnswer { throw error } - - delegate.exceptionFlow.test { - delegate.onCompletion( - result = ChallengeResult.Completed( - transactionStatus = "transactionStatus", - ), - ) + ) - assertEquals(error, awaitItem()) - } + assertEquals(error, exceptionFlow.latestValue) } @Test fun `cancelled, then an error is emitted`() = runTest { - delegate.exceptionFlow.test { - delegate.onCompletion( - result = ChallengeResult.Cancelled( - transactionStatus = "transactionStatus", - additionalDetails = "additionalDetails", - ), - ) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - assertTrue(awaitItem() is Cancelled3DS2Exception) - } - } - - @Test - fun `timedout, then an error is emitted`() = runTest { - val details = JSONObject("{}") - whenever( - adyen3DS2Serializer.createChallengeDetails( + delegate.onCompletion( + result = ChallengeResult.Cancelled( transactionStatus = "transactionStatus", - errorDetails = "additionalDetails", + additionalDetails = "additionalDetails", ), - ) doReturn details - - delegate.detailsFlow.test { - delegate.onCompletion( - result = ChallengeResult.Timeout( - transactionStatus = "transactionStatus", - additionalDetails = "additionalDetails", - ), - ) + ) - val expected = ActionComponentData( - paymentData = null, - details = details, - ) - assertEquals(expected, awaitItem()) - } + assertTrue(exceptionFlow.latestValue is Cancelled3DS2Exception) } @Test - fun `error, then an error is emitted`() = runTest { - val details = JSONObject("{}") - whenever( - adyen3DS2Serializer.createChallengeDetails( + fun `timedout, then details are emitted`() = runTest { + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + delegate.onCompletion( + result = ChallengeResult.Timeout( transactionStatus = "transactionStatus", - errorDetails = "additionalDetails", + additionalDetails = "additionalDetails", ), - ) doReturn details - - delegate.detailsFlow.test { - delegate.onCompletion( - result = ChallengeResult.Error( - transactionStatus = "transactionStatus", - additionalDetails = "additionalDetails", - ), - ) - - val expected = ActionComponentData( - paymentData = null, - details = details, - ) - assertEquals(expected, awaitItem()) - } - } - } - - private class TestTransaction( - val authReqParameters: AuthenticationRequestParameters? = null - ) : Transaction { - - var shouldThrowError: Boolean = false - - private var timesDoChallengeCalled = 0 - - override fun getAuthenticationRequestParameters(): AuthenticationRequestParameters? = authReqParameters - - @Suppress("OVERRIDE_DEPRECATION", "deprecation") - override fun doChallenge(p0: Activity?, p1: ChallengeParameters?, p2: ChallengeStatusReceiver?, p3: Int) = Unit + ) - override fun doChallenge( - currentActivity: Activity?, - challengeParameters: ChallengeParameters?, - challengeStatusHandler: ChallengeStatusHandler?, - timeOut: Int - ) { - timesDoChallengeCalled++ - if (shouldThrowError) { - throw InvalidInputException("test", null) - } + assertNotNull(detailsFlow.latestValue.details) } - override fun getProgressView(p0: Activity?): ProgressDialog { - error("This method should not be used") - } + @Test + fun `error, then details are emitted`() = runTest { + val detailsFlow = delegate.detailsFlow.test(testScheduler) - override fun close() = Unit + delegate.onCompletion( + result = ChallengeResult.Error( + transactionStatus = "transactionStatus", + additionalDetails = "additionalDetails", + ), + ) - fun assertDoChallengeCalled() { - assert(timesDoChallengeCalled > 0) + assertNotNull(detailsFlow.latestValue.details) } } @@ -603,8 +550,87 @@ internal class DefaultAdyen3DS2DelegateTest( { "directoryServerId":"id", "directoryServerPublicKey":"key", + "directoryServerRootCertificates":"certs", "threeDSMessageVersion":"2.1.0" } """.trimIndent() } } + +private class TestThreeDS2Service : ThreeDS2Service { + + var initializeResult: InitializeResult = InitializeResult.Success + + var initializeError: Throwable? = null + + var transactionResult: TransactionResult = TransactionResult.Success(TestTransaction()) + + var createTransactionError: Throwable? = null + + private var didCallInitialize = false + + private var didCallCleanup = false + + override fun initialize(p0: Context?, p1: ConfigParameters?, p2: String?, p3: UiCustomization?): InitializeResult { + didCallInitialize = true + initializeError?.let { throw it } + return initializeResult + } + + override fun createTransaction(p0: String?, p1: String): TransactionResult { + createTransactionError?.let { throw it } + return transactionResult + } + + override fun cleanup(p0: Context?) { + didCallCleanup = true + } + + override fun getSDKVersion(): String { + return TEST_SDK_VERSION + } + + override fun getWarnings(): MutableList { + error("Should not be called.") + } + + companion object { + private const val TEST_SDK_VERSION = "1.2.3-test" + } +} + +private class TestTransaction( + val authReqParameters: AuthenticationRequestParameters? = null +) : Transaction { + + var shouldThrowError: Boolean = false + + private var timesDoChallengeCalled = 0 + + override fun getAuthenticationRequestParameters(): AuthenticationRequestParameters? = authReqParameters + + @Suppress("OVERRIDE_DEPRECATION", "deprecation") + override fun doChallenge(p0: Activity?, p1: ChallengeParameters?, p2: ChallengeStatusReceiver?, p3: Int) = Unit + + override fun doChallenge( + currentActivity: Activity?, + challengeParameters: ChallengeParameters?, + challengeStatusHandler: ChallengeStatusHandler?, + timeOut: Int + ) { + timesDoChallengeCalled++ + if (shouldThrowError) { + throw InvalidInputException("test", null) + } + } + + override fun getProgressView(p0: Activity?): ProgressDialog { + error("This method should not be used") + } + + override fun close() = Unit + + fun assertDoChallengeCalled() { + assert(timesDoChallengeCalled > 0) + } +} From b78a85bf3db9cf79a5ba6484a3023c6716b19a07 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 26 Apr 2024 14:07:25 +0200 Subject: [PATCH 051/272] Fix tests that reference ThreeDS2Service.INSTANCE The instance fields throws an exception in unit tests, so we have to fake this field away. COAND-886 --- .../com/adyen/threeds2/ThreeDS2Service.kt | 15 ++++++++++ .../internal/ui/DefaultCardDelegateTest.kt | 5 ++-- .../internal/ui/StoredCardDelegateTest.kt | 3 +- .../com/adyen/threeds2/ThreeDS2Service.java | 29 +++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 action-core/src/test/java/com/adyen/threeds2/ThreeDS2Service.kt create mode 100644 card/src/test/java/com/adyen/threeds2/ThreeDS2Service.java diff --git a/action-core/src/test/java/com/adyen/threeds2/ThreeDS2Service.kt b/action-core/src/test/java/com/adyen/threeds2/ThreeDS2Service.kt new file mode 100644 index 0000000000..72bc8d3ad2 --- /dev/null +++ b/action-core/src/test/java/com/adyen/threeds2/ThreeDS2Service.kt @@ -0,0 +1,15 @@ +/* + * 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 26/4/2024. + */ + +package com.adyen.threeds2 + +// Fake ThreeDS2Service that overrides the static instance of the actual library, because it crashes unit tests +@Suppress("unused") +object ThreeDS2Service { + val INSTANCE = this +} diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index 0fd637df4e..ff25ae7a26 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -68,6 +68,7 @@ import com.adyen.checkout.ui.core.internal.ui.model.AddressListItem import com.adyen.checkout.ui.core.internal.ui.model.AddressOutputData import com.adyen.checkout.ui.core.internal.ui.model.AddressParams import com.adyen.checkout.ui.core.internal.util.AddressFormUtils +import com.adyen.threeds2.ThreeDS2Service import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel @@ -790,7 +791,7 @@ internal class DefaultCardDelegateTest( assertNull(encryptedPassword) assertNull(fundingSource) assertNull(storedPaymentMethodId) - assertEquals("2.2.15", threeDS2SdkVersion) + assertEquals(ThreeDS2Service.SDK_VERSION, threeDS2SdkVersion) } } } @@ -894,7 +895,7 @@ internal class DefaultCardDelegateTest( assertEquals(PaymentMethodTypes.SCHEME, type) assertEquals(CardType.VISA.txVariant, brand) assertNull(storedPaymentMethodId) - assertEquals("2.2.15", threeDS2SdkVersion) + assertEquals(ThreeDS2Service.SDK_VERSION, threeDS2SdkVersion) } } } diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index 947e81d264..ad22d1a039 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -51,6 +51,7 @@ import com.adyen.checkout.ui.core.internal.ui.AddressFormUIState import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import com.adyen.checkout.ui.core.internal.ui.model.AddressOutputData import com.adyen.checkout.ui.core.internal.util.AddressValidationUtils +import com.adyen.threeds2.ThreeDS2Service import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -339,7 +340,7 @@ internal class StoredCardDelegateTest( assertNull(encryptedPassword) assertNull(fundingSource) assertNull(brand) - assertEquals("2.2.15", threeDS2SdkVersion) + assertEquals(ThreeDS2Service.SDK_VERSION, threeDS2SdkVersion) } } } diff --git a/card/src/test/java/com/adyen/threeds2/ThreeDS2Service.java b/card/src/test/java/com/adyen/threeds2/ThreeDS2Service.java new file mode 100644 index 0000000000..3359ff6a2e --- /dev/null +++ b/card/src/test/java/com/adyen/threeds2/ThreeDS2Service.java @@ -0,0 +1,29 @@ +/* + * 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 26/4/2024. + */ + +package com.adyen.threeds2; + +/** + * @noinspection unused + */ +// Fake ThreeDS2Service that overrides the static instance of the actual library, because it crashes unit tests +public interface ThreeDS2Service { + + /** + * @noinspection UnnecessaryModifier + */ + public String getSDKVersion(); + + + static String SDK_VERSION = "1.2.3-test"; + + /** + * @noinspection UnnecessaryModifier + */ + static ThreeDS2Service INSTANCE = () -> SDK_VERSION; +} From dddc16534a6c6043959eb01d2845d667f0219ab8 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 26 Apr 2024 16:32:57 +0200 Subject: [PATCH 052/272] Replace custom Base64Encoder with Kotlin Base64 COAND-886 --- .../provider/Adyen3DS2ComponentProvider.kt | 2 - .../internal/ui/DefaultAdyen3DS2Delegate.kt | 13 ++-- .../ui/DefaultAdyen3DS2DelegateTest.kt | 36 +++++----- .../core/internal/util/Base64Encoder.kt | 69 ------------------- 4 files changed, 26 insertions(+), 94 deletions(-) delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/util/Base64Encoder.kt diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt index 63633597fc..3e708b9b89 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt @@ -36,7 +36,6 @@ import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams -import com.adyen.checkout.components.core.internal.util.AndroidBase64Encoder import com.adyen.checkout.components.core.internal.util.get import com.adyen.checkout.components.core.internal.util.viewModelFactory import com.adyen.checkout.core.internal.data.api.HttpClientFactory @@ -105,7 +104,6 @@ constructor( redirectHandler = redirectHandler, threeDS2Service = ThreeDS2Service.INSTANCE, coroutineDispatcher = Dispatchers.Default, - base64Encoder = AndroidBase64Encoder(), application = application, ) } diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index af2fe077a7..d201334740 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -34,7 +34,6 @@ import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty -import com.adyen.checkout.components.core.internal.util.Base64Encoder import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -66,6 +65,8 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.json.JSONException import org.json.JSONObject +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultAdyen3DS2Delegate( @@ -78,7 +79,6 @@ internal class DefaultAdyen3DS2Delegate( private val redirectHandler: RedirectHandler, private val threeDS2Service: ThreeDS2Service, private val coroutineDispatcher: CoroutineDispatcher, - private val base64Encoder: Base64Encoder, private val application: Application, ) : Adyen3DS2Delegate, ChallengeStatusHandler, SavedStateHandleContainer { @@ -253,9 +253,10 @@ internal class DefaultAdyen3DS2Delegate( } } + @OptIn(ExperimentalEncodingApi::class) @Throws(ComponentException::class, ModelSerializationException::class) private fun decodeFingerprintToken(encoded: String): FingerprintToken { - val decodedFingerprintToken = base64Encoder.decode(encoded) + val decodedFingerprintToken = Base64.decode(encoded).toString(Charsets.UTF_8) val fingerprintJson: JSONObject = try { JSONObject(decodedFingerprintToken) @@ -315,6 +316,7 @@ internal class DefaultAdyen3DS2Delegate( } } + @OptIn(ExperimentalEncodingApi::class) @Throws(ComponentException::class) private fun createEncodedFingerprint(authenticationRequestParameters: AuthenticationRequestParameters): String { return try { @@ -329,7 +331,7 @@ internal class DefaultAdyen3DS2Delegate( } } - base64Encoder.encode(fingerprintJson.toString()) + Base64.encode(fingerprintJson.toString().toByteArray()) } catch (e: JSONException) { throw ComponentException("Failed to create encoded fingerprint", e) } @@ -389,6 +391,7 @@ internal class DefaultAdyen3DS2Delegate( } } + @OptIn(ExperimentalEncodingApi::class) @VisibleForTesting internal fun challengeShopper(activity: Activity, encodedChallengeToken: String) { adyenLog(AdyenLogLevel.DEBUG) { "challengeShopper" } @@ -400,7 +403,7 @@ internal class DefaultAdyen3DS2Delegate( return } - val decodedChallengeToken = base64Encoder.decode(encodedChallengeToken) + val decodedChallengeToken = Base64.decode(encodedChallengeToken).toString(Charsets.UTF_8) val challengeTokenJson: JSONObject = try { JSONObject(decodedChallengeToken) } catch (e: JSONException) { diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt index 9b9260aa09..2082c93ed7 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt @@ -6,6 +6,8 @@ * Created by oscars on 24/8/2022. */ +@file:OptIn(ExperimentalEncodingApi::class) + package com.adyen.checkout.adyen3ds2.internal.ui import android.app.Activity @@ -28,7 +30,6 @@ import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper -import com.adyen.checkout.components.core.internal.util.JavaBase64Encoder import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.test.TestDispatcherExtension @@ -73,6 +74,8 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import java.io.IOException import java.util.Locale +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) @@ -84,8 +87,6 @@ internal class DefaultAdyen3DS2DelegateTest( private lateinit var delegate: DefaultAdyen3DS2Delegate private lateinit var paymentDataRepository: PaymentDataRepository - private val base64Encoder = JavaBase64Encoder() - private val threeDS2Service: TestThreeDS2Service = TestThreeDS2Service() @BeforeEach @@ -112,7 +113,6 @@ internal class DefaultAdyen3DS2DelegateTest( redirectHandler = redirectHandler, threeDS2Service = threeDS2Service, coroutineDispatcher = UnconfinedTestDispatcher(), - base64Encoder = base64Encoder, application = Application(), ) } @@ -171,7 +171,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(this) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - val encodedJson = base64Encoder.encode("{incorrectJson}") + val encodedJson = Base64.encode("{incorrectJson}".toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) assertTrue(exceptionFlow.latestValue is ComponentException) @@ -184,7 +184,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(CoroutineScope(UnconfinedTestDispatcher(testScheduler))) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) assertEquals(error, exceptionFlow.latestValue.cause) @@ -199,7 +199,7 @@ internal class DefaultAdyen3DS2DelegateTest( val detailsFlow = delegate.detailsFlow.test(testScheduler) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) // We don't care about the encoded value in this test, we just want to know if details are there @@ -213,7 +213,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) assertEquals(error, exceptionFlow.latestValue.cause) @@ -225,7 +225,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val detailsFlow = delegate.detailsFlow.test(testScheduler) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) // We don't care about the encoded value in this test, we just want to know if details are there @@ -237,7 +237,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(this) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) assertEquals("Failed to retrieve 3DS2 authentication parameters", exceptionFlow.latestValue.message) @@ -261,7 +261,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(this) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, true) val expected = ActionComponentData( @@ -289,7 +289,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(this) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, true) redirectHandler.assertLaunchRedirectCalled() @@ -313,7 +313,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(this) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, true) assertEquals(error, exceptionFlow.latestValue.cause) } @@ -332,7 +332,7 @@ internal class DefaultAdyen3DS2DelegateTest( val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.initialize(this) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) assertNotNull(detailsFlow.latestValue.details) @@ -356,7 +356,7 @@ internal class DefaultAdyen3DS2DelegateTest( initializeTransaction(this) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.challengeShopper(Activity(), base64Encoder.encode("token")) + delegate.challengeShopper(Activity(), Base64.encode("token".toByteArray())) assertTrue(exceptionFlow.latestValue.cause is JSONException) } @@ -366,7 +366,7 @@ internal class DefaultAdyen3DS2DelegateTest( val transaction = initializeTransaction(this) // We need to set the messageVersion to workaround an error in the 3DS2 SDK - delegate.challengeShopper(Activity(), base64Encoder.encode("{\"messageVersion\":\"2.1.0\"}")) + delegate.challengeShopper(Activity(), Base64.encode("{\"messageVersion\":\"2.1.0\"}".toByteArray())) transaction.assertDoChallengeCalled() } @@ -380,7 +380,7 @@ internal class DefaultAdyen3DS2DelegateTest( val exceptionFlow = delegate.exceptionFlow.test(testScheduler) // We need to set the messageVersion to workaround an error in the 3DS2 SDK - delegate.challengeShopper(Activity(), base64Encoder.encode("{\"messageVersion\":\"2.1.0\"}")) + delegate.challengeShopper(Activity(), Base64.encode("{\"messageVersion\":\"2.1.0\"}".toByteArray())) assertTrue(exceptionFlow.latestValue.cause is InvalidInputException) } @@ -399,7 +399,7 @@ internal class DefaultAdyen3DS2DelegateTest( delegate.initialize(scope) - val encodedJson = base64Encoder.encode(TEST_FINGERPRINT_TOKEN) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) delegate.identifyShopper(Activity(), encodedJson, false) return transaction diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/Base64Encoder.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/Base64Encoder.kt deleted file mode 100644 index eda51bc8ad..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/Base64Encoder.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2017 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by Ran Haveshush on 22/11/2018. - */ -package com.adyen.checkout.components.core.internal.util - -import android.util.Base64 -import androidx.annotation.RestrictTo -import java.nio.charset.Charset -import java.util.Base64 as JavaBase64 - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -interface Base64Encoder { - - fun encode(decodedData: String, flags: Int = Base64.DEFAULT): String - - fun decode(encodedData: String, flags: Int = Base64.DEFAULT): String -} - -/** - * Use this encoder to base64 encode and decode strings - */ -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AndroidBase64Encoder : Base64Encoder { - - override fun encode(decodedData: String, flags: Int): String { - val decodedBytes = decodedData.toByteArray(DEFAULT_CHARSET) - return Base64.encodeToString(decodedBytes, flags) - } - - override fun decode(encodedData: String, flags: Int): String { - val decodedBytes = Base64.decode(encodedData, flags) - return String(decodedBytes, DEFAULT_CHARSET) - } - - companion object { - private const val UTF_8 = "UTF-8" - private val DEFAULT_CHARSET = - if (Charset.isSupported(UTF_8)) Charset.forName(UTF_8) else Charset.defaultCharset() - } -} - -/** - * Java implementation of [AndroidBase64Encoder]. This implementations can only be used from API 26+ and is mainly used - * for testing. - */ -@Suppress("NewApi") -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class JavaBase64Encoder : Base64Encoder { - - override fun encode(decodedData: String, flags: Int): String { - val decodedBytes = decodedData.toByteArray(DEFAULT_CHARSET) - return JavaBase64.getEncoder().encodeToString(decodedBytes) - } - - override fun decode(encodedData: String, flags: Int): String { - val decodedBytes = JavaBase64.getDecoder().decode(encodedData) - return String(decodedBytes, DEFAULT_CHARSET) - } - - companion object { - private const val UTF_8 = "UTF-8" - private val DEFAULT_CHARSET = - if (Charset.isSupported(UTF_8)) Charset.forName(UTF_8) else Charset.defaultCharset() - } -} From 55a72f2a4b8eb6d042f677aae86de3c9f6da5990 Mon Sep 17 00:00:00 2001 From: jreij Date: Mon, 29 Apr 2024 11:11:27 +0000 Subject: [PATCH 053/272] Update verification metadata --- gradle/verification-metadata.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index d0be6d1131..8b8bd0b92d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -6997,6 +6997,11 @@ + + + + + From 630118c467d63425bd795df745e497b844e6722b Mon Sep 17 00:00:00 2001 From: ozgur <6615094+ozgur00@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:55:02 +0200 Subject: [PATCH 054/272] Display validation error on CardView when no address has been selected COAND-588 --- RELEASE_NOTES.md | 1 + .../com/adyen/checkout/card/internal/ui/view/CardView.kt | 5 +++++ card/src/main/res/values/styles.xml | 1 - .../checkout/ui/core/internal/ui/model/AddressOutputData.kt | 2 +- ui-core/src/main/res/template/values/strings.xml.tt | 1 + ui-core/src/main/res/values-ar/strings.xml | 1 + ui-core/src/main/res/values-cs-rCZ/strings.xml | 1 + ui-core/src/main/res/values-da-rDK/strings.xml | 1 + ui-core/src/main/res/values-de-rDE/strings.xml | 1 + ui-core/src/main/res/values-el-rGR/strings.xml | 1 + ui-core/src/main/res/values-es-rES/strings.xml | 1 + ui-core/src/main/res/values-fi-rFI/strings.xml | 1 + ui-core/src/main/res/values-fr-rFR/strings.xml | 1 + ui-core/src/main/res/values-hr-rHR/strings.xml | 1 + ui-core/src/main/res/values-hu-rHU/strings.xml | 1 + ui-core/src/main/res/values-it-rIT/strings.xml | 1 + ui-core/src/main/res/values-ja-rJP/strings.xml | 1 + ui-core/src/main/res/values-ko-rKR/strings.xml | 1 + ui-core/src/main/res/values-nb-rNO/strings.xml | 1 + ui-core/src/main/res/values-nl-rNL/strings.xml | 1 + ui-core/src/main/res/values-pl-rPL/strings.xml | 1 + ui-core/src/main/res/values-pt-rBR/strings.xml | 1 + ui-core/src/main/res/values-pt-rPT/strings.xml | 1 + ui-core/src/main/res/values-ro-rRO/strings.xml | 1 + ui-core/src/main/res/values-ru-rRU/strings.xml | 1 + ui-core/src/main/res/values-sk-rSK/strings.xml | 1 + ui-core/src/main/res/values-sl-rSI/strings.xml | 1 + ui-core/src/main/res/values-sv-rSE/strings.xml | 1 + ui-core/src/main/res/values-zh-rCN/strings.xml | 1 + ui-core/src/main/res/values-zh-rTW/strings.xml | 1 + ui-core/src/main/res/values/strings.xml | 1 + 31 files changed, 34 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f819cac7c7..ee8cc14470 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -11,6 +11,7 @@ ## Fixed - Fixed various memory leaks. - Drop-in no longer overrides the log level in case of debug builds. +- Address Lookup not displaying validation error on Card Component when no address has been selected. ## Changed - Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/view/CardView.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/view/CardView.kt index 6ab1e427c9..94350714c2 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/view/CardView.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/view/CardView.kt @@ -277,6 +277,11 @@ class CardView @JvmOverloads constructor( if (binding.addressFormInput.isVisible && !it.addressState.isValid) { binding.addressFormInput.highlightValidationErrors(isErrorFocused) } + if (binding.textInputLayoutAddressLookup.isVisible && !it.addressState.isValid) { + binding.textInputLayoutAddressLookup.showError( + localizedContext.getString(R.string.checkout_address_lookup_validation_empty) + ) + } } } diff --git a/card/src/main/res/values/styles.xml b/card/src/main/res/values/styles.xml index 2cfeb04f71..33475416a8 100644 --- a/card/src/main/res/values/styles.xml +++ b/card/src/main/res/values/styles.xml @@ -103,7 +103,6 @@ + + From 5d5b6af120670335c3b3f62bdd8db33fa2c381fb Mon Sep 17 00:00:00 2001 From: josephj Date: Mon, 6 May 2024 17:28:13 +0200 Subject: [PATCH 104/272] Update release notes and customization guide COAND-907 --- RELEASE_NOTES.md | 5 ++++- docs/UI_CUSTOMIZATION.md | 21 ++++++++++++++++++- .../core/internal/util/CustomTabsLauncher.kt | 4 +--- .../ui/core/internal/util/PdfOpener.kt | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3c0a0a62fd..aff3d3ff12 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,13 +1,16 @@ [//]: # (This file will be used for the release notes on GitHub when publishing.) [//]: # (Types of changes: `Breaking changes` `New` `Added` `Improved` `Changed` `Deprecated` `Removed` `Fixed`) [//]: # (Example:) -[//]: # (## Added) +[//]: # (## New) [//]: # ( - New payment method) [//]: # (## Changed) [//]: # ( - DropIn service's package changed from `com.adyen.dropin` to `com.adyen.dropin.services`) [//]: # (## Deprecated) [//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object) +## New +- For external redirects launched in a Custom Tab, you can now [customize the toolbar and navigation bar colors](docs/UI_CUSTOMIZATION.md#styling-custom-tabs) of the Custom Tab. + ## Fixed - Fixed various memory leaks. - Drop-in no longer overrides the log level in case of debug builds. diff --git a/docs/UI_CUSTOMIZATION.md b/docs/UI_CUSTOMIZATION.md index 18e620d8d9..3b35896062 100644 --- a/docs/UI_CUSTOMIZATION.md +++ b/docs/UI_CUSTOMIZATION.md @@ -10,6 +10,7 @@ The base theme can be extended and overridden. You can also use a custom base th - [Customizing a specific view](#customizing-a-specific-view) - [Adding dark mode support](#adding-dark-mode-support) - [Overriding string resources](#overriding-string-resources) +- [Styling Custom Tabs](#styling-custom-tabs) ## Customizing the base theme @@ -125,4 +126,22 @@ CheckoutConfiguration(shopperLocale, environment, clientKey) { } ``` -If you cannot find a certain string in the code base, then check whether it is coming from the Checkout API. Make sure you localize these strings by sending the correct [shopperLocale](https://docs.adyen.com/api-explorer/Checkout/latest/post/sessions#request-shopperLocale). +If you cannot find a certain string in the code base, then check whether it is coming from the Checkout API. Make sure you localize these strings by sending the correct [shopperLocale](https://docs.adyen.com/api-explorer/Checkout/latest/post/sessions#request-shopperLocale). + +## Styling Custom Tabs + +We use Custom Tabs to launch any external redirects that cannot be handled inside the SDK or by another installed native app. +By default we set the toolbar color to match the `colorPrimary` attribute defined in your theme. +To change this color to a different value than your `colorPrimary` attribute, you can override the `AdyenCheckout.CustomTabs` style in your `styles.xml`: + +```xml + +``` diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt index 242760f4f8..ad0c5062a1 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt @@ -62,8 +62,6 @@ object CustomTabsLauncher { } private fun TypedArray.getColorOrNull(@StyleableRes index: Int): Int? { - return getColor(index, COLOR_NOT_DEFINED).takeIf { it != COLOR_NOT_DEFINED } + return getColor(index, -1).takeIf { it != -1 } } - - private const val COLOR_NOT_DEFINED = -1 } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/PdfOpener.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/PdfOpener.kt index 92cb0c6f80..091531e8d9 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/PdfOpener.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/PdfOpener.kt @@ -65,7 +65,7 @@ class PdfOpener { if (isLaunched) { adyenLog(AdyenLogLevel.DEBUG) { "Successfully opened pdf in custom tab" } } else { - adyenLog(AdyenLogLevel.ERROR) { "Couldn't open pdf in custom tab" } + adyenLog(AdyenLogLevel.DEBUG) { "Couldn't open pdf in custom tab" } } return isLaunched } From 4b348a9e79741c122d1f4a06ca129987c24283f7 Mon Sep 17 00:00:00 2001 From: josephj Date: Tue, 14 May 2024 17:30:15 +0200 Subject: [PATCH 105/272] Directly use attr instead of inside a styleable COAND-907 --- .../core/internal/util/CustomTabsLauncher.kt | 31 ++++++------------- ui-core/src/main/res/values/attrs.xml | 10 +++--- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt index ad0c5062a1..6ebe7ed2b5 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CustomTabsLauncher.kt @@ -10,10 +10,9 @@ package com.adyen.checkout.ui.core.internal.util import android.content.ActivityNotFoundException import android.content.Context -import android.content.res.TypedArray import android.net.Uri +import androidx.annotation.AttrRes import androidx.annotation.RestrictTo -import androidx.annotation.StyleableRes import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent import com.adyen.checkout.ui.core.R @@ -35,23 +34,10 @@ object CustomTabsLauncher { } private fun getDefaultColorSchemeParams(context: Context): CustomTabColorSchemeParams { - val typedArray = context.obtainStyledAttributes( - R.style.AdyenCheckout_CustomTabs, - R.styleable.AdyenCheckoutCustomTabs, - ) - val toolbarColor = typedArray.getColorOrNull( - R.styleable.AdyenCheckoutCustomTabs_adyenCustomTabsToolbarColor, - ) - val secondaryToolbarColor = typedArray.getColorOrNull( - R.styleable.AdyenCheckoutCustomTabs_adyenCustomTabsSecondaryToolbarColor, - ) - val navigationBarColor = typedArray.getColorOrNull( - R.styleable.AdyenCheckoutCustomTabs_adyenCustomTabsNavigationBarColor, - ) - val navigationBarDividerColor = typedArray.getColorOrNull( - R.styleable.AdyenCheckoutCustomTabs_adyenCustomTabsNavigationBarDividerColor, - ) - typedArray.recycle() + val toolbarColor = context.getColorOrNull(R.attr.adyenCustomTabsToolbarColor) + val secondaryToolbarColor = context.getColorOrNull(R.attr.adyenCustomTabsSecondaryToolbarColor) + val navigationBarColor = context.getColorOrNull(R.attr.adyenCustomTabsNavigationBarColor) + val navigationBarDividerColor = context.getColorOrNull(R.attr.adyenCustomTabsNavigationBarDividerColor) return CustomTabColorSchemeParams.Builder().apply { toolbarColor?.let { setToolbarColor(it) } @@ -61,7 +47,10 @@ object CustomTabsLauncher { }.build() } - private fun TypedArray.getColorOrNull(@StyleableRes index: Int): Int? { - return getColor(index, -1).takeIf { it != -1 } + private fun Context.getColorOrNull(@AttrRes attribute: Int): Int? { + val typedArray = obtainStyledAttributes(R.style.AdyenCheckout_CustomTabs, intArrayOf(attribute)) + val color = typedArray.getColor(0, -1).takeIf { it != -1 } + typedArray.recycle() + return color } } diff --git a/ui-core/src/main/res/values/attrs.xml b/ui-core/src/main/res/values/attrs.xml index 046d93efd2..aa2c582726 100644 --- a/ui-core/src/main/res/values/attrs.xml +++ b/ui-core/src/main/res/values/attrs.xml @@ -13,10 +13,8 @@ - - - - - - + + + + From a687eafb45ad0dee5059584ee80ab24d74fd13b6 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 29 Feb 2024 14:18:21 +0100 Subject: [PATCH 106/272] Add AndroidX Startup dependency COAND-857 --- checkout-core/build.gradle | 1 + dependencies.gradle | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/checkout-core/build.gradle b/checkout-core/build.gradle index 02ca7d14d9..b2646df43d 100644 --- a/checkout-core/build.gradle +++ b/checkout-core/build.gradle @@ -34,6 +34,7 @@ android { dependencies { // Dependencies api libraries.androidx.annotation + implementation libraries.androidx.startup api libraries.kotlinCoroutines implementation libraries.okhttp api libraries.parcelize diff --git a/dependencies.gradle b/dependencies.gradle index 0ff97b11a0..e2f12d1114 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -43,6 +43,7 @@ ext { material_version = "1.11.0" recyclerview_version = "1.3.2" constraintlayout_version = '2.1.4' + startup_version = '1.1.1' // Compose Dependencies compose_activity_version = '1.9.0' @@ -96,7 +97,8 @@ ext { "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" ], preference : "androidx.preference:preference-ktx:$preference_version", - recyclerview : "androidx.recyclerview:recyclerview:$recyclerview_version" + recyclerview : "androidx.recyclerview:recyclerview:$recyclerview_version", + startup : "androidx.startup:startup-runtime:$startup_version" ], cashAppPay : "app.cash.paykit:core:$cash_app_pay_version", compose : [ From 46bdb44589ea6cf68b45c0168fe76bd05a3bd4d7 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 29 Feb 2024 14:32:40 +0100 Subject: [PATCH 107/272] Create initializer that stores application context on start up COAND-857 --- checkout-core/consumer-rules.pro | 2 ++ checkout-core/src/main/AndroidManifest.xml | 16 +++++++++- .../core/internal/AdyenCheckoutInitializer.kt | 25 ++++++++++++++++ .../internal/ApplicationContextProvider.kt | 30 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt diff --git a/checkout-core/consumer-rules.pro b/checkout-core/consumer-rules.pro index a54de99084..de0679c8b3 100644 --- a/checkout-core/consumer-rules.pro +++ b/checkout-core/consumer-rules.pro @@ -1,2 +1,4 @@ # Keep the model classes for JSON parsing. -keep class * extends com.adyen.checkout.core.internal.data.model.ModelObject { *; } + +-keep class com.adyen.checkout.core.internal.AdyenCheckoutInitializer diff --git a/checkout-core/src/main/AndroidManifest.xml b/checkout-core/src/main/AndroidManifest.xml index 67a1f1cc12..3cdea0dbc7 100644 --- a/checkout-core/src/main/AndroidManifest.xml +++ b/checkout-core/src/main/AndroidManifest.xml @@ -6,4 +6,18 @@ ~ Created by caiof on 17/12/2020. --> - + + + + + + + + diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt new file mode 100644 index 0000000000..f505d01c05 --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt @@ -0,0 +1,25 @@ +/* + * 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 29/2/2024. + */ + +package com.adyen.checkout.core.internal + +import android.content.Context +import androidx.startup.Initializer + +@Suppress("unused") +internal class AdyenCheckoutInitializer : Initializer { + + override fun create(context: Context): AdyenCheckoutInitializerType { + ApplicationContextProvider.initialize(context.applicationContext) + return object : AdyenCheckoutInitializerType {} + } + + override fun dependencies(): List>> = emptyList() +} + +internal interface AdyenCheckoutInitializerType diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt new file mode 100644 index 0000000000..632e8097e7 --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt @@ -0,0 +1,30 @@ +/* + * 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 29/2/2024. + */ + +package com.adyen.checkout.core.internal + +import android.annotation.SuppressLint +import android.content.Context +import androidx.annotation.RestrictTo + +@SuppressLint("StaticFieldLeak") +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +object ApplicationContextProvider { + + private var isInitialized: Boolean = false + + lateinit var context: Context + + fun initialize(applicationContext: Context) { + if (isInitialized) return + + context = applicationContext + + isInitialized = true + } +} From 88cb37d46fe627b6707ca48a0ff6720574df9dcd Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 29 Feb 2024 15:14:36 +0100 Subject: [PATCH 108/272] Try to recreate the action delegate when handleIntent is called COAND-857 --- .../ui/DefaultGenericActionDelegate.kt | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index ebbdd57ed3..1e97a76e0b 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.action.core.internal.ui import android.app.Activity +import android.app.Application import android.content.Intent import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle @@ -32,6 +33,7 @@ import com.adyen.checkout.components.core.internal.util.repeatOnResume import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.core.internal.ApplicationContextProvider import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.ui.core.internal.ui.ComponentViewType @@ -101,6 +103,8 @@ internal class DefaultGenericActionDelegate( } override fun handleAction(action: Action, activity: Activity) { + savedStateHandle[ACTION_KEY] = action + // This check is to support an older flow where you might need to call handleAction several times with 3DS2. // Initially handleAction is called with a fingerprint action then with a challenge action. // During this whole flow the same transaction instance should be used for both fingerprint and challenge. @@ -108,28 +112,32 @@ internal class DefaultGenericActionDelegate( if (isOld3DS2Flow(action)) { adyenLog(AdyenLogLevel.DEBUG) { "Continuing the handling of 3ds2 challenge with old flow." } } else { - val delegate = actionDelegateProvider.getDelegate( - action = action, - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = activity.application, - ) - this._delegate = delegate - adyenLog(AdyenLogLevel.DEBUG) { "Created delegate of type ${delegate::class.simpleName}" } + createDelegateAndObserve(action, activity.application) + } - if (delegate is RedirectableDelegate) { - onRedirectListener?.let { delegate.setOnRedirectListener(it) } - } + delegate.handleAction(action, activity) + } - delegate.initialize(coroutineScope) + private fun createDelegateAndObserve(action: Action, application: Application) { + val delegate = actionDelegateProvider.getDelegate( + action = action, + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) + this._delegate = delegate + adyenLog(AdyenLogLevel.DEBUG) { "Created delegate of type ${delegate::class.simpleName}" } - observeDetails(delegate) - observeExceptions(delegate) - observePermissionRequests(delegate) - observeViewFlow(delegate) + if (delegate is RedirectableDelegate) { + onRedirectListener?.let { delegate.setOnRedirectListener(it) } } - delegate.handleAction(action, activity) + delegate.initialize(coroutineScope) + + observeDetails(delegate) + observeExceptions(delegate) + observePermissionRequests(delegate) + observeViewFlow(delegate) } private fun isOld3DS2Flow(action: Action): Boolean { @@ -170,7 +178,18 @@ internal class DefaultGenericActionDelegate( override fun handleIntent(intent: Intent) { when (val delegate = _delegate) { null -> { - exceptionChannel.trySend(ComponentException("handleIntent should not be called before handleAction")) + val action: Action? = savedStateHandle[ACTION_KEY] + if (action == null) { + exceptionChannel.trySend( + ComponentException("handleIntent should not be called before handleAction"), + ) + return + } + adyenLog(AdyenLogLevel.DEBUG) { "Recreating delegate and trying again" } + // What is the best way to obtain the Application? + createDelegateAndObserve(action, ApplicationContextProvider.context as Application) + // Recursive call or calling the delegate directly? + handleIntent(intent) } !is IntentHandlingDelegate -> { @@ -212,4 +231,8 @@ internal class DefaultGenericActionDelegate( _coroutineScope = null onRedirectListener = null } + + companion object { + private const val ACTION_KEY = "ACTION_KEY" + } } From a850d0fcf4cd9788070710cbc8dbdd0349e8fa64 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 12 Mar 2024 15:40:36 +0100 Subject: [PATCH 109/272] Provide application when creating DefaultGenericActionDelegate COAND-857 --- .../provider/GenericActionComponentProvider.kt | 1 + .../core/internal/ui/DefaultGenericActionDelegate.kt | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt index 31c72334f9..3c7927cb05 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt @@ -91,6 +91,7 @@ constructor( checkoutConfiguration = checkoutConfiguration, componentParams = componentParams, actionDelegateProvider = ActionDelegateProvider(dropInOverrideParams), + application = application, ) } diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 1e97a76e0b..4d3b61705c 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -33,7 +33,6 @@ import com.adyen.checkout.components.core.internal.util.repeatOnResume import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException -import com.adyen.checkout.core.internal.ApplicationContextProvider import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.ui.core.internal.ui.ComponentViewType @@ -53,6 +52,7 @@ internal class DefaultGenericActionDelegate( private val checkoutConfiguration: CheckoutConfiguration, override val componentParams: GenericComponentParams, private val actionDelegateProvider: ActionDelegateProvider, + private val application: Application, ) : GenericActionDelegate { private var _delegate: ActionDelegate? = null @@ -112,13 +112,13 @@ internal class DefaultGenericActionDelegate( if (isOld3DS2Flow(action)) { adyenLog(AdyenLogLevel.DEBUG) { "Continuing the handling of 3ds2 challenge with old flow." } } else { - createDelegateAndObserve(action, activity.application) + createDelegateAndObserve(action) } delegate.handleAction(action, activity) } - private fun createDelegateAndObserve(action: Action, application: Application) { + private fun createDelegateAndObserve(action: Action) { val delegate = actionDelegateProvider.getDelegate( action = action, checkoutConfiguration = checkoutConfiguration, @@ -186,9 +186,7 @@ internal class DefaultGenericActionDelegate( return } adyenLog(AdyenLogLevel.DEBUG) { "Recreating delegate and trying again" } - // What is the best way to obtain the Application? - createDelegateAndObserve(action, ApplicationContextProvider.context as Application) - // Recursive call or calling the delegate directly? + createDelegateAndObserve(action) handleIntent(intent) } From 1bb2952bb065b6c71c0020c703faad183bdd1485 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 12 Mar 2024 15:42:27 +0100 Subject: [PATCH 110/272] Remove AndroidX startup library COAND-857 --- checkout-core/build.gradle | 1 - checkout-core/consumer-rules.pro | 2 -- checkout-core/src/main/AndroidManifest.xml | 16 +--------- .../core/internal/AdyenCheckoutInitializer.kt | 25 ---------------- .../internal/ApplicationContextProvider.kt | 30 ------------------- dependencies.gradle | 4 +-- 6 files changed, 2 insertions(+), 76 deletions(-) delete mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt delete mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt diff --git a/checkout-core/build.gradle b/checkout-core/build.gradle index b2646df43d..02ca7d14d9 100644 --- a/checkout-core/build.gradle +++ b/checkout-core/build.gradle @@ -34,7 +34,6 @@ android { dependencies { // Dependencies api libraries.androidx.annotation - implementation libraries.androidx.startup api libraries.kotlinCoroutines implementation libraries.okhttp api libraries.parcelize diff --git a/checkout-core/consumer-rules.pro b/checkout-core/consumer-rules.pro index de0679c8b3..a54de99084 100644 --- a/checkout-core/consumer-rules.pro +++ b/checkout-core/consumer-rules.pro @@ -1,4 +1,2 @@ # Keep the model classes for JSON parsing. -keep class * extends com.adyen.checkout.core.internal.data.model.ModelObject { *; } - --keep class com.adyen.checkout.core.internal.AdyenCheckoutInitializer diff --git a/checkout-core/src/main/AndroidManifest.xml b/checkout-core/src/main/AndroidManifest.xml index 3cdea0dbc7..67a1f1cc12 100644 --- a/checkout-core/src/main/AndroidManifest.xml +++ b/checkout-core/src/main/AndroidManifest.xml @@ -6,18 +6,4 @@ ~ Created by caiof on 17/12/2020. --> - - - - - - - - + diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt deleted file mode 100644 index f505d01c05..0000000000 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/AdyenCheckoutInitializer.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 29/2/2024. - */ - -package com.adyen.checkout.core.internal - -import android.content.Context -import androidx.startup.Initializer - -@Suppress("unused") -internal class AdyenCheckoutInitializer : Initializer { - - override fun create(context: Context): AdyenCheckoutInitializerType { - ApplicationContextProvider.initialize(context.applicationContext) - return object : AdyenCheckoutInitializerType {} - } - - override fun dependencies(): List>> = emptyList() -} - -internal interface AdyenCheckoutInitializerType diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt deleted file mode 100644 index 632e8097e7..0000000000 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/ApplicationContextProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 29/2/2024. - */ - -package com.adyen.checkout.core.internal - -import android.annotation.SuppressLint -import android.content.Context -import androidx.annotation.RestrictTo - -@SuppressLint("StaticFieldLeak") -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -object ApplicationContextProvider { - - private var isInitialized: Boolean = false - - lateinit var context: Context - - fun initialize(applicationContext: Context) { - if (isInitialized) return - - context = applicationContext - - isInitialized = true - } -} diff --git a/dependencies.gradle b/dependencies.gradle index e2f12d1114..0ff97b11a0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -43,7 +43,6 @@ ext { material_version = "1.11.0" recyclerview_version = "1.3.2" constraintlayout_version = '2.1.4' - startup_version = '1.1.1' // Compose Dependencies compose_activity_version = '1.9.0' @@ -97,8 +96,7 @@ ext { "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" ], preference : "androidx.preference:preference-ktx:$preference_version", - recyclerview : "androidx.recyclerview:recyclerview:$recyclerview_version", - startup : "androidx.startup:startup-runtime:$startup_version" + recyclerview : "androidx.recyclerview:recyclerview:$recyclerview_version" ], cashAppPay : "app.cash.paykit:core:$cash_app_pay_version", compose : [ From 98cbf535b3948f3e265a12f3c29ea02bc1f14162 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 12 Mar 2024 16:00:00 +0100 Subject: [PATCH 111/272] Add unit test COAND-857 --- .../internal/ui/DefaultGenericActionDelegate.kt | 4 ++++ .../core/ui/DefaultGenericActionDelegateTest.kt | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 4d3b61705c..f8320b6e1c 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -134,6 +134,10 @@ internal class DefaultGenericActionDelegate( delegate.initialize(coroutineScope) + observeDelegate(delegate) + } + + private fun observeDelegate(delegate: ActionDelegate) { observeDetails(delegate) observeExceptions(delegate) observePermissionRequests(delegate) diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt index 08097e92ba..6731383d5d 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt @@ -70,6 +70,7 @@ internal class DefaultGenericActionDelegateTest( GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), actionDelegateProvider, + Application(), ) whenever(activity.application) doReturn Application() @@ -232,6 +233,20 @@ internal class DefaultGenericActionDelegateTest( assertTrue(adyen3DS2Delegate.handleActionCalled) } + @Test + fun `when process died during handling action, then handleIntent should restore state and continue`() { + // Create genericActionDelegate and handle action + genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + genericActionDelegate.handleAction(Threeds2FingerprintAction(), activity) + // Simulate destroying and recreating genericActionDelegate + genericActionDelegate.onCleared() + genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + genericActionDelegate.handleIntent(Intent()) + + assertTrue(testDelegate.handleIntentCalled) + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" } From 1d02cb7c630829fd95082d0661e620b1d983adca Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 12 Mar 2024 16:04:57 +0100 Subject: [PATCH 112/272] Add release note COAND-857 --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index aff3d3ff12..7b67d62005 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -17,6 +17,7 @@ - Address Lookup not displaying validation error on Card Component when no address has been selected. - Actions no longer crash when your app uses obfuscation. - Drop-in no longer throws an error while handling a 3DS2 challenge on API 66 and below. +- When the app process dies during action handling, then the state will now be restored and the payment can be continued. ## Changed - Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). From 5b792ed42146bdb724cf94135c14f0f39d46b7d5 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 13 Mar 2024 13:37:49 +0100 Subject: [PATCH 113/272] Move restoring DefaultGenericActionDelegate to when it is initialised COAND-857 --- .../ui/DefaultGenericActionDelegate.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index f8320b6e1c..7d4c22332b 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -78,6 +78,15 @@ internal class DefaultGenericActionDelegate( override fun initialize(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.DEBUG) { "initialize" } _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + val action: Action? = savedStateHandle[ACTION_KEY] + if (_delegate == null && action != null) { + createDelegateAndObserve(action) + } } override fun observe( @@ -125,7 +134,7 @@ internal class DefaultGenericActionDelegate( savedStateHandle = savedStateHandle, application = application, ) - this._delegate = delegate + _delegate = delegate adyenLog(AdyenLogLevel.DEBUG) { "Created delegate of type ${delegate::class.simpleName}" } if (delegate is RedirectableDelegate) { @@ -182,16 +191,7 @@ internal class DefaultGenericActionDelegate( override fun handleIntent(intent: Intent) { when (val delegate = _delegate) { null -> { - val action: Action? = savedStateHandle[ACTION_KEY] - if (action == null) { - exceptionChannel.trySend( - ComponentException("handleIntent should not be called before handleAction"), - ) - return - } - adyenLog(AdyenLogLevel.DEBUG) { "Recreating delegate and trying again" } - createDelegateAndObserve(action) - handleIntent(intent) + exceptionChannel.trySend(ComponentException("handleIntent should not be called before handleAction")) } !is IntentHandlingDelegate -> { From eebda8213fd504aadecad9b281a508a5763e13e5 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 13 Mar 2024 13:46:09 +0100 Subject: [PATCH 114/272] Try to restore state of DefaultVoucherDelegate COAND-857 --- .../provider/VoucherComponentProvider.kt | 3 ++- .../internal/ui/DefaultVoucherDelegate.kt | 16 ++++++++++++++++ .../internal/ui/DefaultVoucherDelegateTest.kt | 10 ++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt index 78aa301a2e..0b8ff473f1 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt @@ -82,6 +82,7 @@ constructor( return DefaultVoucherDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, pdfOpener = PdfOpener(), imageSaver = ImageSaver(), @@ -133,7 +134,7 @@ constructor( PaymentMethodTypes.ECONTEXT_ONLINE, PaymentMethodTypes.ECONTEXT_SEVEN_ELEVEN, PaymentMethodTypes.ECONTEXT_STORES, - PaymentMethodTypes.MULTIBANCO + PaymentMethodTypes.MULTIBANCO, ) } } diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 05ce6555e0..09d9553d09 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -12,6 +12,7 @@ import android.app.Activity import android.content.Context import android.view.View import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.internal.ActionComponentEvent @@ -42,6 +43,7 @@ import java.util.Calendar internal class DefaultVoucherDelegate( private val observerRepository: ActionObserverRepository, + private val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val pdfOpener: PdfOpener, private val imageSaver: ImageSaver, @@ -69,6 +71,14 @@ internal class DefaultVoucherDelegate( override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + val action: Action? = savedStateHandle[ACTION_KEY] + if (action != null) { + handleAction(action) + } } override fun observe( @@ -91,6 +101,11 @@ internal class DefaultVoucherDelegate( } override fun handleAction(action: Action, activity: Activity) { + savedStateHandle[ACTION_KEY] = action + handleAction(action) + } + + private fun handleAction(action: Action) { if (action !is VoucherAction) { exceptionChannel.trySend(ComponentException("Unsupported action")) return @@ -190,5 +205,6 @@ internal class DefaultVoucherDelegate( companion object { private const val IMAGE_NAME_FORMAT = "%s-%s.png" + private const val ACTION_KEY = "ACTION_KEY" } } diff --git a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt index 82536a9327..ca5ba69201 100644 --- a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt +++ b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt @@ -12,6 +12,7 @@ import android.app.Activity import android.content.Context import android.os.Parcel import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import app.cash.turbine.test import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration @@ -71,11 +72,12 @@ internal class DefaultVoucherDelegateTest( voucher() } delegate = DefaultVoucherDelegate( - observerRepository, - GenericComponentParamsMapper(CommonComponentParamsMapper()) + observerRepository = observerRepository, + savedStateHandle = SavedStateHandle(), + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), - pdfOpener, - imageSaver, + pdfOpener = pdfOpener, + imageSaver = imageSaver, ) } From 215914f28a8de914dd392e9b4be65ab160d23aa3 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 13 Mar 2024 13:55:06 +0100 Subject: [PATCH 115/272] Try to restore state of DefaultAwaitDelegate COAND-857 --- .../internal/provider/AwaitComponentProvider.kt | 1 + .../await/internal/ui/DefaultAwaitDelegate.kt | 17 +++++++++++++++++ .../internal/ui/DefaultAwaitDelegateTest.kt | 9 +++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt index c96bc5e10a..3cb471b4c8 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt @@ -90,6 +90,7 @@ constructor( val paymentDataRepository = PaymentDataRepository(savedStateHandle) return DefaultAwaitDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, statusRepository = statusRepository, paymentDataRepository = paymentDataRepository, diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index cbd5b04ee3..159c5911cc 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.await.internal.ui import android.app.Activity import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.await.internal.ui.model.AwaitOutputData import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.action.Action @@ -46,6 +47,7 @@ import java.util.concurrent.TimeUnit @Suppress("TooManyFunctions") internal class DefaultAwaitDelegate( private val observerRepository: ActionObserverRepository, + private val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val statusRepository: StatusRepository, private val paymentDataRepository: PaymentDataRepository, @@ -74,6 +76,14 @@ internal class DefaultAwaitDelegate( override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + val action: Action? = savedStateHandle[ACTION_KEY] + if (action != null) { + handleAction(action) + } } override fun observe( @@ -99,6 +109,11 @@ internal class DefaultAwaitDelegate( } override fun handleAction(action: Action, activity: Activity) { + savedStateHandle[ACTION_KEY] = action + handleAction(action) + } + + private fun handleAction(action: Action) { if (action !is AwaitAction) { exceptionChannel.trySend(ComponentException("Unsupported action")) return @@ -192,6 +207,8 @@ internal class DefaultAwaitDelegate( companion object { private val DEFAULT_MAX_POLLING_DURATION = TimeUnit.MINUTES.toMillis(15) + private const val ACTION_KEY = "ACTION_KEY" + @VisibleForTesting internal const val PAYLOAD_DETAILS_KEY = "payload" } diff --git a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt index f609006dfc..f71d593f92 100644 --- a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt +++ b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt @@ -53,11 +53,12 @@ internal class DefaultAwaitDelegateTest { TEST_CLIENT_KEY, ) delegate = DefaultAwaitDelegate( - ActionObserverRepository(), - GenericComponentParamsMapper(CommonComponentParamsMapper()) + observerRepository = ActionObserverRepository(), + savedStateHandle = SavedStateHandle(), + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), - statusRepository, - paymentDataRepository, + statusRepository = statusRepository, + paymentDataRepository = paymentDataRepository, ) } From 30dfdab677513a861e63f0fc561f146716dae59d Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 20 Mar 2024 18:09:58 +0100 Subject: [PATCH 116/272] Try to restore state of DefaultQRCodeDelegate COAND-857 --- .../provider/QRCodeComponentProvider.kt | 1 + .../internal/ui/DefaultQRCodeDelegate.kt | 32 +++++++++++++++---- .../internal/ui/DefaultQRCodeDelegateTest.kt | 1 + .../internal/ui/DefaultVoucherDelegate.kt | 1 + 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt index 0038093ca1..6823c811b5 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt @@ -93,6 +93,7 @@ constructor( return DefaultQRCodeDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, statusRepository = statusRepository, statusCountDownTimer = countDownTimer, diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index 0eac440161..4056719f7c 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -14,6 +14,7 @@ import android.content.Intent import android.net.Uri import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action @@ -61,6 +62,7 @@ import kotlin.time.Duration.Companion.seconds @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultQRCodeDelegate( private val observerRepository: ActionObserverRepository, + private val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val statusRepository: StatusRepository, private val statusCountDownTimer: QRCodeCountDownTimer, @@ -115,6 +117,14 @@ internal class DefaultQRCodeDelegate( override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + val action: QrCodeAction? = savedStateHandle[ACTION_KEY] + if (action != null) { + handleAction(action) + } } override fun observe( @@ -146,13 +156,8 @@ internal class DefaultQRCodeDelegate( return } - val paymentData = action.paymentData - paymentDataRepository.paymentData = paymentData - if (paymentData == null) { - adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) - return - } + savedStateHandle[ACTION_KEY] = action + paymentDataRepository.paymentData = action.paymentData if (shouldLaunchRedirect(action)) { adyenLog(AdyenLogLevel.DEBUG) { "Action does not require a view, redirecting." } @@ -161,6 +166,17 @@ internal class DefaultQRCodeDelegate( return } + handleAction(action) + } + + private fun handleAction(action: QrCodeAction) { + val paymentData = action.paymentData + if (paymentData == null) { + adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } + exceptionChannel.trySend(ComponentException("Payment data is null")) + return + } + var viewType = QrCodeComponentViewType.SIMPLE_QR_CODE action.paymentMethodType?.let { @@ -359,5 +375,7 @@ internal class DefaultQRCodeDelegate( private const val IMAGE_NAME_FORMAT = "%s-%s.png" private const val QR_IMAGE_BASE_PATH = "%sbarcode.shtml?barcodeType=qrCode&fileType=png&data=%s" + + private const val ACTION_KEY = "ACTION_KEY" } } diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 7cbcd6d0b9..f2461a28ff 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -602,6 +602,7 @@ internal class DefaultQRCodeDelegateTest( imageSaver: ImageSaver = mock(), ) = DefaultQRCodeDelegate( observerRepository = observerRepository, + savedStateHandle = SavedStateHandle(), componentParams = componentParams, statusRepository = statusRepository, statusCountDownTimer = statusCountDownTimer, diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 09d9553d09..51c4e5decd 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import java.util.Calendar +@Suppress("TooManyFunctions") internal class DefaultVoucherDelegate( private val observerRepository: ActionObserverRepository, private val savedStateHandle: SavedStateHandle, From 59ee90ab998bc64ec39e8e47d65cf31ef445ea8f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 4 Apr 2024 09:26:59 +0200 Subject: [PATCH 117/272] Use SavedStateHandleProperty to save the actions COAND-857 --- .../internal/ui/DefaultGenericActionDelegate.kt | 12 ++++++++---- .../await/internal/ui/DefaultAwaitDelegate.kt | 13 +++++++++---- .../qrcode/internal/ui/DefaultQRCodeDelegate.kt | 13 +++++++++---- .../voucher/internal/ui/DefaultVoucherDelegate.kt | 15 +++++++++++---- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 7d4c22332b..7e923d97d2 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PermissionRequestData +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.components.core.internal.ui.DetailsEmittingDelegate import com.adyen.checkout.components.core.internal.ui.IntentHandlingDelegate @@ -48,12 +50,12 @@ import kotlinx.coroutines.flow.receiveAsFlow @Suppress("TooManyFunctions") internal class DefaultGenericActionDelegate( private val observerRepository: ActionObserverRepository, - private val savedStateHandle: SavedStateHandle, + override val savedStateHandle: SavedStateHandle, private val checkoutConfiguration: CheckoutConfiguration, override val componentParams: GenericComponentParams, private val actionDelegateProvider: ActionDelegateProvider, private val application: Application, -) : GenericActionDelegate { +) : GenericActionDelegate, SavedStateHandleContainer { private var _delegate: ActionDelegate? = null override val delegate: ActionDelegate get() = requireNotNull(_delegate) @@ -75,6 +77,8 @@ internal class DefaultGenericActionDelegate( private var onRedirectListener: (() -> Unit)? = null + private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.DEBUG) { "initialize" } _coroutineScope = coroutineScope @@ -83,7 +87,7 @@ internal class DefaultGenericActionDelegate( private fun restoreState() { adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } - val action: Action? = savedStateHandle[ACTION_KEY] + val action: Action? = action if (_delegate == null && action != null) { createDelegateAndObserve(action) } @@ -112,7 +116,7 @@ internal class DefaultGenericActionDelegate( } override fun handleAction(action: Action, activity: Activity) { - savedStateHandle[ACTION_KEY] = action + this.action = action // This check is to support an older flow where you might need to call handleAction several times with 3DS2. // Initially handleAction is called with a fingerprint action then with a challenge action. diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index 159c5911cc..c38b28a677 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -19,6 +19,8 @@ import com.adyen.checkout.components.core.action.AwaitAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -47,11 +49,11 @@ import java.util.concurrent.TimeUnit @Suppress("TooManyFunctions") internal class DefaultAwaitDelegate( private val observerRepository: ActionObserverRepository, - private val savedStateHandle: SavedStateHandle, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val statusRepository: StatusRepository, private val paymentDataRepository: PaymentDataRepository, -) : AwaitDelegate { +) : AwaitDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) override val outputDataFlow: Flow = _outputDataFlow @@ -74,13 +76,16 @@ internal class DefaultAwaitDelegate( private var statusPollingJob: Job? = null + private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope restoreState() } private fun restoreState() { - val action: Action? = savedStateHandle[ACTION_KEY] + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + val action: Action? = action if (action != null) { handleAction(action) } @@ -109,7 +114,7 @@ internal class DefaultAwaitDelegate( } override fun handleAction(action: Action, activity: Activity) { - savedStateHandle[ACTION_KEY] = action + this.action = action handleAction(action) } diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index 4056719f7c..6e735b19ab 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -23,6 +23,8 @@ import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.PermissionRequestData +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -62,14 +64,14 @@ import kotlin.time.Duration.Companion.seconds @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultQRCodeDelegate( private val observerRepository: ActionObserverRepository, - private val savedStateHandle: SavedStateHandle, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val statusRepository: StatusRepository, private val statusCountDownTimer: QRCodeCountDownTimer, private val redirectHandler: RedirectHandler, private val paymentDataRepository: PaymentDataRepository, private val imageSaver: ImageSaver -) : QRCodeDelegate { +) : QRCodeDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) override val outputDataFlow: Flow = _outputDataFlow @@ -101,6 +103,8 @@ internal class DefaultQRCodeDelegate( private var maxPollingDurationMillis = DEFAULT_MAX_POLLING_DURATION + private var action: QrCodeAction? by SavedStateHandleProperty(ACTION_KEY) + private fun attachStatusTimer() { statusCountDownTimer.attach( millisInFuture = maxPollingDurationMillis, @@ -121,7 +125,8 @@ internal class DefaultQRCodeDelegate( } private fun restoreState() { - val action: QrCodeAction? = savedStateHandle[ACTION_KEY] + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + val action: QrCodeAction? = action if (action != null) { handleAction(action) } @@ -156,7 +161,7 @@ internal class DefaultQRCodeDelegate( return } - savedStateHandle[ACTION_KEY] = action + this.action = action paymentDataRepository.paymentData = action.paymentData if (shouldLaunchRedirect(action)) { diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 51c4e5decd..6738faf276 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -18,12 +18,16 @@ import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PermissionRequestData +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.DateUtils import com.adyen.checkout.components.core.internal.util.bufferedChannel +import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.PermissionHandlerCallback import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.ui.core.internal.exception.PermissionRequestException import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.checkout.ui.core.internal.util.ImageSaver @@ -44,11 +48,11 @@ import java.util.Calendar @Suppress("TooManyFunctions") internal class DefaultVoucherDelegate( private val observerRepository: ActionObserverRepository, - private val savedStateHandle: SavedStateHandle, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val pdfOpener: PdfOpener, private val imageSaver: ImageSaver, -) : VoucherDelegate { +) : VoucherDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) override val outputDataFlow: Flow = _outputDataFlow @@ -70,13 +74,16 @@ internal class DefaultVoucherDelegate( private var _coroutineScope: CoroutineScope? = null private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope restoreState() } private fun restoreState() { - val action: Action? = savedStateHandle[ACTION_KEY] + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + val action: Action? = action if (action != null) { handleAction(action) } @@ -102,7 +109,7 @@ internal class DefaultVoucherDelegate( } override fun handleAction(action: Action, activity: Activity) { - savedStateHandle[ACTION_KEY] = action + this.action = action handleAction(action) } From 98236be7364058117994519d90bf0bfaffd56865 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 15 May 2024 11:39:49 +0200 Subject: [PATCH 118/272] Don't save action in DefaultGenericActionDelegate The specific action delegates should do this since they share a SavedStateHandle and are also responsible for removing it. COAND-857 --- .../ui/DefaultGenericActionDelegate.kt | 8 ++-- .../ui/DefaultGenericActionDelegateTest.kt | 44 +++++++++++-------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 7e923d97d2..65c5e92a03 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.action.core.internal.ui import android.app.Activity import android.app.Application import android.content.Intent +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.adyen3ds2.internal.ui.Adyen3DS2Delegate @@ -77,7 +78,7 @@ internal class DefaultGenericActionDelegate( private var onRedirectListener: (() -> Unit)? = null - private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + private val action: Action? by SavedStateHandleProperty(ACTION_KEY) override fun initialize(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.DEBUG) { "initialize" } @@ -116,8 +117,6 @@ internal class DefaultGenericActionDelegate( } override fun handleAction(action: Action, activity: Activity) { - this.action = action - // This check is to support an older flow where you might need to call handleAction several times with 3DS2. // Initially handleAction is called with a fingerprint action then with a challenge action. // During this whole flow the same transaction instance should be used for both fingerprint and challenge. @@ -239,6 +238,7 @@ internal class DefaultGenericActionDelegate( } companion object { - private const val ACTION_KEY = "ACTION_KEY" + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt index 6731383d5d..18ea8a1414 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt @@ -58,20 +58,7 @@ internal class DefaultGenericActionDelegateTest( @BeforeEach fun beforeEach() { - val configuration = CheckoutConfiguration( - shopperLocale = Locale.US, - environment = Environment.TEST, - clientKey = TEST_CLIENT_KEY, - ) - genericActionDelegate = DefaultGenericActionDelegate( - ActionObserverRepository(), - SavedStateHandle(), - configuration, - GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - actionDelegateProvider, - Application(), - ) + genericActionDelegate = createDelegate() whenever(activity.application) doReturn Application() testDelegate = TestActionDelegate() @@ -235,11 +222,10 @@ internal class DefaultGenericActionDelegateTest( @Test fun `when process died during handling action, then handleIntent should restore state and continue`() { - // Create genericActionDelegate and handle action - genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - genericActionDelegate.handleAction(Threeds2FingerprintAction(), activity) - // Simulate destroying and recreating genericActionDelegate - genericActionDelegate.onCleared() + val savedStateHandle = SavedStateHandle().apply { + set(DefaultGenericActionDelegate.ACTION_KEY, RedirectAction()) + } + genericActionDelegate = createDelegate(savedStateHandle) genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) genericActionDelegate.handleIntent(Intent()) @@ -247,6 +233,26 @@ internal class DefaultGenericActionDelegateTest( assertTrue(testDelegate.handleIntentCalled) } + private fun createDelegate( + savedStateHandle: SavedStateHandle = SavedStateHandle() + ): DefaultGenericActionDelegate { + val configuration = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + ) + + return DefaultGenericActionDelegate( + ActionObserverRepository(), + savedStateHandle, + configuration, + GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + actionDelegateProvider, + Application(), + ) + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" } From 93c39008c9dcdbaebcf020e4c8253d51bef44d0c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 15 May 2024 14:51:16 +0200 Subject: [PATCH 119/272] Clear state when flow is final in await COAND-857 --- .../await/internal/ui/DefaultAwaitDelegate.kt | 31 +++- .../internal/ui/DefaultAwaitDelegateTest.kt | 155 ++++++++++++------ 2 files changed, 131 insertions(+), 55 deletions(-) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index c38b28a677..b48901c463 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -120,7 +120,7 @@ internal class DefaultAwaitDelegate( private fun handleAction(action: Action) { if (action !is AwaitAction) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + emitError(ComponentException("Unsupported action")) return } @@ -128,7 +128,7 @@ internal class DefaultAwaitDelegate( paymentDataRepository.paymentData = paymentData if (paymentData == null) { adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) + emitError(ComponentException("Payment data is null")) return } createOutputData(null, action) @@ -153,7 +153,7 @@ internal class DefaultAwaitDelegate( }, onFailure = { adyenLog(AdyenLogLevel.ERROR, it) { "Error while polling status" } - exceptionChannel.trySend(ComponentException("Error while polling status", it)) + emitError(ComponentException("Error while polling status", it)) }, ) } @@ -173,10 +173,9 @@ internal class DefaultAwaitDelegate( // Not authorized status should still call /details so that merchant can get more info val payload = statusResponse.payload if (StatusResponseUtils.isFinalResult(statusResponse) && !payload.isNullOrEmpty()) { - val details = createDetails(payload) - detailsChannel.trySend(createActionComponentData(details)) + emitDetails(payload) } else { - exceptionChannel.trySend(ComponentException("Payment was not completed. - " + statusResponse.resultCode)) + emitError(ComponentException("Payment was not completed. - " + statusResponse.resultCode)) } } @@ -192,11 +191,26 @@ internal class DefaultAwaitDelegate( try { jsonObject.put(PAYLOAD_DETAILS_KEY, payload) } catch (e: JSONException) { - exceptionChannel.trySend(ComponentException("Failed to create details.", e)) + emitError(ComponentException("Failed to create details.", e)) } return jsonObject } + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(payload: String) { + val details = createDetails(payload) + detailsChannel.trySend(createActionComponentData(details)) + clearState() + } + + private fun clearState() { + action = null + } + override fun refreshStatus() { val paymentData = paymentDataRepository.paymentData ?: return statusRepository.refreshStatus(paymentData) @@ -212,7 +226,8 @@ internal class DefaultAwaitDelegate( companion object { private val DEFAULT_MAX_POLLING_DURATION = TimeUnit.MINUTES.toMillis(15) - private const val ACTION_KEY = "ACTION_KEY" + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" @VisibleForTesting internal const val PAYLOAD_DETAILS_KEY = "payload" diff --git a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt index f71d593f92..193c5ae698 100644 --- a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt +++ b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt @@ -10,9 +10,9 @@ package com.adyen.checkout.await.internal.ui import android.app.Activity import androidx.lifecycle.SavedStateHandle -import app.cash.turbine.test import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.action.AwaitAction +import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse @@ -22,6 +22,7 @@ import com.adyen.checkout.components.core.internal.ui.model.GenericComponentPara import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.test.LoggingExtension +import com.adyen.checkout.test.extensions.test import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -29,6 +30,7 @@ import kotlinx.coroutines.test.runTest import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -48,18 +50,7 @@ internal class DefaultAwaitDelegateTest { fun beforeEach() { statusRepository = TestStatusRepository() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) - val configuration = CheckoutConfiguration( - Environment.TEST, - TEST_CLIENT_KEY, - ) - delegate = DefaultAwaitDelegate( - observerRepository = ActionObserverRepository(), - savedStateHandle = SavedStateHandle(), - componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - statusRepository = statusRepository, - paymentDataRepository = paymentDataRepository, - ) + delegate = createDelegate() } @Test @@ -69,23 +60,20 @@ internal class DefaultAwaitDelegateTest { Result.success(StatusResponse(resultCode = "finished")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val outputDataFlow = delegate.outputDataFlow.test(testScheduler) - delegate.outputDataFlow.test { - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) - skipItems(1) + // We skip the first output data value as it's the initial value - with(awaitItem()) { - assertFalse(isValid) - assertEquals("test", paymentMethodType) - } - - with(awaitItem()) { - assertTrue(isValid) - assertEquals("test", paymentMethodType) - } + with(outputDataFlow.values[1]) { + assertFalse(isValid) + assertEquals("test", paymentMethodType) + } - cancelAndIgnoreRemainingEvents() + with(outputDataFlow.values[2]) { + assertTrue(isValid) + assertEquals("test", paymentMethodType) } } @@ -95,20 +83,17 @@ internal class DefaultAwaitDelegateTest { Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val detailsFlow = delegate.detailsFlow.test(testScheduler) - delegate.detailsFlow.test { - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) - val expectedDetails = JSONObject().apply { - put(DefaultAwaitDelegate.PAYLOAD_DETAILS_KEY, "testpayload") - } - - with(awaitItem()) { - assertEquals(expectedDetails.toString(), details.toString()) - assertEquals("paymentData", paymentData) - } + val expectedDetails = JSONObject().apply { + put(DefaultAwaitDelegate.PAYLOAD_DETAILS_KEY, "testpayload") + } - cancelAndIgnoreRemainingEvents() + with(detailsFlow.latestValue) { + assertEquals(expectedDetails.toString(), details.toString()) + assertEquals("paymentData", paymentData) } } @@ -117,14 +102,11 @@ internal class DefaultAwaitDelegateTest { val error = IOException("test") statusRepository.pollingResults = listOf(Result.failure(error)) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) - assertEquals(error, awaitItem().cause) - - cancelAndIgnoreRemainingEvents() - } + assertEquals(error, exceptionFlow.latestValue.cause) } @Test @@ -133,14 +115,93 @@ internal class DefaultAwaitDelegateTest { Result.success(StatusResponse(resultCode = "finished", payload = "")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) - assertTrue(awaitItem() is ComponentException) + assertTrue(exceptionFlow.latestValue is ComponentException) + assertEquals("Payment was not completed. - finished", exceptionFlow.latestValue.message) + } + + @Test + fun `when a wrongly typed action is used, then an error is propagated`() = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + + delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertTrue(exceptionFlow.latestValue is ComponentException) + assertEquals("Unsupported action", exceptionFlow.latestValue.message) + } + + @Test + fun `when payment data is null, then an error is propagated`() = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = null), Activity()) + + assertTrue(exceptionFlow.latestValue is ComponentException) + assertEquals("Payment data is null", exceptionFlow.latestValue.message) + } - cancelAndIgnoreRemainingEvents() + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle().apply { + set(DefaultAwaitDelegate.ACTION_KEY, AwaitAction(paymentMethodType = "test", paymentData = "paymentData")) } + delegate = createDelegate(savedStateHandle) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertTrue(detailsFlow.values.isNotEmpty()) + } + + @Test + fun `when details are emitted, then state is cleared`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertNull(savedStateHandle[DefaultAwaitDelegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertNull(savedStateHandle[DefaultAwaitDelegate.ACTION_KEY]) + } + + private fun createDelegate( + savedStateHandle: SavedStateHandle = SavedStateHandle() + ): DefaultAwaitDelegate { + val configuration = CheckoutConfiguration( + Environment.TEST, + TEST_CLIENT_KEY, + ) + + return DefaultAwaitDelegate( + observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + statusRepository = statusRepository, + paymentDataRepository = paymentDataRepository, + ) } companion object { From 616c080e93881b059bbac659b87dc8059df78f2c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 15 May 2024 15:16:20 +0200 Subject: [PATCH 120/272] Split restoring qr and redirect action state restoration for DefaultQrCodeDelegate COAND-857 --- .../internal/ui/DefaultQRCodeDelegate.kt | 65 ++-- .../internal/ui/DefaultQRCodeDelegateTest.kt | 353 +++++++++--------- 2 files changed, 206 insertions(+), 212 deletions(-) diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index 6e735b19ab..f8a6123421 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -128,7 +128,7 @@ internal class DefaultQRCodeDelegate( adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } val action: QrCodeAction? = action if (action != null) { - handleAction(action) + initState(action) } } @@ -154,7 +154,6 @@ internal class DefaultQRCodeDelegate( observerRepository.removeObservers() } - @Suppress("ReturnCount") override fun handleAction(action: Action, activity: Activity) { if (action !is QrCodeAction) { exceptionChannel.trySend(ComponentException("Unsupported action")) @@ -164,39 +163,44 @@ internal class DefaultQRCodeDelegate( this.action = action paymentDataRepository.paymentData = action.paymentData + launchAction(action, activity) + initState(action) + } + + private fun launchAction(action: QrCodeAction, activity: Activity) { if (shouldLaunchRedirect(action)) { - adyenLog(AdyenLogLevel.DEBUG) { "Action does not require a view, redirecting." } - _viewFlow.tryEmit(QrCodeComponentViewType.REDIRECT) makeRedirect(activity, action) - return } - - handleAction(action) } - private fun handleAction(action: QrCodeAction) { - val paymentData = action.paymentData - if (paymentData == null) { - adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) - return - } - - var viewType = QrCodeComponentViewType.SIMPLE_QR_CODE - - action.paymentMethodType?.let { - val qrConfig = QRCodePaymentMethodConfig.getByPaymentMethodType(it) - viewType = qrConfig.viewType - maxPollingDurationMillis = qrConfig.maxPollingDurationMillis + private fun initState(action: QrCodeAction) { + if (shouldLaunchRedirect(action)) { + adyenLog(AdyenLogLevel.DEBUG) { "Action does not require a view, redirecting." } + _viewFlow.tryEmit(QrCodeComponentViewType.REDIRECT) + } else { + val paymentData = action.paymentData + if (paymentData == null) { + adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } + exceptionChannel.trySend(ComponentException("Payment data is null")) + return + } + + var viewType = QrCodeComponentViewType.SIMPLE_QR_CODE + + action.paymentMethodType?.let { + val qrConfig = QRCodePaymentMethodConfig.getByPaymentMethodType(it) + viewType = qrConfig.viewType + maxPollingDurationMillis = qrConfig.maxPollingDurationMillis + } + _viewFlow.tryEmit(viewType) + + // Notify UI to get the logo. + createOutputData(null, action) + + attachStatusTimer() + startStatusPolling(paymentData, action) + statusCountDownTimer.start() } - _viewFlow.tryEmit(viewType) - - // Notify UI to get the logo. - createOutputData(null, action) - - attachStatusTimer() - startStatusPolling(paymentData, action) - statusCountDownTimer.start() } private fun makeRedirect(activity: Activity, action: QrCodeAction) { @@ -381,6 +385,7 @@ internal class DefaultQRCodeDelegate( private const val IMAGE_NAME_FORMAT = "%s-%s.png" private const val QR_IMAGE_BASE_PATH = "%sbarcode.shtml?barcodeType=qrCode&fileType=png&data=%s" - private const val ACTION_KEY = "ACTION_KEY" + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index f2461a28ff..79d6648b6c 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -14,7 +14,6 @@ import android.content.Intent import android.os.Parcel import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle -import app.cash.turbine.test import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action @@ -37,6 +36,7 @@ import com.adyen.checkout.qrcode.internal.QRCodeCountDownTimer import com.adyen.checkout.qrcode.internal.ui.model.QrCodeUIEvent import com.adyen.checkout.qrcode.qrCode import com.adyen.checkout.test.LoggingExtension +import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.RedirectHandler import com.adyen.checkout.ui.core.internal.exception.PermissionRequestException import com.adyen.checkout.ui.core.internal.test.TestRedirectHandler @@ -142,26 +142,25 @@ internal class DefaultQRCodeDelegateTest( @Test fun `when handleAction is called with unsupported action, then an error should be emitted`() = runTest { - delegate.exceptionFlow.test { - delegate.handleAction( - createTestAction(), - mock(), - ) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - assert(expectMostRecentItem() is ComponentException) - } + delegate.handleAction( + createTestAction(), + mock(), + ) + + assert(exceptionFlow.latestValue is ComponentException) } @Test fun `when handleAction is called with null payment data, then an error should be emitted`() = runTest { - delegate.exceptionFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = null), - mock(), - ) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = null), + mock(), + ) - assert(expectMostRecentItem() is ComponentException) - } + assert(exceptionFlow.latestValue is ComponentException) } @Nested @@ -172,15 +171,10 @@ internal class DefaultQRCodeDelegateTest( fun `timer ticks, then left over time and progress are emitted`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.timerFlow.test { - delegate.onTimerTick(10000) + val timerFlow = delegate.timerFlow.test(testScheduler) + delegate.onTimerTick(10000) - skipItems(1) - - assertEquals(TimerData(10000, 1), awaitItem()) - - cancelAndIgnoreRemainingEvents() - } + assertEquals(TimerData(10000, 1), timerFlow.latestValue) } @Test @@ -191,32 +185,28 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.outputDataFlow.test { - delegate.handleAction( - QrCodeAction( - paymentMethodType = PaymentMethodTypes.PIX, - qrCodeData = "qrData", - paymentData = "paymentData", - ), - Activity(), - ) - - skipItems(1) - - with(awaitItem()) { - assertFalse(isValid) - assertEquals(PaymentMethodTypes.PIX, paymentMethodType) - assertEquals("qrData", qrCodeData) - } + val outputDataFlow = delegate.outputDataFlow.test(testScheduler) + delegate.handleAction( + QrCodeAction( + paymentMethodType = PaymentMethodTypes.PIX, + qrCodeData = "qrData", + paymentData = "paymentData", + ), + Activity(), + ) - with(awaitItem()) { - assertTrue(isValid) - assertEquals(PaymentMethodTypes.PIX, paymentMethodType) - assertEquals("qrData", qrCodeData) - } + with(outputDataFlow.values[1]) { + assertFalse(isValid) + assertEquals(PaymentMethodTypes.PIX, paymentMethodType) + assertEquals("qrData", qrCodeData) + } - cancelAndIgnoreRemainingEvents() + with(outputDataFlow.values[2]) { + assertTrue(isValid) + assertEquals(PaymentMethodTypes.PIX, paymentMethodType) + assertEquals("qrData", qrCodeData) } + } @Test @@ -225,17 +215,17 @@ internal class DefaultQRCodeDelegateTest( Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val detailsFlow = delegate.detailsFlow.test(testScheduler) - delegate.detailsFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), - Activity(), - ) - - assertEquals("testpayload", awaitItem().details?.getString(DefaultQRCodeDelegate.PAYLOAD_DETAILS_KEY)) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + Activity(), + ) - cancelAndIgnoreRemainingEvents() - } + assertEquals( + "testpayload", + detailsFlow.latestValue.details?.getString(DefaultQRCodeDelegate.PAYLOAD_DETAILS_KEY), + ) } @Test @@ -243,17 +233,14 @@ internal class DefaultQRCodeDelegateTest( val error = IOException("test") statusRepository.pollingResults = listOf(Result.failure(error)) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), - Activity(), - ) - - assertEquals(error, awaitItem().cause) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + Activity(), + ) - cancelAndIgnoreRemainingEvents() - } + assertEquals(error, exceptionFlow.latestValue.cause) } @Test @@ -262,33 +249,29 @@ internal class DefaultQRCodeDelegateTest( Result.success(StatusResponse(resultCode = "finished", payload = "")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), - Activity(), - ) - - assertTrue(awaitItem() is ComponentException) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + Activity(), + ) - cancelAndIgnoreRemainingEvents() - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test fun `handleAction is called, then simple qr view flow is updated`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val viewFlow = delegate.viewFlow.test(testScheduler) - delegate.viewFlow.test { - assertNull(awaitItem()) + assertNull(viewFlow.latestValue) - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), - Activity(), - ) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + Activity(), + ) - assertEquals(QrCodeComponentViewType.SIMPLE_QR_CODE, awaitItem()) - } + assertEquals(QrCodeComponentViewType.SIMPLE_QR_CODE, viewFlow.latestValue) } @Test @@ -299,29 +282,26 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.outputDataFlow.test { - delegate.handleAction( - QrCodeAction( - paymentMethodType = PaymentMethodTypes.PAY_NOW, - qrCodeData = "qrData", - paymentData = "paymentData", - ), - Activity(), - ) - - skipItems(1) + val outputDataFlow = delegate.outputDataFlow.test(testScheduler) + delegate.handleAction( + QrCodeAction( + paymentMethodType = PaymentMethodTypes.PAY_NOW, + qrCodeData = "qrData", + paymentData = "paymentData", + ), + Activity(), + ) - with(awaitItem()) { - assertFalse(isValid) - assertEquals(PaymentMethodTypes.PAY_NOW, paymentMethodType) - assertEquals("qrData", qrCodeData) - } + with(outputDataFlow.values[1]) { + assertFalse(isValid) + assertEquals(PaymentMethodTypes.PAY_NOW, paymentMethodType) + assertEquals("qrData", qrCodeData) + } - with(expectMostRecentItem()) { - assertTrue(isValid) - assertEquals(PaymentMethodTypes.PAY_NOW, paymentMethodType) - assertEquals("qrData", qrCodeData) - } + with(outputDataFlow.values[2]) { + assertTrue(isValid) + assertEquals(PaymentMethodTypes.PAY_NOW, paymentMethodType) + assertEquals("qrData", qrCodeData) } } @@ -332,16 +312,16 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.detailsFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), - Activity(), - ) - - assertEquals("testpayload", awaitItem().details?.getString(DefaultQRCodeDelegate.PAYLOAD_DETAILS_KEY)) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + Activity(), + ) - cancelAndIgnoreRemainingEvents() - } + assertEquals( + "testpayload", + detailsFlow.latestValue.details?.getString(DefaultQRCodeDelegate.PAYLOAD_DETAILS_KEY), + ) } @Test @@ -350,17 +330,14 @@ internal class DefaultQRCodeDelegateTest( Result.success(StatusResponse(resultCode = "finished", payload = "")), ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), - Activity(), - ) - - assertTrue(awaitItem() is ComponentException) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + Activity(), + ) - cancelAndIgnoreRemainingEvents() - } + assertTrue(exceptionFlow.latestValue is ComponentException) } @Test @@ -368,31 +345,29 @@ internal class DefaultQRCodeDelegateTest( val error = IOException("test") statusRepository.pollingResults = listOf(Result.failure(error)) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), - Activity(), - ) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + Activity(), + ) - assertEquals(error, expectMostRecentItem().cause) - } + assertEquals(error, exceptionFlow.latestValue.cause) } @Test fun `handleAction is called, then full qr view flow is updated`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val viewFlow = delegate.viewFlow.test(testScheduler) - delegate.viewFlow.test { - assertNull(awaitItem()) + assertNull(viewFlow.latestValue) - delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), - Activity(), - ) + delegate.handleAction( + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + Activity(), + ) - assertEquals(QrCodeComponentViewType.FULL_QR_CODE, awaitItem()) - } + assertEquals(QrCodeComponentViewType.FULL_QR_CODE, viewFlow.latestValue) } } @@ -404,57 +379,54 @@ internal class DefaultQRCodeDelegateTest( fun `handleAction is called and RedirectHandler returns an error, then the error is propagated`() = runTest { val error = ComponentException("Failed to make redirect.") redirectHandler.exception = error + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) - assertEquals(error, awaitItem()) - } + assertEquals(error, exceptionFlow.latestValue) } @Test fun `handleAction is called with valid data, then no error is propagated`() = runTest { - delegate.exceptionFlow.test { - delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - expectNoEvents() - } + delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertTrue(exceptionFlow.values.isEmpty()) } @Test fun `handleIntent is called and RedirectHandler returns an error, then the error is propagated`() = runTest { val error = ComponentException("Failed to parse redirect result.") redirectHandler.exception = error + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.exceptionFlow.test { - delegate.handleIntent(Intent()) + delegate.handleIntent(Intent()) - assertEquals(error, awaitItem()) - } + assertEquals(error, exceptionFlow.latestValue) } @Test fun `handleIntent is called with valid data, then the details are emitted`() = runTest { - delegate.detailsFlow.test { - delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) - delegate.handleIntent(Intent()) - - with(awaitItem()) { - assertEquals(TestRedirectHandler.REDIRECT_RESULT, details) - assertEquals("paymentData", paymentData) - } + val detailsFlow = delegate.detailsFlow.test(testScheduler) + delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) + delegate.handleIntent(Intent()) + + with(detailsFlow.latestValue) { + assertEquals(TestRedirectHandler.REDIRECT_RESULT, details) + assertEquals("paymentData", paymentData) } } @Test fun `handleAction is called, then the view flow is updated`() = runTest { - delegate.viewFlow.test { - assertNull(awaitItem()) + val viewFlow = delegate.viewFlow.test(testScheduler) - delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) + assertNull(viewFlow.latestValue) - assertEquals(awaitItem(), QrCodeComponentViewType.REDIRECT) - } + delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) + + assertEquals(QrCodeComponentViewType.REDIRECT, viewFlow.latestValue) } } @@ -506,13 +478,12 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.eventFlow.test { - val expectedResult = QrCodeUIEvent.QrImageDownloadResult.Success + val eventFlow = delegate.eventFlow.test(testScheduler) + val expectedResult = QrCodeUIEvent.QrImageDownloadResult.Success - delegate.downloadQRImage(context) + delegate.downloadQRImage(context) - assertEquals(expectedResult, expectMostRecentItem()) - } + assertEquals(expectedResult, eventFlow.latestValue) } @Test @@ -522,13 +493,13 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.eventFlow.test { - val expectedResult = QrCodeUIEvent.QrImageDownloadResult.PermissionDenied + val eventFlow = delegate.eventFlow.test(testScheduler) - delegate.downloadQRImage(context) + val expectedResult = QrCodeUIEvent.QrImageDownloadResult.PermissionDenied - assertEquals(expectedResult, expectMostRecentItem()) - } + delegate.downloadQRImage(context) + + assertEquals(expectedResult, eventFlow.latestValue) } @Test @@ -539,13 +510,12 @@ internal class DefaultQRCodeDelegateTest( ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.eventFlow.test { - val expectedResult = QrCodeUIEvent.QrImageDownloadResult.Failure(throwable) + val eventFlow = delegate.eventFlow.test(testScheduler) + val expectedResult = QrCodeUIEvent.QrImageDownloadResult.Failure(throwable) - delegate.downloadQRImage(context) + delegate.downloadQRImage(context) - assertEquals(expectedResult, expectMostRecentItem()) - } + assertEquals(expectedResult, eventFlow.latestValue) } @Test @@ -553,13 +523,31 @@ internal class DefaultQRCodeDelegateTest( val requiredPermission = "Required Permission" val permissionCallback = mock() - delegate.permissionFlow.test { - delegate.requestPermission(context, requiredPermission, permissionCallback) + val permissionFlow = delegate.permissionFlow.test(testScheduler) + delegate.requestPermission(context, requiredPermission, permissionCallback) - val mostRecentValue = expectMostRecentItem() - assertEquals(requiredPermission, mostRecentValue.requiredPermission) - assertEquals(permissionCallback, mostRecentValue.permissionCallback) + val mostRecentValue = permissionFlow.latestValue + assertEquals(requiredPermission, mostRecentValue.requiredPermission) + assertEquals(permissionCallback, mostRecentValue.permissionCallback) + } + + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultQRCodeDelegate.ACTION_KEY, + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + ) } + delegate = createDelegate(savedStateHandle = savedStateHandle) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertTrue(detailsFlow.values.isNotEmpty()) } @Test @@ -595,14 +583,15 @@ internal class DefaultQRCodeDelegateTest( private fun createDelegate( observerRepository: ActionObserverRepository = mock(), componentParams: GenericComponentParams = mock(), - statusRepository: StatusRepository = mock(), + statusRepository: StatusRepository = this.statusRepository, statusCountDownTimer: QRCodeCountDownTimer = mock(), redirectHandler: RedirectHandler = mock(), paymentDataRepository: PaymentDataRepository = mock(), imageSaver: ImageSaver = mock(), + savedStateHandle: SavedStateHandle = SavedStateHandle() ) = DefaultQRCodeDelegate( observerRepository = observerRepository, - savedStateHandle = SavedStateHandle(), + savedStateHandle = savedStateHandle, componentParams = componentParams, statusRepository = statusRepository, statusCountDownTimer = statusCountDownTimer, From 189e0e44181524071cfccc4a77c8e643e88315d0 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 15 May 2024 15:27:55 +0200 Subject: [PATCH 121/272] Clear state when flow is final in qr code COAND-857 --- .../internal/ui/DefaultQRCodeDelegate.kt | 36 +++++++++++++------ .../internal/ui/DefaultQRCodeDelegateTest.kt | 32 +++++++++++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index f8a6123421..ed02a1f9fa 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -156,7 +156,7 @@ internal class DefaultQRCodeDelegate( override fun handleAction(action: Action, activity: Activity) { if (action !is QrCodeAction) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + emitError(ComponentException("Unsupported action")) return } @@ -181,7 +181,7 @@ internal class DefaultQRCodeDelegate( val paymentData = action.paymentData if (paymentData == null) { adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) + emitError(ComponentException("Payment data is null")) return } @@ -209,7 +209,7 @@ internal class DefaultQRCodeDelegate( adyenLog(AdyenLogLevel.DEBUG) { "makeRedirect - $url" } redirectHandler.launchUriRedirect(activity, url) } catch (ex: CheckoutException) { - exceptionChannel.trySend(ex) + emitError(ex) } } @@ -231,7 +231,7 @@ internal class DefaultQRCodeDelegate( }, onFailure = { adyenLog(AdyenLogLevel.ERROR, it) { "Error while polling status" } - exceptionChannel.trySend(ComponentException("Error while polling status", it)) + emitError(ComponentException("Error while polling status", it)) }, ) } @@ -270,9 +270,9 @@ internal class DefaultQRCodeDelegate( // Not authorized status should still call /details so that merchant can get more info if (StatusResponseUtils.isFinalResult(statusResponse) && !payload.isNullOrEmpty()) { val details = createDetails(payload) - detailsChannel.trySend(createActionComponentData(details)) + emitDetails(details) } else { - exceptionChannel.trySend(ComponentException("Payment was not completed. - " + statusResponse.resultCode)) + emitError(ComponentException("Payment was not completed. - " + statusResponse.resultCode)) } } @@ -288,9 +288,9 @@ internal class DefaultQRCodeDelegate( override fun handleIntent(intent: Intent) { try { val details = redirectHandler.parseRedirectResult(intent.data) - detailsChannel.trySend(createActionComponentData(details)) - } catch (ex: CheckoutException) { - exceptionChannel.trySend(ex) + emitDetails(details) + } catch (e: CheckoutException) { + emitError(e) } } @@ -306,13 +306,13 @@ internal class DefaultQRCodeDelegate( try { jsonObject.put(PAYLOAD_DETAILS_KEY, payload) } catch (e: JSONException) { - exceptionChannel.trySend(ComponentException("Failed to create details.", e)) + emitError(ComponentException("Failed to create details.", e)) } return jsonObject } override fun onError(e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } private fun createOutputData() = QRCodeOutputData( @@ -357,6 +357,20 @@ internal class DefaultQRCodeDelegate( redirectHandler.setOnRedirectListener(listener) } + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(details: JSONObject) { + detailsChannel.trySend(createActionComponentData(details)) + clearState() + } + + private fun clearState() { + action = null + } + override fun onCleared() { removeObserver() statusPollingJob?.cancel() diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 79d6648b6c..0ce2989a34 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -550,6 +550,38 @@ internal class DefaultQRCodeDelegateTest( assertTrue(detailsFlow.values.isNotEmpty()) } + @Test + fun `when details are emitted, then state is cleared`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultQRCodeDelegate.ACTION_KEY, + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + ) + } + delegate = createDelegate(savedStateHandle = savedStateHandle) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertNull(savedStateHandle[DefaultQRCodeDelegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultQRCodeDelegate.ACTION_KEY, + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = null), + ) + } + delegate = createDelegate(savedStateHandle = savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertNull(savedStateHandle[DefaultQRCodeDelegate.ACTION_KEY]) + } + @Test fun `when onCleared is called, observers are removed`() { val observerRepository = mock() From f197ee19db9099f3332fa1c9ff17ef80d5cd622f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 15 May 2024 15:48:59 +0200 Subject: [PATCH 122/272] Clear state when flow is final in voucher COAND-857 --- .../internal/ui/DefaultQRCodeDelegateTest.kt | 1 - .../internal/ui/DefaultVoucherDelegate.kt | 22 +++++-- .../internal/ui/DefaultVoucherDelegateTest.kt | 63 +++++++++++++++---- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 0ce2989a34..1208869157 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -206,7 +206,6 @@ internal class DefaultQRCodeDelegateTest( assertEquals(PaymentMethodTypes.PIX, paymentMethodType) assertEquals("qrData", qrCodeData) } - } @Test diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 6738faf276..d53f9145fa 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.voucher.internal.ui import android.app.Activity import android.content.Context import android.view.View +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.components.core.action.Action @@ -115,15 +116,13 @@ internal class DefaultVoucherDelegate( private fun handleAction(action: Action) { if (action !is VoucherAction) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + emitError(ComponentException("Unsupported action")) return } val config = VoucherPaymentMethodConfig.getByPaymentMethodType(action.paymentMethodType) if (config == null) { - exceptionChannel.trySend( - ComponentException("Payment method ${action.paymentMethodType} not supported for this action"), - ) + emitError(ComponentException("Payment method ${action.paymentMethodType} not supported for this action")) return } @@ -172,7 +171,7 @@ internal class DefaultVoucherDelegate( try { pdfOpener.open(context, downloadUrl) } catch (e: IllegalStateException) { - exceptionChannel.trySend(ComponentException(e.message ?: "", e.cause)) + emitError(ComponentException(e.message ?: "", e.cause)) } } @@ -206,6 +205,15 @@ internal class DefaultVoucherDelegate( permissionChannel.trySend(requestData) } + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun clearState() { + action = null + } + override fun onCleared() { removeObserver() _coroutineScope = null @@ -213,6 +221,8 @@ internal class DefaultVoucherDelegate( companion object { private const val IMAGE_NAME_FORMAT = "%s-%s.png" - private const val ACTION_KEY = "ACTION_KEY" + + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt index ca5ba69201..bd95833840 100644 --- a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt +++ b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt @@ -26,6 +26,7 @@ import com.adyen.checkout.components.core.internal.ui.model.GenericComponentPara import com.adyen.checkout.core.Environment import com.adyen.checkout.core.PermissionHandlerCallback import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.exception.PermissionRequestException import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.checkout.ui.core.internal.util.ImageSaver @@ -38,6 +39,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -68,17 +71,7 @@ internal class DefaultVoucherDelegateTest( @BeforeEach fun beforeEach() { - val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) { - voucher() - } - delegate = DefaultVoucherDelegate( - observerRepository = observerRepository, - savedStateHandle = SavedStateHandle(), - componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - pdfOpener = pdfOpener, - imageSaver = imageSaver, - ) + delegate = createDelegate() } @Test @@ -300,6 +293,37 @@ internal class DefaultVoucherDelegateTest( } } + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultVoucherDelegate.ACTION_KEY, + VoucherAction(paymentMethodType = PaymentMethodTypes.MULTIBANCO, paymentData = "paymentData"), + ) + } + delegate = createDelegate(savedStateHandle) + val viewFlow = delegate.viewFlow.test(testScheduler) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertNotNull(viewFlow.latestValue) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultVoucherDelegate.ACTION_KEY, + VoucherAction(paymentMethodType = "not a voucher", paymentData = "paymentData"), + ) + } + delegate = createDelegate(savedStateHandle) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertNull(savedStateHandle[DefaultVoucherDelegate.ACTION_KEY]) + } + @Test fun `when onCleared is called, observers are removed`() { delegate.onCleared() @@ -307,6 +331,23 @@ internal class DefaultVoucherDelegateTest( verify(observerRepository).removeObservers() } + private fun createDelegate( + savedStateHandle: SavedStateHandle = SavedStateHandle() + ): DefaultVoucherDelegate { + val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) { + voucher() + } + + return DefaultVoucherDelegate( + observerRepository = observerRepository, + savedStateHandle = savedStateHandle, + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + pdfOpener = pdfOpener, + imageSaver = imageSaver, + ) + } + private fun createTestAction( type: String = "test", paymentData: String = "paymentData", From ce910616ee22c064dfcb96c03cadb5e3c7a65c54 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 10:47:06 +0200 Subject: [PATCH 123/272] Make naming consistent between action delegates Not for 3ds2 and WeChat though, because they are too specific. COAND-857 --- .../await/internal/ui/DefaultAwaitDelegate.kt | 17 +++++++++-------- .../internal/ui/DefaultRedirectDelegate.kt | 4 ++-- .../internal/ui/DefaultVoucherDelegate.kt | 17 +++++++++-------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index b48901c463..8df3b643aa 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -76,7 +76,7 @@ internal class DefaultAwaitDelegate( private var statusPollingJob: Job? = null - private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + private var action: AwaitAction? by SavedStateHandleProperty(ACTION_KEY) override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope @@ -85,9 +85,9 @@ internal class DefaultAwaitDelegate( private fun restoreState() { adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } - val action: Action? = action + val action: AwaitAction? = action if (action != null) { - handleAction(action) + initState(action) } } @@ -114,16 +114,17 @@ internal class DefaultAwaitDelegate( } override fun handleAction(action: Action, activity: Activity) { - this.action = action - handleAction(action) - } - - private fun handleAction(action: Action) { if (action !is AwaitAction) { emitError(ComponentException("Unsupported action")) return } + this.action = action + + initState(action) + } + + private fun initState(action: AwaitAction) { val paymentData = action.paymentData paymentDataRepository.paymentData = paymentData if (paymentData == null) { 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 3ed7fa0af0..1e7c554bab 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 @@ -98,10 +98,10 @@ internal class DefaultRedirectDelegate( } } - makeRedirect(activity, action.url) + launchAction(activity, action.url) } - private fun makeRedirect(activity: Activity, url: String?) { + private fun launchAction(activity: Activity, url: String?) { try { adyenLog(AdyenLogLevel.DEBUG) { "makeRedirect - $url" } // TODO look into emitting a value to tell observers that a redirect was launched so they can track its diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index d53f9145fa..616af0409c 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -75,7 +75,7 @@ internal class DefaultVoucherDelegate( private var _coroutineScope: CoroutineScope? = null private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) - private var action: Action? by SavedStateHandleProperty(ACTION_KEY) + private var action: VoucherAction? by SavedStateHandleProperty(ACTION_KEY) override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope @@ -84,9 +84,9 @@ internal class DefaultVoucherDelegate( private fun restoreState() { adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } - val action: Action? = action + val action: VoucherAction? = action if (action != null) { - handleAction(action) + initState(action) } } @@ -110,16 +110,17 @@ internal class DefaultVoucherDelegate( } override fun handleAction(action: Action, activity: Activity) { - this.action = action - handleAction(action) - } - - private fun handleAction(action: Action) { if (action !is VoucherAction) { emitError(ComponentException("Unsupported action")) return } + this.action = action + + initState(action) + } + + private fun initState(action: VoucherAction) { val config = VoucherPaymentMethodConfig.getByPaymentMethodType(action.paymentMethodType) if (config == null) { emitError(ComponentException("Payment method ${action.paymentMethodType} not supported for this action")) From 62a123455870c092ba27ffaff9c65c7b2048a2e8 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 May 2024 14:20:46 +0200 Subject: [PATCH 124/272] Restore 3ds2 delegate after process dies COAND-857 --- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 87 +++++++++++-------- .../ui/DefaultAdyen3DS2DelegateTest.kt | 36 +++++++- .../internal/ui/DefaultAwaitDelegateTest.kt | 2 +- 3 files changed, 86 insertions(+), 39 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index d201334740..b0ba012fdd 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -97,6 +97,8 @@ internal class DefaultAdyen3DS2Delegate( private var authorizationToken: String? by SavedStateHandleProperty(AUTHORIZATION_TOKEN_KEY) + private var action: BaseThreeds2Action? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope } @@ -122,10 +124,12 @@ internal class DefaultAdyen3DS2Delegate( override fun handleAction(action: Action, activity: Activity) { if (action !is BaseThreeds2Action) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + emitError(ComponentException("Unsupported action")) return } + this.action = action + val paymentData = action.paymentData paymentDataRepository.paymentData = paymentData when (action) { @@ -140,7 +144,7 @@ internal class DefaultAdyen3DS2Delegate( activity: Activity, ) { if (action.token.isNullOrEmpty()) { - exceptionChannel.trySend(ComponentException("Fingerprint token not found.")) + emitError(ComponentException("Fingerprint token not found.")) return } identifyShopper( @@ -155,7 +159,7 @@ internal class DefaultAdyen3DS2Delegate( activity: Activity, ) { if (action.token.isNullOrEmpty()) { - exceptionChannel.trySend(ComponentException("Challenge token not found.")) + emitError(ComponentException("Challenge token not found.")) return } challengeShopper(activity, action.token.orEmpty()) @@ -166,11 +170,11 @@ internal class DefaultAdyen3DS2Delegate( activity: Activity, ) { if (action.token.isNullOrEmpty()) { - exceptionChannel.trySend(ComponentException("3DS2 token not found.")) + emitError(ComponentException("3DS2 token not found.")) return } if (action.subtype == null) { - exceptionChannel.trySend(ComponentException("3DS2 Action subtype not found.")) + emitError(ComponentException("3DS2 Action subtype not found.")) return } val subtype = Threeds2Action.SubType.parse(action.subtype.orEmpty()) @@ -208,18 +212,18 @@ internal class DefaultAdyen3DS2Delegate( val fingerprintToken = try { decodeFingerprintToken(encodedFingerprintToken) } catch (e: CheckoutException) { - exceptionChannel.trySend(ComponentException("Failed to decode fingerprint token", e)) + emitError(ComponentException("Failed to decode fingerprint token", e)) return } val configParameters = createAdyenConfigParameters(fingerprintToken) ?: run { - exceptionChannel.trySend(ComponentException("Failed to create ConfigParameters.")) + emitError(ComponentException("Failed to create ConfigParameters.")) return } val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> adyenLog(AdyenLogLevel.ERROR, throwable) { "Unexpected uncaught 3DS2 Exception" } - exceptionChannel.trySend(CheckoutException("Unexpected 3DS2 exception.", throwable)) + emitError(CheckoutException("Unexpected 3DS2 exception.", throwable)) } coroutineScope.launch(coroutineDispatcher + coroutineExceptionHandler) { @@ -240,7 +244,7 @@ internal class DefaultAdyen3DS2Delegate( val authenticationRequestParameters = currentTransaction?.authenticationRequestParameters if (authenticationRequestParameters == null) { - exceptionChannel.trySend(ComponentException("Failed to retrieve 3DS2 authentication parameters")) + emitError(ComponentException("Failed to retrieve 3DS2 authentication parameters")) return@launch } val encodedFingerprint = createEncodedFingerprint(authenticationRequestParameters) @@ -292,7 +296,7 @@ internal class DefaultAdyen3DS2Delegate( private fun createTransaction(fingerprintToken: FingerprintToken): Transaction? { if (fingerprintToken.threeDSMessageVersion == null) { val error = "Failed to create 3DS2 Transaction. Missing threeDSMessageVersion inside fingerprintToken." - exceptionChannel.trySend(ComponentException(error)) + emitError(ComponentException(error)) return null } @@ -308,10 +312,10 @@ internal class DefaultAdyen3DS2Delegate( is TransactionResult.Success -> result.transaction } } catch (e: SDKNotInitializedException) { - exceptionChannel.trySend(ComponentException("Failed to create 3DS2 Transaction", e)) + emitError(ComponentException("Failed to create 3DS2 Transaction", e)) null } catch (e: SDKRuntimeException) { - exceptionChannel.trySend(ComponentException("Failed to create 3DS2 Transaction", e)) + emitError(ComponentException("Failed to create 3DS2 Transaction", e)) null } } @@ -348,7 +352,7 @@ internal class DefaultAdyen3DS2Delegate( ) .fold( onSuccess = { result -> onSubmitFingerprintResult(result, activity) }, - onFailure = { e -> exceptionChannel.trySend(ComponentException("Unable to submit fingerprint", e)) }, + onFailure = { e -> emitError(ComponentException("Unable to submit fingerprint", e)) }, ) } @@ -373,21 +377,13 @@ internal class DefaultAdyen3DS2Delegate( } } - private fun emitDetails(details: JSONObject) { - val actionComponentData = ActionComponentData( - details = details, - paymentData = paymentDataRepository.paymentData, - ) - detailsChannel.trySend(actionComponentData) - } - private fun makeRedirect(activity: Activity, action: RedirectAction) { val url = action.url try { adyenLog(AdyenLogLevel.DEBUG) { "makeRedirect - $url" } redirectHandler.launchUriRedirect(activity, url) } catch (e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } } @@ -397,7 +393,7 @@ internal class DefaultAdyen3DS2Delegate( adyenLog(AdyenLogLevel.DEBUG) { "challengeShopper" } if (currentTransaction == null) { - exceptionChannel.trySend( + emitError( Authentication3DS2Exception("Failed to make challenge, missing reference to initial transaction."), ) return @@ -407,7 +403,7 @@ internal class DefaultAdyen3DS2Delegate( val challengeTokenJson: JSONObject = try { JSONObject(decodedChallengeToken) } catch (e: JSONException) { - exceptionChannel.trySend(ComponentException("JSON parsing of FingerprintToken failed", e)) + emitError(ComponentException("JSON parsing of FingerprintToken failed", e)) return } @@ -416,7 +412,7 @@ internal class DefaultAdyen3DS2Delegate( try { currentTransaction?.doChallenge(activity, challengeParameters, this, DEFAULT_CHALLENGE_TIME_OUT) } catch (e: InvalidInputException) { - exceptionChannel.trySend(CheckoutException("Error starting challenge", e)) + emitError(CheckoutException("Error starting challenge", e)) } } @@ -439,7 +435,7 @@ internal class DefaultAdyen3DS2Delegate( val parsedResult = redirectHandler.parseRedirectResult(intent.data) emitDetails(parsedResult) } catch (e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } } @@ -449,7 +445,7 @@ internal class DefaultAdyen3DS2Delegate( val details = makeDetails(transactionStatus) emitDetails(details) } catch (e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } finally { closeTransaction() } @@ -457,7 +453,7 @@ internal class DefaultAdyen3DS2Delegate( private fun onCancelled() { adyenLog(AdyenLogLevel.DEBUG) { "challenge cancelled" } - exceptionChannel.trySend(Cancelled3DS2Exception("Challenge canceled.")) + emitError(Cancelled3DS2Exception("Challenge canceled.")) closeTransaction() } @@ -467,7 +463,7 @@ internal class DefaultAdyen3DS2Delegate( val details = makeDetails(result.transactionStatus, result.additionalDetails) emitDetails(details) } catch (e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } finally { closeTransaction() } @@ -479,7 +475,7 @@ internal class DefaultAdyen3DS2Delegate( val details = makeDetails(result.transactionStatus, result.additionalDetails) emitDetails(details) } catch (e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } finally { closeTransaction() } @@ -510,17 +506,25 @@ internal class DefaultAdyen3DS2Delegate( } override fun onError(e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } override fun setOnRedirectListener(listener: () -> Unit) { redirectHandler.setOnRedirectListener(listener) } - override fun onCleared() { - removeObserver() - _coroutineScope = null - redirectHandler.removeOnRedirectListener() + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(details: JSONObject) { + val actionComponentData = ActionComponentData( + details = details, + paymentData = paymentDataRepository.paymentData, + ) + detailsChannel.trySend(actionComponentData) + clearState() } private fun makeDetails(transactionStatus: String, errorDetails: String? = null): JSONObject { @@ -540,9 +544,22 @@ internal class DefaultAdyen3DS2Delegate( } } + private fun clearState() { + action = null + } + + override fun onCleared() { + removeObserver() + _coroutineScope = null + redirectHandler.removeOnRedirectListener() + } + companion object { private const val AUTHORIZATION_TOKEN_KEY = "authorization_token" private const val DEFAULT_CHALLENGE_TIME_OUT = 10 private const val PROTOCOL_VERSION_2_1_0 = "2.1.0" + + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt index 2082c93ed7..e1cf4e4747 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt @@ -32,6 +32,7 @@ import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.test.LoggingExtension import com.adyen.checkout.test.TestDispatcherExtension import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.test.TestRedirectHandler @@ -58,6 +59,7 @@ import org.json.JSONException import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -78,7 +80,7 @@ import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi @OptIn(ExperimentalCoroutinesApi::class) -@ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) +@ExtendWith(LoggingExtension::class, MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultAdyen3DS2DelegateTest( @Mock private val submitFingerprintRepository: SubmitFingerprintRepository, ) { @@ -97,12 +99,13 @@ internal class DefaultAdyen3DS2DelegateTest( } private fun createDelegate( - adyen3DS2Serializer: Adyen3DS2Serializer = Adyen3DS2Serializer() + adyen3DS2Serializer: Adyen3DS2Serializer = Adyen3DS2Serializer(), + savedStateHandle: SavedStateHandle = SavedStateHandle(), ): DefaultAdyen3DS2Delegate { val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) return DefaultAdyen3DS2Delegate( observerRepository = ActionObserverRepository(), - savedStateHandle = SavedStateHandle(), + savedStateHandle = savedStateHandle, componentParams = Adyen3DS2ComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null) // Set it to null to avoid a crash in 3DS2 library (they use Android APIs) @@ -521,6 +524,33 @@ internal class DefaultAdyen3DS2DelegateTest( } } + @Test + fun `when details are emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultAdyen3DS2Delegate.ACTION_KEY, + Threeds2Action(paymentMethodType = "test", paymentData = "paymentData"), + ) + } + delegate = createDelegate(savedStateHandle = savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.onCompletion(ChallengeResult.Completed("test")) + + assertNull(savedStateHandle[DefaultAdyen3DS2Delegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle = savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleAction(Threeds2Action(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertNull(savedStateHandle[DefaultAdyen3DS2Delegate.ACTION_KEY]) + } + private class TestAuthenticationRequestParameters( private val deviceData: String? = null, private val sdkTransactionID: String? = null, diff --git a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt index 193c5ae698..3385429260 100644 --- a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt +++ b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt @@ -181,7 +181,7 @@ internal class DefaultAwaitDelegateTest { delegate = createDelegate(savedStateHandle) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = null), Activity()) assertNull(savedStateHandle[DefaultAwaitDelegate.ACTION_KEY]) } From 59bdcf14239d89274f52d8db4f94a56c62d9fd31 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 May 2024 14:50:58 +0200 Subject: [PATCH 125/272] Make sure 3DS2s ChallengeStatusHandler survives process death This makes sure the final result from the 3DS2 SDK will be correctly delivered to the newly created delegate. COAND-857 --- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 9 +++- .../ui/SharedChallengeStatusHandler.kt | 40 ++++++++++++++++ .../ui/SharedChallengeStatusHandlerTest.kt | 47 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt create mode 100644 3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandlerTest.kt diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index b0ba012fdd..47fb14a4b8 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -101,6 +101,7 @@ internal class DefaultAdyen3DS2Delegate( override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + SharedChallengeStatusHandler.onCompletionListener = this } override fun observe( @@ -410,7 +411,12 @@ internal class DefaultAdyen3DS2Delegate( val challengeToken = ChallengeToken.SERIALIZER.deserialize(challengeTokenJson) val challengeParameters = createChallengeParameters(challengeToken) try { - currentTransaction?.doChallenge(activity, challengeParameters, this, DEFAULT_CHALLENGE_TIME_OUT) + currentTransaction?.doChallenge( + activity, + challengeParameters, + SharedChallengeStatusHandler, + DEFAULT_CHALLENGE_TIME_OUT, + ) } catch (e: InvalidInputException) { emitError(CheckoutException("Error starting challenge", e)) } @@ -550,6 +556,7 @@ internal class DefaultAdyen3DS2Delegate( override fun onCleared() { removeObserver() + SharedChallengeStatusHandler.onCompletionListener = null _coroutineScope = null redirectHandler.removeOnRedirectListener() } diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt new file mode 100644 index 0000000000..755db11e24 --- /dev/null +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt @@ -0,0 +1,40 @@ +/* + * 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 21/5/2024. + */ + +package com.adyen.checkout.adyen3ds2.internal.ui + +import androidx.annotation.VisibleForTesting +import com.adyen.threeds2.ChallengeResult +import com.adyen.threeds2.ChallengeStatusHandler + +internal object SharedChallengeStatusHandler : ChallengeStatusHandler { + + var onCompletionListener: ChallengeStatusHandler? = null + set(value) { + field = value + resultQueue?.let { onCompletion(it) } + } + + private var resultQueue: ChallengeResult? = null + + override fun onCompletion(result: ChallengeResult) { + onCompletionListener + ?.onCompletion(result) + ?.also { + resultQueue = null + } ?: run { + resultQueue = result + } + } + + @VisibleForTesting + internal fun reset() { + onCompletionListener = null + resultQueue = null + } +} diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandlerTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandlerTest.kt new file mode 100644 index 0000000000..73c6d5e0d2 --- /dev/null +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandlerTest.kt @@ -0,0 +1,47 @@ +package com.adyen.checkout.adyen3ds2.internal.ui + +import com.adyen.threeds2.ChallengeResult +import com.adyen.threeds2.ChallengeStatusHandler +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class SharedChallengeStatusHandlerTest { + + @BeforeEach + fun beforeEach() { + SharedChallengeStatusHandler.reset() + } + + @Test + fun `when onCompletion is triggered, then listener is called`() { + val onCompletionListener = TestOnCompletionListener() + SharedChallengeStatusHandler.onCompletionListener = onCompletionListener + + SharedChallengeStatusHandler.onCompletion(ChallengeResult.Completed("test")) + + onCompletionListener.assertOnCompletionCalled() + } + + @Test + fun `when onCompletion is triggered and no listener is set, then onCompletion is queued until a listener is set`() { + val onCompletionListener = TestOnCompletionListener() + SharedChallengeStatusHandler.onCompletion(ChallengeResult.Completed("test")) + + SharedChallengeStatusHandler.onCompletionListener = onCompletionListener + + onCompletionListener.assertOnCompletionCalled() + } + + private class TestOnCompletionListener : ChallengeStatusHandler { + + private var timesOnCompletionCalled = 0 + + override fun onCompletion(result: ChallengeResult) { + timesOnCompletionCalled++ + } + + fun assertOnCompletionCalled() { + assert(timesOnCompletionCalled > 0) + } + } +} From 7442464978d22b20e4acb31d773eb66a1e454770 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 11:31:06 +0200 Subject: [PATCH 126/272] Don't save authorization token anymore The action is saved now, which holds the authorization token. COAND-857 --- .../adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt | 7 +------ .../internal/ui/SharedChallengeStatusHandler.kt | 10 +++++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index 47fb14a4b8..28135fc16b 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -95,8 +95,6 @@ internal class DefaultAdyen3DS2Delegate( private var currentTransaction: Transaction? = null - private var authorizationToken: String? by SavedStateHandleProperty(AUTHORIZATION_TOKEN_KEY) - private var action: BaseThreeds2Action? by SavedStateHandleProperty(ACTION_KEY) override fun initialize(coroutineScope: CoroutineScope) { @@ -179,8 +177,6 @@ internal class DefaultAdyen3DS2Delegate( return } val subtype = Threeds2Action.SubType.parse(action.subtype.orEmpty()) - // We need to keep authorizationToken in memory to access it later when the 3DS2 challenge is done - authorizationToken = action.authorisationToken handleActionSubtype(activity, subtype, action.token.orEmpty()) } @@ -535,7 +531,7 @@ internal class DefaultAdyen3DS2Delegate( private fun makeDetails(transactionStatus: String, errorDetails: String? = null): JSONObject { // Check whether authorizationToken was set and create the corresponding details object - val token = authorizationToken + val token = (action as? Threeds2Action)?.authorisationToken return if (token == null) { adyen3DS2Serializer.createChallengeDetails( transactionStatus = transactionStatus, @@ -562,7 +558,6 @@ internal class DefaultAdyen3DS2Delegate( } companion object { - private const val AUTHORIZATION_TOKEN_KEY = "authorization_token" private const val DEFAULT_CHALLENGE_TIME_OUT = 10 private const val PROTOCOL_VERSION_2_1_0 = "2.1.0" diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt index 755db11e24..da08169cba 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/SharedChallengeStatusHandler.kt @@ -17,24 +17,24 @@ internal object SharedChallengeStatusHandler : ChallengeStatusHandler { var onCompletionListener: ChallengeStatusHandler? = null set(value) { field = value - resultQueue?.let { onCompletion(it) } + queuedResult?.let { onCompletion(it) } } - private var resultQueue: ChallengeResult? = null + private var queuedResult: ChallengeResult? = null override fun onCompletion(result: ChallengeResult) { onCompletionListener ?.onCompletion(result) ?.also { - resultQueue = null + queuedResult = null } ?: run { - resultQueue = result + queuedResult = result } } @VisibleForTesting internal fun reset() { onCompletionListener = null - resultQueue = null + queuedResult = null } } From e72558be5cf6c58b5817f168a9141a2ed68187f8 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 23 May 2024 15:28:43 +0200 Subject: [PATCH 127/272] Update sonar --- dependencies.gradle | 2 +- gradle/verification-metadata.xml | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 0ff97b11a0..56962668d7 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -31,7 +31,7 @@ ext { detekt_version = "1.23.6" jacoco_version = '0.8.12' ktlint_version = '1.2.1' - sonarqube_version = '4.4.1.3373' + sonarqube_version = '5.0.0.4638' // Android Dependencies annotation_version = "1.7.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index cf135a92b5..625c02a654 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9730,6 +9730,11 @@ + + + + + @@ -9756,6 +9761,14 @@ + + + + + + + + From f7700739633dadc7378a003fb13663fd422d6a80 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 23 May 2024 15:43:49 +0200 Subject: [PATCH 128/272] Split sonar command This makes sure sonar runs after the other tasks are completed --- .github/workflows/sonar_cloud.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonar_cloud.yml b/.github/workflows/sonar_cloud.yml index 9974c6f56c..9d744b7798 100644 --- a/.github/workflows/sonar_cloud.yml +++ b/.github/workflows/sonar_cloud.yml @@ -35,4 +35,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./gradlew detekt assDeb jacocoDebugTestReport lintDeb sonar -Dsonar.gradle.skipCompile=true -Pstrip-resources=true + run: | + ./gradlew detekt assDeb jacocoDebugTestReport lintDeb -Pstrip-resources=true + ./gradlew sonar From f98fdbca7b485ff8c4b14fb5d416c40596aaf4bc Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 18 Oct 2023 16:55:16 +0200 Subject: [PATCH 129/272] Create twint module COAND-806 --- settings.gradle | 1 + twint/build.gradle | 53 +++++++++++++++++++ twint/consumer-rules.pro | 0 twint/src/main/AndroidManifest.xml | 1 + .../adyen/checkout/twint/TwintComponent.kt | 14 +++++ 5 files changed, 69 insertions(+) create mode 100644 twint/build.gradle create mode 100644 twint/consumer-rules.pro create mode 100644 twint/src/main/AndroidManifest.xml create mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt diff --git a/settings.gradle b/settings.gradle index d7905d565b..3af0126e34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,6 +62,7 @@ include ':3ds2', ':sepa', ':sessions-core', ':test-core', + ':twint', ':ui-core', ':upi', ':voucher', diff --git a/twint/build.gradle b/twint/build.gradle new file mode 100644 index 0000000000..3e1fc529c3 --- /dev/null +++ b/twint/build.gradle @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 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 18/10/2023. + */ + +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-parcelize' +} + +ext.mavenArtifactId = "twint" +ext.mavenArtifactName = "Adyen checkout Twint component" +ext.mavenArtifactDescription = "Adyen checkout Twint component client for Adyen's Checkout API." + +apply from: "${rootDir}/config/gradle/sharedTasks.gradle" + +android { + namespace 'com.adyen.checkout.twint' + compileSdkVersion compile_sdk_version + + defaultConfig { + minSdkVersion min_sdk_version + targetSdkVersion target_sdk_version + versionCode version_code + versionName version_name + + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + consumerProguardFiles "consumer-rules.pro" + } + + buildFeatures { + viewBinding true + } +} + +dependencies { + // Checkout + api project(':ui-core') + api project(':sessions-core') + + // Dependencies + implementation libraries.material + + //Tests + testImplementation project(':test-core') + testImplementation testLibraries.junit5 + testImplementation testLibraries.kotlinCoroutines + testImplementation testLibraries.mockito +} diff --git a/twint/consumer-rules.pro b/twint/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/twint/src/main/AndroidManifest.xml b/twint/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..cc947c5679 --- /dev/null +++ b/twint/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt new file mode 100644 index 0000000000..501ec57921 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 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 18/10/2023. + */ + +package com.adyen.checkout.twint + +import androidx.lifecycle.ViewModel + +class TwintComponent : ViewModel() { +} From 809c282a47d29c76cbfd1ebd05c2fec1b729d480 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 19 Oct 2023 16:51:42 +0200 Subject: [PATCH 130/272] Setup basic component + delegate COAND-806 --- .../adyen/checkout/twint/TwintComponent.kt | 40 ++++++++++++++++++- .../checkout/twint/TwintComponentState.kt | 19 +++++++++ .../twint/internal/ui/TwintDelegate.kt | 14 +++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt index 501ec57921..421560674f 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt @@ -8,7 +8,45 @@ package com.adyen.checkout.twint +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.adyen.checkout.components.core.internal.PaymentComponent +import com.adyen.checkout.components.core.internal.PaymentComponentEvent +import com.adyen.checkout.components.core.internal.ui.ComponentDelegate +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.twint.internal.ui.TwintDelegate -class TwintComponent : ViewModel() { +class TwintComponent internal constructor( + private val twintDelegate: TwintDelegate, +) : ViewModel(), + PaymentComponent { + + override val delegate: ComponentDelegate get() = twintDelegate + + init { + twintDelegate.initialize(viewModelScope) + } + + internal fun observe( + lifecycleOwner: LifecycleOwner, + callback: (PaymentComponentEvent) -> Unit + ) { + twintDelegate.observe(lifecycleOwner, viewModelScope, callback) + } + + internal fun removeObserver() { + twintDelegate.removeObserver() + } + + override fun setInteractionBlocked(isInteractionBlocked: Boolean) { + adyenLog(AdyenLogLevel.WARN) { "Interaction with TwintComponent can't be blocked" } + } + + override fun onCleared() { + adyenLog(AdyenLogLevel.DEBUG) { "onCleared" } + super.onCleared() + twintDelegate.onCleared() + } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt new file mode 100644 index 0000000000..ffea7e688b --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 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 18/10/2023. + */ + +package com.adyen.checkout.twint + +import com.adyen.checkout.components.core.PaymentComponentData +import com.adyen.checkout.components.core.PaymentComponentState +import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod + +data class TwintComponentState( + override val data: PaymentComponentData, + override val isInputValid: Boolean, + override val isReady: Boolean, +) : PaymentComponentState diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt new file mode 100644 index 0000000000..1c48d871a7 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023 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 18/10/2023. + */ + +package com.adyen.checkout.twint.internal.ui + +import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate +import com.adyen.checkout.twint.TwintComponentState + +internal interface TwintDelegate : PaymentComponentDelegate From 33898ef6aaba80fe52318473b9f27885fddbfce3 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 19 Oct 2023 16:56:22 +0200 Subject: [PATCH 131/272] Add Twint library COAND-806 --- twint/build.gradle | 1 + twint/libs/TwintSdk-android-7.0.0.aar | Bin 0 -> 29730 bytes 2 files changed, 1 insertion(+) create mode 100644 twint/libs/TwintSdk-android-7.0.0.aar diff --git a/twint/build.gradle b/twint/build.gradle index 3e1fc529c3..43cda7d359 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -44,6 +44,7 @@ dependencies { // Dependencies implementation libraries.material + implementation files('./libs/TwintSdk-android-7.0.0.aar') //Tests testImplementation project(':test-core') diff --git a/twint/libs/TwintSdk-android-7.0.0.aar b/twint/libs/TwintSdk-android-7.0.0.aar new file mode 100644 index 0000000000000000000000000000000000000000..e6dbc1b21f469e53122334d9a90b2863d5c8a160 GIT binary patch literal 29730 zcmV)JK)b(CO9KQH000OG0000%0Ps?fKOrsv03$E~00jU508%b=cy#T3TXWnvlJ5KY z75)M9pek9i{3uIy+Yy$nwbbpIII&T|B2iTwvq(0{lC9a;|9+6T5kTTf^~TP_nTTnZ zi0{KSkw_$xVBJoyincz!Dz@cjS5#zEx3A8Ao&BF0C3L3=r|gIBr{ryRLwDS4yJ_l< z7}XbL^}bXnCCmqeGlQw?nyL1-QzfI8 zZl`ur9!U9P+x?<2{+m=A__J`_ih< zIk0~>eK8#tXOUF@p{VK(c-?NdIZi{fsk#Ydn+jzXjY{fXboWvoYi$y_T~yU}Sxqr5 zRMFi~?2mK~&{;c9Y_^bJ(@@aaL)C?)VT!36Wp1z0YONhUEk|!PbNR&M0IuR&HQ%g`rGcOAMm+&HWs%z<&Lx` zsQ7i?lVT{^(#e;oo381c1ANz2Bs{u+xB;P#v_;`=Ix3ItbW6r^sC#n;I^efM@w3C$li2_gs&uvF7TsvTK^6kFU)V=g1yg5bV))SrXKGNSGI^4^2mf z*4BL-#QOeAH_+j7BtvzFuDJ%4UrhFF)gd42{h^`%chy8dyrsOSO4HS}`+-{0!?Qo- zmJYBZ_2Fkdm50Z}>9}>5c|6vWtA}G_!_q&sRHeSRwVsvuy`B!&Y!w~-xX)yVx@%7T z=IYHknd;9WmJGG{LKVQiaJPmIiqE7ePvg`bXR=GEE3zw2&Ga89GCY4oQ?TM|dNalN%=dZn^i!0Z5N<9474fVfh%Watu$(~BE zv_GhgQJ07)thIN{Ddf^p5<#URlnlZUN@wB_0$?jZrGPAis91mMDOi1~)UNksONMJ^ z3SsqGi9Ofndh}^nfK&pa1O!y;M}j8>sp<)|wNH{|x?>9_lN?o`n};XItRctlpEYmu z`xd&uF6>udwAHmRkxg^mZP@SXVWf2*NmIA9Lur?~8oBH25rQWlieue8A)o3Cw_K51=|?(NUZa%mzv(7s zIMyu{4LU%3y3-h)IBMJ$jhqoTbbi|9jGv*~97?{XqwUnl4QMp$QK8Qwu8mz2sd4lr zaGY4Ge&{Z@E;SF0XP9QF0^FUzpWl#c<`1+Lg@f=w+ZlojH{tcX*~J*2{RwHl=invq zC}Kzs`&*h673~3iE(Tj0c#w|W1VRc)L6(^hh^;Rl(qW1Dpbo2yj#G!9c&*H1@rzG7 zS4SR6F`fnjb{*?HkfFM%Y*2f$o`5I{Y4Zq3t1P|P7G*|a4LGM^ebds~l8UQ9kGQp` zBN@DomyWKcbaQmwmIr(K^}P4_wExmMT}Zu0K6>xSi4?jdf9W3U$*ye=dt{&0!4hF+ zfequ(ZASX{y0YCC?AX)ezw15D{ysQUd+-GfPi$-X&=)1rs=d(S;FEA^T`k>_{hzRQ z`+DO~_ZERYzNbIQV2>6%Uh=L!1KZEKD`KX3^tB0csF#uXVxh`d%6Xdaxkt7~LVma_ zJTgZ$Scaja9v|wR@^4vqI!UJTG~E@{H2X&WJk^7}s8Gny>yd9$af(=OaJE0eWOh8o zGh}2dT3GRneeE!>;3?fV%JUf@yLyhJoGA@8?FZUR*7Kos?O4W%F1edxOI6IKaOjuu zw1v6IwlpZ^=sy^ssgt8y#M996k&bcfT=ZmD^G|zN9O$@y>wY?Wow-Qv>eiupc$!lM z@FZ7(+~pEz&;)5sb5r!ABSDeavUV?(Ul1wGofOOip_8>jz9bJAYXcLgst2b4wxhv> z@*|yZWTY$bgHss%c*lgYz);aZUopAS%te2XF(fbh_rcNj&s|xdy5I$9I@INl7OA2M zJaHf!PMPAVXl+oBu_ZCp+_&p~>d2}N`GO}0-i^b<0gq_T`?~ceytAT(tYl=4XlV|U zL`!|tgN&>+djMkECZuXYdIo})w#6{J3B4<}KzdT>M(IO05UxU9br6`LIC@XZ1qLrp z6J7?`BZr{K$BxmTN@_}wU*^*%y4HVccMV-VT<54yY~e$8!cS(RUCR)LVB%M1^gjDB zP}KnFL;Z_Xd@FHR{L}7NjH%lIhAkH1Y&gW1i(GKLc_@G{2ip?g(otvebNtMDLuG73 zj$2Y~sI4?&7#)mVz2D5y8K4mDb%~#~0DL}l+>}LmAe#eC-*8*38#b_0AE<8KRJuCK zr<*4y8Xtn!;4c<4Ck?#~sANTs&|+Bji`GjV--MtQT}6it$ovKjrqj6DpNgSMM|#`I zx@D&{K(t@IcwYfMKQQ9GQPm^Z(VMPqo})!2Fp~<+DkqvX-)d}5L&FGh#HKY*!u&wk zCyvB5sy33#u?H#)uC&Jc;J)U=3cXR0orY800#%l4(@KUD08a-_e5#Tk8)k}l5quBg zO=gDi#$1C0hMnAo`^JJe`=hOfuC9L3nLLoqQ1?9*&quKJr~Z=g1EA&>SK22M>z=v zW#k{smOzApSSmw#I&rQH6YH2-Kec?@3%)B;KiHMEO58Rlve^&hd2@Ak76-je0$q4P z6(K#X9{siU=QM*Z{2*4+Dw2L4N%vQjC0|D9fiC0ek^FIQw%t_kz_kfhuP}bO;O**W zLJN`sXKy}i_Zu^&i2o(+5D=W87`cHi5t7K1+>$bKFI0hC(^oc^xfhZlhP?sj znOVi6J*~~M2pXfd#z-8yzXT3#&0*6PPhpM}H6A39Sdb)%lei7h7Q#H{2)?vLu8RFh z#4~mJB*Mc~iD}$Uga|EYBBJKBapd9G6#Z*s#jOPKQ?X^7pT@a?n~wqUf_WSbYr`u6+6+>NSm{xi%BA#9_QJW7wNW9z!Ay_bvT0wibfx_{!y* zQU;Q{21Sb-J#;FCFnB5h?l+F)lT57?-rlhpX)dc(UBD{~)nHy# z-QG>9mrhlUN+E)?+n)PTlM4WwZN2v;9jR!Zj=t2<2u08m`3|DD&eLZA_s`e=0^_TC z-mF4Zw6N<$tm*a(V#}hBnbR{^yxNgkX9-WNwuGPO!C$Ar52UCdnm7#|)fh9Bi%BY$ z&gBCOP01i6A){JIgPyifhh#8cAJy_D;st^%lac}T@`Jgg?&i=DuA54%3s>CS??0PMZu`MUWQw#HdA9{Q zRGX(r0$Aa+3JDM~RK`g?M@y%tv7`22H2vysxdb`~OD59!RMw-66Bf4@b-Nwa`)Q2n zKxgEkYwU(gxa~>wz@9~+ROLcHol0AN5zqq3*AbboX}cNW8WdV4HH+DOPUhVhCR+1- z!j8jn4DTjF$c7fj6bJKbzH)Kw?x`49ySnO3J6%gauPy>;5q1@?vO0T#JZa}^_iG?5 zzica>r-_Sn1G>R0*bNvR>IQklVPt-`$|S7TZbC`LRg<|Iu8nEoV0oW}ckljVgvgwb z)>ZOckd7&JDvsk?4gBoG2{ zL2m4ezK-@FCG9(d5G#XiT*|J07I_~d*>n79N~sFN2K@Ricn|ki<W4-;g@Jcnc>-$~DiMG+UAvcta|Eix z0=hOyR>`$m_O}n$0=JZX$^xm-_{U3F_i9!z1*>mHI?Rhq5)UbCx$byF1kh zHEw|B6-W0JyK^vU$zbim?3Cj8HcM*l7)IMJrRCekvu%y*rvur#Jb`_Y7B4t8QxU+ImapZ3i=e6D+{3$tGGI3g4vmddYKYJBp`@|o3LeH3 zn6OlrK2d^thC#w9wS!kVJqDhMG3~m#VlTyvW4*1LdU{r&&rQu=1ft7m(fZ;1UOUM; zO9ifyf$3SM=ebTso+l$;CnGPCk(bHHH_6DWWaQgaWO8?|lLvF1JeceB(vt^sojjQ9 zcSsNL>vw0*7UoWgF(b3A8m2S1Iw4B=KL zP}l~4;FA>jMFsrc(fn!LFSje^8hW^h*y>ICxk9Pq9GWc z+fsk9Z94N(c6c`5KdnEOmNj}tD)Z6MwcU|e9qOpDc!UdLa{@m>K>m)qJfQB2!74!R zj#MAwfiPmb^#tbY|I@e)6P~imMP-IH8C^_EQ0 zgnvQCA;)kxrT({ZC#CqCe}Be!YR{Is-F3#_Q$dY6{RxCrt?UQq=7Z6^v_m{&&S(AM zk*Ytd{3E}p(dg>7Yl-={u^C`b^lbpP=xvA=K#+oQD=NAzGyXKP;v$ITRK++#&;{J{ z?@T=4i;-zE+Y%ZNRFDFG3h@Z~)3F=~X{{$+)2J)i;AXEt^6x!Pgv-^8r`(~iEh=+N z)$_si6wMluAQa~0N@G)!?MNJn`_99nvW|L7B(bfUO40*ULFk|C zP7+ta8PEEnG-Ye-NeSnWWVfUFjnbvtc(ek$VQu3!6QBu&9+4uR)vf)cvs4mXr$!Z5 zVO+E(o(tHLma~(bN?6tLtvw|Y+(>=ov|4LIB=KrRN=_*au_pW z&2)hqNJ;7^qL!Hq=&`1o=jpj8s4;wEQUVJRWhY|BW?jPArFfO&9#8?$PLhz8hxAj+ zU7ytk&Ryv0!veWls(w-ZO{i=os=4Se7GycjzNt-FG|g7OVFU2i9D={s_JapD4e*Vj zRx&bWruu08jR5q4xI*zucY}-y@p7B+*cd5Yk94?lCz=myx~Lnunq$DHEgfsxp>$3^ zL&zOfxn}RrAhC|A100BbhRY+Vj09L?@kFs)RtB=CBR4#U@az|urwt{+{UZ)6okQR- zC&%syV%wCR;?hGuhoZ+8Dg6_qOax}g8=t@o6L($To`w|k1E@|HwuDR68Wr^mHjU*1 zS4f_taB9Kz&X0!h2$;Ah32b`P-A1h=h@Ne=w*-JQ^j*X5HT8PLwfUhFbf?#g@>wP1 zLwtj&O zSwGDsqn))MYFM&B5+lbNKG+roZ20x$ z&${3TNcI~jIzpVTinl4}t{*pR*!TuGz6oRQ5kf*FmMJ?)cnWz2cB11TFabh~I&NvM zxIykiEl~yiUAiFN;e80=dw7%2K5KNq9Z|A7vlAKon(Y|n)c4eu zkEBnz~AHC54hprG)e-Sp_YjpSXU}tcq|NFA4UQuS`#7&Joj?4 z-)yHQM%~3Miyr({8{ISGN_LW2!;6LvirAr3y|pFI8uHLp9x9%Mr3N;Y;#4$qL8pRu z)E46}dZgj$bShG@EQ~NI8vxwc*n}0%p^&ZXdXSCfj6DN zaXf#VLtvhj3<7gPIq?vh%%^8BzStzfmEneoY$Ap!iUN<}>w!AFVZ>BjM{wo0KN` zfzEbucxvPPXkQDxj6gHx6*1sVV!oaEQt@!J5itxm?_6Jo-Ms6n=T$aioyRa;;whAy z!k%ey&MkkPKCd-`l-D3ZI$uJ1okjX6-ucfWed0H}W^v5VWhFC~mBmhUzSq^BOdIuy zi6vEVXJme3BPF9d^P-G&%A)q)>8GbD1pUJui-{ZbiID6Z9`othL%|q}y7hry3bBN< zsJ#5%;7WZ_S^nk5%MtTJeeEG%?obYkM97=VqMksxlVtotnTGkbXsFBld517Az{^NM19i)m3F`ODj1u&{|N_(#i^Fd-e{A~p}) zr<2&1QlM3K?}h)EwPR@}k784nF~_`l_$L^AdP;osCSp`Ok+WXO;20m9PlGQj$Ga^G z68MfRgXZ0R|>U#nBr<}a2mj#bB5h1@HNb8@QPD-RZFrIzA$oPhOoP)lf7oene zx3HK&eB#oBX~{jSAWHJJJ`f+>@!U)}EU5HqVFIGMZQCv)kYktnB#n9+Y6nkotkt^3Y1m>b8Jp0ik?ot~Hd z(n>EV)4PR21o(%216z~@ku2}DNR|xlxA}YHOX&XH8)DuL#ZNM@Pf0&^`@MUI6K6pU zj)|4kciD26m_!U|Yy|!UL68|;$d{iy#cgpi)VIydP~r}1a;JE>A)iS~9?9yAbMZ%_ zGdOwr9X>J*tc)6t6Dy0KswH!>3Sj;bISuky$=#OM$qTzTx2NvK3qGl`uHiQy@jF2w z>Nj>%QEEX7y-_dxWb)G234TJ7EHE=XQ#(Bn@*@)qXO87SrZi2Eqq_xm^Lq>Hf88c7 z!db>rs_XGZroD6riluh`*to^MZ;AFQ`UFIK6C6i*=HDJFqzQUVJ*3QJHjQ0 z{~uPIU@qT1W_;}kOgERcpUm@FK8aCNSpid7u~KnGsP7 zgQ2qGpcaKdWkx@}DDdgUF;9P4xYNrbo&K62rIETHFN~&oSs>NRVyG5{PyGdvQ&tv&|3!O6B%46TRlD#gne~~PRuNmzMKdlN z$Y4dMZFcMr#cF@h@c$=4m+T=yqpTS7yjpYP45&pjTFmS8{k) zeq>ihP*+Ag*RoKqyqPl!yM&rOzL%^0q zec8g(nsqhqr%wPq zz}?=jRw>A-VAm3_y;yP4;wQMW`8_TFJQex9o(|XI>h9M`q|anGy}C$(eq)ZF%Oosq z<1oE?la7U&YZ80qXR<2|XXs~sRo`1Lq2eJKUwhsyEB|21&o!+4qevR^#A~?n58r{X zo?7{b?Lf=<$|f4^ga>v@|EraMz~B+$z~UNlzzAp@}Jl$C$5*tMi9{{Yd26)to< z)@?Bnxhg&|J+Sf*4qS+MUsUX(_mzK0=q9~;we}B0t45a@>TCaausckdAMMrJKOp_Q zs>|A~{ez>Sd4t#fkx^gNS^Eb>Vn9EN4E^IGa`O+2oz<0J-3loHrMo&?ot9t$IIL94;_c<%u^)nkC|`9ObjhF@Zyw%){f=LP_+xa%mwS^-+D(#np({uZ#KTcK-P`y=ij+ zFKD16likoAKUL{M?8t(BAdL@rvIIM_nYvG6n`3sl+}yrDcK@tDlBOqv#|)52cA#L| zBdfR3RROV8&Bmd`eT?B0CNvxBO$?p@fx63$W0p;zXQWF;wf42WF8W(5{WNM6;N$iF z)=KzPmp|?xWXIc78~GS)unQe)Hdr`H9z@G8iCQ$?o~xDOa;JX2Ihz6N8K4%JvvbF) zf=fh`@Le~tPZceo!uHX&mHWQe9Pmh*`1m%zIM|QP*Eec8sC!VGb43xX#7c=I;Rtd_9$*Nn$3JQtAx`*$oGSV0p5QFtF zk^VQ@Eci?!>=lCoVld&s<8x2KvcV|kOcRS3JVHD++M zKX$alkHUc#my2E3QtS6vH_r}dmlOR5n7LqkJdg=aRz`;x3=x|`uOSygS;@F?0866d zLcvr;)7%vO= zU;QD%>{s+_J&VM8)vxtTG(TeGoWMe;lbEwj984sQw>%9NoTLE<7qs~z`*@lAti!Nx2h3 ztE9#HP`A}gOA;5L-P7fNZW}&m^77^w6>(aVCNz}#z`j;S?SQ%1WZ`*$&jHQ6Wcgug zN|QW%Oyy&fmj5!{%-%-kF1Q#AGJ$(L0YxhuHe^?@z0b4T;iXqGFIgjUSqh z#X-F~kI9T922vCmk(jLtLf56`bwpaWG6LNoMMOoBz7RO(elnEy>xj5$7Ti9!glUugem? zk$n-70*0M6r;^r-h#+ttYy6jPpe=uO5gC2PfQ5SK_^~N?*k4^l#$9K9q#MzOZALF5 z1Fo}V^&*4LDDQ`X8ao$}F=x>GfvSb!`PD^iyfrX;Tl69}Lp*|4Th>21=&ZHc zB6t}Ydj37pn{H^y0B>%b##fh-QRkHVf$YdIkV+o7T}Fn!(vWCd~%TX{W{<4r_Z8ZA6tbXG(>8Z$^Ku~`&sj_vs-MMBNIn+mjL(aA#-Ih!o)|Q)A!Sgb zid|J_)aR=?;;c{+%a^a;c)A2-M@67N6u2iGK+%1ifV#p@VD1sz8-79qMmjq1bPzc3 zwWHVct12{^u}|?G9b{Z`A#1c1Hvoju9OaW07uGE&KBPxR@Eseep%^+bw6HH=S29W z)X2rTis!vJSL*j-4*0z|R}sM%JR&%EFyYr6IS+;V#}Q$LftyaiVQT&{Mda`9SIRt$ zX)5WypZp38DZ{fi`$tk?geN!-#BH=~Wd}*L&9;s$q-xV!EI~uDKN3xL&Z;@Q>`Y0? zS+&B#AgKC#B1i~qOj{fsBX6UoV$Vm|Sy%N+z8UMBR$GH?R?Am;HwEg5t-;^GMa4L< z2*|pv&$hD>G{@Q(b6oyg70Gw8nkVmS1{AMnY&8SsDZN_#O_jEqX}BkhD$X_#J22xKUvO;|7VA86jXp?mRE9%LnvMjpKkb+_klfbe%T zg#+M!pa?QIc&ZzIw-fSoc<>N?zKY1S`F7fD(O<0a+aic)S=_;D8Tf9u8;R>>Sh&+z zZWe`It17piU#48YQM#c}3*uQh&#AS&S#vLdxVYH@7ZK6dO}%GVq%5Z19O|ax3frDr zflTDv>GlK*P^bY$d)xh@WPtXQAI_YPXv#TA@)`+y%K4${1USw(l43j!1g^_qK3zDP zqkV^Fo`VW+_=;1w@fE%{z`?*52q;0?&9CBKt>&kAMtHLg-Zii!19|PnQa&H#6lwDC z*?|$>3)(k#sU@;dFFCJ^%F%HKO?#<|pEAs6`(L@Lz#SLPKpypf^=TS#IrDF8DFs6` zQ)!OGpAD#0MdqKg8^rOpw#wY}0YTb3a-gE%(3Y4(cxdHk_jL;vO6`KV-#rWlGnE5fV?6uRwuDMNH4y4u4NA~8!q~6INa`zb zt}!+jdMU^PrQ&-tuQ z*5&+j)>!=Iu7DOXwxb7jtozP?bq{8!B~!jwJnsr{lRH_r@n!SZA+*5 zQ>rU6R&OrdW9~9vSO|^P2z%tcyuiz0I;UfYskD^xAdg9W?Rlr)>o*b4A)lWB8(jL| zVoS@Ub!7$OL%(mNd@dWJZgmZu{8`XV&o>wRrPVK;d_lnOG7s&s>(vn_s+D%x=c1w7VSf@iDqY=zrn{F;qy}ond~O=aw)(A$~7dfE&9B%ukKo z{S{z(H?+}PzT({)Gvzu18wF=xC)H7rlJl?XZk^!T2&kHZ}gp?Y_`rMcfsAcP-B}iwmmIFcNkCq^e z1^`-zoG|xsfr(|Eg;ybj;aj=j7%3bt7kdk>o{`dgK06Cnm-9aAQoF`VyYfnj``O#8 z`DZczREKjHR~PeB?dN=Yz}0JXHdvn;)Q_XZVZPnR9>#bmsFf@tJ98+vztHpuSaT*X<89jRK?({-g8&i!Fclyb9sj?hMT?AA_3IyP|Yl zTYn-$?z!?OGVuBxJjncWV8sPIE4&KIyZhDtXg-Wvz5X}2iX}9o#rFj4<2E#! z7VcQLAKgbW61f$xxx0_=B&zHhx(9b&OM!;ZqB!?bGswJMAI(oUPQP0p%}xh}y-0laTCSn^#l?CTa^ zMB)I6t0_^~yaNhz0Y%C?a9j#ec)SA%_dcRaL>#ygUmaqgtZqj4h9I!$V;ubD^HtIR z#nfS(W&)8qJdTBO3-*~~eEP&A(p$zKYp9G5417>&FI{{I0`O9KQH z000OG0000%0MZ-gS=dojC6y0#s)V-6DMCPEmYk2Z4quk@xgqiZy|_`JKq~8n&2DvxV$3zTvqh> zusYnYD;hjvKmIxO-!_li&2hKBEBAD_qUE;w!;j#&WkT@vJBu$+O9KQH000OG0000% z00v9!wy#0}0KiND01E&B0Ap-nb8}^LE^1+NjJtDmWZ|~H8+Xzjvtrxo*tU&M#kSc| z#kOtRwrv|7qk|4^pR>=o_w0S|Z{M@V_{OMOHRnI;srkO&H^*FxGLTSU|9KI^5<`If zj|=Yak`q%EqL-2vXH@(@hC%*aM&|z}0PY_F&TbaA&i^J1<$s3R13hd^{%ttM|5vz^ zvDLpxA^Jb1n7A96*gIR;**g8-*|7haZRP2_Zww6vX7&vXjN<>4t@^AvH5`RGNtioG!N0gae>G@BGw4RZCkPqyVg zvq0|~1%pKt0&jco)4=J`k=;m@y(zzbzd;9HH#aE(R133ouW7!g%$=7`w?8XA3ZGz1 z{^X!MZ{`DETW#Co@f8{vB_%48<04~DD>W-y_(-82dlkGY$IFh#=kwullop}H+!or! zUdel_lC~{T2RNK{Q@estyc4d-U5a1va6ZZ(VYdzewzV6 zqY4Tn-=NP>H#KRB&x?g0**}t9j{n&|83AAeReR5kra=6%+T6nkM0XQC3fW>S=iE16 zm<@N84m&YnqHzYzhVn)bi+rQbybmy(y>l(H)CqccU7l*ys5{3Qc-Y3N^D89X+O<5oksojM+LtbYdjow8!TEFB>w?36_Ge}A1N zKbA2G6#sD)?Vu!l>6!9t)gg1h!y}~xK+>#}crBQ(6qm<})k(MbO@EhxGEG?vLFq%5 zL8HP@hMJ}AsE&hi z0MAZR=(%(c3nmFp^21t;l30wc?VQe`?duB%@qQDxKxqrA!f<){Gl&2S5q#paN<5YNDle!#G*=ok7IYXbp zZ<5=w*kp2JQla zAY;7uXh`n+&rD_QcSSNvNW~kJTN`O){~p(UFql9p7RUN9@%Wy+la%(JQp(Qjm(V|7 z{bQ=&T=orAa=WtxkzApb#>@JT;-nX@UeXtwCkgFPjWwdfbtg$MMMPkydsv%a&rLSR=d-TY2IN; zyRTy`5JW7JoY=G#xDbZ!w;1G#rc^rNE+)IWq^@N7H-rG!$l=1OBoDm(XJg>+ic0w? zerApaU5G1kmNeWrY*)AuC;Hacj*acq*TLAArQ@cDI+$^N4wcT@r>kJFUZ1>-P@*D1JEGi~yus z!SNQpLEKi6M!~zM(!+%aSd+M^eMd}dBbZL~H5Q7XLov!lZC*;$hS%K{!MDs)zbD5IJz* zVoTB8saWA1t7@Ulr{Ym4%eDC6c{OK_aQ>_*}T3Yr4(ssm9Ou z6Rj7T6Ta)Xe$U5>C%bN#LxVm(U-SEMVX@oaKmS|W*WnwzY#y-|ODqm}ThHI^mo_-n zz`UJhuJ(xEDwzlY)tXwmf?8LZc9F1pxz+_`;UQuW>2bYDGUON5&7%rTY?e%^BaLTn4w_AB>;)OxWU03wmS5+kEM z@bQ*o2T?$nIs_!)1@QP@IU+;MpzM`kAu$dStFb%d*K*ag5S6p7RqpK}FP!KDA8s*A zJ6ja*+`Mm;R28O8=3;X)#3Uow+NYd)JANQy>C!a>8blDSSL^z~i*ML`oOsS4?*xFL)*JUl#n6$h4jeDanYp};vq1FrLW*I7)Sr)%pq3b^g1z;s<+{7W2k zSr#(4Yok?Ox=chdT%j0}2H9CgU!H(ZO95mg(Q=HS>hS?ex~lQ9ag_ zi&pwR8_%KFOjh)(+5wcCfO~>iWE;`*jn5{WG z&{4$Y6PQ{~xjDiwkYYx0>L5DdczPd&xmR2{bj^BxpQD&(ka(9Z7=n%O)clgz=#DN4 zHbokLhAgj$gkOEau)wTK%O{*?_P+5Qx}37YtN?o~o;Sz|GR76`)O{E7L2%iR8!Wbg z6XY|?7QN4VNd?VgNS1PDp%E~&Ea{e@3#(vX9YHb(_06&`uoXe zk;on>0O9-?nCp##9O_0Zko(BY?-4p`Br6F+T=@*AxK5#Z-p2^}ZjYPg9w?88t$V*S z5;ye7w?A9{ygNw4EMM}#5 z;e(atXbn2Zk?N2sWx!@~0o~2<8P5%?7s|vQ2p$5p4%Lm$$!M=7h%pOY{cR8$1Reb~ z64slXi6$1Ml`a$`7MXubg@VhXXe;l#)~MJ6K5NLW}HDrNE|9?YaWd-JmPW%yPS zs#E%(?wI^%$Qx-j24Re&{a~7}+3tHQ$e!v}6Lls!aEzT7la0k*-5X&&}*z{NddX^?D&GV#^L~ zw2P6Mriad|ljqS^Gc~It>>7-}t~xEhg~)4sd3!8vsiZliUzwG2sn%6Alcun#mzBW3 zGvo8IL4)`EV9dk6*aTaWy3)~UW|3!mdi44T+RD5l>%hKRAFTo8o3Dn08N&0#Pve9D zw12tqfi~*o(!*IVWbe|iSROXnM3~8HMb^6liQ^~Z+KH_}$x{sMAc~7b&kjI9l?@s* z)hfOitQ$)Z85jH(uA&Mp!X^){i;h^7&S@5N5PYB;gH0_VKONyj4(PQ~PieE( zaGq&jVpEN#Z<~%ecM22$woyuhgi5UI3WcG(09RUj6n&`8V+by*&NlPQ>=J$YAaQ!i z#?Bhw_>E2?T{wF`D^C}Qr>a=v!nielsVUz^@j4_M=EmJ_n4r1o+2W&iUdMKC$M(=n z21eN8PO4Ozb#<*7>D(+)As>f)L6g<0xSk>iIQ*6RcYKj#AKA$o;jSQuF#RRRMGaPnS&Sc@l*neUwGk!W#SIhOQrE#7{VBqNuMGz|6pgHi$H8K>5 z%4XjGYgYj6^Lli3_FDHeUuBdYc<~`HWR!Z z^dGSvHj^8DADC4v{R;Td91E7xYbu9SVtdC=6T4r>l$RJ_5Lg5EyGpXabp2<`qT6btTut3| zN~@_cZk`Noh2)py;MgV$@ouvWv@!dg;tcdt6j4(1{ArUiRV=`j?n^y>n_^)OUU(^- zZK5c=`q2{2_OtXdtROTx%%1eBSIpx&PP00CV1zOxULyAIWi=(h@b_OQwnbS5^X+7D zV$>6Q^7Vpwc!h5UcWB9kOtrjYjHixbLmaV}z|$YPtJr$Fp)jt{ox}vC)ymcm#Hd?M zt+TkNrV1km{#*W626mhT`3@6v#XtODLyD60LDSkRo%%BhyRb5wPBc!>0KTq=Vs6nl zwtghAd|CKGa{N$$a1j`OaB<=CI60|8JUI657i*A?y zVKQLe5Uo|`fxYzVZNxB2-;GfUR7CKt(UgptR!~^$gzse20zGG+O__1YRH-J>1teFF zU${!I(Q*Su@bRqIJ^W}F)`<~ftG&ro=8g0r>RJvF^w_aD0K{WZ1(uO+3dE;t zAcZjeR<}4e!-Vp$J_kBeKABjRY2H6EXYAb@SYZz$!CqJh0}h6_tp!K)%~YwQt#4gm z>GP*D5kPOyUsc<)tNPVtvJ$)Ct`fH%Ar<;!NP!?BE+3QEMAQ%G{TDbmRKMeb=k>&V ztUZ*&xJa>q9;ZlaW1DGA)i}3|?L9D71pRT7RvhUwQ7l4EJiPQ_t>{bY(IRTcaE_eq ze&9oFY-j}@oXt1?MB;!jEM27UoPG7Jo^N{n2P_jFjb5?zFM7^nVJF)nn+a7Z7cLGe zXKZH1tIt2GW;6_A&7SNhM3{08j|zP6o%mJXzANcoqn)mju3zHl&EO0~xFmpFgKATQ zK><1QI4C~uIR>;S9@(+A(*fyvHJobj;j*sJUB^qXF>z=9TlpY;pS>J;$bnj7q$C(? zeR{7(zppc=AkM6est#a(vMBy+v}Z3`CAU`BuoZn z*9MmeFR5jKfiaRWQGatxvrRS;61^I4$89nb?4Q=&B&dxOy*Oei%}!1Li=_-ueU%Yzo5dps_ul8QfAYb>#AKiV5h?}%rG62y=AU=zNf|pI?H? zMUgskubKVXaY$*!owL=%u*>ZF+q1H)S#5(<#)aCgP6C>lwHH&Cqs9M51{UdhRiTCi z+N#BcYEmp30-7@JFSChYWPKI_u7EWeP8M3Ox=zh-&SWW_M+j4V5NRi^#w-#tk^Fbw zFdxg(k1C7&78!BbSPg3KBGSuQc0JBI8C9!{l_izDqB19|+o2>pO)qnJ`VWRTD2m?q z94g@+fd7W?HB-mecwV#vz*F>PB{5>yS3@-yRPd0} zJ9SO|sD-++28mHFVmzaxJq*XLoY(w9kAfWKj`{f|Gg~q-Y{=Xz&vF9}2DLP5Atca- zWwh!;$#L=CIC#(}vqikW*5e;z+5(gF8DG%)DtSltPa5e!DIU)^Fb(=7ARn`j*398G z->g^?g^CxiXQQ=y+E@F|tAo#MhkPFB+bsyBQmKlvoiPTVNo1>O zPOSTECXO?a-1do9%jVPEs;maS^L*i&fm^NxpSZr_>pYudbk%O$-!!q^)OPI5VM9nW z{WR+Wd&G4;30=tKnz9f}TWMr+JM5^4GF*5qRnbjwcCE>UX@+i`XmmK(${F%xjT{&Qor8Y%atcF>Yj8n1NXlSxN=j8|A5(QV} zosavzXsn@q-zO`S@1ZAuxiqRwc1VT zH2vS@f9_Na*J|k_oRfNgqlOy0r+KTg@Fm3R{xVo_NVg7X3uD@O=IRBmp#R#|{A61w zn=QS!)7HRi|G)=mv?k_lRkc5K4MU@7NT7JjQ!a+rHWGCyg3D$utQtB)^D=e;d6f7u ziKf}o-Z`b(w$td4xe>R?*u_yOhOsJ2aD{S(0f%~d{j`5^dS2&(?)UdWLH@|BzWccn zvh1@U%ii&IhEs+Y?4^{seTPXV1UW8q4P@qn4@#hbp06^39H8okkI{7a&0uq$6WXk~ zR?~FaHtS`yvYcDHPt{cB5ws{@wq1+2U31&4Yk%ne9U?#uki~v8MUv~PapT?QQ)8-o z@`>f?lkI7S&^($!xKRed_fBr8OJ1P|74&7W#|$CZ9F0{#e1h%b%6rb(IS9+-scTpK+4CK?MZcwpz?WAgdhN%gw#ycy= zR=p?eb9*wmpq8j&&{uHz&#+byiQWYWBhcW&-FW5WiTB>md#hs$yaxp>|v;H zLavj0wfc~1v$ed7I;4mA-2=S}wv%m+Z27`!ABD#vcPvZoO-)s`a=5|Nj6U%6VW`90 zIjh2Sj&4XJg-WV-e+iZU{w!zjrOmu~FlMEz5L`q-^%C;#%ZgDxel#$o2k$GNFH(?6 zP8Eu&o2-O`m?BM!wo;GT>i<4LXjT7JRRYO(853of{1{L0$451_8ev%oo<*TUNHg1&{Ljm0v>4|b8-K+o6b+#HXuXDFB~SfA)z)=Wq-v%>_# zz-K2ZH{TE;q zCQtIIVP4P_)b&^#%Q@lO!-P9-Iqi5Iywa{t8fT5|S12RAlN%6cs#NZO5Uxi@izI#N ziqHAO6}J^=uUHR7`=W61+syHnlU-fs!&ZV40V_N{(0v4V98*66dmcbd;qj`|plj*n z3yBYka~YkT9IFyecHLisPpmwl+nJ3-r`3+^OS8ZMINVd}*2~1?PAA%>N`DZc!<~=g zdsCGisr7IQQb@;>1n|N$!e3+J#Vi(eX>=Hwl@oGYa%hXPoj$f&LB(}ke0!BRT!H3i zD>Is#iBvsn@CAG!Y89ctz7nR^IsR7X?3a_#+SPrPNX+1d@}DM)+4)hn>uci zF9UDY)^wCSC1Td@x(zOgnq?<>@FlTCbB_}_EKpVbs^6p$vg&02UOz5+H7JSUpd)B= z0~ry_Fj+)28kwMn>(`GTJLCa9DW@{Yf2rv_8%p5IZ#f78A~G){{(A{Ug0d;R3r_!|?~W|<~A1c~g;)lqC+_K^r5aC`rd zSUVvaXIDWbb(@f@dy_UkO%Wno$@4XJ&&x(1hWBRNM|~P#&uh1%#F%;Oalk$hz_>HD zg9xpl`&{xy2CurPvc#eP%L+sXW))&d#V;=>} z%R!U2?R8r*1`MZwWZ2wI#rJ!1>ulc8W*#XiG+$ws7TP9MCCb2j!8y`6uOEBse_2)?s~&QoL6tV zLr44eJK=T2(0WcK0DxY#Cd2pemg_=MGRsgy@q{r|ZZk<~`E#2@bHgU@cw0DhL4dFn zwI$W3sh?Ls_*7t6nijtrw|CriI|#mjImx|?4!ej`zSP7eE5&go5Kxdxa-IP1(>VF$ zQBCVCS(DN<^tG&|ckBsFki7ZDN#<&U_{SgNrMV}_+k~@>al9b@j&~U<9m2I2_v@|w z?v=Z=TjD+IG~^-9Lh!;Z2f+~?`Z8WR4cQtI@||ng3BE=O47l>Mu9eh3sPpz$0%<{Bd<+zKdGPc6F7vaLqxA)I}GDVzEl%@jI={Sxrum5hqpzI)!LG zFMds^WlVM+qYd#yfT+RJ_X#((YiWn+%B00g&RY9@a@zOzSn7-_gK;V8baF}%VW@mL z_SD(DYCx*&O(yhJ?#gYoI&Awx6n530nC8nZ>q^wY>kup^xZ=H|7)JxN*p-LeWYHsE zy~e%;vx)T#%i)0qG3UQ8mJU^W<(rkbt1ET&g9Ye_3oaHJ5n7S{i%Mr0rb;P=<}#e~ zV$Pc>@L1e`#+RE5*4#DOnfijy$|K`QOc!YyNAxT9mPT%OZ=}fnete9lT2WB*z?p58 z{qDirNt%^f``dX$>PUb-mX$x=6$y5IGbdu8lgV(=EOZXZO2tXBh) z-!4N8FIs}}0E%skve)<}E+?`W!OZ$sz;$RT{rd_we@b6~UfR}KCbM4`XVNCgI;^hE z=p(%>tw-3^85u&jMGnA_i&h=}ORJ~^OpWnE=ooZ@nLkroH5_BfV?DF!lKlXrnh_;T zU_=)SO%`$RHbFvkg1khY%AGL1pwiQRjdp=qPcp1^2ZEk2Zujiu0#93NfD|qh06Cry zWK-#-uN(ea`vverPluBBThe8C2>!%@Ag7kwj$!dqOI@0<5G68y**^@mt&^9NwZjxquch*;Pf7} zXzYa0q0l=8dJyz9_F-w-=N)5jOI2F1(^vWmRq!>(DB}9}ErNbFVRRGqqhnVAj`CQR zaZ@?$)jenAWkc4$CJF0?vs<#Q${naHmaXvs`|e;W{_+_{Y=K?JV}p8^tr13(-R-sg z`4&%!*j`G`VOFyznb){Y$^oNboe7*X-l+j>H@2k>R6T^8VfaIGU<_Fg7((`aaQjZy z_5lbtFGH}V{iwf{b0DEk!zeozT=WHs*CWR2eL$-46=j>jI#x`9=p8t}vV$2McC!rG zSK-mO;U4(YS1DG8V`n-H9BmUHvux2#Rup`Nf^tDn^zf9yj{t1Uv{Oh7J=MQ;t9yN| zF_Gk&Fwt5f!r7OreN+#J-Knp8gW7$S-EKQ#JDEd^{dNYECP1uHd5-;c6FqjL^hc(( zoy3E_*J9!iF{p&gZ9jdH;wV1TVzK8@4Zd9jPcfl`7pG3mQwuiPax_`_QD`;3R-43+ z^p(|?o{Tou(!bD_#$Pnf*pj|HBZlA(x6>jHsfW&AN#DHGU5#~`70>Sj$<*`Y1$zUc z2-S+&s=!uSp$>)Sl3#v%6$PC5*;c^QEtsg|Q~8J~7pq_(|4#_K<`jCmGwYLPo;pAL z`&~1&1f*-+HiN-O^^jKjjuv){-Y)zV`zbp}Zw`Z_?dsxb{2650*y2(kFw7Sw&dRJv zSd+PqHy-+vQw6DM2BO{9?VC798_SIIqoj$b$h@!FEbTn@Ox7~E<(pnMy(&)T1s!sc zg^sfo+#2QEvwjBp9CjkbiJ$1yHp0*2`Jr}26~pVTo1QuY!-k*N2STGuvwc(JcU;yl zYe!*6QDZ*bor?SSFqjZ&atpQ*@(zLKl2tZ68J>3SOA4|f^?YanqPB@oeD=op^}v!{ z(+2C52=A$5jKBZA#DU|qOs83R7Ad}eqTxjQ~KtbBQ*dXScSt{cGQC- zb2#K1E+IcMan4)7)(Er&!8R1Ms#)4x5^5K!RH>#i;w+&lRg3MjcziTU97+XSwb(-)# zm`@kXi0VeA@)Dpid~bMsQ_el-bJd_5gC7`C^&8pnk}1jo_CGlT0Y2pO%VteR4R&|> zJa?>nGUwJ@pPk2yR6-z+We<`(nmccQ_wO7yuv12GXoK8Pq6LyXnvct%y#pMmXn})J z_pti(aQ?(ikY0XCdzjmq9T4*Aa~tOQyM-`Hb~XNGDns;*Q<{1#HMCGSyFDQ!OEgxNM0tiY|vJ}yl|GmaF4 zNDFGDcW#gIrB2?WJ*WZW<3)*njRZ^BO3^NVGt9RTsANMB{)3dWy5ND?MPbJkrk)a8 zkV9eU!0N8Kz@8o*Yqhh%^uid9WKm^p0kZq5pd+IcSiol5^;UzfU)d6N_&PSRxID=G zy{3ul;ex9(JNTC?&EKXl9C!IyQrEOgZc`;lhiP-PTp#BaN!#!y-=;uDDP(1=w0d>z z=aA;{ zmX2Q?ostVoN7Xzp$#>tfUW0PUrog@lWh!%)`GuS+gO=yqi_U~AHp~-!3b!JRl#%KQ zk|cc=?gKQWCj=F22bft1i!eDKS$|YL`8j@2w^%~joUrV$=x%u~4W!MamsyPxws5I- zbTR8^J@%3P_;E%|g~MS{g;T#DC2wFbE_vE@mula8n;02dru_sk63aHv)7wXlax$d< zE&RW8$``Io+G$`~+(!susLamUDNBUnEU?7JpHpP`!IXsd0o<%vlmd+26%L`l6?lz3 z1zh{?t|ug6nUG3=v;6XjU5gk9+Yfg)8#^1RhnL${QA6 zqf!d>u}x7+%sX;=HuoRZO!P?-;Po{9@I+$Wy+r=t0DA#y0sKPc{y_fB3X-3BSG2x7ujQZrexg>pdnkgwovUlY#wgybMvcPaD**|mtJe^PLn2B z`Zln5X^|&e(9bog`FS)e(hvtUbH_Ygd*fi^KAwK#;_peq(R)cuaZeYGfbD-c}1 zEy`EGu0-$_L+tw=0Br0q0%!S9M6+!ElUOU`T{K2B62$%w{AgiLw0Q8+2hL=-=BP{y zqQ9cfa<-U*ZWITssGxfUA>!**NC;Df-k0Ty**nQvL-6lMbHYelH~#4Up*Q-Tf7_d=HjU z>P5%jRW)pnwE4#S=KPB!)|{1(!CqqC?KN1tn|cNuU;dY>_*^@aVN^e9vDJjqMF$>3 zHmJ3*FIZ6pab5!Te|QL?$1y{T?Y;HE0re>OdJF7jEAlr~S*V%!^B2 zyZ-%+yoikBqGT0@l>IIRS?4bVsI|N5{BK|^bc$JHPEe(kP)=G$!05A(wEjc+JoFdi zH}iXpGhUcCr|BXbslB-tqgh0?l^b3~kH#(1gJnADM483G(@1Dh|3^$Frzk5*#TWiN zeme1c$>b24gMI4tB#Hbq-JfMcuhvGOpFS5YzFis9-y%eLrssv0qp6M{eyZciFMBGc z8X`x9M|o}h5 zv2yc7J!p~aCYWT`Nl%S$x$xVUUlR(;Q7K+&G$fd+8#@>I-^~ko5KoFMj&}9mS2BK) z84DfU967*1X_qp)<>AmlQ$R3O?h4ze0k%E#*m-U#-A8m3Tkk1}l?8JvLtBZN`t!)v zwF%GlDV7FMkCm}xx00&szuMll?q_y7wm^%wTdSw5zW6tKFbE9%s%eDu><+zxx~{rN z6(U=v?IUV|GKL@%Z6BV!^~?d8GZCP%Jcd8s9_8p9)hqchC5@usD9nB8p-ixJm)8XL z0I}3L+;mp8dc7c0=?e**>ptm7qv)=0dFaKI9wD#m>hwJVy$UYCLEcX$|pMJV2> zSF69|0QhmCX763?@T43QL%K?nVZ7cJ(37IO)r<_Gl*qQ?{V}W;pOCL(XaP@M^5+0Z zzqB7GeAbK5u6g`^2|&mOjslJV`Rr0H`n`x(+#A9KMQE z$Uc=%v@h8i_ee#%_Ju|&288{oEV|Z1K?F<|$f>mgy}B&6U8%xzhNo~^>#CbgOnj;< zLPAHIr5dT)MQ-G|Tfjc|>oXQ)1Pyb|4OoHD&H7U>uH4sk*wwv>j93 zRmi8AnZpBLj-4q?pNvqAazv=0ftUQ4$5PK2i^HMFnbht&N*{Fm)vPE9YwB{-)Fze5 zyZ9l6Z_g9dMEgX+e(T& zKZAOI;=kvfllQb_*4)Z*(hkwD4eVSu(aWLR3X)}FeBQOPNL1`G0x_I3|GCi*HIZkm z>dnb=dPNFY@8o}$h#w+l0w~d2cNqp2uNmRE$2FEHfjHnkmM9Y~i+hG2NpR{gtX8T;59A8Dni}$0*OmnklzYTNN$8)amsK zHEZjrkc`d$Kp8rSLfgoulz*1r$~#+LVi`lX3UmG7lJvu+pIoPfAM3)-zmtzjyIYAdyXy&{-YyE?pSf%ezE7D;3V&jXpf z6Uzm!1+fCge)vQmjGPLYO8?PH>}2@$dpx2NjU^*%`PGTjqH6n1Cud(yTKe*2Z>^zC zCn#Iq)TRORAON)ybqo9RQ?#FdFKSvNOEp zBB5o*wDk?PusTq0iLjtj3H#|IQ`LKd_=XVM6rkon(wP7T8m)NWc~!CE0!kgWyr*7+oT{V}U!hH`t(MGOoD z&g&K8UHD$nTHCRMUx1T1LzDA{&8p<1eF{o0(&*C2QDaNGkr_ZdMZui5`F^*9NP6T8))UyT+Db<#@eS)~M zV4;N=l3Ul{28-)!ek9!FTRmVLf21$fKwl;0A5Ks{zkg6oD=xb|e;<^B=3628O^2>O z4N_NqTa4LCo`eNQA5qe%Yuh=mgobt|xIRmC7JHyQ|0m-^EAXC~t+9iKW`#ZAY^@7x z%@k~~D#)8C+jK(#5(VSDgL zucN(5jhqf0sHW3+mYn0)ZxHV62#W35F R!UsC601eRrjM&wB#8aa@@jh zAag|`F+^;R_bT%3FSdNlV-0yJhF3iBz{+x22!4P=~z#~rvj&Mmn z9bofs|91r=@WzM7Q{P9S5#{Sbw0!qkbE_?fFjY`4TDD9@(aVUiXa!iyG<2@gUGWH+ zjxc#xi4J)ASBVb70U?C1UZ5Vp+V4rvhn*>QVnA9>uUFD?2&L%vgD-1xPHJZcunI(br!2;9m?qHixe;I+*gwGV8J; zKjlI4qCR0${(i3^sKTAb<8e}q5%RD;1srOP(*>!JVJ0MvN#&%19ISvNUJ=z6D2>*& zC&OMb0aYH|QG zlw6;VwNw>l6G~bZLUAaDJC-za3+Y-W=BXBae3rG3B(WbE)!Lx|ng!D+PyvdDp?VGj zQcIWhls1jL5Sc*WfYZ*Ex@&ncDE{BWMfL|m@?B{^DZAB_joKxWSg?}QOs`WYCquQJ zjQ|dtEZo_UhMGM#y@VD5I{FV8)ZWc67+%v*fp*BP5|H|!E5*icIM4`^F%+z3d_3xn z=xZ3DPY?jY7Ufy=2hDh6%-#?OXJnQY=xy;~%py2R(WFv}@QFtVWewXY>sw|e*yOmi zM{DJlN}-pD@Wf?YlCU1@oNQLtwe+S=HL5(To(}y46_*q`s5Da^hH2(e$V{rR)W3Tz zYRE3(6IKxPc^s_4tTHPY3VPuj91u>j@D$Xm(B{vx_7*XvSR9&tDkx%+md(vGdx^Vi z?5h_;y$ZKk6eONG--t!W7puKrC9$`~>v7MeDFRs1?RueNR zjJO(AIB23nenp$ar-CIpvtjhtELBso^64bJ3P7i!F*W^KYLiQW-0D)4(Ogvl9U%%f z(|v^P;KUD7PzfQh%%M8{FoVNW1ob+l(x7BJ+wK=>Q06SsU=rGo2b~5sHfNSjBLQ0?C0eiH$5rR@#PSOBtI972RDkjO| zHj$m%OiX>OB`E@0Xp#8p{Y2;Co|Q3A(o=p#>ZU}u(?Z41HMMj3W6PW9A;RW5Y3-Qk z-uUs47PWUVT)#DI__KR zu|cvdc~e>!wg=~vw6?u=_g%1$SJ3McJqov>^2UL3W|RIQ+TNwLlo5lR9G9akpW8Dj zLE9464lEOyqyX$%oSGSmYD^EO77+14k*i>6qYX*+STd7>!Y}orQMoEeqY9t>I~HdN zTdh+w)^-#ovh(S6EcDtTgXWWgI{VI1YX}aw^DncV?WfCWX};*xZMPAZeDwgmHmeBOD>Q^jd6T?AlO|8MX^r@#Xvw3 z>P)c?mOI2bFy;szK8~kb7g)yO@|n{ObI^sl8X-(AK&S%*CD zd+RsQms$iDWT%?udCXE6Pu(q_Q-7BFSRK6v zLrhG}rkx^jl-J3bGM0p41TN#W#4_Ey*YFygeDifZD^6gg7$SSvx`t^`LCX<(b~&cR zjKPkOmS2wVEyR1!&A6wt=@Cbl5XR`>2(F-uLErXJ(u$zMSxoVke>sWbdy-W1 z6bxc~xQSj%*!l_9Y^OzYf#mU^X|Ju@r~9wy@`enfC%4Por%U5Ue}zko(HK7<{OC^+ zFDOQpIYeKVlj^d$4!&n(czon&*HgTg& zzbj%!^HoRZJ0LAxSj>r=l^v!Mgwg%d>NozcHO!t{Av}RS0|N{M14HOFu@Q`e%P_Y$*3-Xm9B8KGJ`n6E(i%iug z(debGs*a9piVv^SV=xF>gJsu;d(5wF%8}&V6-fa`jFpmM29_*D+9*Fpa0N;6U6MzN z?u}IhH%5XYi3E3fQ*B2!N*exA-8-6Gz zP6lym?V$D`5H_lig^mQxmBqw&{@rKKl_AsA-FBW*F{?Cok*yuajUWiF`(YKAe^+*X zvhkEb&Eo_g(I1v}HNCB@S*4Dsr|PPFb;Qj{=TF&-bB|AzcLs9qNH$Ra6k`&#q8?oG z{3B|9;;Y^R%RcX>!c+#J@!|ApTyLT}yrOQ^aKapS%A!@)kmz4b%@*XC*V`P>ff+YqIY_Qn0^tFKlHjH-*Rn4t|MfIY%eo- zP_Y9m{94rYzdzsj*-^>IDE1i%K5$1pt7wU{RIgHVY$+^$@4Tugg|Ut#$gxTl6=QuO z$@%NOHD;@49lq&Z%yBJ=GLLV_8UyIvdVB$tyfGex! zfSMm8;x|EjZlP6bC{!wxFttK4ri84abai5l2{@SvXGAz=>oqdOh&%ZIw zv@bOEv0t7{C}JuwTU3ThbyMfaFi)_0myC+xwgoQx3rA!;$MsF;0P&G`%?5ZaIO)ZA{^ILL6Z$oai(8NaqR!ySAhZ2_pn;1r-L9oID=mzIY0YE(GBrh zA@}j?J8aA4;;CKE9c>L?P%*~z>;Pu08qiYKIe|l?*DlA`Cfz&#}_UlTui<>Y`UB zZ^&QO5#H3s+|-Sv8PxOrfdCVD$(i|#yftp-I*H5M1s`(q2RFA+pWKQYG2Pp=0p)S& zptLbWFKbLS%?Mn*e`CiYOh@DgpbjBn3di3E_b0ehni((#?Q|*=$6h?Z)W*~&Xdkfz zir4j2Tx0}NX2m{GJk*nzFMajParT!%mPy-X!aYe>p z$x!E(YnCimOJldyWv9l-6E}^V`P{P)uPEHd=RpU!3vE3TsVO6&h~HS9%%U!b7TvAX zsT#hRM5DZ3rH8k*P7729`wSwOjkS-dEPqwh=`I$5)CgJ;>;V)u%ER6}f-QPwz2PrH zp7gSm?(j*R(+*n_I++vo&*ZmW*A|IzAu2Ts%4*kztMJ>dd|mz>ivEylxu(~6|E1O7 zL^gBcGD6I6ZjPw0izv@2iU_O@FM^y~KhjHAqkN>?`w_F+h_?5HfFl`u5>$$md;p3A zkSVdo@8E-wl^N=1=&W^0XE|*4&cH+NM#lfa72?EEJy#c`iJ@11iZ(9ex;%HESXf@Z zeLOzaqL1c<`aMkhlUuS+NDS|LJOgXQFCxSaCx^yHxBpl$f*8Hx-6i7D6(e<$a4cO! z?6LAODM7{o6)Tx)1q-SWKhyE9#Nzn(R#3&4B#8mXN}ju>ed_UK65dm5$>=6vyzp7z zGMVAj-PP2V!>vo`0Gt5=gf<|=+Li9yM12FbigbLqVDe#Zb+o|s(PK5uh@NWl+X(GV z_HWht)I|^&$cU8y#9${(bYgOLQdl#$VHN63(^8jmeBBuTl7=6fgWa?=9fZE`!(^rZd3xW?F`9$0 zC+fscGY>Yr%T%#F-14U!ey7CTV-`IUZ;NZuAcEWOo`0)tr3!pr!@wjoM1(VH;3>~=@Ag7OQJSs4nb;Eo^P$x3aKOv& zFib!-i*IXSr@CL-5yTit#E@^I1*MLFPBc^EE!htYME3B4UKiL~YpQx?SEI(g>jepD zSoT;sRjyLALY02B$oQ1w9Iw6!Zg!8$$5|SE9d2m-OF}uXRW|BJeb(<{QHD-CT-P>b zk6dUr1WWnelWhdP8j zVj&+MrzEwo>O4<@-C=2%|JH_nOu9V$ zpGv5L#`TI9u&9+TKQE#^3!gH*SFRxS#pT0A6s9nSn;taizsIiuU1r$*mJ*uLmRc0p zr(dVN^IMEZcCD{tL#uCOZD#na?f+_neD@V_o-@lOYCv$DKDV!AN{zAAva>8A(#9Rv zeV~KbAt|%t)ya$D%RqyT`Xx`4AT#|LHZ|RNJ~D$#TfDF312^xU3x}<5#vJ_M@XMcA zc+8zCfNPBKZQFxBnwU*(i%mT4e`2kjwl()+)X<{YS6v@4t5SocY)I!RPVsXqs4MQW zFrj5uEym!v1+v)h@GT&R4GwmXINi4)jYvuR1!h&9F@gfk=~If}p=Erx;VN>GOCBNzyKs!#R82lVS`}EEU|NbDBEm!o^&dgBa=?#A#%#!rW@u5~->a%e&?aos zxW0v~?$K+?Fy}72LJ1Wdi7Nkzvf?iKbJG3%8Ri@zh z{raU%4kiv+IpgmP0$E&td4Q{zoSBrVo?VfaqnDVJfu5I@r}*zhc6@w1*bt3`sWldS zG#->kZfAbA$4~T&h)o=;nYRW#?sC`plc<8geRsTJVL-#MEUePv<0q>0b8GI}^}_ku z^cce4^cV>5G{`6>8N(f#G6+Flbf6YCb0nJ!*qH?7(8giEzoR3RtxX`s#zQ@+vG<0E zLeuPE3j7^rfxqF9h_+~cb`uaY?574bLtSw~Ucy?E+T64-A3A`+i7T6;EF?cWx{nd{ zfHoiALrZ(%rhiWWgh5akL&j#rTs8l0v47owPWD|YsPp0+Ixe9!SB3Nf5{YxrJo!5! zSnVQ#=>+~Z_mhP}u!mWbL^_EGJDQIrxPf&SZ#^>4nQ0+B9Ptzl3z>Qi%OaFpR|D7kXH3BQS>sWw}?XzmR;BCWL zW%H;)t*{*uLG{=Eh&3>zF=}==_{u>HHZ|GuSZA@?GXkct@LT_N;NNZ!_ zY+!8QY(Qu4XlHNY=xkx)BqdGbI7vStLjx&AOEoh!+sL}ayeBD5^B_YjAuT~8A&fRN zJ>@hrOFK3t0V_M+!YX{+shV7T5|9#z`sbts<=CvW4V|>~s2J=hxzy~05EGOI=HFTY zflz?|-<2=_E%?7$alrq6{8t^!|1|ke(8+%r000yR2>&ag`G3Kl{HKHeB!B#m184gG og9GxPhX1*>|6?fa`2Vrd@>2hTiGK~`Uq=B10C@Z73;^JN0h;^1k^lez literal 0 HcmV?d00001 From 089ca021fddb35ca185879b6ae3133c378f30a7c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 20 Oct 2023 16:33:07 +0200 Subject: [PATCH 132/272] Implement TwintDelegate COAND-806 --- .../components/core/PaymentMethodTypes.kt | 2 + .../paymentmethod/PaymentMethodDetails.kt | 1 + .../core/paymentmethod/TwintPaymentMethod.kt | 54 ++++ twint/build.gradle | 1 + .../adyen/checkout/twint/TwintComponent.kt | 36 ++- .../checkout/twint/TwintComponentState.kt | 6 +- .../checkout/twint/TwintConfiguration.kt | 125 ++++++++ .../provider/TwintComponentProvider.kt | 279 ++++++++++++++++++ .../twint/internal/ui/DefaultTwintDelegate.kt | 134 +++++++++ .../twint/internal/ui/TwintDelegate.kt | 12 +- 10 files changed, 644 insertions(+), 6 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt index d8c8e4b29b..4a97ba42a1 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt @@ -46,6 +46,7 @@ object PaymentMethodTypes { const val PAY_BY_BANK = "paybybank" const val SCHEME = "scheme" const val SEPA = "sepadirectdebit" + const val TWINT = "twint" const val UPI = "upi" const val UPI_COLLECT = "upi_collect" const val UPI_QR = "upi_qr" @@ -129,6 +130,7 @@ object PaymentMethodTypes { PROMPT_PAY, SCHEME, SEPA, + TWINT, UPI, UPI_COLLECT, UPI_QR, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt index 9ff95241e0..fe12f6c6da 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt @@ -88,6 +88,7 @@ abstract class PaymentMethodDetails : ModelObject() { SepaPaymentMethod.PAYMENT_METHOD_TYPE -> SepaPaymentMethod.SERIALIZER SevenElevenPaymentMethod.PAYMENT_METHOD_TYPE -> SevenElevenPaymentMethod.SERIALIZER + TwintPaymentMethod.PAYMENT_METHOD_TYPE -> TwintPaymentMethod.SERIALIZER else -> GenericPaymentMethod.SERIALIZER } @Suppress("UNCHECKED_CAST") diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt new file mode 100644 index 0000000000..d27b9f43bf --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 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 20/10/2023. + */ + +package com.adyen.checkout.components.core.paymentmethod + +import com.adyen.checkout.components.core.PaymentMethodTypes +import com.adyen.checkout.core.exception.ModelSerializationException +import com.adyen.checkout.core.internal.data.model.getStringOrNull +import kotlinx.parcelize.Parcelize +import org.json.JSONException +import org.json.JSONObject + +@Parcelize +data class TwintPaymentMethod( + override var type: String?, + var subtype: String?, + override var checkoutAttemptId: String?, +) : PaymentMethodDetails() { + + companion object { + + const val PAYMENT_METHOD_TYPE = PaymentMethodTypes.TWINT + + private const val SUBTYPE = "subtype" + + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: TwintPaymentMethod): JSONObject { + return try { + JSONObject().apply { + putOpt(TYPE, modelObject.type) + putOpt(SUBTYPE, modelObject.subtype) + putOpt(CHECKOUT_ATTEMPT_ID, modelObject.checkoutAttemptId) + } + } catch (e: JSONException) { + throw ModelSerializationException(TwintPaymentMethod::class.java, e) + } + } + + override fun deserialize(jsonObject: JSONObject): TwintPaymentMethod { + return TwintPaymentMethod( + type = jsonObject.getStringOrNull(TYPE), + subtype = jsonObject.getStringOrNull(SUBTYPE), + checkoutAttemptId = jsonObject.getStringOrNull(CHECKOUT_ATTEMPT_ID), + ) + } + } + } +} diff --git a/twint/build.gradle b/twint/build.gradle index 43cda7d359..8f9f5dd588 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -39,6 +39,7 @@ android { dependencies { // Checkout + api project(':action-core') api project(':ui-core') api project(':sessions-core') diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt index 421560674f..b6b554aefb 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt @@ -8,25 +8,43 @@ package com.adyen.checkout.twint +import androidx.activity.ComponentActivity import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.adyen.checkout.action.core.internal.ActionHandlingComponent +import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent +import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate +import com.adyen.checkout.components.core.PaymentMethodTypes +import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentComponent import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.ui.ComponentDelegate import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.twint.internal.provider.TwintComponentProvider import com.adyen.checkout.twint.internal.ui.TwintDelegate +import com.adyen.checkout.ui.core.internal.ui.ComponentViewType +import com.adyen.checkout.ui.core.internal.ui.ViewableComponent +import kotlinx.coroutines.flow.Flow class TwintComponent internal constructor( private val twintDelegate: TwintDelegate, + private val genericActionDelegate: GenericActionDelegate, + private val actionHandlingComponent: DefaultActionHandlingComponent, + internal val componentEventHandler: ComponentEventHandler, ) : ViewModel(), - PaymentComponent { + PaymentComponent, + ViewableComponent, + ActionHandlingComponent by actionHandlingComponent { - override val delegate: ComponentDelegate get() = twintDelegate + override val delegate: ComponentDelegate get() = actionHandlingComponent.activeDelegate + + override val viewFlow: Flow = genericActionDelegate.viewFlow init { twintDelegate.initialize(viewModelScope) + componentEventHandler.initialize(viewModelScope) } internal fun observe( @@ -40,6 +58,10 @@ class TwintComponent internal constructor( twintDelegate.removeObserver() } + fun startTwintScreen(activity: ComponentActivity) { + twintDelegate.startTwintScreen(activity) + } + override fun setInteractionBlocked(isInteractionBlocked: Boolean) { adyenLog(AdyenLogLevel.WARN) { "Interaction with TwintComponent can't be blocked" } } @@ -48,5 +70,15 @@ class TwintComponent internal constructor( adyenLog(AdyenLogLevel.DEBUG) { "onCleared" } super.onCleared() twintDelegate.onCleared() + componentEventHandler.onCleared() + } + + companion object { + + @JvmField + val PROVIDER = TwintComponentProvider() + + @JvmField + val PAYMENT_METHOD_TYPES = listOf(PaymentMethodTypes.TWINT) } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt index ffea7e688b..c0111e8477 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt @@ -10,10 +10,10 @@ package com.adyen.checkout.twint import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentComponentState -import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod +import com.adyen.checkout.components.core.paymentmethod.TwintPaymentMethod data class TwintComponentState( - override val data: PaymentComponentData, + override val data: PaymentComponentData, override val isInputValid: Boolean, override val isReady: Boolean, -) : PaymentComponentState +) : PaymentComponentState diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt new file mode 100644 index 0000000000..1b1bb7b156 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 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 20/10/2023. + */ + +package com.adyen.checkout.twint + +import android.content.Context +import com.adyen.checkout.action.core.GenericActionConfiguration +import com.adyen.checkout.action.core.internal.ActionHandlingPaymentMethodConfigurationBuilder +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.AnalyticsConfiguration +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.internal.Configuration +import com.adyen.checkout.components.core.internal.util.CheckoutConfigurationMarker +import com.adyen.checkout.core.Environment +import kotlinx.parcelize.Parcelize +import java.util.Locale + +@Parcelize +class TwintConfiguration private constructor( + override val shopperLocale: Locale?, + override val environment: Environment, + override val clientKey: String, + override val analyticsConfiguration: AnalyticsConfiguration?, + override val amount: Amount?, + internal val genericActionConfiguration: GenericActionConfiguration, +) : Configuration { + + class Builder : ActionHandlingPaymentMethodConfigurationBuilder { + + /** + * Initialize a configuration builder with the required fields. + * + * The shopper locale will match the value passed to the API with the sessions flow, or the primary user locale + * on the device otherwise. Check out the + * [Sessions API documentation](https://docs.adyen.com/api-explorer/Checkout/latest/post/sessions) on how to set + * this value. + * + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + constructor(environment: Environment, clientKey: String) : super( + environment, + clientKey, + ) + + /** + * Alternative constructor that uses the [context] to fetch the user locale and use it as a shopper locale. + * + * @param context A context + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + @Suppress("DEPRECATION") + @Deprecated("You can omit the context parameter") + constructor(context: Context, environment: Environment, clientKey: String) : super( + context, + environment, + clientKey, + ) + + /** + * Initialize a configuration builder with the required fields. + * + * @param shopperLocale The [Locale] of the shopper. + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + constructor( + shopperLocale: Locale, + environment: Environment, + clientKey: String + ) : super(shopperLocale, environment, clientKey) + + override fun buildInternal(): TwintConfiguration { + return TwintConfiguration( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + analyticsConfiguration = analyticsConfiguration, + amount = amount, + genericActionConfiguration = genericActionConfigurationBuilder.build(), + ) + } + } +} + +fun CheckoutConfiguration.twint( + configuration: @CheckoutConfigurationMarker TwintConfiguration.Builder.() -> Unit = {} +): CheckoutConfiguration { + val config = TwintConfiguration.Builder(environment, clientKey) + .apply { + shopperLocale?.let { setShopperLocale(it) } + amount?.let { setAmount(it) } + analyticsConfiguration?.let { setAnalyticsConfiguration(it) } + } + .apply(configuration) + .build() + addActionConfiguration(config) + return this +} + +internal fun CheckoutConfiguration.getTwintConfiguration(): TwintConfiguration? { + return getActionConfiguration(TwintConfiguration::class.java) +} + +internal fun TwintConfiguration.toCheckoutConfiguration(): CheckoutConfiguration { + return CheckoutConfiguration( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + amount = amount, + analyticsConfiguration = analyticsConfiguration, + ) { + addActionConfiguration(this@toCheckoutConfiguration) + + genericActionConfiguration.getAllConfigurations().forEach { + addActionConfiguration(it) + } + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt new file mode 100644 index 0000000000..f22391ce69 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2023 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 20/10/2023. + */ + +package com.adyen.checkout.twint.internal.provider + +import android.app.Application +import androidx.annotation.RestrictTo +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner +import androidx.savedstate.SavedStateRegistryOwner +import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent +import com.adyen.checkout.action.core.internal.provider.GenericActionComponentProvider +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.ComponentCallback +import com.adyen.checkout.components.core.Order +import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler +import com.adyen.checkout.components.core.internal.PaymentObserverRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams +import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper +import com.adyen.checkout.components.core.internal.util.get +import com.adyen.checkout.components.core.internal.util.viewModelFactory +import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.core.internal.data.api.HttpClientFactory +import com.adyen.checkout.core.internal.util.LocaleProvider +import com.adyen.checkout.sessions.core.CheckoutSession +import com.adyen.checkout.sessions.core.SessionComponentCallback +import com.adyen.checkout.sessions.core.internal.SessionComponentEventHandler +import com.adyen.checkout.sessions.core.internal.SessionInteractor +import com.adyen.checkout.sessions.core.internal.SessionSavedStateHandleContainer +import com.adyen.checkout.sessions.core.internal.data.api.SessionRepository +import com.adyen.checkout.sessions.core.internal.data.api.SessionService +import com.adyen.checkout.sessions.core.internal.provider.SessionPaymentComponentProvider +import com.adyen.checkout.twint.TwintComponent +import com.adyen.checkout.twint.TwintComponentState +import com.adyen.checkout.twint.TwintConfiguration +import com.adyen.checkout.twint.internal.ui.DefaultTwintDelegate +import com.adyen.checkout.twint.toCheckoutConfiguration + +class TwintComponentProvider +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +constructor( + private val dropInOverrideParams: DropInOverrideParams? = null, + private val localeProvider: LocaleProvider = LocaleProvider(), + private val analyticsRepository: AnalyticsRepository? = null, +) : + PaymentComponentProvider< + TwintComponent, + TwintConfiguration, + TwintComponentState, + ComponentCallback, + >, + SessionPaymentComponentProvider< + TwintComponent, + TwintConfiguration, + TwintComponentState, + SessionComponentCallback, + > { + + @Suppress("LongMethod") + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + paymentMethod: PaymentMethod, + checkoutConfiguration: CheckoutConfiguration, + application: Application, + componentCallback: ComponentCallback, + order: Order?, + key: String? + ): TwintComponent { + assertSupported(paymentMethod) + + val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = checkoutConfiguration, + deviceLocale = localeProvider.getLocale(application), + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = null, + ) + val viewModelFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> + + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + analyticsRepositoryData = AnalyticsRepositoryData( + application = application, + componentParams = componentParams, + paymentMethod = paymentMethod, + ), + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + analyticsMapper = AnalyticsMapper(), + ) + + val twintDelegate = DefaultTwintDelegate( + observerRepository = PaymentObserverRepository(), + paymentMethod = paymentMethod, + order = order, + componentParams = componentParams, + analyticsRepository = analyticsRepository, + ) + + val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) + + TwintComponent( + twintDelegate = twintDelegate, + genericActionDelegate = genericActionDelegate, + actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, twintDelegate), + componentEventHandler = DefaultComponentEventHandler(), + ) + } + + return ViewModelProvider(viewModelStoreOwner, viewModelFactory)[key, TwintComponent::class.java] + .also { component -> + component.observe(lifecycleOwner) { + component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) + } + } + } + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + paymentMethod: PaymentMethod, + configuration: TwintConfiguration, + application: Application, + componentCallback: ComponentCallback, + order: Order?, + key: String?, + ): TwintComponent { + return get( + savedStateRegistryOwner = savedStateRegistryOwner, + viewModelStoreOwner = viewModelStoreOwner, + lifecycleOwner = lifecycleOwner, + paymentMethod = paymentMethod, + checkoutConfiguration = configuration.toCheckoutConfiguration(), + application = application, + componentCallback = componentCallback, + order = order, + key = key, + ) + } + + @Suppress("LongMethod") + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + checkoutSession: CheckoutSession, + paymentMethod: PaymentMethod, + checkoutConfiguration: CheckoutConfiguration, + application: Application, + componentCallback: SessionComponentCallback, + key: String? + ): TwintComponent { + assertSupported(paymentMethod) + + val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = checkoutConfiguration, + deviceLocale = localeProvider.getLocale(application), + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = null, + ) + val viewModelFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> + val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) + + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + analyticsRepositoryData = AnalyticsRepositoryData( + application = application, + componentParams = componentParams, + paymentMethod = paymentMethod, + sessionId = checkoutSession.sessionSetupResponse.id, + ), + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + analyticsMapper = AnalyticsMapper(), + ) + + val twintDelegate = DefaultTwintDelegate( + observerRepository = PaymentObserverRepository(), + paymentMethod = paymentMethod, + order = checkoutSession.order, + componentParams = componentParams, + analyticsRepository = analyticsRepository, + ) + + val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) + + val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( + savedStateHandle = savedStateHandle, + checkoutSession = checkoutSession, + ) + + val sessionInteractor = SessionInteractor( + sessionRepository = SessionRepository( + sessionService = SessionService(httpClient), + clientKey = componentParams.clientKey, + ), + sessionModel = sessionSavedStateHandleContainer.getSessionModel(), + isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false, + ) + + val sessionComponentEventHandler = SessionComponentEventHandler( + sessionInteractor = sessionInteractor, + sessionSavedStateHandleContainer = sessionSavedStateHandleContainer, + ) + + TwintComponent( + twintDelegate = twintDelegate, + genericActionDelegate = genericActionDelegate, + actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, twintDelegate), + componentEventHandler = sessionComponentEventHandler, + ) + } + + return ViewModelProvider(viewModelStoreOwner, viewModelFactory)[key, TwintComponent::class.java] + .also { component -> + component.observe(lifecycleOwner) { + component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) + } + } + } + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + checkoutSession: CheckoutSession, + paymentMethod: PaymentMethod, + configuration: TwintConfiguration, + application: Application, + componentCallback: SessionComponentCallback, + key: String? + ): TwintComponent { + return get( + savedStateRegistryOwner = savedStateRegistryOwner, + viewModelStoreOwner = viewModelStoreOwner, + lifecycleOwner = lifecycleOwner, + checkoutSession = checkoutSession, + paymentMethod = paymentMethod, + checkoutConfiguration = configuration.toCheckoutConfiguration(), + application = application, + componentCallback = componentCallback, + key = key, + ) + } + + private fun assertSupported(paymentMethod: PaymentMethod) { + if (!isPaymentMethodSupported(paymentMethod)) { + throw ComponentException("Unsupported payment method ${paymentMethod.type}") + } + } + + override fun isPaymentMethodSupported(paymentMethod: PaymentMethod): Boolean { + return TwintComponent.PAYMENT_METHOD_TYPES.contains(paymentMethod.type) + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt new file mode 100644 index 0000000000..5cc2458b94 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2023 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 20/10/2023. + */ + +package com.adyen.checkout.twint.internal.ui + +import androidx.activity.ComponentActivity +import androidx.lifecycle.LifecycleOwner +import ch.twint.payment.sdk.Twint +import ch.twint.payment.sdk.TwintPayResult +import com.adyen.checkout.components.core.OrderRequest +import com.adyen.checkout.components.core.PaymentComponentData +import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.PaymentMethodTypes +import com.adyen.checkout.components.core.internal.PaymentComponentEvent +import com.adyen.checkout.components.core.internal.PaymentObserverRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.components.core.internal.util.bufferedChannel +import com.adyen.checkout.components.core.paymentmethod.TwintPaymentMethod +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.exception.CheckoutException +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.twint.TwintComponentState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch + +internal class DefaultTwintDelegate( + private val observerRepository: PaymentObserverRepository, + private val paymentMethod: PaymentMethod, + private val order: OrderRequest?, + override val componentParams: ComponentParams, + private val analyticsRepository: AnalyticsRepository, +) : TwintDelegate { + + private val _componentStateFlow = MutableStateFlow(createComponentState()) + override val componentStateFlow: Flow = _componentStateFlow + + private val exceptionChannel: Channel = bufferedChannel() + override val exceptionFlow: Flow = exceptionChannel.receiveAsFlow() + + private val submitChannel: Channel = bufferedChannel() + override val submitFlow: Flow = submitChannel.receiveAsFlow() + + override fun initialize(coroutineScope: CoroutineScope) { + setupAnalytics(coroutineScope) + + componentStateFlow.onEach { + onState(it) + }.launchIn(coroutineScope) + } + + private fun setupAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.DEBUG) { "setupAnalytics" } + coroutineScope.launch { + analyticsRepository.setupAnalytics() + } + } + + private fun onState(state: TwintComponentState) { + if (state.isValid) { + submitChannel.trySend(state) + } + } + + override fun observe( + lifecycleOwner: LifecycleOwner, + coroutineScope: CoroutineScope, + callback: (PaymentComponentEvent) -> Unit + ) { + observerRepository.addObservers( + stateFlow = componentStateFlow, + exceptionFlow = exceptionFlow, + submitFlow = submitFlow, + lifecycleOwner = lifecycleOwner, + coroutineScope = coroutineScope, + callback = callback, + ) + } + + override fun removeObserver() { + observerRepository.removeObservers() + } + + private fun createComponentState(): TwintComponentState { + val paymentMethod = TwintPaymentMethod( + type = paymentMethod.type, + subtype = "sdk", + checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + ) + + val paymentComponentData = PaymentComponentData( + paymentMethod = paymentMethod, + order = order, + amount = componentParams.amount, + ) + + return TwintComponentState( + data = paymentComponentData, + isInputValid = true, + isReady = true, + ) + } + + override fun startTwintScreen(activity: ComponentActivity) { + val twint = Twint(activity) { result -> + when (result) { + TwintPayResult.TW_B_SUCCESS -> TODO() + TwintPayResult.TW_B_ERROR -> TODO() + TwintPayResult.TW_B_APP_NOT_INSTALLED -> TODO() + } + } + + twint.payWithCode("test") + } + + override fun getPaymentMethodType(): String { + return paymentMethod.type ?: PaymentMethodTypes.UNKNOWN + } + + override fun onCleared() { + removeObserver() + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt index 1c48d871a7..3783532610 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt @@ -8,7 +8,17 @@ package com.adyen.checkout.twint.internal.ui +import androidx.activity.ComponentActivity import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate +import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.twint.TwintComponentState +import kotlinx.coroutines.flow.Flow -internal interface TwintDelegate : PaymentComponentDelegate +internal interface TwintDelegate : PaymentComponentDelegate { + + val componentStateFlow: Flow + + val exceptionFlow: Flow + + fun startTwintScreen(activity: ComponentActivity) +} From bacb2f3be870653b523a18ba7f9fc0e4580cd4ae Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 20 Oct 2023 16:33:43 +0200 Subject: [PATCH 133/272] Add support for Twint in drop-in COAND-806 --- action-core/build.gradle | 3 +- .../action/core/GenericActionConfiguration.kt | 9 + .../ActionHandlingConfigurationBuilder.kt | 6 + ...ndlingPaymentMethodConfigurationBuilder.kt | 9 + .../provider/ActionComponentExtensions.kt | 2 + .../internal/ui/ActionDelegateProvider.kt | 17 +- .../internal/ui/DefaultBoletoDelegate.kt | 1 + .../components/core/PaymentMethodTypes.kt | 3 +- .../components/core/action/SdkAction.kt | 1 + .../components/core/action/TwintSdkData.kt | 48 +++ .../paymentmethod/GenericPaymentMethod.kt | 5 + .../paymentmethod/PaymentMethodDetails.kt | 1 - .../core/paymentmethod/TwintPaymentMethod.kt | 54 ---- drop-in/build.gradle | 1 + .../checkout/dropin/DropInConfiguration.kt | 1 + .../dropin/internal/ui/DropInActivity.kt | 6 + .../ui/DefaultInstantPaymentDelegate.kt | 10 + twint/build.gradle | 3 - .../java/com/adyen/checkout/twint/Twint.kt | 52 ++++ .../checkout/twint/TwintActionComponent.kt | 67 +++++ ...uration.kt => TwintActionConfiguration.kt} | 32 +- .../adyen/checkout/twint/TwintComponent.kt | 84 ------ .../checkout/twint/TwintComponentState.kt | 19 -- .../provider/TwintActionComponentProvider.kt | 121 ++++++++ .../provider/TwintComponentProvider.kt | 279 ------------------ .../twint/internal/ui/DefaultTwintDelegate.kt | 142 +++++---- .../twint/internal/ui/TwintDelegate.kt | 22 +- .../twint/internal/ui/TwintViewProvider.kt | 30 ++ .../WeChatPayActionComponentProvider.kt | 3 +- 29 files changed, 483 insertions(+), 548 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/Twint.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintActionComponent.kt rename twint/src/main/java/com/adyen/checkout/twint/{TwintConfiguration.kt => TwintActionConfiguration.kt} (77%) delete mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt delete mode 100644 twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt delete mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt diff --git a/action-core/build.gradle b/action-core/build.gradle index b2c6de067c..61c0203b73 100644 --- a/action-core/build.gradle +++ b/action-core/build.gradle @@ -41,8 +41,9 @@ dependencies { api project(':await') api project(':qr-code') api project(':redirect') - compileOnly project(':wechatpay') + api project(':twint') api project(':voucher') + compileOnly project(':wechatpay') //Tests testImplementation project(':3ds2') diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/GenericActionConfiguration.kt b/action-core/src/main/java/com/adyen/checkout/action/core/GenericActionConfiguration.kt index a8b6792ba3..e9dad0c222 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/GenericActionConfiguration.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/GenericActionConfiguration.kt @@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.internal.Configuration import com.adyen.checkout.core.Environment import com.adyen.checkout.qrcode.QRCodeConfiguration import com.adyen.checkout.redirect.RedirectConfiguration +import com.adyen.checkout.twint.TwintActionConfiguration import com.adyen.checkout.voucher.VoucherConfiguration import com.adyen.checkout.wechatpay.WeChatPayActionConfiguration import kotlinx.parcelize.Parcelize @@ -136,6 +137,14 @@ class GenericActionConfiguration private constructor( return this } + /** + * Add configuration for Twint action. + */ + override fun addTwintActionConfiguration(configuration: TwintActionConfiguration): Builder { + availableActionConfigs[configuration::class.java] = configuration + return this + } + /** * Add configuration for WeChat Pay action. */ diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingConfigurationBuilder.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingConfigurationBuilder.kt index 2f841fc99b..91da96d609 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingConfigurationBuilder.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingConfigurationBuilder.kt @@ -12,6 +12,7 @@ import com.adyen.checkout.adyen3ds2.Adyen3DS2Configuration import com.adyen.checkout.await.AwaitConfiguration import com.adyen.checkout.qrcode.QRCodeConfiguration import com.adyen.checkout.redirect.RedirectConfiguration +import com.adyen.checkout.twint.TwintActionConfiguration import com.adyen.checkout.voucher.VoucherConfiguration import com.adyen.checkout.wechatpay.WeChatPayActionConfiguration @@ -37,6 +38,11 @@ internal interface ActionHandlingConfigurationBuilder { */ fun addRedirectActionConfiguration(configuration: RedirectConfiguration): BuilderT + /** + * Add configuration for Twint action. + */ + fun addTwintActionConfiguration(configuration: TwintActionConfiguration): BuilderT + /** * Add configuration for Voucher action. */ diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt index 411c6f740f..75d3fa8837 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt @@ -17,6 +17,7 @@ import com.adyen.checkout.components.core.internal.Configuration import com.adyen.checkout.core.Environment import com.adyen.checkout.qrcode.QRCodeConfiguration import com.adyen.checkout.redirect.RedirectConfiguration +import com.adyen.checkout.twint.TwintActionConfiguration import com.adyen.checkout.voucher.VoucherConfiguration import com.adyen.checkout.wechatpay.WeChatPayActionConfiguration import java.util.Locale @@ -124,6 +125,14 @@ constructor( return this as BuilderT } + /** + * Add configuration for Twint action. + */ + override fun addTwintActionConfiguration(configuration: TwintActionConfiguration): BuilderT { + genericActionConfigurationBuilder.addTwintActionConfiguration(configuration) + return this as BuilderT + } + /** * Add configuration for Voucher action. */ diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/ActionComponentExtensions.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/ActionComponentExtensions.kt index 70adbf359d..b57cacbad2 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/ActionComponentExtensions.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/ActionComponentExtensions.kt @@ -15,6 +15,7 @@ import com.adyen.checkout.components.core.internal.provider.ActionComponentProvi import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.qrcode.QRCodeComponent import com.adyen.checkout.redirect.RedirectComponent +import com.adyen.checkout.twint.TwintActionComponent import com.adyen.checkout.voucher.VoucherComponent import com.adyen.checkout.wechatpay.WeChatPayActionComponent @@ -23,6 +24,7 @@ private val allActionProviders = listOfNotNull( runCompileOnly { AwaitComponent.PROVIDER }, runCompileOnly { QRCodeComponent.PROVIDER }, runCompileOnly { RedirectComponent.PROVIDER }, + runCompileOnly { TwintActionComponent.PROVIDER }, runCompileOnly { VoucherComponent.PROVIDER }, runCompileOnly { WeChatPayActionComponent.PROVIDER }, ) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index 951b17dccf..8efbd75626 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -13,6 +13,7 @@ import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.adyen3ds2.internal.provider.Adyen3DS2ComponentProvider import com.adyen.checkout.await.internal.provider.AwaitComponentProvider import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.AwaitAction import com.adyen.checkout.components.core.action.BaseThreeds2Action @@ -26,6 +27,7 @@ import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.internal.util.LocaleProvider import com.adyen.checkout.qrcode.internal.provider.QRCodeComponentProvider import com.adyen.checkout.redirect.internal.provider.RedirectComponentProvider +import com.adyen.checkout.twint.internal.provider.TwintActionComponentProvider import com.adyen.checkout.voucher.internal.provider.VoucherComponentProvider import com.adyen.checkout.wechatpay.internal.provider.WeChatPayActionComponentProvider @@ -46,7 +48,20 @@ internal class ActionDelegateProvider( is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(dropInOverrideParams, localeProvider) is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) - is SdkAction<*> -> WeChatPayActionComponentProvider(dropInOverrideParams, localeProvider) + is SdkAction<*> -> { + when (action.paymentMethodType) { + PaymentMethodTypes.TWINT -> TwintActionComponentProvider(dropInOverrideParams, localeProvider) + PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPayActionComponentProvider( + dropInOverrideParams, + localeProvider, + ) + + else -> throw CheckoutException( + "Can't find delegate for action: ${action.type} and type: ${action.paymentMethodType}", + ) + } + } + else -> throw CheckoutException("Can't find delegate for action: ${action.type}") } diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt index 97d0b10dca..a4c1164446 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt @@ -235,6 +235,7 @@ internal class DefaultBoletoDelegate( paymentMethod = GenericPaymentMethod( type = paymentMethod.type, checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + subtype = null, ), order = order, amount = componentParams.amount, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt index 4a97ba42a1..ecbdf4e74e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethodTypes.kt @@ -46,7 +46,6 @@ object PaymentMethodTypes { const val PAY_BY_BANK = "paybybank" const val SCHEME = "scheme" const val SEPA = "sepadirectdebit" - const val TWINT = "twint" const val UPI = "upi" const val UPI_COLLECT = "upi_collect" const val UPI_QR = "upi_qr" @@ -56,6 +55,7 @@ object PaymentMethodTypes { const val PAY_NOW = "paynow" const val PIX = "pix" const val PROMPT_PAY = "promptpay" + const val TWINT = "twint" const val WECHAT_PAY_SDK = "wechatpaySDK" const val MULTIBANCO = "multibanco" @@ -143,6 +143,7 @@ object PaymentMethodTypes { PAY_NOW, PIX, PROMPT_PAY, + TWINT, WECHAT_PAY_SDK, MULTIBANCO, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/action/SdkAction.kt b/components-core/src/main/java/com/adyen/checkout/components/core/action/SdkAction.kt index 68b9bd4186..b8d77ba4fc 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/action/SdkAction.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/action/SdkAction.kt @@ -64,6 +64,7 @@ data class SdkAction( } @Suppress("UNCHECKED_CAST") return when (paymentMethodType) { + PaymentMethodTypes.TWINT -> TwintSdkData.SERIALIZER as Serializer PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPaySdkData.SERIALIZER as Serializer else -> throw CheckoutException("sdkData not found for type paymentMethodType - $paymentMethodType") } 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 new file mode 100644 index 0000000000..88ce7ad4a8 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/action/TwintSdkData.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 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 27/10/2023. + */ + +package com.adyen.checkout.components.core.action + +import com.adyen.checkout.core.exception.ModelSerializationException +import kotlinx.parcelize.Parcelize +import org.json.JSONException +import org.json.JSONObject + +@Parcelize +data class TwintSdkData( + val token: String, +) : SdkData() { + + companion object { + + private const val TOKEN = "token" + + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: TwintSdkData): JSONObject { + return try { + JSONObject().apply { + putOpt(TOKEN, modelObject.token) + } + } catch (e: JSONException) { + throw ModelSerializationException(TwintSdkData::class.java, e) + } + } + + override fun deserialize(jsonObject: JSONObject): TwintSdkData { + return try { + TwintSdkData( + token = jsonObject.getString(TOKEN), + ) + } catch (e: JSONException) { + throw ModelSerializationException(TwintSdkData::class.java, e) + } + } + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/GenericPaymentMethod.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/GenericPaymentMethod.kt index 944e8ea62f..de62ba78b7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/GenericPaymentMethod.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/GenericPaymentMethod.kt @@ -17,10 +17,13 @@ import org.json.JSONObject data class GenericPaymentMethod( override var type: String?, override var checkoutAttemptId: String?, + var subtype: String?, ) : PaymentMethodDetails() { companion object { + private const val SUBTYPE = "subtype" + @JvmField val SERIALIZER: Serializer = object : Serializer { override fun serialize(modelObject: GenericPaymentMethod): JSONObject { @@ -28,6 +31,7 @@ data class GenericPaymentMethod( JSONObject().apply { putOpt(TYPE, modelObject.type) putOpt(CHECKOUT_ATTEMPT_ID, modelObject.checkoutAttemptId) + putOpt(SUBTYPE, modelObject.subtype) } } catch (e: JSONException) { throw ModelSerializationException(GenericPaymentMethod::class.java, e) @@ -38,6 +42,7 @@ data class GenericPaymentMethod( return GenericPaymentMethod( type = jsonObject.getStringOrNull(TYPE), checkoutAttemptId = jsonObject.getStringOrNull(CHECKOUT_ATTEMPT_ID), + subtype = jsonObject.getStringOrNull(SUBTYPE) ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt index fe12f6c6da..9ff95241e0 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/PaymentMethodDetails.kt @@ -88,7 +88,6 @@ abstract class PaymentMethodDetails : ModelObject() { SepaPaymentMethod.PAYMENT_METHOD_TYPE -> SepaPaymentMethod.SERIALIZER SevenElevenPaymentMethod.PAYMENT_METHOD_TYPE -> SevenElevenPaymentMethod.SERIALIZER - TwintPaymentMethod.PAYMENT_METHOD_TYPE -> TwintPaymentMethod.SERIALIZER else -> GenericPaymentMethod.SERIALIZER } @Suppress("UNCHECKED_CAST") diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt deleted file mode 100644 index d27b9f43bf..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/TwintPaymentMethod.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2023 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 20/10/2023. - */ - -package com.adyen.checkout.components.core.paymentmethod - -import com.adyen.checkout.components.core.PaymentMethodTypes -import com.adyen.checkout.core.exception.ModelSerializationException -import com.adyen.checkout.core.internal.data.model.getStringOrNull -import kotlinx.parcelize.Parcelize -import org.json.JSONException -import org.json.JSONObject - -@Parcelize -data class TwintPaymentMethod( - override var type: String?, - var subtype: String?, - override var checkoutAttemptId: String?, -) : PaymentMethodDetails() { - - companion object { - - const val PAYMENT_METHOD_TYPE = PaymentMethodTypes.TWINT - - private const val SUBTYPE = "subtype" - - @JvmField - val SERIALIZER: Serializer = object : Serializer { - override fun serialize(modelObject: TwintPaymentMethod): JSONObject { - return try { - JSONObject().apply { - putOpt(TYPE, modelObject.type) - putOpt(SUBTYPE, modelObject.subtype) - putOpt(CHECKOUT_ATTEMPT_ID, modelObject.checkoutAttemptId) - } - } catch (e: JSONException) { - throw ModelSerializationException(TwintPaymentMethod::class.java, e) - } - } - - override fun deserialize(jsonObject: JSONObject): TwintPaymentMethod { - return TwintPaymentMethod( - type = jsonObject.getStringOrNull(TYPE), - subtype = jsonObject.getStringOrNull(SUBTYPE), - checkoutAttemptId = jsonObject.getStringOrNull(CHECKOUT_ATTEMPT_ID), - ) - } - } - } -} diff --git a/drop-in/build.gradle b/drop-in/build.gradle index 085efd47b2..3456563921 100644 --- a/drop-in/build.gradle +++ b/drop-in/build.gradle @@ -74,6 +74,7 @@ dependencies { api project(':sepa') api project(':seven-eleven') api project(':sessions-core') + api project(':twint') api project(':upi') api project(':wechatpay') diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt index e6df140c20..9e906071c4 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt @@ -45,6 +45,7 @@ import com.adyen.checkout.openbanking.OpenBankingConfiguration import com.adyen.checkout.payeasy.PayEasyConfiguration import com.adyen.checkout.sepa.SepaConfiguration import com.adyen.checkout.seveneleven.SevenElevenConfiguration +import com.adyen.checkout.twint.TwintActionConfiguration import com.adyen.checkout.upi.UPIConfiguration import kotlinx.parcelize.Parcelize import java.util.Locale diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt index 0cfd2e0709..ae0f8534c1 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt @@ -37,6 +37,7 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.internal.util.createLocalizedContext import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.dropin.AddressLookupDropInServiceResult import com.adyen.checkout.dropin.BalanceDropInServiceResult import com.adyen.checkout.dropin.BaseDropInServiceResult @@ -62,6 +63,7 @@ import com.adyen.checkout.giftcard.GiftCardComponentState import com.adyen.checkout.redirect.RedirectComponent import com.adyen.checkout.sessions.core.CheckoutSession import com.adyen.checkout.sessions.core.SessionPaymentResult +import com.adyen.checkout.twint.Twint import com.adyen.checkout.wechatpay.WeChatPayUtils import kotlinx.coroutines.launch import com.adyen.checkout.ui.core.R as UICoreR @@ -164,6 +166,10 @@ internal class DropInActivity : initObservers() startDropInService() + + runCompileOnly { + Twint.initialize(this) + } } private fun noDialogPresent(): Boolean { diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index c623dafb3d..aae741547a 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -55,6 +55,7 @@ internal class DefaultInstantPaymentDelegate( paymentMethod = GenericPaymentMethod( type = paymentMethod.type, checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + subtype = getSubtype(paymentMethod), ), order = order, amount = componentParams.amount, @@ -62,6 +63,11 @@ internal class DefaultInstantPaymentDelegate( return InstantComponentState(paymentComponentData, isInputValid = true, isReady = true) } + private fun getSubtype(paymentMethod: PaymentMethod): String? = when (paymentMethod.type) { + PaymentMethodTypes.TWINT -> SDK_SUBTYPE + else -> null + } + override fun initialize(coroutineScope: CoroutineScope) { setupAnalytics(coroutineScope) } @@ -95,4 +101,8 @@ internal class DefaultInstantPaymentDelegate( override fun onCleared() { removeObserver() } + + companion object { + private const val SDK_SUBTYPE = "sdk" + } } diff --git a/twint/build.gradle b/twint/build.gradle index 8f9f5dd588..5d8e5f0213 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -39,12 +39,9 @@ android { dependencies { // Checkout - api project(':action-core') api project(':ui-core') - api project(':sessions-core') // Dependencies - implementation libraries.material implementation files('./libs/TwintSdk-android-7.0.0.aar') //Tests diff --git a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt new file mode 100644 index 0000000000..d46d7ba9b4 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 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 17/11/2023. + */ + +package com.adyen.checkout.twint + +import androidx.activity.ComponentActivity +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import ch.twint.payment.sdk.Twint +import ch.twint.payment.sdk.TwintPayResult +import com.adyen.checkout.core.exception.CheckoutException + +object Twint { + + private var twint: Twint? = null + + private var onResultListener: ((TwintPayResult) -> Unit)? = null + + fun initialize(activity: ComponentActivity) { + twint = Twint(activity, ::onTwintResult) + + val observer = object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + onDestroy() + super.onDestroy(owner) + } + } + activity.lifecycle.addObserver(observer) + } + + private fun onTwintResult(result: TwintPayResult) { + onResultListener?.invoke(result) + } + + internal fun setResultListener(listener: (TwintPayResult) -> Unit) { + onResultListener = listener + } + + internal fun payWithCode(code: String) { + twint?.payWithCode(code) ?: throw CheckoutException("Twint not initialised before payment.") + } + + private fun onDestroy() { + onResultListener = null + twint = null + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintActionComponent.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintActionComponent.kt new file mode 100644 index 0000000000..5c0895adfe --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintActionComponent.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 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 18/10/2023. + */ + +package com.adyen.checkout.twint + +import android.app.Activity +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.adyen.checkout.components.core.action.Action +import com.adyen.checkout.components.core.internal.ActionComponent +import com.adyen.checkout.components.core.internal.ActionComponentEvent +import com.adyen.checkout.components.core.internal.ActionComponentEventHandler +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.twint.internal.provider.TwintActionComponentProvider +import com.adyen.checkout.twint.internal.ui.TwintDelegate +import com.adyen.checkout.ui.core.internal.ui.ComponentViewType +import com.adyen.checkout.ui.core.internal.ui.ViewableComponent +import kotlinx.coroutines.flow.Flow + +class TwintActionComponent internal constructor( + override val delegate: TwintDelegate, + internal val actionComponentEventHandler: ActionComponentEventHandler, +) : ViewModel(), + ActionComponent, + ViewableComponent { + + override val viewFlow: Flow = delegate.viewFlow + + init { + delegate.initialize(viewModelScope) + } + + internal fun observe(lifecycleOwner: LifecycleOwner, callback: (ActionComponentEvent) -> Unit) { + delegate.observe(lifecycleOwner, viewModelScope, callback) + } + + internal fun removeObserver() { + delegate.removeObserver() + } + + override fun canHandleAction(action: Action): Boolean { + return PROVIDER.canHandleAction(action) + } + + override fun handleAction(action: Action, activity: Activity) { + delegate.handleAction(action, activity) + } + + override fun onCleared() { + adyenLog(AdyenLogLevel.DEBUG) { "onCleared" } + super.onCleared() + delegate.onCleared() + } + + companion object { + + @JvmField + val PROVIDER = TwintActionComponentProvider() + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintActionConfiguration.kt similarity index 77% rename from twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt rename to twint/src/main/java/com/adyen/checkout/twint/TwintActionConfiguration.kt index 1b1bb7b156..b01898a6f7 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintConfiguration.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/TwintActionConfiguration.kt @@ -9,28 +9,29 @@ package com.adyen.checkout.twint import android.content.Context -import com.adyen.checkout.action.core.GenericActionConfiguration -import com.adyen.checkout.action.core.internal.ActionHandlingPaymentMethodConfigurationBuilder import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.AnalyticsConfiguration import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.internal.BaseConfigurationBuilder import com.adyen.checkout.components.core.internal.Configuration import com.adyen.checkout.components.core.internal.util.CheckoutConfigurationMarker import com.adyen.checkout.core.Environment import kotlinx.parcelize.Parcelize import java.util.Locale +/** + * Configuration class for the [TwintActionComponent]. + */ @Parcelize -class TwintConfiguration private constructor( +class TwintActionConfiguration private constructor( override val shopperLocale: Locale?, override val environment: Environment, override val clientKey: String, override val analyticsConfiguration: AnalyticsConfiguration?, override val amount: Amount?, - internal val genericActionConfiguration: GenericActionConfiguration, ) : Configuration { - class Builder : ActionHandlingPaymentMethodConfigurationBuilder { + class Builder : BaseConfigurationBuilder { /** * Initialize a configuration builder with the required fields. @@ -76,23 +77,22 @@ class TwintConfiguration private constructor( clientKey: String ) : super(shopperLocale, environment, clientKey) - override fun buildInternal(): TwintConfiguration { - return TwintConfiguration( + override fun buildInternal(): TwintActionConfiguration { + return TwintActionConfiguration( shopperLocale = shopperLocale, environment = environment, clientKey = clientKey, analyticsConfiguration = analyticsConfiguration, amount = amount, - genericActionConfiguration = genericActionConfigurationBuilder.build(), ) } } } -fun CheckoutConfiguration.twint( - configuration: @CheckoutConfigurationMarker TwintConfiguration.Builder.() -> Unit = {} +fun CheckoutConfiguration.twintAction( + configuration: @CheckoutConfigurationMarker TwintActionConfiguration.Builder.() -> Unit = {} ): CheckoutConfiguration { - val config = TwintConfiguration.Builder(environment, clientKey) + val config = TwintActionConfiguration.Builder(environment, clientKey) .apply { shopperLocale?.let { setShopperLocale(it) } amount?.let { setAmount(it) } @@ -104,11 +104,11 @@ fun CheckoutConfiguration.twint( return this } -internal fun CheckoutConfiguration.getTwintConfiguration(): TwintConfiguration? { - return getActionConfiguration(TwintConfiguration::class.java) +internal fun CheckoutConfiguration.getTwintActionConfiguration(): TwintActionConfiguration? { + return getActionConfiguration(TwintActionConfiguration::class.java) } -internal fun TwintConfiguration.toCheckoutConfiguration(): CheckoutConfiguration { +internal fun TwintActionConfiguration.toCheckoutConfiguration(): CheckoutConfiguration { return CheckoutConfiguration( shopperLocale = shopperLocale, environment = environment, @@ -117,9 +117,5 @@ internal fun TwintConfiguration.toCheckoutConfiguration(): CheckoutConfiguration analyticsConfiguration = analyticsConfiguration, ) { addActionConfiguration(this@toCheckoutConfiguration) - - genericActionConfiguration.getAllConfigurations().forEach { - addActionConfiguration(it) - } } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt deleted file mode 100644 index b6b554aefb..0000000000 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintComponent.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2023 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 18/10/2023. - */ - -package com.adyen.checkout.twint - -import androidx.activity.ComponentActivity -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.adyen.checkout.action.core.internal.ActionHandlingComponent -import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent -import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate -import com.adyen.checkout.components.core.PaymentMethodTypes -import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.PaymentComponent -import com.adyen.checkout.components.core.internal.PaymentComponentEvent -import com.adyen.checkout.components.core.internal.ui.ComponentDelegate -import com.adyen.checkout.core.AdyenLogLevel -import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.twint.internal.provider.TwintComponentProvider -import com.adyen.checkout.twint.internal.ui.TwintDelegate -import com.adyen.checkout.ui.core.internal.ui.ComponentViewType -import com.adyen.checkout.ui.core.internal.ui.ViewableComponent -import kotlinx.coroutines.flow.Flow - -class TwintComponent internal constructor( - private val twintDelegate: TwintDelegate, - private val genericActionDelegate: GenericActionDelegate, - private val actionHandlingComponent: DefaultActionHandlingComponent, - internal val componentEventHandler: ComponentEventHandler, -) : ViewModel(), - PaymentComponent, - ViewableComponent, - ActionHandlingComponent by actionHandlingComponent { - - override val delegate: ComponentDelegate get() = actionHandlingComponent.activeDelegate - - override val viewFlow: Flow = genericActionDelegate.viewFlow - - init { - twintDelegate.initialize(viewModelScope) - componentEventHandler.initialize(viewModelScope) - } - - internal fun observe( - lifecycleOwner: LifecycleOwner, - callback: (PaymentComponentEvent) -> Unit - ) { - twintDelegate.observe(lifecycleOwner, viewModelScope, callback) - } - - internal fun removeObserver() { - twintDelegate.removeObserver() - } - - fun startTwintScreen(activity: ComponentActivity) { - twintDelegate.startTwintScreen(activity) - } - - override fun setInteractionBlocked(isInteractionBlocked: Boolean) { - adyenLog(AdyenLogLevel.WARN) { "Interaction with TwintComponent can't be blocked" } - } - - override fun onCleared() { - adyenLog(AdyenLogLevel.DEBUG) { "onCleared" } - super.onCleared() - twintDelegate.onCleared() - componentEventHandler.onCleared() - } - - companion object { - - @JvmField - val PROVIDER = TwintComponentProvider() - - @JvmField - val PAYMENT_METHOD_TYPES = listOf(PaymentMethodTypes.TWINT) - } -} diff --git a/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt b/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt deleted file mode 100644 index c0111e8477..0000000000 --- a/twint/src/main/java/com/adyen/checkout/twint/TwintComponentState.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2023 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 18/10/2023. - */ - -package com.adyen.checkout.twint - -import com.adyen.checkout.components.core.PaymentComponentData -import com.adyen.checkout.components.core.PaymentComponentState -import com.adyen.checkout.components.core.paymentmethod.TwintPaymentMethod - -data class TwintComponentState( - override val data: PaymentComponentData, - override val isInputValid: Boolean, - override val isReady: Boolean, -) : PaymentComponentState diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt new file mode 100644 index 0000000000..5376ffe4c3 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 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 20/10/2023. + */ + +package com.adyen.checkout.twint.internal.provider + +import android.app.Application +import androidx.annotation.RestrictTo +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner +import androidx.savedstate.SavedStateRegistryOwner +import com.adyen.checkout.components.core.ActionComponentCallback +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethodTypes +import com.adyen.checkout.components.core.action.Action +import com.adyen.checkout.components.core.action.SdkAction +import com.adyen.checkout.components.core.internal.ActionObserverRepository +import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler +import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams +import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper +import com.adyen.checkout.components.core.internal.util.get +import com.adyen.checkout.components.core.internal.util.viewModelFactory +import com.adyen.checkout.core.internal.util.LocaleProvider +import com.adyen.checkout.twint.TwintActionComponent +import com.adyen.checkout.twint.TwintActionConfiguration +import com.adyen.checkout.twint.internal.ui.DefaultTwintDelegate +import com.adyen.checkout.twint.internal.ui.TwintDelegate +import com.adyen.checkout.twint.toCheckoutConfiguration + +class TwintActionComponentProvider +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +constructor( + private val dropInOverrideParams: DropInOverrideParams? = null, + private val localeProvider: LocaleProvider = LocaleProvider(), +) : ActionComponentProvider { + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + application: Application, + checkoutConfiguration: CheckoutConfiguration, + callback: ActionComponentCallback, + key: String? + ): TwintActionComponent { + val twintFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> + val twintDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) + TwintActionComponent( + delegate = twintDelegate, + actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + ) + } + + return ViewModelProvider(viewModelStoreOwner, twintFactory)[key, TwintActionComponent::class.java] + .also { component -> + component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + } + } + + override fun getDelegate( + checkoutConfiguration: CheckoutConfiguration, + savedStateHandle: SavedStateHandle, + application: Application, + ): TwintDelegate { + val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = checkoutConfiguration, + deviceLocale = localeProvider.getLocale(application), + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = null, + ) + + return DefaultTwintDelegate( + observerRepository = ActionObserverRepository(), + componentParams = componentParams, + paymentDataRepository = PaymentDataRepository(savedStateHandle), + ) + } + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + application: Application, + configuration: TwintActionConfiguration, + callback: ActionComponentCallback, + key: String?, + ): TwintActionComponent { + return get( + savedStateRegistryOwner = savedStateRegistryOwner, + viewModelStoreOwner = viewModelStoreOwner, + lifecycleOwner = lifecycleOwner, + application = application, + checkoutConfiguration = configuration.toCheckoutConfiguration(), + callback = callback, + key = key, + ) + } + + override val supportedActionTypes: List = listOf(SdkAction.ACTION_TYPE) + + override fun canHandleAction(action: Action): Boolean { + return supportedActionTypes.contains(action.type) && PAYMENT_METHODS.contains(action.paymentMethodType) + } + + override fun providesDetails(action: Action): Boolean { + return true + } + + companion object { + private val PAYMENT_METHODS = listOf(PaymentMethodTypes.TWINT) + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt deleted file mode 100644 index f22391ce69..0000000000 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintComponentProvider.kt +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2023 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 20/10/2023. - */ - -package com.adyen.checkout.twint.internal.provider - -import android.app.Application -import androidx.annotation.RestrictTo -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelStoreOwner -import androidx.savedstate.SavedStateRegistryOwner -import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent -import com.adyen.checkout.action.core.internal.provider.GenericActionComponentProvider -import com.adyen.checkout.components.core.CheckoutConfiguration -import com.adyen.checkout.components.core.ComponentCallback -import com.adyen.checkout.components.core.Order -import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler -import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository -import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider -import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper -import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams -import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper -import com.adyen.checkout.components.core.internal.util.get -import com.adyen.checkout.components.core.internal.util.viewModelFactory -import com.adyen.checkout.core.exception.ComponentException -import com.adyen.checkout.core.internal.data.api.HttpClientFactory -import com.adyen.checkout.core.internal.util.LocaleProvider -import com.adyen.checkout.sessions.core.CheckoutSession -import com.adyen.checkout.sessions.core.SessionComponentCallback -import com.adyen.checkout.sessions.core.internal.SessionComponentEventHandler -import com.adyen.checkout.sessions.core.internal.SessionInteractor -import com.adyen.checkout.sessions.core.internal.SessionSavedStateHandleContainer -import com.adyen.checkout.sessions.core.internal.data.api.SessionRepository -import com.adyen.checkout.sessions.core.internal.data.api.SessionService -import com.adyen.checkout.sessions.core.internal.provider.SessionPaymentComponentProvider -import com.adyen.checkout.twint.TwintComponent -import com.adyen.checkout.twint.TwintComponentState -import com.adyen.checkout.twint.TwintConfiguration -import com.adyen.checkout.twint.internal.ui.DefaultTwintDelegate -import com.adyen.checkout.twint.toCheckoutConfiguration - -class TwintComponentProvider -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -constructor( - private val dropInOverrideParams: DropInOverrideParams? = null, - private val localeProvider: LocaleProvider = LocaleProvider(), - private val analyticsRepository: AnalyticsRepository? = null, -) : - PaymentComponentProvider< - TwintComponent, - TwintConfiguration, - TwintComponentState, - ComponentCallback, - >, - SessionPaymentComponentProvider< - TwintComponent, - TwintConfiguration, - TwintComponentState, - SessionComponentCallback, - > { - - @Suppress("LongMethod") - override fun get( - savedStateRegistryOwner: SavedStateRegistryOwner, - viewModelStoreOwner: ViewModelStoreOwner, - lifecycleOwner: LifecycleOwner, - paymentMethod: PaymentMethod, - checkoutConfiguration: CheckoutConfiguration, - application: Application, - componentCallback: ComponentCallback, - order: Order?, - key: String? - ): TwintComponent { - assertSupported(paymentMethod) - - val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( - checkoutConfiguration = checkoutConfiguration, - deviceLocale = localeProvider.getLocale(application), - dropInOverrideParams = dropInOverrideParams, - componentSessionParams = null, - ) - val viewModelFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> - - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), - ) - - val twintDelegate = DefaultTwintDelegate( - observerRepository = PaymentObserverRepository(), - paymentMethod = paymentMethod, - order = order, - componentParams = componentParams, - analyticsRepository = analyticsRepository, - ) - - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) - - TwintComponent( - twintDelegate = twintDelegate, - genericActionDelegate = genericActionDelegate, - actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, twintDelegate), - componentEventHandler = DefaultComponentEventHandler(), - ) - } - - return ViewModelProvider(viewModelStoreOwner, viewModelFactory)[key, TwintComponent::class.java] - .also { component -> - component.observe(lifecycleOwner) { - component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) - } - } - } - - override fun get( - savedStateRegistryOwner: SavedStateRegistryOwner, - viewModelStoreOwner: ViewModelStoreOwner, - lifecycleOwner: LifecycleOwner, - paymentMethod: PaymentMethod, - configuration: TwintConfiguration, - application: Application, - componentCallback: ComponentCallback, - order: Order?, - key: String?, - ): TwintComponent { - return get( - savedStateRegistryOwner = savedStateRegistryOwner, - viewModelStoreOwner = viewModelStoreOwner, - lifecycleOwner = lifecycleOwner, - paymentMethod = paymentMethod, - checkoutConfiguration = configuration.toCheckoutConfiguration(), - application = application, - componentCallback = componentCallback, - order = order, - key = key, - ) - } - - @Suppress("LongMethod") - override fun get( - savedStateRegistryOwner: SavedStateRegistryOwner, - viewModelStoreOwner: ViewModelStoreOwner, - lifecycleOwner: LifecycleOwner, - checkoutSession: CheckoutSession, - paymentMethod: PaymentMethod, - checkoutConfiguration: CheckoutConfiguration, - application: Application, - componentCallback: SessionComponentCallback, - key: String? - ): TwintComponent { - assertSupported(paymentMethod) - - val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( - checkoutConfiguration = checkoutConfiguration, - deviceLocale = localeProvider.getLocale(application), - dropInOverrideParams = dropInOverrideParams, - componentSessionParams = null, - ) - val viewModelFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> - val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), - ) - - val twintDelegate = DefaultTwintDelegate( - observerRepository = PaymentObserverRepository(), - paymentMethod = paymentMethod, - order = checkoutSession.order, - componentParams = componentParams, - analyticsRepository = analyticsRepository, - ) - - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) - - val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( - savedStateHandle = savedStateHandle, - checkoutSession = checkoutSession, - ) - - val sessionInteractor = SessionInteractor( - sessionRepository = SessionRepository( - sessionService = SessionService(httpClient), - clientKey = componentParams.clientKey, - ), - sessionModel = sessionSavedStateHandleContainer.getSessionModel(), - isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false, - ) - - val sessionComponentEventHandler = SessionComponentEventHandler( - sessionInteractor = sessionInteractor, - sessionSavedStateHandleContainer = sessionSavedStateHandleContainer, - ) - - TwintComponent( - twintDelegate = twintDelegate, - genericActionDelegate = genericActionDelegate, - actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, twintDelegate), - componentEventHandler = sessionComponentEventHandler, - ) - } - - return ViewModelProvider(viewModelStoreOwner, viewModelFactory)[key, TwintComponent::class.java] - .also { component -> - component.observe(lifecycleOwner) { - component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) - } - } - } - - override fun get( - savedStateRegistryOwner: SavedStateRegistryOwner, - viewModelStoreOwner: ViewModelStoreOwner, - lifecycleOwner: LifecycleOwner, - checkoutSession: CheckoutSession, - paymentMethod: PaymentMethod, - configuration: TwintConfiguration, - application: Application, - componentCallback: SessionComponentCallback, - key: String? - ): TwintComponent { - return get( - savedStateRegistryOwner = savedStateRegistryOwner, - viewModelStoreOwner = viewModelStoreOwner, - lifecycleOwner = lifecycleOwner, - checkoutSession = checkoutSession, - paymentMethod = paymentMethod, - checkoutConfiguration = configuration.toCheckoutConfiguration(), - application = application, - componentCallback = componentCallback, - key = key, - ) - } - - private fun assertSupported(paymentMethod: PaymentMethod) { - if (!isPaymentMethodSupported(paymentMethod)) { - throw ComponentException("Unsupported payment method ${paymentMethod.type}") - } - } - - override fun isPaymentMethodSupported(paymentMethod: PaymentMethod): Boolean { - return TwintComponent.PAYMENT_METHOD_TYPES.contains(paymentMethod.type) - } -} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 5cc2458b94..3b73214f71 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -8,80 +8,56 @@ package com.adyen.checkout.twint.internal.ui -import androidx.activity.ComponentActivity +import android.app.Activity import androidx.lifecycle.LifecycleOwner -import ch.twint.payment.sdk.Twint import ch.twint.payment.sdk.TwintPayResult -import com.adyen.checkout.components.core.OrderRequest -import com.adyen.checkout.components.core.PaymentComponentData -import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.PaymentMethodTypes -import com.adyen.checkout.components.core.internal.PaymentComponentEvent -import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.components.core.ActionComponentData +import com.adyen.checkout.components.core.action.Action +import com.adyen.checkout.components.core.action.SdkAction +import com.adyen.checkout.components.core.action.TwintSdkData +import com.adyen.checkout.components.core.internal.ActionComponentEvent +import com.adyen.checkout.components.core.internal.ActionObserverRepository +import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel -import com.adyen.checkout.components.core.paymentmethod.TwintPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException +import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.twint.TwintComponentState +import com.adyen.checkout.twint.Twint +import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch +import org.json.JSONObject internal class DefaultTwintDelegate( - private val observerRepository: PaymentObserverRepository, - private val paymentMethod: PaymentMethod, - private val order: OrderRequest?, - override val componentParams: ComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val observerRepository: ActionObserverRepository, + override val componentParams: GenericComponentParams, + private val paymentDataRepository: PaymentDataRepository, ) : TwintDelegate { - private val _componentStateFlow = MutableStateFlow(createComponentState()) - override val componentStateFlow: Flow = _componentStateFlow + private val detailsChannel: Channel = bufferedChannel() + override val detailsFlow: Flow = detailsChannel.receiveAsFlow() private val exceptionChannel: Channel = bufferedChannel() override val exceptionFlow: Flow = exceptionChannel.receiveAsFlow() - private val submitChannel: Channel = bufferedChannel() - override val submitFlow: Flow = submitChannel.receiveAsFlow() + override val viewFlow: Flow = MutableStateFlow(TwintComponentViewType) - override fun initialize(coroutineScope: CoroutineScope) { - setupAnalytics(coroutineScope) - - componentStateFlow.onEach { - onState(it) - }.launchIn(coroutineScope) - } - - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.DEBUG) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } - } - - private fun onState(state: TwintComponentState) { - if (state.isValid) { - submitChannel.trySend(state) - } - } + override fun initialize(coroutineScope: CoroutineScope) = Unit override fun observe( lifecycleOwner: LifecycleOwner, coroutineScope: CoroutineScope, - callback: (PaymentComponentEvent) -> Unit + callback: (ActionComponentEvent) -> Unit ) { observerRepository.addObservers( - stateFlow = componentStateFlow, + detailsFlow = detailsFlow, exceptionFlow = exceptionFlow, - submitFlow = submitFlow, + permissionFlow = null, lifecycleOwner = lifecycleOwner, coroutineScope = coroutineScope, callback = callback, @@ -92,40 +68,62 @@ internal class DefaultTwintDelegate( observerRepository.removeObservers() } - private fun createComponentState(): TwintComponentState { - val paymentMethod = TwintPaymentMethod( - type = paymentMethod.type, - subtype = "sdk", - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), - ) + override fun handleAction(action: Action, activity: Activity) { + @Suppress("UNCHECKED_CAST") + val sdkAction = (action as? SdkAction) + if (sdkAction == null) { + exceptionChannel.trySend(ComponentException("Unsupported action")) + return + } - val paymentComponentData = PaymentComponentData( - paymentMethod = paymentMethod, - order = order, - amount = componentParams.amount, - ) + val paymentData = action.paymentData + paymentDataRepository.paymentData = paymentData + if (paymentData == null) { + adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } + exceptionChannel.trySend(ComponentException("Payment data is null")) + return + } - return TwintComponentState( - data = paymentComponentData, - isInputValid = true, - isReady = true, - ) + val sdkData = action.sdkData + if (sdkData == null) { + exceptionChannel.trySend(ComponentException("SDK Data is null")) + return + } + + Twint.setResultListener(::handleTwintResult) + try { + Twint.payWithCode(sdkData.token) + } catch (e: CheckoutException) { + exceptionChannel.trySend(e) + } } - override fun startTwintScreen(activity: ComponentActivity) { - val twint = Twint(activity) { result -> - when (result) { - TwintPayResult.TW_B_SUCCESS -> TODO() - TwintPayResult.TW_B_ERROR -> TODO() - TwintPayResult.TW_B_APP_NOT_INSTALLED -> TODO() + private fun handleTwintResult(result: TwintPayResult) { + when (result) { + TwintPayResult.TW_B_SUCCESS -> { + detailsChannel.trySend(createActionComponentData()) + } + + TwintPayResult.TW_B_ERROR -> { + onError(ComponentException("Twint encountered an error.")) + } + + TwintPayResult.TW_B_APP_NOT_INSTALLED -> { + onError(ComponentException("Twint app not installed.")) } } + } - twint.payWithCode("test") + private fun createActionComponentData(): ActionComponentData { + return ActionComponentData( + // The backend doesn't accept null, so we have to send an empty json object. + details = JSONObject(), + paymentData = paymentDataRepository.paymentData, + ) } - override fun getPaymentMethodType(): String { - return paymentMethod.type ?: PaymentMethodTypes.UNKNOWN + override fun onError(e: CheckoutException) { + exceptionChannel.trySend(e) } override fun onCleared() { diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt index 3783532610..98e0c1adbd 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt @@ -8,17 +8,13 @@ package com.adyen.checkout.twint.internal.ui -import androidx.activity.ComponentActivity -import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate -import com.adyen.checkout.core.exception.CheckoutException -import com.adyen.checkout.twint.TwintComponentState -import kotlinx.coroutines.flow.Flow +import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.internal.ui.ActionDelegate +import com.adyen.checkout.components.core.internal.ui.DetailsEmittingDelegate +import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate -internal interface TwintDelegate : PaymentComponentDelegate { - - val componentStateFlow: Flow - - val exceptionFlow: Flow - - fun startTwintScreen(activity: ComponentActivity) -} +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +interface TwintDelegate : + ActionDelegate, + DetailsEmittingDelegate, + ViewProvidingDelegate diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt new file mode 100644 index 0000000000..bb3e31659e --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 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 31/10/2023. + */ + +package com.adyen.checkout.twint.internal.ui + +import android.content.Context +import com.adyen.checkout.ui.core.internal.ui.ComponentView +import com.adyen.checkout.ui.core.internal.ui.ComponentViewType +import com.adyen.checkout.ui.core.internal.ui.ViewProvider +import com.adyen.checkout.ui.core.internal.ui.view.PaymentInProgressView + +internal object TwintViewProvider : ViewProvider { + + override fun getView( + viewType: ComponentViewType, + context: Context, + ): ComponentView = when (viewType) { + TwintComponentViewType -> PaymentInProgressView(context) + else -> throw IllegalArgumentException("Unsupported view type") + } +} + +internal object TwintComponentViewType : ComponentViewType { + override val viewProvider: ViewProvider = TwintViewProvider +} diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt index 8b199b49ef..301ea6af84 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt @@ -115,8 +115,7 @@ constructor( ) } - override val supportedActionTypes: List - get() = listOf(SdkAction.ACTION_TYPE) + override val supportedActionTypes: List = listOf(SdkAction.ACTION_TYPE) override fun canHandleAction(action: Action): Boolean { return supportedActionTypes.contains(action.type) && PAYMENT_METHODS.contains(action.paymentMethodType) From 6a21b744546d722165df274863ed62d46179a3df Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 17 Nov 2023 16:53:45 +0100 Subject: [PATCH 134/272] Publish Twint SDK as a standalone dependency It is not possible to publish nested AAR files, so we have to publish the Twint AAR as a standalone dependency. COAND-806 --- settings.gradle | 1 + .../TwintSdk-android-7.0.0.aar | Bin twint-sdk/build.gradle | 143 ++++++++++++++++++ twint/build.gradle | 2 +- 4 files changed, 145 insertions(+), 1 deletion(-) rename {twint/libs => twint-sdk}/TwintSdk-android-7.0.0.aar (100%) create mode 100644 twint-sdk/build.gradle diff --git a/settings.gradle b/settings.gradle index 3af0126e34..c573f65e98 100644 --- a/settings.gradle +++ b/settings.gradle @@ -63,6 +63,7 @@ include ':3ds2', ':sessions-core', ':test-core', ':twint', + ':twint-sdk', ':ui-core', ':upi', ':voucher', diff --git a/twint/libs/TwintSdk-android-7.0.0.aar b/twint-sdk/TwintSdk-android-7.0.0.aar similarity index 100% rename from twint/libs/TwintSdk-android-7.0.0.aar rename to twint-sdk/TwintSdk-android-7.0.0.aar diff --git a/twint-sdk/build.gradle b/twint-sdk/build.gradle new file mode 100644 index 0000000000..a74fd88fe5 --- /dev/null +++ b/twint-sdk/build.gradle @@ -0,0 +1,143 @@ +configurations.maybeCreate("default") +artifacts.add("default", file('TwintSdk-android-7.0.0.aar')) + +ext.mavenArtifactId = "twint-sdk" +ext.mavenArtifactName = "Adyen checkout Twint SDK wrapper" +ext.mavenArtifactDescription = "Adyen checkout Twint SDK wrapper for Adyen's Checkout API." + +apply plugin: "maven-publish" +apply plugin: "signing" + +ext["signing.keyId"] = '' +ext["signing.password"] = '' +ext["signing.secretKeyRingFile"] = '' +ext["ossrhUsername"] = '' +ext["ossrhPassword"] = '' +ext["sonatypeStagingProfileId"] = '' + +File secretPropsFile = project.rootProject.file('local.properties') +if (secretPropsFile.exists()) { + Properties p = new Properties() + p.load(new FileInputStream(secretPropsFile)) + p.each { name, value -> + ext[name] = value + } +} else { + ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') + ext["signing.password"] = System.getenv('SIGNING_PASSWORD') + ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') + ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') + ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') + ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') +} + +final theGroupId = "com.adyen.checkout" +final theArtifactId = project.mavenArtifactId +final theVersion = project.version_name + +final theName = project.mavenArtifactName +final theDescription = project.mavenArtifactDescription +final theUrl = "https://github.com/Adyen/adyen-android" + +final theLicenseName = "MIT License" +final theLicenseUrl = "https://opensource.org/licenses/MIT" + +final theOrganizationName = "Adyen N.V." +final theOrganizationUrl = "https://www.adyen.com/" + +final theTeamName = "Checkout" + +final theScmConnection = "scm:git:git://github.com/Adyen/adyen-android.git" +final theScmUrl = "https://github.com/Adyen/adyen-android" + +group theGroupId + +project.afterEvaluate { + publishing { + publications { + release(MavenPublication) { + groupId theGroupId + artifactId theArtifactId + version theVersion + + artifact('TwintSdk-android-7.0.0.aar') + + pom { + name = theName + description = theDescription + url = theUrl + licenses { + license { + name = theLicenseName + url = theLicenseUrl + } + } + organization { + name = theOrganizationName + url = theOrganizationUrl + } + developers { + developer { + name = theTeamName + organization = theOrganizationName + organizationUrl = theOrganizationUrl + } + } + scm { + connection = theScmConnection + developerConnection = theScmConnection + url = theScmUrl + } + } + + // Add dependencies to the POM file. + pom.withXml { + logger.lifecycle("\nPublishing maven repository $theGroupId:$theArtifactId:$theVersion\n") + + final dependenciesNode = asNode().appendNode("dependencies") + + ext.addDependency = { dep, scope -> + if (dep.name != 'unspecified') { + final depGroup = dep.group != rootProject.name ? dep.group : theGroupId + final depName = dep.name + final depVersion = dep.version != 'unspecified' ? dep.version : theVersion + + final dependencyNode = dependenciesNode.appendNode("dependency") + dependencyNode.appendNode("groupId", depGroup) + dependencyNode.appendNode("artifactId", depName) + dependencyNode.appendNode("version", depVersion) + dependencyNode.appendNode("scope", scope) + + if (!dep.hasProperty("transitive") || !dep.transitive) { + final exclusionNode = dependencyNode.appendNode("exclusions").appendNode("exclusion") + exclusionNode.appendNode("groupId", "*") + exclusionNode.appendNode("artifactId", "*") + } else if (!dep.properties.excludeRules.empty) { + final exclusionNode = dependencyNode.appendNode("exclusions").appendNode("exclusion") + dep.properties.excludeRules.each { rule -> + exclusionNode.appendNode("groupId", rule.group ?: "*") + exclusionNode.appendNode("artifactId", rule.module ?: "*") + } + } + } + } + } + } + } + + repositories { + maven { + name = "sonatype" + url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + credentials { + username ossrhUsername + password ossrhPassword + } + } + } + } + + signing { + sign publishing.publications + } +} diff --git a/twint/build.gradle b/twint/build.gradle index 5d8e5f0213..151be7788d 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -42,7 +42,7 @@ dependencies { api project(':ui-core') // Dependencies - implementation files('./libs/TwintSdk-android-7.0.0.aar') + implementation project(':twint-sdk') //Tests testImplementation project(':test-core') From 7bb498b67f4d5f47d87dd2b2191ff71ac63b787b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 9 Apr 2024 16:11:44 +0200 Subject: [PATCH 135/272] Make it possible to add InstantPaymentConfiguration to DropInConfiguration COAND-806 --- .../adyen/checkout/dropin/DropInConfiguration.kt | 15 ++++++++++++++- .../instant/InstantPaymentConfiguration.kt | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt index 9e906071c4..f5241f1fb8 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt @@ -35,6 +35,8 @@ import com.adyen.checkout.eps.EPSConfiguration import com.adyen.checkout.giftcard.GiftCardConfiguration import com.adyen.checkout.googlepay.GooglePayConfiguration import com.adyen.checkout.ideal.IdealConfiguration +import com.adyen.checkout.instant.GLOBAL_INSTANT_CONFIG_KEY +import com.adyen.checkout.instant.InstantPaymentConfiguration import com.adyen.checkout.mbway.MBWayConfiguration import com.adyen.checkout.molpay.MolpayConfiguration import com.adyen.checkout.onlinebankingcz.OnlineBankingCZConfiguration @@ -45,7 +47,6 @@ import com.adyen.checkout.openbanking.OpenBankingConfiguration import com.adyen.checkout.payeasy.PayEasyConfiguration import com.adyen.checkout.sepa.SepaConfiguration import com.adyen.checkout.seveneleven.SevenElevenConfiguration -import com.adyen.checkout.twint.TwintActionConfiguration import com.adyen.checkout.upi.UPIConfiguration import kotlinx.parcelize.Parcelize import java.util.Locale @@ -426,6 +427,18 @@ class DropInConfiguration private constructor( return this } + /** + * Add configuration for instant payment methods. + */ + @JvmOverloads + fun addInstantPaymentConfiguration( + instantPaymentConfiguration: InstantPaymentConfiguration, + paymentMethod: String = GLOBAL_INSTANT_CONFIG_KEY, + ): Builder { + availablePaymentConfigs[paymentMethod] = instantPaymentConfiguration + return this + } + /** * Provide a custom name to be shown in Drop-in for payment methods with a type matching [paymentMethodType]. * For [paymentMethodType] you can pass [PaymentMethodTypes] or any other custom value. diff --git a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt index 5523eb3058..7443becbb8 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.instant import android.content.Context +import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting import com.adyen.checkout.action.core.GenericActionConfiguration import com.adyen.checkout.action.core.internal.ActionHandlingPaymentMethodConfigurationBuilder @@ -95,7 +96,8 @@ class InstantPaymentConfiguration private constructor( } } -private const val GLOBAL_INSTANT_CONFIG_KEY = "GLOBAL_INSTANT_CONFIG_KEY" +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +const val GLOBAL_INSTANT_CONFIG_KEY = "GLOBAL_INSTANT_CONFIG_KEY" fun CheckoutConfiguration.instantPayment( paymentMethod: String = GLOBAL_INSTANT_CONFIG_KEY, From 6aeeeee78ba95e0f58731e6177a7c21104b923c7 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 Nov 2023 14:40:18 +0100 Subject: [PATCH 136/272] Add configuration for actions to be handled with a sdk or not COAND-806 --- .../internal/ui/ActionDelegateProvider.kt | 31 +++++++------ .../CheckoutConfigurationProvider.kt | 5 ++ .../instant/InstantPaymentConfiguration.kt | 21 +++++++-- .../InstantPaymentComponentProvider.kt | 8 ++-- .../ui/DefaultInstantPaymentDelegate.kt | 13 ++++-- .../ui/model/InstantComponentParams.kt | 17 +++++++ .../ui/model/InstantComponentParamsMapper.kt | 46 +++++++++++++++++++ .../ui/DefaultInstantPaymentDelegateTest.kt | 9 ++-- 8 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt create mode 100644 instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index 8efbd75626..b4710b77a7 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.action.QrCodeAction import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.VoucherAction +import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.core.exception.CheckoutException @@ -48,19 +49,7 @@ internal class ActionDelegateProvider( is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(dropInOverrideParams, localeProvider) is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) - is SdkAction<*> -> { - when (action.paymentMethodType) { - PaymentMethodTypes.TWINT -> TwintActionComponentProvider(dropInOverrideParams, localeProvider) - PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPayActionComponentProvider( - dropInOverrideParams, - localeProvider, - ) - - else -> throw CheckoutException( - "Can't find delegate for action: ${action.type} and type: ${action.paymentMethodType}", - ) - } - } + is SdkAction<*> -> getSdkActionComponentProvider(action) else -> throw CheckoutException("Can't find delegate for action: ${action.type}") } @@ -71,4 +60,20 @@ internal class ActionDelegateProvider( application = application, ) } + + private fun getSdkActionComponentProvider( + action: Action, + ): ActionComponentProvider<*, *, *> { + return when (action.paymentMethodType) { + PaymentMethodTypes.TWINT -> TwintActionComponentProvider(dropInOverrideParams, localeProvider) + PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPayActionComponentProvider( + dropInOverrideParams, + localeProvider, + ) + + else -> throw CheckoutException( + "Can't find delegate for action: ${action.type} and type: ${action.paymentMethodType}", + ) + } + } } diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt index 4206855e70..4282dddf4f 100644 --- a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt +++ b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt @@ -22,6 +22,7 @@ import com.adyen.checkout.example.data.storage.CardInstallmentOptionsMode import com.adyen.checkout.example.data.storage.KeyValueStorage import com.adyen.checkout.giftcard.giftCard import com.adyen.checkout.googlepay.googlePay +import com.adyen.checkout.instant.instantPayment import dagger.hilt.android.qualifiers.ApplicationContext import java.util.Locale import javax.inject.Inject @@ -83,6 +84,10 @@ internal class CheckoutConfigurationProvider @Inject constructor( setCountryCode(keyValueStorage.getCountry()) } + instantPayment { + setUseSdk(true) + } + // Actions adyen3DS2 { setThreeDSRequestorAppURL("https://www.adyen.com") diff --git a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt index 7443becbb8..7b008c10ad 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt @@ -10,7 +10,6 @@ package com.adyen.checkout.instant import android.content.Context import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting import com.adyen.checkout.action.core.GenericActionConfiguration import com.adyen.checkout.action.core.internal.ActionHandlingPaymentMethodConfigurationBuilder import com.adyen.checkout.components.core.Amount @@ -26,12 +25,15 @@ import java.util.Locale * Configuration class for the [InstantPaymentComponent]. */ @Parcelize -class InstantPaymentConfiguration private constructor( +class InstantPaymentConfiguration +@Suppress("LongParameterList") +private constructor( override val shopperLocale: Locale?, override val environment: Environment, override val clientKey: String, override val analyticsConfiguration: AnalyticsConfiguration?, override val amount: Amount?, + val shouldUseSdk: Boolean?, internal val genericActionConfiguration: GenericActionConfiguration, ) : Configuration { @@ -40,6 +42,8 @@ class InstantPaymentConfiguration private constructor( */ class Builder : ActionHandlingPaymentMethodConfigurationBuilder { + private var shouldUseSdk: Boolean? = null + /** * Initialize a configuration builder with the required fields. * @@ -83,6 +87,17 @@ class InstantPaymentConfiguration private constructor( clientKey, ) + /** + * Sets if the payment method will be handled with the corresponding SDK or with a web flow. If there is no SDK + * available then the payment method will be handled with a web flow. + * + * Default is true. + */ + fun setUseSdk(shouldUseSdk: Boolean): Builder { + this.shouldUseSdk = shouldUseSdk + return this + } + override fun buildInternal(): InstantPaymentConfiguration { return InstantPaymentConfiguration( shopperLocale = shopperLocale, @@ -90,6 +105,7 @@ class InstantPaymentConfiguration private constructor( clientKey = clientKey, analyticsConfiguration = analyticsConfiguration, amount = amount, + shouldUseSdk = shouldUseSdk, genericActionConfiguration = genericActionConfigurationBuilder.build(), ) } @@ -115,7 +131,6 @@ fun CheckoutConfiguration.instantPayment( return this } -@VisibleForTesting internal fun CheckoutConfiguration.getInstantPaymentConfiguration( paymentMethod: String = GLOBAL_INSTANT_CONFIG_KEY, ): InstantPaymentConfiguration? { diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index 6928b8b3ad..19deb75e1e 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -31,7 +31,6 @@ import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepo import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams -import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.components.core.internal.util.get import com.adyen.checkout.components.core.internal.util.viewModelFactory import com.adyen.checkout.core.exception.ComponentException @@ -41,6 +40,7 @@ import com.adyen.checkout.instant.InstantComponentState import com.adyen.checkout.instant.InstantPaymentComponent import com.adyen.checkout.instant.InstantPaymentConfiguration import com.adyen.checkout.instant.internal.ui.DefaultInstantPaymentDelegate +import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper import com.adyen.checkout.instant.toCheckoutConfiguration import com.adyen.checkout.sessions.core.CheckoutSession import com.adyen.checkout.sessions.core.SessionComponentCallback @@ -86,11 +86,12 @@ constructor( assertSupported(paymentMethod) val genericFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> - val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + val componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = checkoutConfiguration, deviceLocale = localeProvider.getLocale(application), dropInOverrideParams = dropInOverrideParams, componentSessionParams = null, + paymentMethod = paymentMethod, ) val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( @@ -174,11 +175,12 @@ constructor( assertSupported(paymentMethod) val genericFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> - val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + val componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = checkoutConfiguration, deviceLocale = localeProvider.getLocale(application), dropInOverrideParams = dropInOverrideParams, componentSessionParams = SessionParamsFactory.create(checkoutSession), + paymentMethod = paymentMethod, ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index aae741547a..9b47c928ee 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -16,13 +16,13 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.instant.InstantComponentState +import com.adyen.checkout.instant.internal.ui.model.InstantComponentParams import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -35,7 +35,7 @@ internal class DefaultInstantPaymentDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: Order?, - override val componentParams: GenericComponentParams, + override val componentParams: InstantComponentParams, private val analyticsRepository: AnalyticsRepository, ) : InstantPaymentDelegate { @@ -63,9 +63,12 @@ internal class DefaultInstantPaymentDelegate( return InstantComponentState(paymentComponentData, isInputValid = true, isReady = true) } - private fun getSubtype(paymentMethod: PaymentMethod): String? = when (paymentMethod.type) { - PaymentMethodTypes.TWINT -> SDK_SUBTYPE - else -> null + private fun getSubtype(paymentMethod: PaymentMethod): String? { + if (!componentParams.shouldUseSdk) return null + return when (paymentMethod.type) { + PaymentMethodTypes.TWINT -> SDK_SUBTYPE + else -> null + } } override fun initialize(coroutineScope: CoroutineScope) { diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt new file mode 100644 index 0000000000..d9effa0284 --- /dev/null +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 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/11/2023. + */ + +package com.adyen.checkout.instant.internal.ui.model + +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParams +import com.adyen.checkout.components.core.internal.ui.model.ComponentParams + +internal data class InstantComponentParams( + private val commonComponentParams: CommonComponentParams, + val shouldUseSdk: Boolean, +) : ComponentParams by commonComponentParams diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt new file mode 100644 index 0000000000..7b74dbe356 --- /dev/null +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 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/11/2023. + */ + +package com.adyen.checkout.instant.internal.ui.model + +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams +import com.adyen.checkout.components.core.internal.ui.model.SessionParams +import com.adyen.checkout.instant.getInstantPaymentConfiguration +import java.util.Locale + +internal class InstantComponentParamsMapper( + private val commonComponentParamsMapper: CommonComponentParamsMapper, +) { + + fun mapToParams( + checkoutConfiguration: CheckoutConfiguration, + deviceLocale: Locale, + dropInOverrideParams: DropInOverrideParams?, + componentSessionParams: SessionParams?, + paymentMethod: PaymentMethod, + ): InstantComponentParams { + val commonComponentParamsMapperData = commonComponentParamsMapper.mapToParams( + checkoutConfiguration, + deviceLocale, + dropInOverrideParams, + componentSessionParams, + ) + + val instantActionConfiguration = + paymentMethod.type?.let { checkoutConfiguration.getInstantPaymentConfiguration(it) } + ?: checkoutConfiguration.getInstantPaymentConfiguration() + + return InstantComponentParams( + commonComponentParams = commonComponentParamsMapperData.commonComponentParams, + shouldUseSdk = instantActionConfiguration?.shouldUseSdk ?: true, + ) + } +} diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index d16c1bfca4..2415aaac70 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -13,10 +13,11 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper -import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper +import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.test.LoggingExtension import kotlinx.coroutines.CoroutineScope @@ -119,9 +120,9 @@ class DefaultInstantPaymentDelegateTest( observerRepository = PaymentObserverRepository(), paymentMethod = PaymentMethod(type = TYPE), order = TEST_ORDER, - componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - analyticsRepository = analyticsRepository, + componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null, PaymentMethod(type = "paypal")), + analyticsRepository = analyticsRepository ) } From 00be242a77aa3ec22fc61085ba7338da2f6d62e0 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 Nov 2023 16:21:36 +0100 Subject: [PATCH 137/272] Add unit tests COAND-806 --- twint/build.gradle | 1 + .../java/com/adyen/checkout/twint/Twint.kt | 8 +- .../twint/internal/ui/DefaultTwintDelegate.kt | 14 +- .../twint/TwintActionComponentTest.kt | 106 ++++++++++++++ .../internal/ui/DefaultTwintDelegateTest.kt | 137 ++++++++++++++++++ .../internal/ui/DefaultWeChatDelegate.kt | 5 +- 6 files changed, 258 insertions(+), 13 deletions(-) create mode 100644 twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt create mode 100644 twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt diff --git a/twint/build.gradle b/twint/build.gradle index 151be7788d..41af91cb34 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -46,6 +46,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt index d46d7ba9b4..77e954895e 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt @@ -17,12 +17,12 @@ import com.adyen.checkout.core.exception.CheckoutException object Twint { - private var twint: Twint? = null + private var twintObject: Twint? = null private var onResultListener: ((TwintPayResult) -> Unit)? = null fun initialize(activity: ComponentActivity) { - twint = Twint(activity, ::onTwintResult) + twintObject = Twint(activity, ::onTwintResult) val observer = object : DefaultLifecycleObserver { override fun onDestroy(owner: LifecycleOwner) { @@ -42,11 +42,11 @@ object Twint { } internal fun payWithCode(code: String) { - twint?.payWithCode(code) ?: throw CheckoutException("Twint not initialised before payment.") + twintObject?.payWithCode(code) ?: throw CheckoutException("Twint not initialised before payment.") } private fun onDestroy() { onResultListener = null - twint = null + twintObject = null } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 3b73214f71..7f6a8dd95b 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.twint.internal.ui import android.app.Activity +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import ch.twint.payment.sdk.TwintPayResult import com.adyen.checkout.components.core.ActionComponentData @@ -68,9 +69,9 @@ internal class DefaultTwintDelegate( observerRepository.removeObservers() } + @SuppressWarnings("ReturnCount") override fun handleAction(action: Action, activity: Activity) { - @Suppress("UNCHECKED_CAST") - val sdkAction = (action as? SdkAction) + val sdkAction = action as? SdkAction<*> if (sdkAction == null) { exceptionChannel.trySend(ComponentException("Unsupported action")) return @@ -84,9 +85,9 @@ internal class DefaultTwintDelegate( return } - val sdkData = action.sdkData - if (sdkData == null) { - exceptionChannel.trySend(ComponentException("SDK Data is null")) + val sdkData = sdkAction.sdkData + if (sdkData == null || sdkData !is TwintSdkData) { + exceptionChannel.trySend(ComponentException("SDK Data is null or of wrong type")) return } @@ -98,7 +99,8 @@ internal class DefaultTwintDelegate( } } - private fun handleTwintResult(result: TwintPayResult) { + @VisibleForTesting + internal fun handleTwintResult(result: TwintPayResult) { when (result) { TwintPayResult.TW_B_SUCCESS -> { detailsChannel.trySend(createActionComponentData()) diff --git a/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt b/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt new file mode 100644 index 0000000000..437698edd8 --- /dev/null +++ b/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt @@ -0,0 +1,106 @@ +package com.adyen.checkout.twint + +import android.app.Activity +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.viewModelScope +import com.adyen.checkout.components.core.action.SdkAction +import com.adyen.checkout.components.core.action.SdkData +import com.adyen.checkout.components.core.internal.ActionComponentEvent +import com.adyen.checkout.components.core.internal.ActionComponentEventHandler +import com.adyen.checkout.core.AdyenLogger +import com.adyen.checkout.core.internal.util.Logger +import com.adyen.checkout.test.extensions.invokeOnCleared +import com.adyen.checkout.test.extensions.test +import com.adyen.checkout.twint.internal.ui.TwintComponentViewType +import com.adyen.checkout.twint.internal.ui.TwintDelegate +import com.adyen.checkout.ui.core.internal.test.TestComponentViewType +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockitoExtension::class) +internal class TwintActionComponentTest( + @Mock private val twintDelegate: TwintDelegate, + @Mock private val actionComponentEventHandler: ActionComponentEventHandler, +) { + + private lateinit var component: TwintActionComponent + + @BeforeEach + fun beforeEach() { + AdyenLogger.setLogLevel(Logger.NONE) + + whenever(twintDelegate.viewFlow) doReturn MutableStateFlow(TwintComponentViewType) + component = TwintActionComponent(twintDelegate, actionComponentEventHandler) + } + + @Test + fun `when component is created, then delegate is initialized`() { + verify(twintDelegate).initialize(component.viewModelScope) + } + + @Test + fun `when component is cleared, then delegate is cleared`() { + component.invokeOnCleared() + + verify(twintDelegate).onCleared() + } + + @Test + fun `when observe is called, then observe in delegate is called`() { + val lifecycleOwner = mock() + val callback: (ActionComponentEvent) -> Unit = {} + + component.observe(lifecycleOwner, callback) + + verify(twintDelegate).observe(lifecycleOwner, component.viewModelScope, callback) + } + + @Test + fun `when removeObserver is called, then removeObserver in delegate is called`() { + component.removeObserver() + + verify(twintDelegate).removeObserver() + } + + @Test + fun `when component is initialized, then view flow should match delegate view flow`() = runTest { + val testFlow = component.viewFlow.test(testScheduler) + + assertEquals(TwintComponentViewType, testFlow.latestValue) + } + + @Test + fun `when delegate view flow emits a value, then component view flow should match that value`() = runTest { + val delegateViewFlow = MutableStateFlow(TestComponentViewType.VIEW_TYPE_1) + whenever(twintDelegate.viewFlow) doReturn delegateViewFlow + component = TwintActionComponent(twintDelegate, actionComponentEventHandler) + + val testFlow = component.viewFlow.test(testScheduler) + + assertEquals(TestComponentViewType.VIEW_TYPE_1, testFlow.latestValue) + + delegateViewFlow.emit(TestComponentViewType.VIEW_TYPE_2) + assertEquals(TestComponentViewType.VIEW_TYPE_2, testFlow.latestValue) + } + + @Test + fun `when handleAction is called, then handleAction in delegate is called`() { + val action = SdkAction() + val activity = Activity() + component.handleAction(action, activity) + + verify(twintDelegate).handleAction(action, activity) + } +} diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt new file mode 100644 index 0000000000..bea1c5223b --- /dev/null +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2023 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/11/2023. + */ + +package com.adyen.checkout.twint.internal.ui + +import android.app.Activity +import androidx.lifecycle.SavedStateHandle +import ch.twint.payment.sdk.TwintPayResult +import com.adyen.checkout.components.core.ActionComponentData +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.action.Action +import com.adyen.checkout.components.core.action.AwaitAction +import com.adyen.checkout.components.core.action.SdkAction +import com.adyen.checkout.components.core.action.TwintSdkData +import com.adyen.checkout.components.core.action.WeChatPaySdkData +import com.adyen.checkout.components.core.internal.ActionObserverRepository +import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper +import com.adyen.checkout.core.Environment +import com.adyen.checkout.test.LoggingExtension +import com.adyen.checkout.test.extensions.test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import org.mockito.junit.jupiter.MockitoExtension +import java.util.Locale + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockitoExtension::class, LoggingExtension::class) +internal class DefaultTwintDelegateTest { + + private lateinit var delegate: DefaultTwintDelegate + + @BeforeEach + fun beforeEach() { + val configuration = CheckoutConfiguration(Environment.TEST, "") + + delegate = DefaultTwintDelegate( + observerRepository = ActionObserverRepository(), + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + paymentDataRepository = PaymentDataRepository(SavedStateHandle()), + ) + } + + @ParameterizedTest + @MethodSource("handleActionSource") + fun `when handling action, then expect`(action: Action, expectedErrorMessage: String) = runTest { + val testFlow = delegate.exceptionFlow.test(testScheduler) + + delegate.handleAction(action, Activity()) + + assertEquals(expectedErrorMessage, testFlow.latestValue.message) + } + + @ParameterizedTest + @MethodSource("handleTwintResult") + fun `when handling twint result, then expect`(result: TwintPayResult, testResult: TwintTestResult) = runTest { + val detailsFlow = delegate.detailsFlow.test(testScheduler) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + + delegate.handleTwintResult(result) + + when (testResult) { + is TwintTestResult.Error -> { + assertEquals(testResult.expectedMessage, exceptionFlow.latestValue.message) + } + + is TwintTestResult.Success -> { + with(detailsFlow.latestValue) { + assertEquals(testResult.expectedActionComponentData.paymentData, paymentData) + assertEquals(testResult.expectedActionComponentData.details.toString(), details.toString()) + } + } + } + } + + companion object { + private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + + @JvmStatic + fun handleActionSource() = listOf( + arguments(AwaitAction(), "Unsupported action"), + arguments( + SdkAction(paymentData = "something", sdkData = WeChatPaySdkData()), + "SDK Data is null or of wrong type", + ), + arguments(SdkAction(paymentData = "something"), "SDK Data is null or of wrong type"), + arguments(SdkAction(paymentData = null), "Payment data is null"), + arguments( + SdkAction(paymentData = "something", sdkData = null), + "SDK Data is null or of wrong type", + ), + // Success case: we cannot instantiate the Twint SDK (because it uses activity), but if this happens we + // successfully handed the action over to Twint. + arguments( + SdkAction(paymentData = "something", sdkData = TwintSdkData("token")), + "Twint not initialised before payment.", + ), + ) + + @JvmStatic + fun handleTwintResult() = listOf( + arguments( + TwintPayResult.TW_B_SUCCESS, + TwintTestResult.Success(ActionComponentData("test", JSONObject())), + ), + arguments( + TwintPayResult.TW_B_ERROR, + TwintTestResult.Error("Twint encountered an error."), + ), + arguments( + TwintPayResult.TW_B_APP_NOT_INSTALLED, + TwintTestResult.Error("Twint app not installed."), + ), + ) + } + + sealed class TwintTestResult { + data class Success(val expectedActionComponentData: ActionComponentData) : TwintTestResult() + + data class Error(val expectedMessage: String) : TwintTestResult() + } +} diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt index 8cfabdc65e..24877f7192 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt @@ -111,8 +111,7 @@ internal class DefaultWeChatDelegate( @SuppressWarnings("ReturnCount") override fun handleAction(action: Action, activity: Activity) { - @Suppress("UNCHECKED_CAST") - val sdkAction = (action as? SdkAction) + val sdkAction = (action as? SdkAction<*>) if (sdkAction == null) { exceptionChannel.trySend(ComponentException("Unsupported action")) return @@ -130,7 +129,7 @@ internal class DefaultWeChatDelegate( } val sdkData = action.sdkData - if (sdkData == null) { + if (sdkData == null || sdkData !is WeChatPaySdkData) { exceptionChannel.trySend(ComponentException("SDK Data is null")) return } From 64b006da7191959eb4d1bfcb8d2bb90ab95e4f1b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 Nov 2023 09:52:47 +0100 Subject: [PATCH 138/272] Use compileOnly for twint in action-core COAND-806 --- action-core/build.gradle | 2 +- econtext/build.gradle | 1 + issuer-list/build.gradle | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/action-core/build.gradle b/action-core/build.gradle index 61c0203b73..775852c081 100644 --- a/action-core/build.gradle +++ b/action-core/build.gradle @@ -41,7 +41,7 @@ dependencies { api project(':await') api project(':qr-code') api project(':redirect') - api project(':twint') + compileOnly project(':twint') api project(':voucher') compileOnly project(':wechatpay') diff --git a/econtext/build.gradle b/econtext/build.gradle index 67715bcc74..a375c9c912 100644 --- a/econtext/build.gradle +++ b/econtext/build.gradle @@ -48,6 +48,7 @@ dependencies { //Tests testImplementation project(':3ds2') testImplementation project(':test-core') + testImplementation project(':twint') testImplementation project(':wechatpay') testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/issuer-list/build.gradle b/issuer-list/build.gradle index fa5cabdb27..926b06c5c9 100644 --- a/issuer-list/build.gradle +++ b/issuer-list/build.gradle @@ -49,6 +49,7 @@ dependencies { //Tests testImplementation project(':3ds2') testImplementation project(':test-core') + testImplementation project(':twint') testImplementation project(':wechatpay') testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines From 0690fab04714dc7eb543ec31983710ef94436596 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 Nov 2023 11:00:35 +0100 Subject: [PATCH 139/272] Use an enum in the config to specify how actions should be handled COAND-806 --- action-core/build.gradle | 1 + .../internal/ui/ActionDelegateProviderTest.kt | 23 +- .../CheckoutConfigurationProvider.kt | 2 +- .../checkout/instant/ActionHandlingMethod.kt | 26 ++ .../instant/InstantPaymentConfiguration.kt | 15 +- .../ui/DefaultInstantPaymentDelegate.kt | 14 +- .../ui/model/InstantComponentParams.kt | 3 +- .../ui/model/InstantComponentParamsMapper.kt | 4 +- .../ui/DefaultInstantPaymentDelegateTest.kt | 5 +- .../model/InstantComponentParamsMapperTest.kt | 251 ++++++++++++++++++ .../twint/TwintActionComponentTest.kt | 7 +- .../internal/ui/DefaultTwintDelegateTest.kt | 2 +- 12 files changed, 328 insertions(+), 25 deletions(-) create mode 100644 instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt create mode 100644 instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt diff --git a/action-core/build.gradle b/action-core/build.gradle index 775852c081..34d6d806e5 100644 --- a/action-core/build.gradle +++ b/action-core/build.gradle @@ -48,6 +48,7 @@ dependencies { //Tests testImplementation project(':3ds2') testImplementation project(':test-core') + testImplementation project(':twint') testImplementation project(':wechatpay') testImplementation testLibraries.json testImplementation testLibraries.junit5 diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt index 6cd18f7d04..fbdfdf0124 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.adyen3ds2.internal.ui.Adyen3DS2Delegate import com.adyen.checkout.await.internal.ui.AwaitDelegate import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.AwaitAction import com.adyen.checkout.components.core.action.QrCodeAction @@ -14,6 +15,7 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.Threeds2Action import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.action.Threeds2FingerprintAction +import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ui.ActionDelegate @@ -22,6 +24,7 @@ import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.internal.util.LocaleProvider import com.adyen.checkout.qrcode.internal.ui.QRCodeDelegate import com.adyen.checkout.redirect.internal.ui.RedirectDelegate +import com.adyen.checkout.twint.internal.ui.TwintDelegate import com.adyen.checkout.voucher.internal.ui.VoucherDelegate import com.adyen.checkout.wechatpay.internal.ui.WeChatDelegate import org.junit.jupiter.api.Assertions.assertInstanceOf @@ -74,6 +77,20 @@ internal class ActionDelegateProviderTest( } } + @Test + fun `when sdk action with unknown paymentMethodType is used, then an error will be thrown`() { + val configuration = CheckoutConfiguration(Environment.TEST, "") + + assertThrows { + actionDelegateProvider.getDelegate( + action = SdkAction(paymentMethodType = "test"), + checkoutConfiguration = configuration, + savedStateHandle = SavedStateHandle(), + application = Application(), + ) + } + } + companion object { @JvmStatic @@ -85,7 +102,11 @@ internal class ActionDelegateProviderTest( arguments(Threeds2ChallengeAction(), Adyen3DS2Delegate::class.java), arguments(Threeds2FingerprintAction(), Adyen3DS2Delegate::class.java), arguments(VoucherAction(), VoucherDelegate::class.java), - arguments(SdkAction(), WeChatDelegate::class.java), + arguments( + SdkAction(paymentMethodType = PaymentMethodTypes.WECHAT_PAY_SDK), + WeChatDelegate::class.java, + ), + arguments(SdkAction(paymentMethodType = PaymentMethodTypes.TWINT), TwintDelegate::class.java), ) } diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt index 4282dddf4f..9bc0a3d34e 100644 --- a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt +++ b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt @@ -85,7 +85,7 @@ internal class CheckoutConfigurationProvider @Inject constructor( } instantPayment { - setUseSdk(true) + setActionHandlingMethod(ActionHandlingMethod.PREFER_NATIVE) } // Actions diff --git a/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt b/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt new file mode 100644 index 0000000000..193dd52c00 --- /dev/null +++ b/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 28/11/2023. + */ + +package com.adyen.checkout.instant + +/** + * Used in [InstantPaymentConfiguration.Builder.setActionHandlingMethod] to set the method used to handle actions. + */ +enum class ActionHandlingMethod { + /** + * The action will be handled in a native way (e.g. using a SDK). **If** there is no way to handle the action + * natively, then a fallback method will be used (e.g. a web flow). + */ + PREFER_NATIVE, + + /** + * The action will be handled with a web flow. **If** there is no way to handle the action with a web flow, then + * other methods might be used. + */ + PREFER_WEB, +} diff --git a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt index 7b008c10ad..b56ae21b8d 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/InstantPaymentConfiguration.kt @@ -33,7 +33,7 @@ private constructor( override val clientKey: String, override val analyticsConfiguration: AnalyticsConfiguration?, override val amount: Amount?, - val shouldUseSdk: Boolean?, + val actionHandlingMethod: ActionHandlingMethod?, internal val genericActionConfiguration: GenericActionConfiguration, ) : Configuration { @@ -42,7 +42,7 @@ private constructor( */ class Builder : ActionHandlingPaymentMethodConfigurationBuilder { - private var shouldUseSdk: Boolean? = null + private var actionHandlingMethod: ActionHandlingMethod? = null /** * Initialize a configuration builder with the required fields. @@ -88,13 +88,12 @@ private constructor( ) /** - * Sets if the payment method will be handled with the corresponding SDK or with a web flow. If there is no SDK - * available then the payment method will be handled with a web flow. + * Sets the method used to handle actions. See [ActionHandlingMethod] for the available options. * - * Default is true. + * Default is [ActionHandlingMethod.PREFER_NATIVE]. */ - fun setUseSdk(shouldUseSdk: Boolean): Builder { - this.shouldUseSdk = shouldUseSdk + fun setActionHandlingMethod(actionHandlingMethod: ActionHandlingMethod): Builder { + this.actionHandlingMethod = actionHandlingMethod return this } @@ -105,7 +104,7 @@ private constructor( clientKey = clientKey, analyticsConfiguration = analyticsConfiguration, amount = amount, - shouldUseSdk = shouldUseSdk, + actionHandlingMethod = actionHandlingMethod, genericActionConfiguration = genericActionConfigurationBuilder.build(), ) } diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index 9b47c928ee..ef05504d0b 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.instant.ActionHandlingMethod import com.adyen.checkout.instant.InstantComponentState import com.adyen.checkout.instant.internal.ui.model.InstantComponentParams import kotlinx.coroutines.CoroutineScope @@ -64,10 +65,15 @@ internal class DefaultInstantPaymentDelegate( } private fun getSubtype(paymentMethod: PaymentMethod): String? { - if (!componentParams.shouldUseSdk) return null - return when (paymentMethod.type) { - PaymentMethodTypes.TWINT -> SDK_SUBTYPE - else -> null + return when (componentParams.actionHandlingMethod) { + ActionHandlingMethod.PREFER_NATIVE -> { + when (paymentMethod.type) { + PaymentMethodTypes.TWINT -> SDK_SUBTYPE + else -> null + } + } + + ActionHandlingMethod.PREFER_WEB -> null } } diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt index d9effa0284..769f4651e8 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParams.kt @@ -10,8 +10,9 @@ package com.adyen.checkout.instant.internal.ui.model import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParams import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.instant.ActionHandlingMethod internal data class InstantComponentParams( private val commonComponentParams: CommonComponentParams, - val shouldUseSdk: Boolean, + val actionHandlingMethod: ActionHandlingMethod, ) : ComponentParams by commonComponentParams diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt index 7b74dbe356..6dbaa8d693 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapper.kt @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.internal.ui.model.SessionParams +import com.adyen.checkout.instant.ActionHandlingMethod import com.adyen.checkout.instant.getInstantPaymentConfiguration import java.util.Locale @@ -40,7 +41,8 @@ internal class InstantComponentParamsMapper( return InstantComponentParams( commonComponentParams = commonComponentParamsMapperData.commonComponentParams, - shouldUseSdk = instantActionConfiguration?.shouldUseSdk ?: true, + actionHandlingMethod = instantActionConfiguration?.actionHandlingMethod + ?: ActionHandlingMethod.PREFER_NATIVE, ) } } diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index 2415aaac70..29dd8d9686 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -13,12 +13,11 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper -import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper import com.adyen.checkout.core.Environment +import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper import com.adyen.checkout.test.LoggingExtension import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -122,7 +121,7 @@ class DefaultInstantPaymentDelegateTest( order = TEST_ORDER, componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null, PaymentMethod(type = "paypal")), - analyticsRepository = analyticsRepository + analyticsRepository = analyticsRepository, ) } diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt new file mode 100644 index 0000000000..5e959513e0 --- /dev/null +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2023 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 28/11/2023. + */ + +package com.adyen.checkout.instant.internal.ui.model + +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParams +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams +import com.adyen.checkout.components.core.internal.ui.model.SessionInstallmentConfiguration +import com.adyen.checkout.components.core.internal.ui.model.SessionParams +import com.adyen.checkout.core.Environment +import com.adyen.checkout.instant.ActionHandlingMethod +import com.adyen.checkout.instant.instantPayment +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.Locale + +internal class InstantComponentParamsMapperTest { + + private val instantComponentParamsMapper = InstantComponentParamsMapper(CommonComponentParamsMapper()) + + @Test + fun `when drop-in override params are null then params should match the component configuration`() { + val configuration = createCheckoutConfiguration() + + val params = instantComponentParamsMapper.mapToParams(configuration, DEVICE_LOCALE, null, null, PAYMENT_METHOD) + + val expected = getInstantComponentParams() + + assertEquals(expected, params) + } + + @Test + fun `when parent configuration is set, then parent configuration fields should override component configuration fields`() { + val configuration = createCheckoutConfiguration( + shopperLocale = Locale.GERMAN, + environment = Environment.EUROPE, + clientKey = TEST_CLIENT_KEY_2, + amount = Amount( + currency = "EUR", + value = 49_00L, + ), + ) + + val dropInOverrideParams = DropInOverrideParams(Amount("CAD", 123L), null) + + val params = instantComponentParamsMapper.mapToParams( + checkoutConfiguration = configuration, + deviceLocale = DEVICE_LOCALE, + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = null, + paymentMethod = PAYMENT_METHOD, + ) + + val expected = getInstantComponentParams( + shopperLocale = Locale.GERMAN, + environment = Environment.EUROPE, + clientKey = TEST_CLIENT_KEY_2, + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + isCreatedByDropIn = true, + amount = Amount( + currency = "CAD", + value = 123L, + ), + ) + + assertEquals(expected, params) + } + + @ParameterizedTest + @MethodSource("amountSource") + fun `amount should match value set in sessions if it exists, then should match drop in value, then configuration`( + configurationValue: Amount, + dropInValue: Amount?, + sessionsValue: Amount?, + expectedValue: Amount + ) { + val configuration = createCheckoutConfiguration(amount = configurationValue) + + val dropInOverrideParams = dropInValue?.let { DropInOverrideParams(it, null) } + + val sessionParams = createSessionParams( + amount = sessionsValue, + ) + + val params = instantComponentParamsMapper.mapToParams( + configuration, + DEVICE_LOCALE, + dropInOverrideParams, + sessionParams, + PAYMENT_METHOD, + ) + + val expected = getInstantComponentParams( + amount = expectedValue, + isCreatedByDropIn = dropInOverrideParams != null, + ) + + assertEquals(expected, params) + } + + @ParameterizedTest + @MethodSource("shopperLocaleSource") + fun `shopper locale should match value set in configuration then sessions then device locale`( + configurationValue: Locale?, + sessionsValue: Locale?, + deviceLocaleValue: Locale, + expectedValue: Locale, + ) { + val configuration = createCheckoutConfiguration(shopperLocale = configurationValue) + + val sessionParams = createSessionParams( + shopperLocale = sessionsValue, + ) + + val params = instantComponentParamsMapper.mapToParams( + checkoutConfiguration = configuration, + deviceLocale = deviceLocaleValue, + dropInOverrideParams = null, + componentSessionParams = sessionParams, + paymentMethod = PAYMENT_METHOD, + ) + + val expected = getInstantComponentParams( + shopperLocale = expectedValue, + ) + + assertEquals(expected, params) + } + + @Test + fun `environment and client key should match value set in sessions`() { + val configuration = createCheckoutConfiguration() + + val sessionParams = createSessionParams( + environment = Environment.INDIA, + clientKey = TEST_CLIENT_KEY_2, + ) + + val params = instantComponentParamsMapper.mapToParams( + checkoutConfiguration = configuration, + deviceLocale = DEVICE_LOCALE, + dropInOverrideParams = null, + componentSessionParams = sessionParams, + paymentMethod = PAYMENT_METHOD, + ) + + val expected = getInstantComponentParams( + environment = Environment.INDIA, + clientKey = TEST_CLIENT_KEY_2, + ) + + assertEquals(expected, params) + } + + private fun getInstantComponentParams( + shopperLocale: Locale = DEVICE_LOCALE, + environment: Environment = Environment.TEST, + clientKey: String = TEST_CLIENT_KEY_1, + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + isCreatedByDropIn: Boolean = false, + amount: Amount? = null, + ): InstantComponentParams { + return InstantComponentParams( + commonComponentParams = CommonComponentParams( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + analyticsParams = analyticsParams, + isCreatedByDropIn = isCreatedByDropIn, + amount = amount, + ), + actionHandlingMethod = ActionHandlingMethod.PREFER_NATIVE, + ) + } + + private fun createCheckoutConfiguration( + shopperLocale: Locale? = null, + environment: Environment = Environment.TEST, + clientKey: String = TEST_CLIENT_KEY_1, + amount: Amount? = null, + actionHandlingMethod: ActionHandlingMethod? = null, + ) = CheckoutConfiguration( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + amount = amount, + ) { + instantPayment { + actionHandlingMethod?.let { setActionHandlingMethod(it) } + } + } + + @Suppress("LongParameterList") + private fun createSessionParams( + environment: Environment = Environment.TEST, + clientKey: String = TEST_CLIENT_KEY_1, + enableStoreDetails: Boolean? = null, + installmentConfiguration: SessionInstallmentConfiguration? = null, + showRemovePaymentMethodButton: Boolean? = null, + amount: Amount? = null, + returnUrl: String? = "", + shopperLocale: Locale? = null, + ) = SessionParams( + environment = environment, + clientKey = clientKey, + enableStoreDetails = enableStoreDetails, + installmentConfiguration = installmentConfiguration, + showRemovePaymentMethodButton = showRemovePaymentMethodButton, + amount = amount, + returnUrl = returnUrl, + shopperLocale = shopperLocale, + ) + + companion object { + private const val TEST_CLIENT_KEY_1 = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_CLIENT_KEY_2 = "live_qwertyui34566776787zxcvbnmqwerty" + private val DEVICE_LOCALE = Locale("nl", "NL") + private val PAYMENT_METHOD = PaymentMethod(type = "paypal") + + @JvmStatic + fun amountSource() = listOf( + // configurationValue, dropInValue, sessionsValue, expectedValue + arguments(Amount("EUR", 100), Amount("USD", 200), Amount("CAD", 300), Amount("CAD", 300)), + arguments(Amount("EUR", 100), Amount("USD", 200), null, Amount("USD", 200)), + arguments(Amount("EUR", 100), null, null, Amount("EUR", 100)), + ) + + @JvmStatic + fun shopperLocaleSource() = listOf( + // configurationValue, sessionsValue, deviceLocaleValue, expectedValue + arguments(null, null, Locale.US, Locale.US), + arguments(Locale.GERMAN, null, Locale.US, Locale.GERMAN), + arguments(null, Locale.CHINESE, Locale.US, Locale.CHINESE), + arguments(Locale.GERMAN, Locale.CHINESE, Locale.US, Locale.GERMAN), + ) + } +} diff --git a/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt b/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt index 437698edd8..2509b1cf26 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/TwintActionComponentTest.kt @@ -7,8 +7,7 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.SdkData import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionComponentEventHandler -import com.adyen.checkout.core.AdyenLogger -import com.adyen.checkout.core.internal.util.Logger +import com.adyen.checkout.test.LoggingExtension import com.adyen.checkout.test.extensions.invokeOnCleared import com.adyen.checkout.test.extensions.test import com.adyen.checkout.twint.internal.ui.TwintComponentViewType @@ -29,7 +28,7 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) -@ExtendWith(MockitoExtension::class) +@ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class TwintActionComponentTest( @Mock private val twintDelegate: TwintDelegate, @Mock private val actionComponentEventHandler: ActionComponentEventHandler, @@ -39,8 +38,6 @@ internal class TwintActionComponentTest( @BeforeEach fun beforeEach() { - AdyenLogger.setLogLevel(Logger.NONE) - whenever(twintDelegate.viewFlow) doReturn MutableStateFlow(TwintComponentViewType) component = TwintActionComponent(twintDelegate, actionComponentEventHandler) } diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt index bea1c5223b..43fb08bb46 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -45,7 +45,7 @@ internal class DefaultTwintDelegateTest { @BeforeEach fun beforeEach() { - val configuration = CheckoutConfiguration(Environment.TEST, "") + val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) delegate = DefaultTwintDelegate( observerRepository = ActionObserverRepository(), From 32311083bd3d44f86d6a16a0127e882532e95fba Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 Nov 2023 11:23:52 +0100 Subject: [PATCH 140/272] Add documentation to the Twint object COAND-806 --- .../components/core/PaymentComponentDataTest.kt | 4 ++-- .../configuration/CheckoutConfigurationProvider.kt | 1 + .../ui/model/InstantComponentParamsMapperTest.kt | 1 + .../src/main/java/com/adyen/checkout/twint/Twint.kt | 12 ++++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/PaymentComponentDataTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/PaymentComponentDataTest.kt index 8ad5ca6d8d..ecff80de2a 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/PaymentComponentDataTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/PaymentComponentDataTest.kt @@ -10,7 +10,7 @@ internal class PaymentComponentDataTest { @Test fun `when serializing, then all fields should be serialized correctly`() { - val paymentMethod = GenericPaymentMethod("type", "checkoutAttemptId") + val paymentMethod = GenericPaymentMethod("type", "checkoutAttemptId", "subtype") val order = OrderRequest("pspReference", "orderData") val amount = Amount("EUR", 1L) val billingAddress = Address(city = "city") @@ -57,7 +57,7 @@ internal class PaymentComponentDataTest { @Test fun `when deserializing, then all fields should be deserializing correctly`() { - val paymentMethod = GenericPaymentMethod("type", "checkoutAttemptId") + val paymentMethod = GenericPaymentMethod("type", "checkoutAttemptId", "subtype") val order = OrderRequest("pspReference", "orderData") val amount = Amount("EUR", 1L) val billingAddress = Address(city = "city") diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt index 9bc0a3d34e..f13bc2fba4 100644 --- a/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt +++ b/example-app/src/main/java/com/adyen/checkout/example/ui/configuration/CheckoutConfigurationProvider.kt @@ -22,6 +22,7 @@ import com.adyen.checkout.example.data.storage.CardInstallmentOptionsMode import com.adyen.checkout.example.data.storage.KeyValueStorage import com.adyen.checkout.giftcard.giftCard import com.adyen.checkout.googlepay.googlePay +import com.adyen.checkout.instant.ActionHandlingMethod import com.adyen.checkout.instant.instantPayment import dagger.hilt.android.qualifiers.ApplicationContext import java.util.Locale diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt index 5e959513e0..3029375788 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt @@ -166,6 +166,7 @@ internal class InstantComponentParamsMapperTest { assertEquals(expected, params) } + @Suppress("LongParameterList") private fun getInstantComponentParams( shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, diff --git a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt index 77e954895e..027489344d 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt @@ -9,18 +9,30 @@ package com.adyen.checkout.twint import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import ch.twint.payment.sdk.Twint import ch.twint.payment.sdk.TwintPayResult +import com.adyen.checkout.components.core.action.SdkAction +import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.core.exception.CheckoutException +import com.adyen.checkout.twint.Twint.initialize +/** + * Object used to manage the Twint SDK. Call [initialize] in [ComponentActivity.onCreate] to make sure [SdkAction]s + * with [TwintSdkData] can be handled. + */ object Twint { private var twintObject: Twint? = null private var onResultListener: ((TwintPayResult) -> Unit)? = null + /** + * Initializes the [Twint] object. This method **must** be called in [ComponentActivity.onCreate] or before, because + * the Twint SDK creates an [ActivityResultLauncher] under the hood. + */ fun initialize(activity: ComponentActivity) { twintObject = Twint(activity, ::onTwintResult) From 6a4e627e3c1c06dc9509ef7b5467704718d670d9 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 8 Feb 2024 10:35:37 +0100 Subject: [PATCH 141/272] Update to Twint 8.0.0 COAND-806 --- .../ui/ActionComponentDialogFragment.kt | 9 ++ .../dropin/internal/ui/DropInActivity.kt | 6 - .../example/ui/instant/InstantFragment.kt | 7 + settings.gradle | 1 - twint-sdk/TwintSdk-android-7.0.0.aar | Bin 29730 -> 0 bytes twint-sdk/build.gradle | 143 ------------------ twint/build.gradle | 2 +- twint/libs/TwintSdk-android-8.0.0.jar | Bin 0 -> 7850 bytes twint/src/main/AndroidManifest.xml | 15 +- .../java/com/adyen/checkout/twint/Twint.kt | 16 +- 10 files changed, 46 insertions(+), 153 deletions(-) delete mode 100644 twint-sdk/TwintSdk-android-7.0.0.aar delete mode 100644 twint-sdk/build.gradle create mode 100644 twint/libs/TwintSdk-android-8.0.0.jar diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt index 732f48dee9..a685f4218b 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt @@ -32,9 +32,12 @@ import com.adyen.checkout.core.PermissionHandlerCallback import com.adyen.checkout.core.exception.CancellationException import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.dropin.R import com.adyen.checkout.dropin.databinding.FragmentGenericActionComponentBinding import com.adyen.checkout.dropin.internal.util.arguments +import com.adyen.checkout.twint.Twint +import com.adyen.checkout.twint.TwintActionComponent import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -75,6 +78,12 @@ internal class ActionComponentDialogFragment : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) adyenLog(AdyenLogLevel.DEBUG) { "onCreate" } + + if (TwintActionComponent.PROVIDER.canHandleAction(action)) { + runCompileOnly { + Twint.initialize(this) + } + } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt index ae0f8534c1..0cfd2e0709 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt @@ -37,7 +37,6 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.internal.util.createLocalizedContext import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.dropin.AddressLookupDropInServiceResult import com.adyen.checkout.dropin.BalanceDropInServiceResult import com.adyen.checkout.dropin.BaseDropInServiceResult @@ -63,7 +62,6 @@ import com.adyen.checkout.giftcard.GiftCardComponentState import com.adyen.checkout.redirect.RedirectComponent import com.adyen.checkout.sessions.core.CheckoutSession import com.adyen.checkout.sessions.core.SessionPaymentResult -import com.adyen.checkout.twint.Twint import com.adyen.checkout.wechatpay.WeChatPayUtils import kotlinx.coroutines.launch import com.adyen.checkout.ui.core.R as UICoreR @@ -166,10 +164,6 @@ internal class DropInActivity : initObservers() startDropInService() - - runCompileOnly { - Twint.initialize(this) - } } private fun noDialogPresent(): Boolean { diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt index ef71dd936d..777e7dc9b4 100644 --- a/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt +++ b/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt @@ -22,11 +22,13 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.example.databinding.FragmentInstantBinding import com.adyen.checkout.example.ui.configuration.CheckoutConfigurationProvider import com.adyen.checkout.instant.InstantPaymentComponent import com.adyen.checkout.redirect.RedirectComponent +import com.adyen.checkout.twint.Twint import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -61,6 +63,11 @@ class InstantFragment : BottomSheetDialogFragment() { arguments = (arguments ?: bundleOf()).apply { putString(RETURN_URL_EXTRA, returnUrl) } + + if (arguments?.getString(PAYMENT_METHOD_TYPE_EXTRA) == PaymentMethodTypes.TWINT) { + Twint.initialize(this) + } + _binding = FragmentInstantBinding.inflate(inflater, container, false) return binding.root } diff --git a/settings.gradle b/settings.gradle index c573f65e98..3af0126e34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -63,7 +63,6 @@ include ':3ds2', ':sessions-core', ':test-core', ':twint', - ':twint-sdk', ':ui-core', ':upi', ':voucher', diff --git a/twint-sdk/TwintSdk-android-7.0.0.aar b/twint-sdk/TwintSdk-android-7.0.0.aar deleted file mode 100644 index e6dbc1b21f469e53122334d9a90b2863d5c8a160..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29730 zcmV)JK)b(CO9KQH000OG0000%0Ps?fKOrsv03$E~00jU508%b=cy#T3TXWnvlJ5KY z75)M9pek9i{3uIy+Yy$nwbbpIII&T|B2iTwvq(0{lC9a;|9+6T5kTTf^~TP_nTTnZ zi0{KSkw_$xVBJoyincz!Dz@cjS5#zEx3A8Ao&BF0C3L3=r|gIBr{ryRLwDS4yJ_l< z7}XbL^}bXnCCmqeGlQw?nyL1-QzfI8 zZl`ur9!U9P+x?<2{+m=A__J`_ih< zIk0~>eK8#tXOUF@p{VK(c-?NdIZi{fsk#Ydn+jzXjY{fXboWvoYi$y_T~yU}Sxqr5 zRMFi~?2mK~&{;c9Y_^bJ(@@aaL)C?)VT!36Wp1z0YONhUEk|!PbNR&M0IuR&HQ%g`rGcOAMm+&HWs%z<&Lx` zsQ7i?lVT{^(#e;oo381c1ANz2Bs{u+xB;P#v_;`=Ix3ItbW6r^sC#n;I^efM@w3C$li2_gs&uvF7TsvTK^6kFU)V=g1yg5bV))SrXKGNSGI^4^2mf z*4BL-#QOeAH_+j7BtvzFuDJ%4UrhFF)gd42{h^`%chy8dyrsOSO4HS}`+-{0!?Qo- zmJYBZ_2Fkdm50Z}>9}>5c|6vWtA}G_!_q&sRHeSRwVsvuy`B!&Y!w~-xX)yVx@%7T z=IYHknd;9WmJGG{LKVQiaJPmIiqE7ePvg`bXR=GEE3zw2&Ga89GCY4oQ?TM|dNalN%=dZn^i!0Z5N<9474fVfh%Watu$(~BE zv_GhgQJ07)thIN{Ddf^p5<#URlnlZUN@wB_0$?jZrGPAis91mMDOi1~)UNksONMJ^ z3SsqGi9Ofndh}^nfK&pa1O!y;M}j8>sp<)|wNH{|x?>9_lN?o`n};XItRctlpEYmu z`xd&uF6>udwAHmRkxg^mZP@SXVWf2*NmIA9Lur?~8oBH25rQWlieue8A)o3Cw_K51=|?(NUZa%mzv(7s zIMyu{4LU%3y3-h)IBMJ$jhqoTbbi|9jGv*~97?{XqwUnl4QMp$QK8Qwu8mz2sd4lr zaGY4Ge&{Z@E;SF0XP9QF0^FUzpWl#c<`1+Lg@f=w+ZlojH{tcX*~J*2{RwHl=invq zC}Kzs`&*h673~3iE(Tj0c#w|W1VRc)L6(^hh^;Rl(qW1Dpbo2yj#G!9c&*H1@rzG7 zS4SR6F`fnjb{*?HkfFM%Y*2f$o`5I{Y4Zq3t1P|P7G*|a4LGM^ebds~l8UQ9kGQp` zBN@DomyWKcbaQmwmIr(K^}P4_wExmMT}Zu0K6>xSi4?jdf9W3U$*ye=dt{&0!4hF+ zfequ(ZASX{y0YCC?AX)ezw15D{ysQUd+-GfPi$-X&=)1rs=d(S;FEA^T`k>_{hzRQ z`+DO~_ZERYzNbIQV2>6%Uh=L!1KZEKD`KX3^tB0csF#uXVxh`d%6Xdaxkt7~LVma_ zJTgZ$Scaja9v|wR@^4vqI!UJTG~E@{H2X&WJk^7}s8Gny>yd9$af(=OaJE0eWOh8o zGh}2dT3GRneeE!>;3?fV%JUf@yLyhJoGA@8?FZUR*7Kos?O4W%F1edxOI6IKaOjuu zw1v6IwlpZ^=sy^ssgt8y#M996k&bcfT=ZmD^G|zN9O$@y>wY?Wow-Qv>eiupc$!lM z@FZ7(+~pEz&;)5sb5r!ABSDeavUV?(Ul1wGofOOip_8>jz9bJAYXcLgst2b4wxhv> z@*|yZWTY$bgHss%c*lgYz);aZUopAS%te2XF(fbh_rcNj&s|xdy5I$9I@INl7OA2M zJaHf!PMPAVXl+oBu_ZCp+_&p~>d2}N`GO}0-i^b<0gq_T`?~ceytAT(tYl=4XlV|U zL`!|tgN&>+djMkECZuXYdIo})w#6{J3B4<}KzdT>M(IO05UxU9br6`LIC@XZ1qLrp z6J7?`BZr{K$BxmTN@_}wU*^*%y4HVccMV-VT<54yY~e$8!cS(RUCR)LVB%M1^gjDB zP}KnFL;Z_Xd@FHR{L}7NjH%lIhAkH1Y&gW1i(GKLc_@G{2ip?g(otvebNtMDLuG73 zj$2Y~sI4?&7#)mVz2D5y8K4mDb%~#~0DL}l+>}LmAe#eC-*8*38#b_0AE<8KRJuCK zr<*4y8Xtn!;4c<4Ck?#~sANTs&|+Bji`GjV--MtQT}6it$ovKjrqj6DpNgSMM|#`I zx@D&{K(t@IcwYfMKQQ9GQPm^Z(VMPqo})!2Fp~<+DkqvX-)d}5L&FGh#HKY*!u&wk zCyvB5sy33#u?H#)uC&Jc;J)U=3cXR0orY800#%l4(@KUD08a-_e5#Tk8)k}l5quBg zO=gDi#$1C0hMnAo`^JJe`=hOfuC9L3nLLoqQ1?9*&quKJr~Z=g1EA&>SK22M>z=v zW#k{smOzApSSmw#I&rQH6YH2-Kec?@3%)B;KiHMEO58Rlve^&hd2@Ak76-je0$q4P z6(K#X9{siU=QM*Z{2*4+Dw2L4N%vQjC0|D9fiC0ek^FIQw%t_kz_kfhuP}bO;O**W zLJN`sXKy}i_Zu^&i2o(+5D=W87`cHi5t7K1+>$bKFI0hC(^oc^xfhZlhP?sj znOVi6J*~~M2pXfd#z-8yzXT3#&0*6PPhpM}H6A39Sdb)%lei7h7Q#H{2)?vLu8RFh z#4~mJB*Mc~iD}$Uga|EYBBJKBapd9G6#Z*s#jOPKQ?X^7pT@a?n~wqUf_WSbYr`u6+6+>NSm{xi%BA#9_QJW7wNW9z!Ay_bvT0wibfx_{!y* zQU;Q{21Sb-J#;FCFnB5h?l+F)lT57?-rlhpX)dc(UBD{~)nHy# z-QG>9mrhlUN+E)?+n)PTlM4WwZN2v;9jR!Zj=t2<2u08m`3|DD&eLZA_s`e=0^_TC z-mF4Zw6N<$tm*a(V#}hBnbR{^yxNgkX9-WNwuGPO!C$Ar52UCdnm7#|)fh9Bi%BY$ z&gBCOP01i6A){JIgPyifhh#8cAJy_D;st^%lac}T@`Jgg?&i=DuA54%3s>CS??0PMZu`MUWQw#HdA9{Q zRGX(r0$Aa+3JDM~RK`g?M@y%tv7`22H2vysxdb`~OD59!RMw-66Bf4@b-Nwa`)Q2n zKxgEkYwU(gxa~>wz@9~+ROLcHol0AN5zqq3*AbboX}cNW8WdV4HH+DOPUhVhCR+1- z!j8jn4DTjF$c7fj6bJKbzH)Kw?x`49ySnO3J6%gauPy>;5q1@?vO0T#JZa}^_iG?5 zzica>r-_Sn1G>R0*bNvR>IQklVPt-`$|S7TZbC`LRg<|Iu8nEoV0oW}ckljVgvgwb z)>ZOckd7&JDvsk?4gBoG2{ zL2m4ezK-@FCG9(d5G#XiT*|J07I_~d*>n79N~sFN2K@Ricn|ki<W4-;g@Jcnc>-$~DiMG+UAvcta|Eix z0=hOyR>`$m_O}n$0=JZX$^xm-_{U3F_i9!z1*>mHI?Rhq5)UbCx$byF1kh zHEw|B6-W0JyK^vU$zbim?3Cj8HcM*l7)IMJrRCekvu%y*rvur#Jb`_Y7B4t8QxU+ImapZ3i=e6D+{3$tGGI3g4vmddYKYJBp`@|o3LeH3 zn6OlrK2d^thC#w9wS!kVJqDhMG3~m#VlTyvW4*1LdU{r&&rQu=1ft7m(fZ;1UOUM; zO9ifyf$3SM=ebTso+l$;CnGPCk(bHHH_6DWWaQgaWO8?|lLvF1JeceB(vt^sojjQ9 zcSsNL>vw0*7UoWgF(b3A8m2S1Iw4B=KL zP}l~4;FA>jMFsrc(fn!LFSje^8hW^h*y>ICxk9Pq9GWc z+fsk9Z94N(c6c`5KdnEOmNj}tD)Z6MwcU|e9qOpDc!UdLa{@m>K>m)qJfQB2!74!R zj#MAwfiPmb^#tbY|I@e)6P~imMP-IH8C^_EQ0 zgnvQCA;)kxrT({ZC#CqCe}Be!YR{Is-F3#_Q$dY6{RxCrt?UQq=7Z6^v_m{&&S(AM zk*Ytd{3E}p(dg>7Yl-={u^C`b^lbpP=xvA=K#+oQD=NAzGyXKP;v$ITRK++#&;{J{ z?@T=4i;-zE+Y%ZNRFDFG3h@Z~)3F=~X{{$+)2J)i;AXEt^6x!Pgv-^8r`(~iEh=+N z)$_si6wMluAQa~0N@G)!?MNJn`_99nvW|L7B(bfUO40*ULFk|C zP7+ta8PEEnG-Ye-NeSnWWVfUFjnbvtc(ek$VQu3!6QBu&9+4uR)vf)cvs4mXr$!Z5 zVO+E(o(tHLma~(bN?6tLtvw|Y+(>=ov|4LIB=KrRN=_*au_pW z&2)hqNJ;7^qL!Hq=&`1o=jpj8s4;wEQUVJRWhY|BW?jPArFfO&9#8?$PLhz8hxAj+ zU7ytk&Ryv0!veWls(w-ZO{i=os=4Se7GycjzNt-FG|g7OVFU2i9D={s_JapD4e*Vj zRx&bWruu08jR5q4xI*zucY}-y@p7B+*cd5Yk94?lCz=myx~Lnunq$DHEgfsxp>$3^ zL&zOfxn}RrAhC|A100BbhRY+Vj09L?@kFs)RtB=CBR4#U@az|urwt{+{UZ)6okQR- zC&%syV%wCR;?hGuhoZ+8Dg6_qOax}g8=t@o6L($To`w|k1E@|HwuDR68Wr^mHjU*1 zS4f_taB9Kz&X0!h2$;Ah32b`P-A1h=h@Ne=w*-JQ^j*X5HT8PLwfUhFbf?#g@>wP1 zLwtj&O zSwGDsqn))MYFM&B5+lbNKG+roZ20x$ z&${3TNcI~jIzpVTinl4}t{*pR*!TuGz6oRQ5kf*FmMJ?)cnWz2cB11TFabh~I&NvM zxIykiEl~yiUAiFN;e80=dw7%2K5KNq9Z|A7vlAKon(Y|n)c4eu zkEBnz~AHC54hprG)e-Sp_YjpSXU}tcq|NFA4UQuS`#7&Joj?4 z-)yHQM%~3Miyr({8{ISGN_LW2!;6LvirAr3y|pFI8uHLp9x9%Mr3N;Y;#4$qL8pRu z)E46}dZgj$bShG@EQ~NI8vxwc*n}0%p^&ZXdXSCfj6DN zaXf#VLtvhj3<7gPIq?vh%%^8BzStzfmEneoY$Ap!iUN<}>w!AFVZ>BjM{wo0KN` zfzEbucxvPPXkQDxj6gHx6*1sVV!oaEQt@!J5itxm?_6Jo-Ms6n=T$aioyRa;;whAy z!k%ey&MkkPKCd-`l-D3ZI$uJ1okjX6-ucfWed0H}W^v5VWhFC~mBmhUzSq^BOdIuy zi6vEVXJme3BPF9d^P-G&%A)q)>8GbD1pUJui-{ZbiID6Z9`othL%|q}y7hry3bBN< zsJ#5%;7WZ_S^nk5%MtTJeeEG%?obYkM97=VqMksxlVtotnTGkbXsFBld517Az{^NM19i)m3F`ODj1u&{|N_(#i^Fd-e{A~p}) zr<2&1QlM3K?}h)EwPR@}k784nF~_`l_$L^AdP;osCSp`Ok+WXO;20m9PlGQj$Ga^G z68MfRgXZ0R|>U#nBr<}a2mj#bB5h1@HNb8@QPD-RZFrIzA$oPhOoP)lf7oene zx3HK&eB#oBX~{jSAWHJJJ`f+>@!U)}EU5HqVFIGMZQCv)kYktnB#n9+Y6nkotkt^3Y1m>b8Jp0ik?ot~Hd z(n>EV)4PR21o(%216z~@ku2}DNR|xlxA}YHOX&XH8)DuL#ZNM@Pf0&^`@MUI6K6pU zj)|4kciD26m_!U|Yy|!UL68|;$d{iy#cgpi)VIydP~r}1a;JE>A)iS~9?9yAbMZ%_ zGdOwr9X>J*tc)6t6Dy0KswH!>3Sj;bISuky$=#OM$qTzTx2NvK3qGl`uHiQy@jF2w z>Nj>%QEEX7y-_dxWb)G234TJ7EHE=XQ#(Bn@*@)qXO87SrZi2Eqq_xm^Lq>Hf88c7 z!db>rs_XGZroD6riluh`*to^MZ;AFQ`UFIK6C6i*=HDJFqzQUVJ*3QJHjQ0 z{~uPIU@qT1W_;}kOgERcpUm@FK8aCNSpid7u~KnGsP7 zgQ2qGpcaKdWkx@}DDdgUF;9P4xYNrbo&K62rIETHFN~&oSs>NRVyG5{PyGdvQ&tv&|3!O6B%46TRlD#gne~~PRuNmzMKdlN z$Y4dMZFcMr#cF@h@c$=4m+T=yqpTS7yjpYP45&pjTFmS8{k) zeq>ihP*+Ag*RoKqyqPl!yM&rOzL%^0q zec8g(nsqhqr%wPq zz}?=jRw>A-VAm3_y;yP4;wQMW`8_TFJQex9o(|XI>h9M`q|anGy}C$(eq)ZF%Oosq z<1oE?la7U&YZ80qXR<2|XXs~sRo`1Lq2eJKUwhsyEB|21&o!+4qevR^#A~?n58r{X zo?7{b?Lf=<$|f4^ga>v@|EraMz~B+$z~UNlzzAp@}Jl$C$5*tMi9{{Yd26)to< z)@?Bnxhg&|J+Sf*4qS+MUsUX(_mzK0=q9~;we}B0t45a@>TCaausckdAMMrJKOp_Q zs>|A~{ez>Sd4t#fkx^gNS^Eb>Vn9EN4E^IGa`O+2oz<0J-3loHrMo&?ot9t$IIL94;_c<%u^)nkC|`9ObjhF@Zyw%){f=LP_+xa%mwS^-+D(#np({uZ#KTcK-P`y=ij+ zFKD16likoAKUL{M?8t(BAdL@rvIIM_nYvG6n`3sl+}yrDcK@tDlBOqv#|)52cA#L| zBdfR3RROV8&Bmd`eT?B0CNvxBO$?p@fx63$W0p;zXQWF;wf42WF8W(5{WNM6;N$iF z)=KzPmp|?xWXIc78~GS)unQe)Hdr`H9z@G8iCQ$?o~xDOa;JX2Ihz6N8K4%JvvbF) zf=fh`@Le~tPZceo!uHX&mHWQe9Pmh*`1m%zIM|QP*Eec8sC!VGb43xX#7c=I;Rtd_9$*Nn$3JQtAx`*$oGSV0p5QFtF zk^VQ@Eci?!>=lCoVld&s<8x2KvcV|kOcRS3JVHD++M zKX$alkHUc#my2E3QtS6vH_r}dmlOR5n7LqkJdg=aRz`;x3=x|`uOSygS;@F?0866d zLcvr;)7%vO= zU;QD%>{s+_J&VM8)vxtTG(TeGoWMe;lbEwj984sQw>%9NoTLE<7qs~z`*@lAti!Nx2h3 ztE9#HP`A}gOA;5L-P7fNZW}&m^77^w6>(aVCNz}#z`j;S?SQ%1WZ`*$&jHQ6Wcgug zN|QW%Oyy&fmj5!{%-%-kF1Q#AGJ$(L0YxhuHe^?@z0b4T;iXqGFIgjUSqh z#X-F~kI9T922vCmk(jLtLf56`bwpaWG6LNoMMOoBz7RO(elnEy>xj5$7Ti9!glUugem? zk$n-70*0M6r;^r-h#+ttYy6jPpe=uO5gC2PfQ5SK_^~N?*k4^l#$9K9q#MzOZALF5 z1Fo}V^&*4LDDQ`X8ao$}F=x>GfvSb!`PD^iyfrX;Tl69}Lp*|4Th>21=&ZHc zB6t}Ydj37pn{H^y0B>%b##fh-QRkHVf$YdIkV+o7T}Fn!(vWCd~%TX{W{<4r_Z8ZA6tbXG(>8Z$^Ku~`&sj_vs-MMBNIn+mjL(aA#-Ih!o)|Q)A!Sgb zid|J_)aR=?;;c{+%a^a;c)A2-M@67N6u2iGK+%1ifV#p@VD1sz8-79qMmjq1bPzc3 zwWHVct12{^u}|?G9b{Z`A#1c1Hvoju9OaW07uGE&KBPxR@Eseep%^+bw6HH=S29W z)X2rTis!vJSL*j-4*0z|R}sM%JR&%EFyYr6IS+;V#}Q$LftyaiVQT&{Mda`9SIRt$ zX)5WypZp38DZ{fi`$tk?geN!-#BH=~Wd}*L&9;s$q-xV!EI~uDKN3xL&Z;@Q>`Y0? zS+&B#AgKC#B1i~qOj{fsBX6UoV$Vm|Sy%N+z8UMBR$GH?R?Am;HwEg5t-;^GMa4L< z2*|pv&$hD>G{@Q(b6oyg70Gw8nkVmS1{AMnY&8SsDZN_#O_jEqX}BkhD$X_#J22xKUvO;|7VA86jXp?mRE9%LnvMjpKkb+_klfbe%T zg#+M!pa?QIc&ZzIw-fSoc<>N?zKY1S`F7fD(O<0a+aic)S=_;D8Tf9u8;R>>Sh&+z zZWe`It17piU#48YQM#c}3*uQh&#AS&S#vLdxVYH@7ZK6dO}%GVq%5Z19O|ax3frDr zflTDv>GlK*P^bY$d)xh@WPtXQAI_YPXv#TA@)`+y%K4${1USw(l43j!1g^_qK3zDP zqkV^Fo`VW+_=;1w@fE%{z`?*52q;0?&9CBKt>&kAMtHLg-Zii!19|PnQa&H#6lwDC z*?|$>3)(k#sU@;dFFCJ^%F%HKO?#<|pEAs6`(L@Lz#SLPKpypf^=TS#IrDF8DFs6` zQ)!OGpAD#0MdqKg8^rOpw#wY}0YTb3a-gE%(3Y4(cxdHk_jL;vO6`KV-#rWlGnE5fV?6uRwuDMNH4y4u4NA~8!q~6INa`zb zt}!+jdMU^PrQ&-tuQ z*5&+j)>!=Iu7DOXwxb7jtozP?bq{8!B~!jwJnsr{lRH_r@n!SZA+*5 zQ>rU6R&OrdW9~9vSO|^P2z%tcyuiz0I;UfYskD^xAdg9W?Rlr)>o*b4A)lWB8(jL| zVoS@Ub!7$OL%(mNd@dWJZgmZu{8`XV&o>wRrPVK;d_lnOG7s&s>(vn_s+D%x=c1w7VSf@iDqY=zrn{F;qy}ond~O=aw)(A$~7dfE&9B%ukKo z{S{z(H?+}PzT({)Gvzu18wF=xC)H7rlJl?XZk^!T2&kHZ}gp?Y_`rMcfsAcP-B}iwmmIFcNkCq^e z1^`-zoG|xsfr(|Eg;ybj;aj=j7%3bt7kdk>o{`dgK06Cnm-9aAQoF`VyYfnj``O#8 z`DZczREKjHR~PeB?dN=Yz}0JXHdvn;)Q_XZVZPnR9>#bmsFf@tJ98+vztHpuSaT*X<89jRK?({-g8&i!Fclyb9sj?hMT?AA_3IyP|Yl zTYn-$?z!?OGVuBxJjncWV8sPIE4&KIyZhDtXg-Wvz5X}2iX}9o#rFj4<2E#! z7VcQLAKgbW61f$xxx0_=B&zHhx(9b&OM!;ZqB!?bGswJMAI(oUPQP0p%}xh}y-0laTCSn^#l?CTa^ zMB)I6t0_^~yaNhz0Y%C?a9j#ec)SA%_dcRaL>#ygUmaqgtZqj4h9I!$V;ubD^HtIR z#nfS(W&)8qJdTBO3-*~~eEP&A(p$zKYp9G5417>&FI{{I0`O9KQH z000OG0000%0MZ-gS=dojC6y0#s)V-6DMCPEmYk2Z4quk@xgqiZy|_`JKq~8n&2DvxV$3zTvqh> zusYnYD;hjvKmIxO-!_li&2hKBEBAD_qUE;w!;j#&WkT@vJBu$+O9KQH000OG0000% z00v9!wy#0}0KiND01E&B0Ap-nb8}^LE^1+NjJtDmWZ|~H8+Xzjvtrxo*tU&M#kSc| z#kOtRwrv|7qk|4^pR>=o_w0S|Z{M@V_{OMOHRnI;srkO&H^*FxGLTSU|9KI^5<`If zj|=Yak`q%EqL-2vXH@(@hC%*aM&|z}0PY_F&TbaA&i^J1<$s3R13hd^{%ttM|5vz^ zvDLpxA^Jb1n7A96*gIR;**g8-*|7haZRP2_Zww6vX7&vXjN<>4t@^AvH5`RGNtioG!N0gae>G@BGw4RZCkPqyVg zvq0|~1%pKt0&jco)4=J`k=;m@y(zzbzd;9HH#aE(R133ouW7!g%$=7`w?8XA3ZGz1 z{^X!MZ{`DETW#Co@f8{vB_%48<04~DD>W-y_(-82dlkGY$IFh#=kwullop}H+!or! zUdel_lC~{T2RNK{Q@estyc4d-U5a1va6ZZ(VYdzewzV6 zqY4Tn-=NP>H#KRB&x?g0**}t9j{n&|83AAeReR5kra=6%+T6nkM0XQC3fW>S=iE16 zm<@N84m&YnqHzYzhVn)bi+rQbybmy(y>l(H)CqccU7l*ys5{3Qc-Y3N^D89X+O<5oksojM+LtbYdjow8!TEFB>w?36_Ge}A1N zKbA2G6#sD)?Vu!l>6!9t)gg1h!y}~xK+>#}crBQ(6qm<})k(MbO@EhxGEG?vLFq%5 zL8HP@hMJ}AsE&hi z0MAZR=(%(c3nmFp^21t;l30wc?VQe`?duB%@qQDxKxqrA!f<){Gl&2S5q#paN<5YNDle!#G*=ok7IYXbp zZ<5=w*kp2JQla zAY;7uXh`n+&rD_QcSSNvNW~kJTN`O){~p(UFql9p7RUN9@%Wy+la%(JQp(Qjm(V|7 z{bQ=&T=orAa=WtxkzApb#>@JT;-nX@UeXtwCkgFPjWwdfbtg$MMMPkydsv%a&rLSR=d-TY2IN; zyRTy`5JW7JoY=G#xDbZ!w;1G#rc^rNE+)IWq^@N7H-rG!$l=1OBoDm(XJg>+ic0w? zerApaU5G1kmNeWrY*)AuC;Hacj*acq*TLAArQ@cDI+$^N4wcT@r>kJFUZ1>-P@*D1JEGi~yus z!SNQpLEKi6M!~zM(!+%aSd+M^eMd}dBbZL~H5Q7XLov!lZC*;$hS%K{!MDs)zbD5IJz* zVoTB8saWA1t7@Ulr{Ym4%eDC6c{OK_aQ>_*}T3Yr4(ssm9Ou z6Rj7T6Ta)Xe$U5>C%bN#LxVm(U-SEMVX@oaKmS|W*WnwzY#y-|ODqm}ThHI^mo_-n zz`UJhuJ(xEDwzlY)tXwmf?8LZc9F1pxz+_`;UQuW>2bYDGUON5&7%rTY?e%^BaLTn4w_AB>;)OxWU03wmS5+kEM z@bQ*o2T?$nIs_!)1@QP@IU+;MpzM`kAu$dStFb%d*K*ag5S6p7RqpK}FP!KDA8s*A zJ6ja*+`Mm;R28O8=3;X)#3Uow+NYd)JANQy>C!a>8blDSSL^z~i*ML`oOsS4?*xFL)*JUl#n6$h4jeDanYp};vq1FrLW*I7)Sr)%pq3b^g1z;s<+{7W2k zSr#(4Yok?Ox=chdT%j0}2H9CgU!H(ZO95mg(Q=HS>hS?ex~lQ9ag_ zi&pwR8_%KFOjh)(+5wcCfO~>iWE;`*jn5{WG z&{4$Y6PQ{~xjDiwkYYx0>L5DdczPd&xmR2{bj^BxpQD&(ka(9Z7=n%O)clgz=#DN4 zHbokLhAgj$gkOEau)wTK%O{*?_P+5Qx}37YtN?o~o;Sz|GR76`)O{E7L2%iR8!Wbg z6XY|?7QN4VNd?VgNS1PDp%E~&Ea{e@3#(vX9YHb(_06&`uoXe zk;on>0O9-?nCp##9O_0Zko(BY?-4p`Br6F+T=@*AxK5#Z-p2^}ZjYPg9w?88t$V*S z5;ye7w?A9{ygNw4EMM}#5 z;e(atXbn2Zk?N2sWx!@~0o~2<8P5%?7s|vQ2p$5p4%Lm$$!M=7h%pOY{cR8$1Reb~ z64slXi6$1Ml`a$`7MXubg@VhXXe;l#)~MJ6K5NLW}HDrNE|9?YaWd-JmPW%yPS zs#E%(?wI^%$Qx-j24Re&{a~7}+3tHQ$e!v}6Lls!aEzT7la0k*-5X&&}*z{NddX^?D&GV#^L~ zw2P6Mriad|ljqS^Gc~It>>7-}t~xEhg~)4sd3!8vsiZliUzwG2sn%6Alcun#mzBW3 zGvo8IL4)`EV9dk6*aTaWy3)~UW|3!mdi44T+RD5l>%hKRAFTo8o3Dn08N&0#Pve9D zw12tqfi~*o(!*IVWbe|iSROXnM3~8HMb^6liQ^~Z+KH_}$x{sMAc~7b&kjI9l?@s* z)hfOitQ$)Z85jH(uA&Mp!X^){i;h^7&S@5N5PYB;gH0_VKONyj4(PQ~PieE( zaGq&jVpEN#Z<~%ecM22$woyuhgi5UI3WcG(09RUj6n&`8V+by*&NlPQ>=J$YAaQ!i z#?Bhw_>E2?T{wF`D^C}Qr>a=v!nielsVUz^@j4_M=EmJ_n4r1o+2W&iUdMKC$M(=n z21eN8PO4Ozb#<*7>D(+)As>f)L6g<0xSk>iIQ*6RcYKj#AKA$o;jSQuF#RRRMGaPnS&Sc@l*neUwGk!W#SIhOQrE#7{VBqNuMGz|6pgHi$H8K>5 z%4XjGYgYj6^Lli3_FDHeUuBdYc<~`HWR!Z z^dGSvHj^8DADC4v{R;Td91E7xYbu9SVtdC=6T4r>l$RJ_5Lg5EyGpXabp2<`qT6btTut3| zN~@_cZk`Noh2)py;MgV$@ouvWv@!dg;tcdt6j4(1{ArUiRV=`j?n^y>n_^)OUU(^- zZK5c=`q2{2_OtXdtROTx%%1eBSIpx&PP00CV1zOxULyAIWi=(h@b_OQwnbS5^X+7D zV$>6Q^7Vpwc!h5UcWB9kOtrjYjHixbLmaV}z|$YPtJr$Fp)jt{ox}vC)ymcm#Hd?M zt+TkNrV1km{#*W626mhT`3@6v#XtODLyD60LDSkRo%%BhyRb5wPBc!>0KTq=Vs6nl zwtghAd|CKGa{N$$a1j`OaB<=CI60|8JUI657i*A?y zVKQLe5Uo|`fxYzVZNxB2-;GfUR7CKt(UgptR!~^$gzse20zGG+O__1YRH-J>1teFF zU${!I(Q*Su@bRqIJ^W}F)`<~ftG&ro=8g0r>RJvF^w_aD0K{WZ1(uO+3dE;t zAcZjeR<}4e!-Vp$J_kBeKABjRY2H6EXYAb@SYZz$!CqJh0}h6_tp!K)%~YwQt#4gm z>GP*D5kPOyUsc<)tNPVtvJ$)Ct`fH%Ar<;!NP!?BE+3QEMAQ%G{TDbmRKMeb=k>&V ztUZ*&xJa>q9;ZlaW1DGA)i}3|?L9D71pRT7RvhUwQ7l4EJiPQ_t>{bY(IRTcaE_eq ze&9oFY-j}@oXt1?MB;!jEM27UoPG7Jo^N{n2P_jFjb5?zFM7^nVJF)nn+a7Z7cLGe zXKZH1tIt2GW;6_A&7SNhM3{08j|zP6o%mJXzANcoqn)mju3zHl&EO0~xFmpFgKATQ zK><1QI4C~uIR>;S9@(+A(*fyvHJobj;j*sJUB^qXF>z=9TlpY;pS>J;$bnj7q$C(? zeR{7(zppc=AkM6est#a(vMBy+v}Z3`CAU`BuoZn z*9MmeFR5jKfiaRWQGatxvrRS;61^I4$89nb?4Q=&B&dxOy*Oei%}!1Li=_-ueU%Yzo5dps_ul8QfAYb>#AKiV5h?}%rG62y=AU=zNf|pI?H? zMUgskubKVXaY$*!owL=%u*>ZF+q1H)S#5(<#)aCgP6C>lwHH&Cqs9M51{UdhRiTCi z+N#BcYEmp30-7@JFSChYWPKI_u7EWeP8M3Ox=zh-&SWW_M+j4V5NRi^#w-#tk^Fbw zFdxg(k1C7&78!BbSPg3KBGSuQc0JBI8C9!{l_izDqB19|+o2>pO)qnJ`VWRTD2m?q z94g@+fd7W?HB-mecwV#vz*F>PB{5>yS3@-yRPd0} zJ9SO|sD-++28mHFVmzaxJq*XLoY(w9kAfWKj`{f|Gg~q-Y{=Xz&vF9}2DLP5Atca- zWwh!;$#L=CIC#(}vqikW*5e;z+5(gF8DG%)DtSltPa5e!DIU)^Fb(=7ARn`j*398G z->g^?g^CxiXQQ=y+E@F|tAo#MhkPFB+bsyBQmKlvoiPTVNo1>O zPOSTECXO?a-1do9%jVPEs;maS^L*i&fm^NxpSZr_>pYudbk%O$-!!q^)OPI5VM9nW z{WR+Wd&G4;30=tKnz9f}TWMr+JM5^4GF*5qRnbjwcCE>UX@+i`XmmK(${F%xjT{&Qor8Y%atcF>Yj8n1NXlSxN=j8|A5(QV} zosavzXsn@q-zO`S@1ZAuxiqRwc1VT zH2vS@f9_Na*J|k_oRfNgqlOy0r+KTg@Fm3R{xVo_NVg7X3uD@O=IRBmp#R#|{A61w zn=QS!)7HRi|G)=mv?k_lRkc5K4MU@7NT7JjQ!a+rHWGCyg3D$utQtB)^D=e;d6f7u ziKf}o-Z`b(w$td4xe>R?*u_yOhOsJ2aD{S(0f%~d{j`5^dS2&(?)UdWLH@|BzWccn zvh1@U%ii&IhEs+Y?4^{seTPXV1UW8q4P@qn4@#hbp06^39H8okkI{7a&0uq$6WXk~ zR?~FaHtS`yvYcDHPt{cB5ws{@wq1+2U31&4Yk%ne9U?#uki~v8MUv~PapT?QQ)8-o z@`>f?lkI7S&^($!xKRed_fBr8OJ1P|74&7W#|$CZ9F0{#e1h%b%6rb(IS9+-scTpK+4CK?MZcwpz?WAgdhN%gw#ycy= zR=p?eb9*wmpq8j&&{uHz&#+byiQWYWBhcW&-FW5WiTB>md#hs$yaxp>|v;H zLavj0wfc~1v$ed7I;4mA-2=S}wv%m+Z27`!ABD#vcPvZoO-)s`a=5|Nj6U%6VW`90 zIjh2Sj&4XJg-WV-e+iZU{w!zjrOmu~FlMEz5L`q-^%C;#%ZgDxel#$o2k$GNFH(?6 zP8Eu&o2-O`m?BM!wo;GT>i<4LXjT7JRRYO(853of{1{L0$451_8ev%oo<*TUNHg1&{Ljm0v>4|b8-K+o6b+#HXuXDFB~SfA)z)=Wq-v%>_# zz-K2ZH{TE;q zCQtIIVP4P_)b&^#%Q@lO!-P9-Iqi5Iywa{t8fT5|S12RAlN%6cs#NZO5Uxi@izI#N ziqHAO6}J^=uUHR7`=W61+syHnlU-fs!&ZV40V_N{(0v4V98*66dmcbd;qj`|plj*n z3yBYka~YkT9IFyecHLisPpmwl+nJ3-r`3+^OS8ZMINVd}*2~1?PAA%>N`DZc!<~=g zdsCGisr7IQQb@;>1n|N$!e3+J#Vi(eX>=Hwl@oGYa%hXPoj$f&LB(}ke0!BRT!H3i zD>Is#iBvsn@CAG!Y89ctz7nR^IsR7X?3a_#+SPrPNX+1d@}DM)+4)hn>uci zF9UDY)^wCSC1Td@x(zOgnq?<>@FlTCbB_}_EKpVbs^6p$vg&02UOz5+H7JSUpd)B= z0~ry_Fj+)28kwMn>(`GTJLCa9DW@{Yf2rv_8%p5IZ#f78A~G){{(A{Ug0d;R3r_!|?~W|<~A1c~g;)lqC+_K^r5aC`rd zSUVvaXIDWbb(@f@dy_UkO%Wno$@4XJ&&x(1hWBRNM|~P#&uh1%#F%;Oalk$hz_>HD zg9xpl`&{xy2CurPvc#eP%L+sXW))&d#V;=>} z%R!U2?R8r*1`MZwWZ2wI#rJ!1>ulc8W*#XiG+$ws7TP9MCCb2j!8y`6uOEBse_2)?s~&QoL6tV zLr44eJK=T2(0WcK0DxY#Cd2pemg_=MGRsgy@q{r|ZZk<~`E#2@bHgU@cw0DhL4dFn zwI$W3sh?Ls_*7t6nijtrw|CriI|#mjImx|?4!ej`zSP7eE5&go5Kxdxa-IP1(>VF$ zQBCVCS(DN<^tG&|ckBsFki7ZDN#<&U_{SgNrMV}_+k~@>al9b@j&~U<9m2I2_v@|w z?v=Z=TjD+IG~^-9Lh!;Z2f+~?`Z8WR4cQtI@||ng3BE=O47l>Mu9eh3sPpz$0%<{Bd<+zKdGPc6F7vaLqxA)I}GDVzEl%@jI={Sxrum5hqpzI)!LG zFMds^WlVM+qYd#yfT+RJ_X#((YiWn+%B00g&RY9@a@zOzSn7-_gK;V8baF}%VW@mL z_SD(DYCx*&O(yhJ?#gYoI&Awx6n530nC8nZ>q^wY>kup^xZ=H|7)JxN*p-LeWYHsE zy~e%;vx)T#%i)0qG3UQ8mJU^W<(rkbt1ET&g9Ye_3oaHJ5n7S{i%Mr0rb;P=<}#e~ zV$Pc>@L1e`#+RE5*4#DOnfijy$|K`QOc!YyNAxT9mPT%OZ=}fnete9lT2WB*z?p58 z{qDirNt%^f``dX$>PUb-mX$x=6$y5IGbdu8lgV(=EOZXZO2tXBh) z-!4N8FIs}}0E%skve)<}E+?`W!OZ$sz;$RT{rd_we@b6~UfR}KCbM4`XVNCgI;^hE z=p(%>tw-3^85u&jMGnA_i&h=}ORJ~^OpWnE=ooZ@nLkroH5_BfV?DF!lKlXrnh_;T zU_=)SO%`$RHbFvkg1khY%AGL1pwiQRjdp=qPcp1^2ZEk2Zujiu0#93NfD|qh06Cry zWK-#-uN(ea`vverPluBBThe8C2>!%@Ag7kwj$!dqOI@0<5G68y**^@mt&^9NwZjxquch*;Pf7} zXzYa0q0l=8dJyz9_F-w-=N)5jOI2F1(^vWmRq!>(DB}9}ErNbFVRRGqqhnVAj`CQR zaZ@?$)jenAWkc4$CJF0?vs<#Q${naHmaXvs`|e;W{_+_{Y=K?JV}p8^tr13(-R-sg z`4&%!*j`G`VOFyznb){Y$^oNboe7*X-l+j>H@2k>R6T^8VfaIGU<_Fg7((`aaQjZy z_5lbtFGH}V{iwf{b0DEk!zeozT=WHs*CWR2eL$-46=j>jI#x`9=p8t}vV$2McC!rG zSK-mO;U4(YS1DG8V`n-H9BmUHvux2#Rup`Nf^tDn^zf9yj{t1Uv{Oh7J=MQ;t9yN| zF_Gk&Fwt5f!r7OreN+#J-Knp8gW7$S-EKQ#JDEd^{dNYECP1uHd5-;c6FqjL^hc(( zoy3E_*J9!iF{p&gZ9jdH;wV1TVzK8@4Zd9jPcfl`7pG3mQwuiPax_`_QD`;3R-43+ z^p(|?o{Tou(!bD_#$Pnf*pj|HBZlA(x6>jHsfW&AN#DHGU5#~`70>Sj$<*`Y1$zUc z2-S+&s=!uSp$>)Sl3#v%6$PC5*;c^QEtsg|Q~8J~7pq_(|4#_K<`jCmGwYLPo;pAL z`&~1&1f*-+HiN-O^^jKjjuv){-Y)zV`zbp}Zw`Z_?dsxb{2650*y2(kFw7Sw&dRJv zSd+PqHy-+vQw6DM2BO{9?VC798_SIIqoj$b$h@!FEbTn@Ox7~E<(pnMy(&)T1s!sc zg^sfo+#2QEvwjBp9CjkbiJ$1yHp0*2`Jr}26~pVTo1QuY!-k*N2STGuvwc(JcU;yl zYe!*6QDZ*bor?SSFqjZ&atpQ*@(zLKl2tZ68J>3SOA4|f^?YanqPB@oeD=op^}v!{ z(+2C52=A$5jKBZA#DU|qOs83R7Ad}eqTxjQ~KtbBQ*dXScSt{cGQC- zb2#K1E+IcMan4)7)(Er&!8R1Ms#)4x5^5K!RH>#i;w+&lRg3MjcziTU97+XSwb(-)# zm`@kXi0VeA@)Dpid~bMsQ_el-bJd_5gC7`C^&8pnk}1jo_CGlT0Y2pO%VteR4R&|> zJa?>nGUwJ@pPk2yR6-z+We<`(nmccQ_wO7yuv12GXoK8Pq6LyXnvct%y#pMmXn})J z_pti(aQ?(ikY0XCdzjmq9T4*Aa~tOQyM-`Hb~XNGDns;*Q<{1#HMCGSyFDQ!OEgxNM0tiY|vJ}yl|GmaF4 zNDFGDcW#gIrB2?WJ*WZW<3)*njRZ^BO3^NVGt9RTsANMB{)3dWy5ND?MPbJkrk)a8 zkV9eU!0N8Kz@8o*Yqhh%^uid9WKm^p0kZq5pd+IcSiol5^;UzfU)d6N_&PSRxID=G zy{3ul;ex9(JNTC?&EKXl9C!IyQrEOgZc`;lhiP-PTp#BaN!#!y-=;uDDP(1=w0d>z z=aA;{ zmX2Q?ostVoN7Xzp$#>tfUW0PUrog@lWh!%)`GuS+gO=yqi_U~AHp~-!3b!JRl#%KQ zk|cc=?gKQWCj=F22bft1i!eDKS$|YL`8j@2w^%~joUrV$=x%u~4W!MamsyPxws5I- zbTR8^J@%3P_;E%|g~MS{g;T#DC2wFbE_vE@mula8n;02dru_sk63aHv)7wXlax$d< zE&RW8$``Io+G$`~+(!susLamUDNBUnEU?7JpHpP`!IXsd0o<%vlmd+26%L`l6?lz3 z1zh{?t|ug6nUG3=v;6XjU5gk9+Yfg)8#^1RhnL${QA6 zqf!d>u}x7+%sX;=HuoRZO!P?-;Po{9@I+$Wy+r=t0DA#y0sKPc{y_fB3X-3BSG2x7ujQZrexg>pdnkgwovUlY#wgybMvcPaD**|mtJe^PLn2B z`Zln5X^|&e(9bog`FS)e(hvtUbH_Ygd*fi^KAwK#;_peq(R)cuaZeYGfbD-c}1 zEy`EGu0-$_L+tw=0Br0q0%!S9M6+!ElUOU`T{K2B62$%w{AgiLw0Q8+2hL=-=BP{y zqQ9cfa<-U*ZWITssGxfUA>!**NC;Df-k0Ty**nQvL-6lMbHYelH~#4Up*Q-Tf7_d=HjU z>P5%jRW)pnwE4#S=KPB!)|{1(!CqqC?KN1tn|cNuU;dY>_*^@aVN^e9vDJjqMF$>3 zHmJ3*FIZ6pab5!Te|QL?$1y{T?Y;HE0re>OdJF7jEAlr~S*V%!^B2 zyZ-%+yoikBqGT0@l>IIRS?4bVsI|N5{BK|^bc$JHPEe(kP)=G$!05A(wEjc+JoFdi zH}iXpGhUcCr|BXbslB-tqgh0?l^b3~kH#(1gJnADM483G(@1Dh|3^$Frzk5*#TWiN zeme1c$>b24gMI4tB#Hbq-JfMcuhvGOpFS5YzFis9-y%eLrssv0qp6M{eyZciFMBGc z8X`x9M|o}h5 zv2yc7J!p~aCYWT`Nl%S$x$xVUUlR(;Q7K+&G$fd+8#@>I-^~ko5KoFMj&}9mS2BK) z84DfU967*1X_qp)<>AmlQ$R3O?h4ze0k%E#*m-U#-A8m3Tkk1}l?8JvLtBZN`t!)v zwF%GlDV7FMkCm}xx00&szuMll?q_y7wm^%wTdSw5zW6tKFbE9%s%eDu><+zxx~{rN z6(U=v?IUV|GKL@%Z6BV!^~?d8GZCP%Jcd8s9_8p9)hqchC5@usD9nB8p-ixJm)8XL z0I}3L+;mp8dc7c0=?e**>ptm7qv)=0dFaKI9wD#m>hwJVy$UYCLEcX$|pMJV2> zSF69|0QhmCX763?@T43QL%K?nVZ7cJ(37IO)r<_Gl*qQ?{V}W;pOCL(XaP@M^5+0Z zzqB7GeAbK5u6g`^2|&mOjslJV`Rr0H`n`x(+#A9KMQE z$Uc=%v@h8i_ee#%_Ju|&288{oEV|Z1K?F<|$f>mgy}B&6U8%xzhNo~^>#CbgOnj;< zLPAHIr5dT)MQ-G|Tfjc|>oXQ)1Pyb|4OoHD&H7U>uH4sk*wwv>j93 zRmi8AnZpBLj-4q?pNvqAazv=0ftUQ4$5PK2i^HMFnbht&N*{Fm)vPE9YwB{-)Fze5 zyZ9l6Z_g9dMEgX+e(T& zKZAOI;=kvfllQb_*4)Z*(hkwD4eVSu(aWLR3X)}FeBQOPNL1`G0x_I3|GCi*HIZkm z>dnb=dPNFY@8o}$h#w+l0w~d2cNqp2uNmRE$2FEHfjHnkmM9Y~i+hG2NpR{gtX8T;59A8Dni}$0*OmnklzYTNN$8)amsK zHEZjrkc`d$Kp8rSLfgoulz*1r$~#+LVi`lX3UmG7lJvu+pIoPfAM3)-zmtzjyIYAdyXy&{-YyE?pSf%ezE7D;3V&jXpf z6Uzm!1+fCge)vQmjGPLYO8?PH>}2@$dpx2NjU^*%`PGTjqH6n1Cud(yTKe*2Z>^zC zCn#Iq)TRORAON)ybqo9RQ?#FdFKSvNOEp zBB5o*wDk?PusTq0iLjtj3H#|IQ`LKd_=XVM6rkon(wP7T8m)NWc~!CE0!kgWyr*7+oT{V}U!hH`t(MGOoD z&g&K8UHD$nTHCRMUx1T1LzDA{&8p<1eF{o0(&*C2QDaNGkr_ZdMZui5`F^*9NP6T8))UyT+Db<#@eS)~M zV4;N=l3Ul{28-)!ek9!FTRmVLf21$fKwl;0A5Ks{zkg6oD=xb|e;<^B=3628O^2>O z4N_NqTa4LCo`eNQA5qe%Yuh=mgobt|xIRmC7JHyQ|0m-^EAXC~t+9iKW`#ZAY^@7x z%@k~~D#)8C+jK(#5(VSDgL zucN(5jhqf0sHW3+mYn0)ZxHV62#W35F R!UsC601eRrjM&wB#8aa@@jh zAag|`F+^;R_bT%3FSdNlV-0yJhF3iBz{+x22!4P=~z#~rvj&Mmn z9bofs|91r=@WzM7Q{P9S5#{Sbw0!qkbE_?fFjY`4TDD9@(aVUiXa!iyG<2@gUGWH+ zjxc#xi4J)ASBVb70U?C1UZ5Vp+V4rvhn*>QVnA9>uUFD?2&L%vgD-1xPHJZcunI(br!2;9m?qHixe;I+*gwGV8J; zKjlI4qCR0${(i3^sKTAb<8e}q5%RD;1srOP(*>!JVJ0MvN#&%19ISvNUJ=z6D2>*& zC&OMb0aYH|QG zlw6;VwNw>l6G~bZLUAaDJC-za3+Y-W=BXBae3rG3B(WbE)!Lx|ng!D+PyvdDp?VGj zQcIWhls1jL5Sc*WfYZ*Ex@&ncDE{BWMfL|m@?B{^DZAB_joKxWSg?}QOs`WYCquQJ zjQ|dtEZo_UhMGM#y@VD5I{FV8)ZWc67+%v*fp*BP5|H|!E5*icIM4`^F%+z3d_3xn z=xZ3DPY?jY7Ufy=2hDh6%-#?OXJnQY=xy;~%py2R(WFv}@QFtVWewXY>sw|e*yOmi zM{DJlN}-pD@Wf?YlCU1@oNQLtwe+S=HL5(To(}y46_*q`s5Da^hH2(e$V{rR)W3Tz zYRE3(6IKxPc^s_4tTHPY3VPuj91u>j@D$Xm(B{vx_7*XvSR9&tDkx%+md(vGdx^Vi z?5h_;y$ZKk6eONG--t!W7puKrC9$`~>v7MeDFRs1?RueNR zjJO(AIB23nenp$ar-CIpvtjhtELBso^64bJ3P7i!F*W^KYLiQW-0D)4(Ogvl9U%%f z(|v^P;KUD7PzfQh%%M8{FoVNW1ob+l(x7BJ+wK=>Q06SsU=rGo2b~5sHfNSjBLQ0?C0eiH$5rR@#PSOBtI972RDkjO| zHj$m%OiX>OB`E@0Xp#8p{Y2;Co|Q3A(o=p#>ZU}u(?Z41HMMj3W6PW9A;RW5Y3-Qk z-uUs47PWUVT)#DI__KR zu|cvdc~e>!wg=~vw6?u=_g%1$SJ3McJqov>^2UL3W|RIQ+TNwLlo5lR9G9akpW8Dj zLE9464lEOyqyX$%oSGSmYD^EO77+14k*i>6qYX*+STd7>!Y}orQMoEeqY9t>I~HdN zTdh+w)^-#ovh(S6EcDtTgXWWgI{VI1YX}aw^DncV?WfCWX};*xZMPAZeDwgmHmeBOD>Q^jd6T?AlO|8MX^r@#Xvw3 z>P)c?mOI2bFy;szK8~kb7g)yO@|n{ObI^sl8X-(AK&S%*CD zd+RsQms$iDWT%?udCXE6Pu(q_Q-7BFSRK6v zLrhG}rkx^jl-J3bGM0p41TN#W#4_Ey*YFygeDifZD^6gg7$SSvx`t^`LCX<(b~&cR zjKPkOmS2wVEyR1!&A6wt=@Cbl5XR`>2(F-uLErXJ(u$zMSxoVke>sWbdy-W1 z6bxc~xQSj%*!l_9Y^OzYf#mU^X|Ju@r~9wy@`enfC%4Por%U5Ue}zko(HK7<{OC^+ zFDOQpIYeKVlj^d$4!&n(czon&*HgTg& zzbj%!^HoRZJ0LAxSj>r=l^v!Mgwg%d>NozcHO!t{Av}RS0|N{M14HOFu@Q`e%P_Y$*3-Xm9B8KGJ`n6E(i%iug z(debGs*a9piVv^SV=xF>gJsu;d(5wF%8}&V6-fa`jFpmM29_*D+9*Fpa0N;6U6MzN z?u}IhH%5XYi3E3fQ*B2!N*exA-8-6Gz zP6lym?V$D`5H_lig^mQxmBqw&{@rKKl_AsA-FBW*F{?Cok*yuajUWiF`(YKAe^+*X zvhkEb&Eo_g(I1v}HNCB@S*4Dsr|PPFb;Qj{=TF&-bB|AzcLs9qNH$Ra6k`&#q8?oG z{3B|9;;Y^R%RcX>!c+#J@!|ApTyLT}yrOQ^aKapS%A!@)kmz4b%@*XC*V`P>ff+YqIY_Qn0^tFKlHjH-*Rn4t|MfIY%eo- zP_Y9m{94rYzdzsj*-^>IDE1i%K5$1pt7wU{RIgHVY$+^$@4Tugg|Ut#$gxTl6=QuO z$@%NOHD;@49lq&Z%yBJ=GLLV_8UyIvdVB$tyfGex! zfSMm8;x|EjZlP6bC{!wxFttK4ri84abai5l2{@SvXGAz=>oqdOh&%ZIw zv@bOEv0t7{C}JuwTU3ThbyMfaFi)_0myC+xwgoQx3rA!;$MsF;0P&G`%?5ZaIO)ZA{^ILL6Z$oai(8NaqR!ySAhZ2_pn;1r-L9oID=mzIY0YE(GBrh zA@}j?J8aA4;;CKE9c>L?P%*~z>;Pu08qiYKIe|l?*DlA`Cfz&#}_UlTui<>Y`UB zZ^&QO5#H3s+|-Sv8PxOrfdCVD$(i|#yftp-I*H5M1s`(q2RFA+pWKQYG2Pp=0p)S& zptLbWFKbLS%?Mn*e`CiYOh@DgpbjBn3di3E_b0ehni((#?Q|*=$6h?Z)W*~&Xdkfz zir4j2Tx0}NX2m{GJk*nzFMajParT!%mPy-X!aYe>p z$x!E(YnCimOJldyWv9l-6E}^V`P{P)uPEHd=RpU!3vE3TsVO6&h~HS9%%U!b7TvAX zsT#hRM5DZ3rH8k*P7729`wSwOjkS-dEPqwh=`I$5)CgJ;>;V)u%ER6}f-QPwz2PrH zp7gSm?(j*R(+*n_I++vo&*ZmW*A|IzAu2Ts%4*kztMJ>dd|mz>ivEylxu(~6|E1O7 zL^gBcGD6I6ZjPw0izv@2iU_O@FM^y~KhjHAqkN>?`w_F+h_?5HfFl`u5>$$md;p3A zkSVdo@8E-wl^N=1=&W^0XE|*4&cH+NM#lfa72?EEJy#c`iJ@11iZ(9ex;%HESXf@Z zeLOzaqL1c<`aMkhlUuS+NDS|LJOgXQFCxSaCx^yHxBpl$f*8Hx-6i7D6(e<$a4cO! z?6LAODM7{o6)Tx)1q-SWKhyE9#Nzn(R#3&4B#8mXN}ju>ed_UK65dm5$>=6vyzp7z zGMVAj-PP2V!>vo`0Gt5=gf<|=+Li9yM12FbigbLqVDe#Zb+o|s(PK5uh@NWl+X(GV z_HWht)I|^&$cU8y#9${(bYgOLQdl#$VHN63(^8jmeBBuTl7=6fgWa?=9fZE`!(^rZd3xW?F`9$0 zC+fscGY>Yr%T%#F-14U!ey7CTV-`IUZ;NZuAcEWOo`0)tr3!pr!@wjoM1(VH;3>~=@Ag7OQJSs4nb;Eo^P$x3aKOv& zFib!-i*IXSr@CL-5yTit#E@^I1*MLFPBc^EE!htYME3B4UKiL~YpQx?SEI(g>jepD zSoT;sRjyLALY02B$oQ1w9Iw6!Zg!8$$5|SE9d2m-OF}uXRW|BJeb(<{QHD-CT-P>b zk6dUr1WWnelWhdP8j zVj&+MrzEwo>O4<@-C=2%|JH_nOu9V$ zpGv5L#`TI9u&9+TKQE#^3!gH*SFRxS#pT0A6s9nSn;taizsIiuU1r$*mJ*uLmRc0p zr(dVN^IMEZcCD{tL#uCOZD#na?f+_neD@V_o-@lOYCv$DKDV!AN{zAAva>8A(#9Rv zeV~KbAt|%t)ya$D%RqyT`Xx`4AT#|LHZ|RNJ~D$#TfDF312^xU3x}<5#vJ_M@XMcA zc+8zCfNPBKZQFxBnwU*(i%mT4e`2kjwl()+)X<{YS6v@4t5SocY)I!RPVsXqs4MQW zFrj5uEym!v1+v)h@GT&R4GwmXINi4)jYvuR1!h&9F@gfk=~If}p=Erx;VN>GOCBNzyKs!#R82lVS`}EEU|NbDBEm!o^&dgBa=?#A#%#!rW@u5~->a%e&?aos zxW0v~?$K+?Fy}72LJ1Wdi7Nkzvf?iKbJG3%8Ri@zh z{raU%4kiv+IpgmP0$E&td4Q{zoSBrVo?VfaqnDVJfu5I@r}*zhc6@w1*bt3`sWldS zG#->kZfAbA$4~T&h)o=;nYRW#?sC`plc<8geRsTJVL-#MEUePv<0q>0b8GI}^}_ku z^cce4^cV>5G{`6>8N(f#G6+Flbf6YCb0nJ!*qH?7(8giEzoR3RtxX`s#zQ@+vG<0E zLeuPE3j7^rfxqF9h_+~cb`uaY?574bLtSw~Ucy?E+T64-A3A`+i7T6;EF?cWx{nd{ zfHoiALrZ(%rhiWWgh5akL&j#rTs8l0v47owPWD|YsPp0+Ixe9!SB3Nf5{YxrJo!5! zSnVQ#=>+~Z_mhP}u!mWbL^_EGJDQIrxPf&SZ#^>4nQ0+B9Ptzl3z>Qi%OaFpR|D7kXH3BQS>sWw}?XzmR;BCWL zW%H;)t*{*uLG{=Eh&3>zF=}==_{u>HHZ|GuSZA@?GXkct@LT_N;NNZ!_ zY+!8QY(Qu4XlHNY=xkx)BqdGbI7vStLjx&AOEoh!+sL}ayeBD5^B_YjAuT~8A&fRN zJ>@hrOFK3t0V_M+!YX{+shV7T5|9#z`sbts<=CvW4V|>~s2J=hxzy~05EGOI=HFTY zflz?|-<2=_E%?7$alrq6{8t^!|1|ke(8+%r000yR2>&ag`G3Kl{HKHeB!B#m184gG og9GxPhX1*>|6?fa`2Vrd@>2hTiGK~`Uq=B10C@Z73;^JN0h;^1k^lez diff --git a/twint-sdk/build.gradle b/twint-sdk/build.gradle deleted file mode 100644 index a74fd88fe5..0000000000 --- a/twint-sdk/build.gradle +++ /dev/null @@ -1,143 +0,0 @@ -configurations.maybeCreate("default") -artifacts.add("default", file('TwintSdk-android-7.0.0.aar')) - -ext.mavenArtifactId = "twint-sdk" -ext.mavenArtifactName = "Adyen checkout Twint SDK wrapper" -ext.mavenArtifactDescription = "Adyen checkout Twint SDK wrapper for Adyen's Checkout API." - -apply plugin: "maven-publish" -apply plugin: "signing" - -ext["signing.keyId"] = '' -ext["signing.password"] = '' -ext["signing.secretKeyRingFile"] = '' -ext["ossrhUsername"] = '' -ext["ossrhPassword"] = '' -ext["sonatypeStagingProfileId"] = '' - -File secretPropsFile = project.rootProject.file('local.properties') -if (secretPropsFile.exists()) { - Properties p = new Properties() - p.load(new FileInputStream(secretPropsFile)) - p.each { name, value -> - ext[name] = value - } -} else { - ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') - ext["signing.password"] = System.getenv('SIGNING_PASSWORD') - ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') - ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') - ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') - ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') -} - -final theGroupId = "com.adyen.checkout" -final theArtifactId = project.mavenArtifactId -final theVersion = project.version_name - -final theName = project.mavenArtifactName -final theDescription = project.mavenArtifactDescription -final theUrl = "https://github.com/Adyen/adyen-android" - -final theLicenseName = "MIT License" -final theLicenseUrl = "https://opensource.org/licenses/MIT" - -final theOrganizationName = "Adyen N.V." -final theOrganizationUrl = "https://www.adyen.com/" - -final theTeamName = "Checkout" - -final theScmConnection = "scm:git:git://github.com/Adyen/adyen-android.git" -final theScmUrl = "https://github.com/Adyen/adyen-android" - -group theGroupId - -project.afterEvaluate { - publishing { - publications { - release(MavenPublication) { - groupId theGroupId - artifactId theArtifactId - version theVersion - - artifact('TwintSdk-android-7.0.0.aar') - - pom { - name = theName - description = theDescription - url = theUrl - licenses { - license { - name = theLicenseName - url = theLicenseUrl - } - } - organization { - name = theOrganizationName - url = theOrganizationUrl - } - developers { - developer { - name = theTeamName - organization = theOrganizationName - organizationUrl = theOrganizationUrl - } - } - scm { - connection = theScmConnection - developerConnection = theScmConnection - url = theScmUrl - } - } - - // Add dependencies to the POM file. - pom.withXml { - logger.lifecycle("\nPublishing maven repository $theGroupId:$theArtifactId:$theVersion\n") - - final dependenciesNode = asNode().appendNode("dependencies") - - ext.addDependency = { dep, scope -> - if (dep.name != 'unspecified') { - final depGroup = dep.group != rootProject.name ? dep.group : theGroupId - final depName = dep.name - final depVersion = dep.version != 'unspecified' ? dep.version : theVersion - - final dependencyNode = dependenciesNode.appendNode("dependency") - dependencyNode.appendNode("groupId", depGroup) - dependencyNode.appendNode("artifactId", depName) - dependencyNode.appendNode("version", depVersion) - dependencyNode.appendNode("scope", scope) - - if (!dep.hasProperty("transitive") || !dep.transitive) { - final exclusionNode = dependencyNode.appendNode("exclusions").appendNode("exclusion") - exclusionNode.appendNode("groupId", "*") - exclusionNode.appendNode("artifactId", "*") - } else if (!dep.properties.excludeRules.empty) { - final exclusionNode = dependencyNode.appendNode("exclusions").appendNode("exclusion") - dep.properties.excludeRules.each { rule -> - exclusionNode.appendNode("groupId", rule.group ?: "*") - exclusionNode.appendNode("artifactId", rule.module ?: "*") - } - } - } - } - } - } - } - - repositories { - maven { - name = "sonatype" - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - credentials { - username ossrhUsername - password ossrhPassword - } - } - } - } - - signing { - sign publishing.publications - } -} diff --git a/twint/build.gradle b/twint/build.gradle index 41af91cb34..8b4809aa89 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -42,7 +42,7 @@ dependencies { api project(':ui-core') // Dependencies - implementation project(':twint-sdk') + implementation files('libs/TwintSdk-android-8.0.0.jar') //Tests testImplementation project(':test-core') diff --git a/twint/libs/TwintSdk-android-8.0.0.jar b/twint/libs/TwintSdk-android-8.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..f076a446ac099391d8242673d22bab998f3ba03f GIT binary patch literal 7850 zcma)>byQUQ`v2)xx*LWD=|&NTj-g9(fMMtkVJPWFS~?^oB_stU1f&}Tq(hM!N@{=~ z=brn$XC2nM_jmRmd$0Z3>+`(-*srHPkESXr8q)oRg~W)(h$4=|Lb{HEg!C942?_gt z70e5MV&w?-@QBwn)gYE3c?y&+M^{1DB6vkwlAp?37;?doq=aJ2JyF2#)<*kta^HC= zSA?bj8CB-B&+y+9qsv|;^kOl^JXKYhf<8jNRMY~4!>10!$#X$twoKuoE z=i-jQ`(oippc+VHn2d&r3zqy@Z^jE}It)V0g8z@`=#~VjXQFXCH`Yb}`S+zJ zgc-$BR0Khn(E_(YE*a*8s(gP=qu@_m#^4h$+Qjl>`l}wS)@AvK0wGhnh&;n~J=`n( zI2FCVoES1A`)^z3)<+BxlnNTeLT_!-<(ORtgmj-|U^~9}h|Dghv(2&=L2qBLl*iM` z=oDe_Iep&3h`8RC?7;PH0WQpQMo4u=C3fL1y`|2WkY_b9`rc3%ZkK6 zwk598G%$|e`tA^s4ScL6rE<RpDUVOZrA9I;W(uY?g9+SyZ-Il%cRk^L!p}oHAWHdhdyMVf#YOKNc^aX$b z{SKBbb7HZaSu-=B-i$BV=uIE@NZ)E?S`;@rGzX6z+STlD`Ifln<3oLZGd1Y5{hhcIQw7n6mKmhC3cJ_IOdjRv)2#PO-!7 zRK$mqn#%2$i+&09tv_9qiCa#h*Zi9!(4Hll;-VrUz50(MSpMk_$$qi5^#2{}FK{5p zlT`frUCL?Bumr5S5qcPKwK13Bg4D+^|MGfUo#RgBJ(h>MgkFHGXvQhwlBa{$5ZS9O ziTNz!v^dygdUu9@3lpNMm1#uyv)0L3Gz<9JUG6dL=*{@c8uO0#$i)!Pp(lzDJ zV9baeiC6sv{4)~l;TLMSv*W;@txn?ld;@;vrww9EQGErB3Q^g4B(MBe(I&C6dZ!(S z*is?GniB5J?87xNUf^%tMj~_u`X!VklTLN&nX+%1#$BTdGS&UiBC(;f-#eRVth_pG z4DnzIwWR$GG%ErDkCeNg8;**LzS&@UN|^Uf`7yvmxsk0FlY=X1E99!>Ek)EC76$fG zx{gcjnOpbjZMnUeasdwGd<|(oM@~W!Jyfi6`qqpvCl-wL1KX7KTqFdSn{B;i+S-g& zPAqgC!zYBVHSz1SdQ3gx1d_=?9^lVKJ@qa3R!=nMu)>aTF6U`qiD}QL#NNQZBnPry z1{%|J8Q&>>1F1PT>Oc=etn9m)%b>izSf^Pjo(`|IOLx%}$6sACEK1E~vMm-h6kV!$ zY;T{4IB+h!uc17_@nw4Po67mPYE0TZ$BbJ#HNM^Glk(s`i!ueMX-US6?UJWU z@@P4CUgof$kKvWr)r)>!vG$bniKN4y<}bx^)cpJEHnGhx?*TT2)`yu z36Im%hn-9jr-joiuB$WEE1Sl8h$@B5b-<0|$XhrsiozIkPLvZ{u{(Wccd%n?!5C)neuj zDAQ6*?5flgg-bR)Q#koj({o3B0BEo4B%1?;Qq+jR+WcaLL|`9RMfh*i#(q-^yo1_2 z{&kh+EM1D({(j&#zn&n_&R3#DbBvg{FseX_-3m^Z# zSZ-g48pFY6t>X-FEoRLv<{(xNx9#j?jmonH54&B;MK0Eju6e)Y%`YgObn+7N7UyoG z81?ax&X_vz*nsRl$^W)yYXhTknDYwr-!hx?Q*4my$iBicZ)|q&7&F-R5Q0@TRry|O z^#^d(NST>!zt}nRp8i;2SChD{Zwfu&gdI?2eY}$Js64DWee%{9B01Or`b8`BWQ^Y2 zx2Rpg&=RwUCt$8g%qS^NpRCN(+7R?UQqQA(ib{`a@Xerl@6XxzP}C*7!e>UR6m3~= za0c9Ta&<>`UKhgECh-~Pt$c$HRF?nL#NfBhQrJ;#Jbg;vFtqi3e8^y)j z$-<9NI0-#Ijpt6RcgGsrZqKrOphnp)36-B(Kb2Jbj0RLK zhkaus?rzz_)ws{*((8S8`rhP-yZ1^YoNTIcC*RWaZ$U*_IaCo5Xuyr!vkO zVt+Olz2=C=AB@P+R$Dt>ya|X+adV(!;eOs1m4w;6;pM#dh4#sGOKnoq5bwL}+Wtq_ z)pXNVrs(NwkLb|G8S<9d!5^vj85(Lhx=U_fljq`HBUHC)lB<|D-zjEfzx003-EW#2 zn2s`H{!<#?MojkqouZ>xBD%0{xbF!C%8xUmiYp zwsR%NYWgK-?gd3Ah)eWbV90%^@hsdWoH$ZXr*K~~I8gM8?>PodG5d65_`xV&?WV%a ziqiKe%W%WTjC*g1pLUOX0%h1%Q_oqj#w}4*BeChov(s~wg#%^@dDzwp+ zO<@>v&QAIy_G;VB>2n8@k4JgWy0jX4=^on7vtspE~0u;k7z&EJ5FP)QUr@=I?u_5T}kCR_wS!hdBA;n6cqL)&{oUZQw#6JOC!XpDn zDebAvP&@N&tA0b`A(>GlzD+W$^Xkh5%BP=wRu@Dix2;d3cOwMy%h3{fb$lf( zu`l=YH-35UW9tyxGwH-FGVNucAto?Qw?p~UXN|+Hnw5T_nB7Dyjlp|wymg{djT~xb zvIK>jCWU}fG)UUh`G7vYF(BCWdvJ0O-SB!2HPkH+e1f>9lgR7eEM_sB(=}1XO#}G5 zbfrn-GOZb*G>i7@ma1ht`wl|f)zW;%JqK^8k6C`wWW?9H2PsUf$FTI1B0A#_HzJ9e zqN`-QJ7Vak)PG~(L$IYwf>mEI;|H>?pNdOqC~Az#6*&`3~A+qQD2l5a-T*@ zw;t68a}NbZ7|xg3s|2wM7f}%U%K1O9z^b&$a^)l4B!+P`WfxaeyYoIz&0BRP??&2^NaPDsa&BB z=3_*-f)#w6%A$auzzIw4(8=Br-Z*TT;nHktrZHl)=Oh){R4UkVg0k?o13KYGhxzLH z9!-K6;r>~NFWEWzsKusKagTB?6~$YdZAJ&5V6E&vv88i7DX2#kWTB@#l_g z9yCz6zHC60CN)Yt)XkEgqM=KG?AvUSg-bprY%bXXM7;-|jv6AJK>G#~hzY%V^vZVn zoG!#IuZy|N4GQ!mqZf-z6z4O+0w9r4TaY93sZu%k&dd8VQHjMX+PF=g+d7gZN?NTM zNge6Fvez?=f4|7Ei><4qMQIBU2-T$WU&-o9390)wDy%vTlW?J90mGKLteNWOF_b5_ zr)*qV7lKIj)ITXgT?_X~I3qOH=%9;zoeGn`0Si<%M{iTG(2F4HznRHhg%_SHAO`RP z194`0UcGZlhr*2)*~#ISr`&--J1AyFw~5oA)o${6jr_U3^79rY3z6KgP8$UfES;XC z*uh1!h(mUThk`Q0V&8XjfHT8XPw!m#ep5B!UI|RJ!+9?llyP&0?R$-q$b8&^U-yh|juLZM<8v9K}d5787xI zT>m!g#?|jc(@0}1_DihE-ErgF=vu#Yl!x$uE533a{Nz$eQLk3pfOfb?fm1LcMH^K$)s$NsQ3_D(Us#T z{Pj?d7kgFHo{zPkSH7h7a7iM*J8fW2LmGZvOS@}X3C$Uf%ij5Q0x*VnB4sm;B>b}E z(wJ-K(2EbFXYV&G{bJA?t;cJZ?=ZmOquK2n>3yu^`kB$WG@D52YV3KRcB~LQt{LXQ z?vuwp_{Wgu6{E@o3!`>(>lQ=}7>zKZ&i>=BcvE%|Kx%oNjxBFi9D7`kzfo=m1!I;q zz#$3j>q;f~fn#KR-3>SudNq#^9cwJ_+DXpCV*L=_wSyu#=^6hCDWI!?zK0Rg_Qlnz zSS=HWyi(fvnQfMNh6-e=IQtmfs=rvn4+yBOYnb}JP$SA^Y!mSI1 ziQQ?`wSnLAJ3MQDVL2e?b*pQC>0HB7s3prs>K;9R>*xJKZW=99{px1c;#IgyL|_ASJ5(iDt=>psHhTfA zl=4DRdS<}a*vFg!E07FJoL1$fPlK+?h_{wsAyjvx0OK2J{M1dV14{M~0o-(3CdwLJ zCj-YbNm!A=519QyQE9-c`}>u*0Tr`8Pd_qyJK0UGMeKS%y$fQP>t{F3Wo2y1)Am9J zutf@#TT7KJ8}PpiDA%wxWfMw3n#Pj1u_-fO+aR~kBIsqi@KjQ&SYxt7#5sIkjYSra zek5)*+`eV|BS+45Ti6{nY*bugJBi+1j480-*ahpUZ+)gAR(tolD))75RtxFr8ZG@Z zm8vlRm!gP|-79D|!>d)4S%T*43l9F57va(;)SQ6uyt138$;eU+@e#H3N^3*|D7gyd z_HSQ3N6+Al(t{&EnIbcwvLJGc6DCMkQi@YmQ&Lj_uslOh|J^Bx{l8_`BO@Wz-=DY1 z{WqtCytIP2dfK@-d;Hlu0bQ&i@=mUvz8dbbj$mg8`TzU=XH!+8t?ok1PY}?MU2KQ- zngoZ#b%D5mHM=xQ0bNg%U0aS&JJxRix%K1Yy4mZ(#wmQg>V?nU(OhhR$M1e6(TCS& z=>DoB!c2rN55wpdPp-$t%#rlrQLf(Z^)u4P-bMsRfK&Yri%lLs87io(0MBGGZ5gBJ z%TX3+O!nrRa&NwEnPS$uV|_(6&QCuf<_r8o3*GkclC6W2ah$Ci)mvMp66D`6YvL-9 zuX4?P%*XhcpcC3J?f~#FBpJoQJQUY{=75E6moWY z5-;1Vl|Q&;=F3Enx2E@zg2KnY$7%D(uLrGWg{w7?qp>Y}j2I^gH_Qe%*u943sfDL~hOn>S?{ucXNUy!tGD)oS}Zd%rw?Hdjk;Gk>l*} z{pLeyji3}wUk;+exnC`bmsB?6OsRYGVd2%vMe@TwVMHI3KZBBW$%zZc<>pgC^gROc z0Tx9zgs59((zWmocGp$VevjoQmL4#Vwwty3xw=oZ3O*+3ET*?s8&gRvbhpfMJ~4c^(7zh>7=O{Ncj)8LpC5@ESA+Mem_mvwn| z-LHWp*)jsZ+_Y}KnUG!ub9-+3AY51H5Pa$ac*?dvXO)+ao{TeWXcDGb#Rb;Av;@)IJ!{k;#`-W8)O2JR|870@!C_X7zz zKBH6Xm*ivREkDdVWS>o0+Wx*BlWH~Ez3GuW;_~=Gu6%y5{R{} zUcbcFtIDVgeiYFy_~L0#-+`fVW>kRP<|`tN51{?i4-snQgAw$j}rGyK_K) zyvq~@<^i6|#7d!ibOiae>qmAUxAIsPlTreMww4$b`r+5ZD)jQ9dZ7*&4j8)_b9YF8 zXJNH^+jo9t%C)+fdU8g;y@0 z{XdFk^8vRqmBIqe`klCE`69`Hb{3^yYd;99e~7X(V6WrdkxwpeyI{dlPgVa_#glHg zN4p`6OV2Kih%TX*4?*>@IV4+oU1wTi8c1xkF z4p?h#R{HOhYz~Sv*WFW6bx(=RKc(c)5>FH2Y;ETZeE`NEDP-z7L4XyT9Tf`T!#x<6 zXz!w@NG9H)1D=&cF%^=WC8I7ZWZOVsPLMe#!XnO~aAE_H(oZaUAt)a)cXQKgPEoIuPAQ4M(Z#NH5cjj%U&I}+AvNzrmO<4YbZ(n00N7I5~2C+okP28+G;NCoC9d+(x(En zyVvr=a|d#Xfc4sm{gCFYsoo>ZbGqrDQUfkk9`WjY$He!+ibq)qyg8V1Obi>;VGO_L zftGvbo`=HI!M&JTso@eb>=i3AYglesJ z=FY=`VhKkNmk(zNUkti0^^s9X?*IIM zo8kTI(|>ONndm*-_Hg3ypB+fp_niZn_uuo6hua<=`~I~}6661W2>j66!$##VYjgPj z%i4ps<)PV!rPg0&9f|*oS;+qtR{yN + + + + + + + + + + + + + diff --git a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt index 027489344d..8f8a3cb88e 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt @@ -10,7 +10,9 @@ package com.adyen.checkout.twint import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher +import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import ch.twint.payment.sdk.Twint import ch.twint.payment.sdk.TwintPayResult @@ -35,14 +37,26 @@ object Twint { */ fun initialize(activity: ComponentActivity) { twintObject = Twint(activity, ::onTwintResult) + observeLifecycle(activity.lifecycle) + } + + /** + * Initializes the [Twint] object. This method **must** be called in [Fragment.onCreateView] or before, because + * the Twint SDK creates an [ActivityResultLauncher] under the hood. + */ + fun initialize(fragment: Fragment) { + twintObject = Twint(fragment, ::onTwintResult) + observeLifecycle(fragment.lifecycle) + } + private fun observeLifecycle(lifecycle: Lifecycle) { val observer = object : DefaultLifecycleObserver { override fun onDestroy(owner: LifecycleOwner) { onDestroy() super.onDestroy(owner) } } - activity.lifecycle.addObserver(observer) + lifecycle.addObserver(observer) } private fun onTwintResult(result: TwintPayResult) { From ee174f39eba1b985d2bd588185b9516e72ceab7f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 10 Apr 2024 13:40:39 +0200 Subject: [PATCH 142/272] Implement polling mechanism for Twint COAND-806 --- .../core/internal/util/StatusResponseUtils.kt | 5 +- .../provider/TwintActionComponentProvider.kt | 8 ++ .../twint/internal/ui/DefaultTwintDelegate.kt | 87 ++++++++++++++++-- .../twint/internal/ui/TwintDelegate.kt | 2 + .../internal/ui/DefaultTwintDelegateTest.kt | 92 ++++++++++++++++++- 5 files changed, 186 insertions(+), 8 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt index 5811c35368..2f36615704 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt @@ -18,8 +18,11 @@ object StatusResponseUtils { const val RESULT_ERROR = "error" const val RESULT_CANCELED = "canceled" - @JvmStatic fun isFinalResult(statusResponse: StatusResponse): Boolean { return RESULT_PENDING != statusResponse.resultCode } + + fun isPendingResult(statusResponse: StatusResponse): Boolean { + return RESULT_PENDING == statusResponse.resultCode + } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt index 5376ffe4c3..4d410b3c70 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt @@ -23,12 +23,15 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.data.api.DefaultStatusRepository +import com.adyen.checkout.components.core.internal.data.api.StatusService import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.components.core.internal.util.get import com.adyen.checkout.components.core.internal.util.viewModelFactory +import com.adyen.checkout.core.internal.data.api.HttpClientFactory import com.adyen.checkout.core.internal.util.LocaleProvider import com.adyen.checkout.twint.TwintActionComponent import com.adyen.checkout.twint.TwintActionConfiguration @@ -78,10 +81,15 @@ constructor( componentSessionParams = null, ) + val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) + val statusService = StatusService(httpClient) + val statusRepository = DefaultStatusRepository(statusService, componentParams.clientKey) + return DefaultTwintDelegate( observerRepository = ActionObserverRepository(), componentParams = componentParams, paymentDataRepository = PaymentDataRepository(savedStateHandle), + statusRepository = statusRepository, ) } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 7f6a8dd95b..0a982170cb 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -19,7 +19,11 @@ import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.data.api.StatusRepository +import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams +import com.adyen.checkout.components.core.internal.ui.model.TimerData +import com.adyen.checkout.components.core.internal.util.StatusResponseUtils import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -28,16 +32,23 @@ import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.twint.Twint import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import org.json.JSONObject +import java.util.concurrent.TimeUnit +@Suppress("TooManyFunctions") internal class DefaultTwintDelegate( private val observerRepository: ActionObserverRepository, override val componentParams: GenericComponentParams, private val paymentDataRepository: PaymentDataRepository, + private val statusRepository: StatusRepository, ) : TwintDelegate { private val detailsChannel: Channel = bufferedChannel() @@ -48,7 +59,17 @@ internal class DefaultTwintDelegate( override val viewFlow: Flow = MutableStateFlow(TwintComponentViewType) - override fun initialize(coroutineScope: CoroutineScope) = Unit + // Not used for Twint action + override val timerFlow: Flow = flow {} + + private var _coroutineScope: CoroutineScope? = null + private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + + private var statusPollingJob: Job? = null + + override fun initialize(coroutineScope: CoroutineScope) { + _coroutineScope = coroutineScope + } override fun observe( lifecycleOwner: LifecycleOwner, @@ -103,7 +124,7 @@ internal class DefaultTwintDelegate( internal fun handleTwintResult(result: TwintPayResult) { when (result) { TwintPayResult.TW_B_SUCCESS -> { - detailsChannel.trySend(createActionComponentData()) + startStatusPolling() } TwintPayResult.TW_B_ERROR -> { @@ -116,11 +137,52 @@ internal class DefaultTwintDelegate( } } - private fun createActionComponentData(): ActionComponentData { + private fun startStatusPolling() { + statusPollingJob?.cancel() + + val paymentData = paymentDataRepository.paymentData + if (paymentData == null) { + exceptionChannel.trySend(ComponentException("PaymentData should not be null.")) + return + } + + statusPollingJob = statusRepository.poll(paymentData, DEFAULT_MAX_POLLING_DURATION) + .onEach { onStatus(it) } + .launchIn(coroutineScope) + } + + private fun onStatus(result: Result) { + result.fold( + onSuccess = { response -> + adyenLog(AdyenLogLevel.VERBOSE) { "Status changed - ${response.resultCode}" } + onPollingSuccessful(response) + }, + onFailure = { + adyenLog(AdyenLogLevel.ERROR, it) { "Error while polling status" } + exceptionChannel.trySend(ComponentException("Error while polling status.", it)) + }, + ) + } + + private fun onPollingSuccessful(statusResponse: StatusResponse) { + val payload = statusResponse.payload + // Not authorized status should still call /details so that merchant can get more info + if (StatusResponseUtils.isFinalResult(statusResponse)) { + if (!payload.isNullOrEmpty()) { + detailsChannel.trySend(createActionComponentData(payload)) + } else { + exceptionChannel.trySend( + ComponentException("Payload is missing from StatusResponse."), + ) + } + } + } + + private fun createActionComponentData(payload: String): ActionComponentData { return ActionComponentData( - // The backend doesn't accept null, so we have to send an empty json object. - details = JSONObject(), - paymentData = paymentDataRepository.paymentData, + details = JSONObject().put(PAYLOAD_DETAILS_KEY, payload), + // We don't share paymentData on purpose, so merchant will not use it to build their own polling. + paymentData = null, ) } @@ -128,7 +190,20 @@ internal class DefaultTwintDelegate( exceptionChannel.trySend(e) } + override fun refreshStatus() { + if (statusPollingJob == null) return + val paymentData = paymentDataRepository.paymentData ?: return + statusRepository.refreshStatus(paymentData) + } + override fun onCleared() { removeObserver() } + + companion object { + private val DEFAULT_MAX_POLLING_DURATION = TimeUnit.MINUTES.toMillis(15) + + @VisibleForTesting + internal const val PAYLOAD_DETAILS_KEY = "payload" + } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt index 98e0c1adbd..f6fc7b40dc 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt @@ -11,10 +11,12 @@ package com.adyen.checkout.twint.internal.ui import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.components.core.internal.ui.DetailsEmittingDelegate +import com.adyen.checkout.components.core.internal.ui.StatusPollingDelegate import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) interface TwintDelegate : ActionDelegate, DetailsEmittingDelegate, + StatusPollingDelegate, ViewProvidingDelegate diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt index 43fb08bb46..2124cb8e17 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -20,38 +20,51 @@ import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.data.model.StatusResponse +import com.adyen.checkout.components.core.internal.test.TestStatusRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper +import com.adyen.checkout.components.core.internal.util.StatusResponseUtils import com.adyen.checkout.core.Environment import com.adyen.checkout.test.LoggingExtension import com.adyen.checkout.test.extensions.test +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.junit.jupiter.MockitoExtension +import java.io.IOException import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultTwintDelegateTest { + private lateinit var statusRepository: TestStatusRepository private lateinit var delegate: DefaultTwintDelegate @BeforeEach fun beforeEach() { val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) + statusRepository = TestStatusRepository() delegate = DefaultTwintDelegate( observerRepository = ActionObserverRepository(), componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), paymentDataRepository = PaymentDataRepository(SavedStateHandle()), + statusRepository = statusRepository, ) } @@ -68,6 +81,10 @@ internal class DefaultTwintDelegateTest { @ParameterizedTest @MethodSource("handleTwintResult") fun `when handling twint result, then expect`(result: TwintPayResult, testResult: TwintTestResult) = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = StatusResponseUtils.RESULT_AUTHORIZED, payload = TEST_PAYLOAD)), + ) val detailsFlow = delegate.detailsFlow.test(testScheduler) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) @@ -88,8 +105,79 @@ internal class DefaultTwintDelegateTest { } } + @Nested + @DisplayName("when polling and") + inner class PollingTest { + + @Test + fun `paymentData is missing, then an error is propagated`() = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + + delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) + + val expectedErrorMessage = "PaymentData should not be null." + assertEquals(expectedErrorMessage, exceptionFlow.latestValue.message) + } + + @Test + fun `polling fails, then an error is propagated`() = runTest { + statusRepository.pollingResults = listOf(Result.failure(IOException("Test"))) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + + delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) + + val expectedErrorMessage = "Error while polling status." + assertEquals(expectedErrorMessage, exceptionFlow.latestValue.message) + } + + @Test + fun `polling succeeds and payload is missing, then an error is propagated`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = StatusResponseUtils.RESULT_AUTHORIZED, payload = null)), + ) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val exceptionFlow = delegate.exceptionFlow.test(testScheduler) + delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + + delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) + + val expectedErrorMessage = "Payload is missing from StatusResponse." + assertEquals(expectedErrorMessage, exceptionFlow.latestValue.message) + } + + @Test + fun `polling succeeds and payload is available, then details are emitted`() = runTest { + statusRepository.pollingResults = listOf( + Result.success( + StatusResponse( + resultCode = StatusResponseUtils.RESULT_AUTHORIZED, + payload = TEST_PAYLOAD, + ), + ), + ) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + + delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) + + val expected = ActionComponentData( + paymentData = null, + details = JSONObject().put(DefaultTwintDelegate.PAYLOAD_DETAILS_KEY, TEST_PAYLOAD), + ) + with(detailsFlow.latestValue) { + assertNull(paymentData) + assertEquals(expected.details.toString(), details.toString()) + } + } + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYLOAD = "TEST_PAYLOAD" @JvmStatic fun handleActionSource() = listOf( @@ -116,7 +204,9 @@ internal class DefaultTwintDelegateTest { fun handleTwintResult() = listOf( arguments( TwintPayResult.TW_B_SUCCESS, - TwintTestResult.Success(ActionComponentData("test", JSONObject())), + TwintTestResult.Success( + ActionComponentData(null, JSONObject().put(DefaultTwintDelegate.PAYLOAD_DETAILS_KEY, TEST_PAYLOAD)), + ), ), arguments( TwintPayResult.TW_B_ERROR, From 5dbf86511276168d09938d150120e6cf34f19958 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 26 Apr 2024 14:47:48 +0200 Subject: [PATCH 143/272] Fix action component callback leak COAND-806 --- .../twint/internal/provider/TwintActionComponentProvider.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt index 4d410b3c70..7be01fe36c 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt @@ -59,13 +59,15 @@ constructor( val twintDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) TwintActionComponent( delegate = twintDelegate, - actionComponentEventHandler = DefaultActionComponentEventHandler(callback), + actionComponentEventHandler = DefaultActionComponentEventHandler(), ) } return ViewModelProvider(viewModelStoreOwner, twintFactory)[key, TwintActionComponent::class.java] .also { component -> - component.observe(lifecycleOwner, component.actionComponentEventHandler::onActionComponentEvent) + component.observe(lifecycleOwner) { + component.actionComponentEventHandler.onActionComponentEvent(it, callback) + } } } From f8d345615c29f11dfee2847dd8ec3845b0b55b1e Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 10 May 2024 11:44:35 +0200 Subject: [PATCH 144/272] Wrap Twint in a fragment This makes it possible for us to handle the activity result stuff and makes the implementation for a merchant significantly easier. COAND-806 --- .../ui/ActionComponentDialogFragment.kt | 9 --- .../example/ui/instant/InstantFragment.kt | 6 -- .../java/com/adyen/checkout/twint/Twint.kt | 78 ------------------- .../twint/internal/ui/DefaultTwintDelegate.kt | 14 ++-- .../twint/internal/ui/TwintDelegate.kt | 9 ++- .../twint/internal/ui/TwintFragment.kt | 50 ++++++++++++ .../checkout/twint/internal/ui/TwintView.kt | 51 ++++++++++++ .../twint/internal/ui/TwintViewProvider.kt | 12 ++- twint/src/main/res/layout/fragment_twint.xml | 17 ++++ twint/src/main/res/layout/view_twint.xml | 23 ++++++ .../internal/ui/DefaultTwintDelegateTest.kt | 16 ++-- .../checkout/ui/core/AdyenComponentView.kt | 14 +++- .../ui/core/internal/ui/ViewProvider.kt | 6 ++ .../internal/ui/view/PaymentInProgressView.kt | 12 +-- 14 files changed, 200 insertions(+), 117 deletions(-) delete mode 100644 twint/src/main/java/com/adyen/checkout/twint/Twint.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt create mode 100644 twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt create mode 100644 twint/src/main/res/layout/fragment_twint.xml create mode 100644 twint/src/main/res/layout/view_twint.xml diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt index a685f4218b..732f48dee9 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt @@ -32,12 +32,9 @@ import com.adyen.checkout.core.PermissionHandlerCallback import com.adyen.checkout.core.exception.CancellationException import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.core.internal.util.runCompileOnly import com.adyen.checkout.dropin.R import com.adyen.checkout.dropin.databinding.FragmentGenericActionComponentBinding import com.adyen.checkout.dropin.internal.util.arguments -import com.adyen.checkout.twint.Twint -import com.adyen.checkout.twint.TwintActionComponent import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -78,12 +75,6 @@ internal class ActionComponentDialogFragment : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) adyenLog(AdyenLogLevel.DEBUG) { "onCreate" } - - if (TwintActionComponent.PROVIDER.canHandleAction(action)) { - runCompileOnly { - Twint.initialize(this) - } - } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { diff --git a/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt b/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt index 777e7dc9b4..92345c370d 100644 --- a/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt +++ b/example-app/src/main/java/com/adyen/checkout/example/ui/instant/InstantFragment.kt @@ -22,13 +22,11 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.example.databinding.FragmentInstantBinding import com.adyen.checkout.example.ui.configuration.CheckoutConfigurationProvider import com.adyen.checkout.instant.InstantPaymentComponent import com.adyen.checkout.redirect.RedirectComponent -import com.adyen.checkout.twint.Twint import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -64,10 +62,6 @@ class InstantFragment : BottomSheetDialogFragment() { putString(RETURN_URL_EXTRA, returnUrl) } - if (arguments?.getString(PAYMENT_METHOD_TYPE_EXTRA) == PaymentMethodTypes.TWINT) { - Twint.initialize(this) - } - _binding = FragmentInstantBinding.inflate(inflater, container, false) return binding.root } diff --git a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt b/twint/src/main/java/com/adyen/checkout/twint/Twint.kt deleted file mode 100644 index 8f8a3cb88e..0000000000 --- a/twint/src/main/java/com/adyen/checkout/twint/Twint.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2023 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 17/11/2023. - */ - -package com.adyen.checkout.twint - -import androidx.activity.ComponentActivity -import androidx.activity.result.ActivityResultLauncher -import androidx.fragment.app.Fragment -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import ch.twint.payment.sdk.Twint -import ch.twint.payment.sdk.TwintPayResult -import com.adyen.checkout.components.core.action.SdkAction -import com.adyen.checkout.components.core.action.TwintSdkData -import com.adyen.checkout.core.exception.CheckoutException -import com.adyen.checkout.twint.Twint.initialize - -/** - * Object used to manage the Twint SDK. Call [initialize] in [ComponentActivity.onCreate] to make sure [SdkAction]s - * with [TwintSdkData] can be handled. - */ -object Twint { - - private var twintObject: Twint? = null - - private var onResultListener: ((TwintPayResult) -> Unit)? = null - - /** - * Initializes the [Twint] object. This method **must** be called in [ComponentActivity.onCreate] or before, because - * the Twint SDK creates an [ActivityResultLauncher] under the hood. - */ - fun initialize(activity: ComponentActivity) { - twintObject = Twint(activity, ::onTwintResult) - observeLifecycle(activity.lifecycle) - } - - /** - * Initializes the [Twint] object. This method **must** be called in [Fragment.onCreateView] or before, because - * the Twint SDK creates an [ActivityResultLauncher] under the hood. - */ - fun initialize(fragment: Fragment) { - twintObject = Twint(fragment, ::onTwintResult) - observeLifecycle(fragment.lifecycle) - } - - private fun observeLifecycle(lifecycle: Lifecycle) { - val observer = object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - onDestroy() - super.onDestroy(owner) - } - } - lifecycle.addObserver(observer) - } - - private fun onTwintResult(result: TwintPayResult) { - onResultListener?.invoke(result) - } - - internal fun setResultListener(listener: (TwintPayResult) -> Unit) { - onResultListener = listener - } - - internal fun payWithCode(code: String) { - twintObject?.payWithCode(code) ?: throw CheckoutException("Twint not initialised before payment.") - } - - private fun onDestroy() { - onResultListener = null - twintObject = null - } -} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 0a982170cb..0fc93436f5 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -29,7 +29,6 @@ import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException import com.adyen.checkout.core.exception.ComponentException import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.twint.Twint import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -62,6 +61,9 @@ internal class DefaultTwintDelegate( // Not used for Twint action override val timerFlow: Flow = flow {} + private val payEventChannel: Channel = bufferedChannel() + override val payEventFlow: Flow = payEventChannel.receiveAsFlow() + private var _coroutineScope: CoroutineScope? = null private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) @@ -112,16 +114,10 @@ internal class DefaultTwintDelegate( return } - Twint.setResultListener(::handleTwintResult) - try { - Twint.payWithCode(sdkData.token) - } catch (e: CheckoutException) { - exceptionChannel.trySend(e) - } + payEventChannel.trySend(sdkData.token) } - @VisibleForTesting - internal fun handleTwintResult(result: TwintPayResult) { + override fun handleTwintResult(result: TwintPayResult) { when (result) { TwintPayResult.TW_B_SUCCESS -> { startStatusPolling() diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt index f6fc7b40dc..217ab22bb2 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintDelegate.kt @@ -9,14 +9,21 @@ package com.adyen.checkout.twint.internal.ui import androidx.annotation.RestrictTo +import ch.twint.payment.sdk.TwintPayResult import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.components.core.internal.ui.DetailsEmittingDelegate import com.adyen.checkout.components.core.internal.ui.StatusPollingDelegate import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate +import kotlinx.coroutines.flow.Flow @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) interface TwintDelegate : ActionDelegate, DetailsEmittingDelegate, StatusPollingDelegate, - ViewProvidingDelegate + ViewProvidingDelegate { + + val payEventFlow: Flow + + fun handleTwintResult(result: TwintPayResult) +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt new file mode 100644 index 0000000000..0bb99ecf74 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt @@ -0,0 +1,50 @@ +/* + * 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 7/5/2024. + */ + +package com.adyen.checkout.twint.internal.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import ch.twint.payment.sdk.Twint +import com.adyen.checkout.twint.databinding.FragmentTwintBinding +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +internal class TwintFragment : Fragment() { + + private var _binding: FragmentTwintBinding? = null + private val binding: FragmentTwintBinding get() = requireNotNull(_binding) + + private var _twintDelegate: TwintDelegate? = null + private val twintDelegate: TwintDelegate get() = requireNotNull(_twintDelegate) + + private var twint: Twint? = Twint(this) { twintDelegate.handleTwintResult(it) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentTwintBinding.inflate(inflater, container, false) + return binding.root + } + + fun initialize(delegate: TwintDelegate) { + _twintDelegate = delegate + twintDelegate.payEventFlow + .onEach { twint?.payWithCode(it) } + .launchIn(viewLifecycleOwner.lifecycleScope) + } + + override fun onDestroyView() { + twint = null + _twintDelegate = null + _binding = null + super.onDestroyView() + } +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt new file mode 100644 index 0000000000..76b8e6a778 --- /dev/null +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt @@ -0,0 +1,51 @@ +/* + * 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 7/5/2024. + */ + +package com.adyen.checkout.twint.internal.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import com.adyen.checkout.components.core.internal.ui.ComponentDelegate +import com.adyen.checkout.twint.databinding.ViewTwintBinding +import com.adyen.checkout.ui.core.internal.ui.ComponentView +import kotlinx.coroutines.CoroutineScope + +internal class TwintView internal constructor( + layoutInflater: LayoutInflater, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(layoutInflater.context, attrs, defStyleAttr), ComponentView { + + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 + ) : this(LayoutInflater.from(context), attrs, defStyleAttr) + + private var binding = ViewTwintBinding.inflate(layoutInflater, this) + + private var delegate: TwintDelegate? = null + + override fun initView(delegate: ComponentDelegate, coroutineScope: CoroutineScope, localizedContext: Context) { + require(delegate is TwintDelegate) { "Unsupported delegate type" } + this.delegate = delegate + initializeFragment(delegate) + } + + private fun initializeFragment(delegate: TwintDelegate) { + binding.fragmentContainer.getFragment()?.initialize(delegate) + } + + override fun highlightValidationErrors() = Unit + + override fun getView(): View = this +} diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt index bb3e31659e..be026247b4 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintViewProvider.kt @@ -9,10 +9,10 @@ package com.adyen.checkout.twint.internal.ui import android.content.Context +import android.view.LayoutInflater import com.adyen.checkout.ui.core.internal.ui.ComponentView import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.checkout.ui.core.internal.ui.ViewProvider -import com.adyen.checkout.ui.core.internal.ui.view.PaymentInProgressView internal object TwintViewProvider : ViewProvider { @@ -20,7 +20,15 @@ internal object TwintViewProvider : ViewProvider { viewType: ComponentViewType, context: Context, ): ComponentView = when (viewType) { - TwintComponentViewType -> PaymentInProgressView(context) + TwintComponentViewType -> TwintView(context) + else -> throw IllegalArgumentException("Unsupported view type") + } + + override fun getView( + viewType: ComponentViewType, + layoutInflater: LayoutInflater + ): ComponentView = when (viewType) { + TwintComponentViewType -> TwintView(layoutInflater) else -> throw IllegalArgumentException("Unsupported view type") } } diff --git a/twint/src/main/res/layout/fragment_twint.xml b/twint/src/main/res/layout/fragment_twint.xml new file mode 100644 index 0000000000..1e20bfdcc9 --- /dev/null +++ b/twint/src/main/res/layout/fragment_twint.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/twint/src/main/res/layout/view_twint.xml b/twint/src/main/res/layout/view_twint.xml new file mode 100644 index 0000000000..e2402a57f9 --- /dev/null +++ b/twint/src/main/res/layout/view_twint.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt index 2124cb8e17..e47ebdcd65 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -68,6 +68,16 @@ internal class DefaultTwintDelegateTest { ) } + @Test + fun `when handling action successfully, then a pay event should be emitted`() = runTest { + val payEventFlow = delegate.payEventFlow.test(testScheduler) + val action = SdkAction(paymentData = "something", sdkData = TwintSdkData("token")) + + delegate.handleAction(action, Activity()) + + assertEquals(action.sdkData?.token, payEventFlow.latestValue) + } + @ParameterizedTest @MethodSource("handleActionSource") fun `when handling action, then expect`(action: Action, expectedErrorMessage: String) = runTest { @@ -192,12 +202,6 @@ internal class DefaultTwintDelegateTest { SdkAction(paymentData = "something", sdkData = null), "SDK Data is null or of wrong type", ), - // Success case: we cannot instantiate the Twint SDK (because it uses activity), but if this happens we - // successfully handed the action over to Twint. - arguments( - SdkAction(paymentData = "something", sdkData = TwintSdkData("token")), - "Twint not initialised before payment.", - ), ) @JvmStatic diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt index 9f311f21e0..600bbc4ddb 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt @@ -14,7 +14,10 @@ import android.view.MotionEvent import android.widget.LinearLayout import androidx.core.view.children import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.findFragment import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import com.adyen.checkout.components.core.internal.Component import com.adyen.checkout.components.core.internal.ui.ComponentDelegate @@ -108,6 +111,7 @@ class AdyenComponentView @JvmOverloads constructor( coroutineScope = lifecycleOwner.lifecycleScope, ) } + .flowWithLifecycle(lifecycleOwner.lifecycle) .launchIn(lifecycleOwner.lifecycleScope) isVisible = true } @@ -118,7 +122,8 @@ class AdyenComponentView @JvmOverloads constructor( componentParams: ComponentParams, coroutineScope: CoroutineScope, ) { - val componentView = viewType.viewProvider.getView(viewType, context) + val layoutInflater = getLayoutInflater() + val componentView = viewType.viewProvider.getView(viewType, layoutInflater) this.componentView = componentView val localizedContext = context.createLocalizedContext(componentParams.shopperLocale) @@ -152,6 +157,13 @@ class AdyenComponentView @JvmOverloads constructor( } } + @Suppress("SwallowedException") + private fun getLayoutInflater(): LayoutInflater = try { + findFragment().layoutInflater + } catch (e: IllegalStateException) { + LayoutInflater.from(context) + } + private fun setInteractionBlocked(isInteractionBlocked: Boolean) { this.isInteractionBlocked = isInteractionBlocked diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/ViewProvider.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/ViewProvider.kt index 1c4a592f52..a503f5a38f 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/ViewProvider.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/ViewProvider.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.ui.core.internal.ui import android.content.Context +import android.view.LayoutInflater import androidx.annotation.RestrictTo @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -17,4 +18,9 @@ interface ViewProvider { viewType: ComponentViewType, context: Context, ): ComponentView + + fun getView( + viewType: ComponentViewType, + layoutInflater: LayoutInflater + ): ComponentView = getView(viewType, layoutInflater.context) } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PaymentInProgressView.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PaymentInProgressView.kt index 14d760682d..155bbf6735 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PaymentInProgressView.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/PaymentInProgressView.kt @@ -23,8 +23,10 @@ import com.adyen.checkout.ui.core.internal.ui.ComponentView import com.adyen.checkout.ui.core.internal.util.setLocalizedTextFromStyle import kotlinx.coroutines.CoroutineScope +class PaymentInProgressView +@JvmOverloads @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class PaymentInProgressView @JvmOverloads constructor( +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 @@ -32,7 +34,7 @@ class PaymentInProgressView @JvmOverloads constructor( ConstraintLayout( context, attrs, - defStyleAttr + defStyleAttr, ), ComponentView { @@ -52,15 +54,15 @@ class PaymentInProgressView @JvmOverloads constructor( with(binding) { textViewPaymentInProgressTitle.setLocalizedTextFromStyle( R.style.AdyenCheckout_PaymentInProgressView_TitleTextView, - localizedContext + localizedContext, ) textViewPaymentInProgressDescription.setLocalizedTextFromStyle( R.style.AdyenCheckout_PaymentInProgressView_DescriptionTextView, - localizedContext + localizedContext, ) buttonPaymentInProgressCancel.setLocalizedTextFromStyle( R.style.AdyenCheckout_PaymentInProgressView_CancelButton, - localizedContext + localizedContext, ) } } From d978fa8bcb0380a254c539bcb4a0c507cbaff326 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 12:02:06 +0200 Subject: [PATCH 145/272] Add TwintActionConfigurationTest COAND-806 --- .../twint/TwintActionConfigurationTest.kt | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 twint/src/test/java/com/adyen/checkout/twint/TwintActionConfigurationTest.kt diff --git a/twint/src/test/java/com/adyen/checkout/twint/TwintActionConfigurationTest.kt b/twint/src/test/java/com/adyen/checkout/twint/TwintActionConfigurationTest.kt new file mode 100644 index 0000000000..548cdeb62f --- /dev/null +++ b/twint/src/test/java/com/adyen/checkout/twint/TwintActionConfigurationTest.kt @@ -0,0 +1,82 @@ +package com.adyen.checkout.twint + +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.AnalyticsConfiguration +import com.adyen.checkout.components.core.AnalyticsLevel +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.core.Environment +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.util.Locale + +internal class TwintActionConfigurationTest { + + @Test + fun `when creating the configuration through CheckoutConfiguration, then it should be the same as when the builder is used`() { + val checkoutConfiguration = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + amount = Amount("EUR", 123L), + analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), + ) { + twintAction() + } + + val actual = checkoutConfiguration.getTwintActionConfiguration() + + val expected = TwintActionConfiguration.Builder( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + ) + .setAmount(Amount("EUR", 123L)) + .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .build() + + assertEquals(expected.shopperLocale, actual?.shopperLocale) + assertEquals(expected.environment, actual?.environment) + assertEquals(expected.clientKey, actual?.clientKey) + assertEquals(expected.amount, actual?.amount) + assertEquals(expected.analyticsConfiguration, actual?.analyticsConfiguration) + } + + @Test + fun `when the configuration is mapped to CheckoutConfiguration, then CheckoutConfiguration is created correctly`() { + val config = TwintActionConfiguration.Builder( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + ) + .setAmount(Amount("EUR", 123L)) + .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .build() + + val actual = config.toCheckoutConfiguration() + + val expected = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + amount = Amount("EUR", 123L), + analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), + ) + + assertEquals(expected.shopperLocale, actual.shopperLocale) + assertEquals(expected.environment, actual.environment) + assertEquals(expected.clientKey, actual.clientKey) + assertEquals(expected.amount, actual.amount) + assertEquals(expected.analyticsConfiguration, actual.analyticsConfiguration) + + val actualAwaitConfig = actual.getTwintActionConfiguration() + assertEquals(config.shopperLocale, actualAwaitConfig?.shopperLocale) + assertEquals(config.environment, actualAwaitConfig?.environment) + assertEquals(config.clientKey, actualAwaitConfig?.clientKey) + assertEquals(config.amount, actualAwaitConfig?.amount) + assertEquals(config.analyticsConfiguration, actualAwaitConfig?.analyticsConfiguration) + } + + companion object { + private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + } +} From d7cf94a3d4245dc27e6adaa2c8e9cde7a37772d1 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 12:07:38 +0200 Subject: [PATCH 146/272] Add release notes COAND-806 --- RELEASE_NOTES.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7b67d62005..09274c325a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,20 @@ ## New - For external redirects launched in a Custom Tab, you can now [customize the toolbar and navigation bar colors](docs/UI_CUSTOMIZATION.md#styling-custom-tabs) of the Custom Tab. +- Payment method **twint** now supports a native flow, eliminating the need to redirect through the browser. +You can change this behaviour by using the following configuration: +```kotlin +CheckoutConfiguration( + environment = environment, + clientKey = clientKey, + .. +) { + // Optionally pass the payment method type to only configure it for the specific payment method. + instantPayment(PaymentMethodTypes.TWINT) { + setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB) + } +} +``` ## Fixed - Fixed various memory leaks. From 89498c8de3942a22063c5dda09ed2037814c7085 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 16:50:12 +0200 Subject: [PATCH 147/272] Restore state when process dies COAND-806 --- .../provider/TwintActionComponentProvider.kt | 1 + .../twint/internal/ui/DefaultTwintDelegate.kt | 83 +++++++++++++++---- .../twint/internal/ui/TwintFragment.kt | 41 +++++++-- .../checkout/twint/internal/ui/TwintView.kt | 6 +- twint/src/main/res/layout/fragment_twint.xml | 1 + .../internal/ui/DefaultTwintDelegateTest.kt | 73 +++++++++++++--- 6 files changed, 168 insertions(+), 37 deletions(-) diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt index 7be01fe36c..53c33c979e 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt @@ -89,6 +89,7 @@ constructor( return DefaultTwintDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, paymentDataRepository = PaymentDataRepository(savedStateHandle), statusRepository = statusRepository, diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 0fc93436f5..11887363ee 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.twint.internal.ui import android.app.Activity import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import ch.twint.payment.sdk.TwintPayResult import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.action.Action @@ -19,6 +20,8 @@ import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -45,10 +48,11 @@ import java.util.concurrent.TimeUnit @Suppress("TooManyFunctions") internal class DefaultTwintDelegate( private val observerRepository: ActionObserverRepository, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val paymentDataRepository: PaymentDataRepository, private val statusRepository: StatusRepository, -) : TwintDelegate { +) : TwintDelegate, SavedStateHandleContainer { private val detailsChannel: Channel = bufferedChannel() override val detailsFlow: Flow = detailsChannel.receiveAsFlow() @@ -69,8 +73,20 @@ internal class DefaultTwintDelegate( private var statusPollingJob: Job? = null + private var action: SdkAction? by SavedStateHandleProperty(ACTION_KEY) + private var isPolling: Boolean? by SavedStateHandleProperty(IS_POLLING_KEY) + override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + val action: SdkAction? = action + if (action != null) { + initState(action) + } } override fun observe( @@ -92,28 +108,41 @@ internal class DefaultTwintDelegate( observerRepository.removeObservers() } - @SuppressWarnings("ReturnCount") override fun handleAction(action: Action, activity: Activity) { - val sdkAction = action as? SdkAction<*> - if (sdkAction == null) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + if (action !is SdkAction<*>) { + emitError(ComponentException("Unsupported action")) + return + } + + val sdkData = action.sdkData + if (action.sdkData == null || sdkData !is TwintSdkData) { + emitError(ComponentException("SDK Data is null or of wrong type")) return } + @Suppress("UNCHECKED_CAST") + this.action = action as SdkAction + + + initState(action) + launchAction(sdkData) + } + + private fun initState(action: SdkAction) { val paymentData = action.paymentData paymentDataRepository.paymentData = paymentData if (paymentData == null) { adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) + emitError(ComponentException("Payment data is null")) return } - val sdkData = sdkAction.sdkData - if (sdkData == null || sdkData !is TwintSdkData) { - exceptionChannel.trySend(ComponentException("SDK Data is null or of wrong type")) - return + if (isPolling == true) { + startStatusPolling() } + } + private fun launchAction(sdkData: TwintSdkData) { payEventChannel.trySend(sdkData.token) } @@ -134,11 +163,12 @@ internal class DefaultTwintDelegate( } private fun startStatusPolling() { + isPolling = true statusPollingJob?.cancel() val paymentData = paymentDataRepository.paymentData if (paymentData == null) { - exceptionChannel.trySend(ComponentException("PaymentData should not be null.")) + emitError(ComponentException("PaymentData should not be null.")) return } @@ -155,7 +185,7 @@ internal class DefaultTwintDelegate( }, onFailure = { adyenLog(AdyenLogLevel.ERROR, it) { "Error while polling status" } - exceptionChannel.trySend(ComponentException("Error while polling status.", it)) + emitError(ComponentException("Error while polling status.", it)) }, ) } @@ -165,11 +195,9 @@ internal class DefaultTwintDelegate( // Not authorized status should still call /details so that merchant can get more info if (StatusResponseUtils.isFinalResult(statusResponse)) { if (!payload.isNullOrEmpty()) { - detailsChannel.trySend(createActionComponentData(payload)) + emitDetails(payload) } else { - exceptionChannel.trySend( - ComponentException("Payload is missing from StatusResponse."), - ) + emitError(ComponentException("Payload is missing from StatusResponse.")) } } } @@ -183,7 +211,7 @@ internal class DefaultTwintDelegate( } override fun onError(e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } override fun refreshStatus() { @@ -192,6 +220,21 @@ internal class DefaultTwintDelegate( statusRepository.refreshStatus(paymentData) } + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(payload: String) { + detailsChannel.trySend(createActionComponentData(payload)) + clearState() + } + + private fun clearState() { + action = null + isPolling = null + } + override fun onCleared() { removeObserver() } @@ -199,6 +242,12 @@ internal class DefaultTwintDelegate( companion object { private val DEFAULT_MAX_POLLING_DURATION = TimeUnit.MINUTES.toMillis(15) + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" + + @VisibleForTesting + internal const val IS_POLLING_KEY = "IS_POLLING_KEY" + @VisibleForTesting internal const val PAYLOAD_DETAILS_KEY = "payload" } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt index 0bb99ecf74..87f4c197d4 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt @@ -8,6 +8,7 @@ package com.adyen.checkout.twint.internal.ui +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -15,7 +16,11 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import ch.twint.payment.sdk.Twint +import ch.twint.payment.sdk.TwintPayResult +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.twint.databinding.FragmentTwintBinding +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -24,26 +29,48 @@ internal class TwintFragment : Fragment() { private var _binding: FragmentTwintBinding? = null private val binding: FragmentTwintBinding get() = requireNotNull(_binding) - private var _twintDelegate: TwintDelegate? = null - private val twintDelegate: TwintDelegate get() = requireNotNull(_twintDelegate) + private var twintDelegate: TwintDelegate? = null - private var twint: Twint? = Twint(this) { twintDelegate.handleTwintResult(it) } + private var twint: Twint? = Twint(this, ::onTwintResult) + + private var twintResultQueue: TwintPayResult? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentTwintBinding.inflate(inflater, container, false) return binding.root } - fun initialize(delegate: TwintDelegate) { - _twintDelegate = delegate - twintDelegate.payEventFlow + fun initialize(delegate: TwintDelegate, coroutineScope: CoroutineScope, localizedContext: Context) { + adyenLog(AdyenLogLevel.ERROR) { "initialize" } + + binding.paymentInProgressView.initView(delegate, coroutineScope, localizedContext) + + twintDelegate = delegate + delegate.payEventFlow .onEach { twint?.payWithCode(it) } .launchIn(viewLifecycleOwner.lifecycleScope) + + twintResultQueue?.let { + adyenLog(AdyenLogLevel.ERROR) { "initialize: executing queue" } + onTwintResult(it) + } + } + + private fun onTwintResult(result: TwintPayResult) { + adyenLog(AdyenLogLevel.ERROR) { "onTwintResult" } + twintDelegate + ?.handleTwintResult(result) + ?.also { + adyenLog(AdyenLogLevel.ERROR) { "onTwintResult: clearing queue" } + twintResultQueue = null + } ?: run { + adyenLog(AdyenLogLevel.ERROR) { "onTwintResult: setting queue" } + twintResultQueue = result + } } override fun onDestroyView() { twint = null - _twintDelegate = null _binding = null super.onDestroyView() } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt index 76b8e6a778..9020f47937 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintView.kt @@ -38,11 +38,11 @@ internal class TwintView internal constructor( override fun initView(delegate: ComponentDelegate, coroutineScope: CoroutineScope, localizedContext: Context) { require(delegate is TwintDelegate) { "Unsupported delegate type" } this.delegate = delegate - initializeFragment(delegate) + initializeFragment(delegate, coroutineScope, localizedContext) } - private fun initializeFragment(delegate: TwintDelegate) { - binding.fragmentContainer.getFragment()?.initialize(delegate) + private fun initializeFragment(delegate: TwintDelegate, coroutineScope: CoroutineScope, localizedContext: Context) { + binding.fragmentContainer.getFragment()?.initialize(delegate, coroutineScope, localizedContext) } override fun highlightValidationErrors() = Unit diff --git a/twint/src/main/res/layout/fragment_twint.xml b/twint/src/main/res/layout/fragment_twint.xml index 1e20bfdcc9..af5eaf7da2 100644 --- a/twint/src/main/res/layout/fragment_twint.xml +++ b/twint/src/main/res/layout/fragment_twint.xml @@ -11,6 +11,7 @@ android:layout_height="wrap_content"> diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt index e47ebdcd65..0b837b212b 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -15,6 +15,7 @@ import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.AwaitAction +import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.action.WeChatPaySdkData @@ -35,6 +36,7 @@ import kotlinx.coroutines.test.runTest import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested @@ -56,16 +58,8 @@ internal class DefaultTwintDelegateTest { @BeforeEach fun beforeEach() { - val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) statusRepository = TestStatusRepository() - - delegate = DefaultTwintDelegate( - observerRepository = ActionObserverRepository(), - componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - paymentDataRepository = PaymentDataRepository(SavedStateHandle()), - statusRepository = statusRepository, - ) + delegate = createDelegate() } @Test @@ -185,6 +179,65 @@ internal class DefaultTwintDelegateTest { } } + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle().apply { + set(DefaultTwintDelegate.ACTION_KEY, SdkAction(paymentData = "test", sdkData = TwintSdkData("token"))) + set(DefaultTwintDelegate.IS_POLLING_KEY, true) + } + delegate = createDelegate(savedStateHandle) + val detailsFlow = delegate.detailsFlow.test(testScheduler) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertTrue(detailsFlow.values.isNotEmpty()) + } + + @Test + fun `when details are emitted, then state is cleared`() = runTest { + statusRepository.pollingResults = listOf( + Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), + ) + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) + + assertNull(savedStateHandle[DefaultTwintDelegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set(DefaultTwintDelegate.ACTION_KEY, SdkAction(paymentData = "test", sdkData = TwintSdkData("token"))) + } + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertNull(savedStateHandle[DefaultTwintDelegate.ACTION_KEY]) + } + + private fun createDelegate( + savedStateHandle: SavedStateHandle = SavedStateHandle() + ): DefaultTwintDelegate { + val configuration = CheckoutConfiguration(Environment.TEST, TEST_CLIENT_KEY) + + return DefaultTwintDelegate( + observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + paymentDataRepository = PaymentDataRepository(SavedStateHandle()), + statusRepository = statusRepository, + ) + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private const val TEST_PAYLOAD = "TEST_PAYLOAD" @@ -197,7 +250,7 @@ internal class DefaultTwintDelegateTest { "SDK Data is null or of wrong type", ), arguments(SdkAction(paymentData = "something"), "SDK Data is null or of wrong type"), - arguments(SdkAction(paymentData = null), "Payment data is null"), + arguments(SdkAction(paymentData = null, sdkData = TwintSdkData("")), "Payment data is null"), arguments( SdkAction(paymentData = "something", sdkData = null), "SDK Data is null or of wrong type", From 02f6563feab4489460a915911b7dd1a6b2fb9f9d Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 17:19:44 +0200 Subject: [PATCH 148/272] Add more tests COAND-806 --- ...ndlingPaymentMethodConfigurationBuilder.kt | 2 +- .../core/action/TwintSdkDataTest.kt | 47 +++++++++++++++++++ .../InstantPaymentConfigurationTest.kt | 14 +++++- .../twint/internal/ui/DefaultTwintDelegate.kt | 1 - .../twint/internal/ui/TwintFragment.kt | 10 ++-- 5 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/action/TwintSdkDataTest.kt diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt index 75d3fa8837..f3256e4464 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder.kt @@ -128,7 +128,7 @@ constructor( /** * Add configuration for Twint action. */ - override fun addTwintActionConfiguration(configuration: TwintActionConfiguration): BuilderT { + final override fun addTwintActionConfiguration(configuration: TwintActionConfiguration): BuilderT { genericActionConfigurationBuilder.addTwintActionConfiguration(configuration) return this as BuilderT } diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/action/TwintSdkDataTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/action/TwintSdkDataTest.kt new file mode 100644 index 0000000000..a39f00fbe2 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/action/TwintSdkDataTest.kt @@ -0,0 +1,47 @@ +package com.adyen.checkout.components.core.action + +import com.adyen.checkout.core.exception.ModelSerializationException +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +internal class TwintSdkDataTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val request = TwintSdkData( + token = "testToken", + ) + + val actual = TwintSdkData.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("token", "testToken") + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val response = JSONObject() + .put("token", "testToken") + + val actual = TwintSdkData.SERIALIZER.deserialize(response) + + val expected = TwintSdkData( + token = "testToken", + ) + + assertEquals(expected, actual) + } + + @Test + fun `when deserializing and a field is missing, then an error is thrown`() { + val response = JSONObject() + + assertThrows { + TwintSdkData.SERIALIZER.deserialize(response) + } + } +} diff --git a/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentConfigurationTest.kt b/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentConfigurationTest.kt index 787b1ad1ee..4f612825d7 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentConfigurationTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/InstantPaymentConfigurationTest.kt @@ -20,7 +20,9 @@ internal class InstantPaymentConfigurationTest { amount = Amount("EUR", 123L), analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), ) { - instantPayment("paypal") + instantPayment("paypal") { + setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB) + } } val actual = checkoutConfiguration.getInstantPaymentConfiguration("paypal") @@ -32,6 +34,7 @@ internal class InstantPaymentConfigurationTest { ) .setAmount(Amount("EUR", 123L)) .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB) .build() assertEquals(expected.shopperLocale, actual?.shopperLocale) @@ -39,6 +42,7 @@ internal class InstantPaymentConfigurationTest { assertEquals(expected.clientKey, actual?.clientKey) assertEquals(expected.amount, actual?.amount) assertEquals(expected.analyticsConfiguration, actual?.analyticsConfiguration) + assertEquals(expected.actionHandlingMethod, actual?.actionHandlingMethod) } @Test @@ -50,6 +54,7 @@ internal class InstantPaymentConfigurationTest { ) .setAmount(Amount("EUR", 123L)) .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB) .build() val actual = config.toCheckoutConfiguration() @@ -60,7 +65,11 @@ internal class InstantPaymentConfigurationTest { clientKey = TEST_CLIENT_KEY, amount = Amount("EUR", 123L), analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), - ) + ) { + instantPayment { + setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB) + } + } assertEquals(expected.shopperLocale, actual.shopperLocale) assertEquals(expected.environment, actual.environment) @@ -74,6 +83,7 @@ internal class InstantPaymentConfigurationTest { assertEquals(config.clientKey, actualInstantConfig?.clientKey) assertEquals(config.amount, actualInstantConfig?.amount) assertEquals(config.analyticsConfiguration, actualInstantConfig?.analyticsConfiguration) + assertEquals(config.actionHandlingMethod, actualInstantConfig?.actionHandlingMethod) } companion object { diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index 11887363ee..e1207627bc 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -123,7 +123,6 @@ internal class DefaultTwintDelegate( @Suppress("UNCHECKED_CAST") this.action = action as SdkAction - initState(action) launchAction(sdkData) } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt index 87f4c197d4..e992ae06df 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt @@ -41,7 +41,7 @@ internal class TwintFragment : Fragment() { } fun initialize(delegate: TwintDelegate, coroutineScope: CoroutineScope, localizedContext: Context) { - adyenLog(AdyenLogLevel.ERROR) { "initialize" } + adyenLog(AdyenLogLevel.DEBUG) { "initialize" } binding.paymentInProgressView.initView(delegate, coroutineScope, localizedContext) @@ -51,20 +51,20 @@ internal class TwintFragment : Fragment() { .launchIn(viewLifecycleOwner.lifecycleScope) twintResultQueue?.let { - adyenLog(AdyenLogLevel.ERROR) { "initialize: executing queue" } + adyenLog(AdyenLogLevel.DEBUG) { "initialize: executing queue" } onTwintResult(it) } } private fun onTwintResult(result: TwintPayResult) { - adyenLog(AdyenLogLevel.ERROR) { "onTwintResult" } + adyenLog(AdyenLogLevel.DEBUG) { "onTwintResult" } twintDelegate ?.handleTwintResult(result) ?.also { - adyenLog(AdyenLogLevel.ERROR) { "onTwintResult: clearing queue" } + adyenLog(AdyenLogLevel.DEBUG) { "onTwintResult: clearing queue" } twintResultQueue = null } ?: run { - adyenLog(AdyenLogLevel.ERROR) { "onTwintResult: setting queue" } + adyenLog(AdyenLogLevel.DEBUG) { "onTwintResult: setting queue" } twintResultQueue = result } } From a1cdfec5a5a14905634e6b2613aab0929f475672 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 17:08:04 +0200 Subject: [PATCH 149/272] Add comment to explain LayoutInflater problem COAND-806 --- .../components/core/internal/util/StatusResponseUtils.kt | 4 ---- .../checkout/twint/internal/ui/DefaultTwintDelegate.kt | 5 +---- .../com/adyen/checkout/twint/internal/ui/TwintFragment.kt | 8 ++++---- .../java/com/adyen/checkout/ui/core/AdyenComponentView.kt | 5 +++++ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt index 2f36615704..1636cd6400 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/util/StatusResponseUtils.kt @@ -21,8 +21,4 @@ object StatusResponseUtils { fun isFinalResult(statusResponse: StatusResponse): Boolean { return RESULT_PENDING != statusResponse.resultCode } - - fun isPendingResult(statusResponse: StatusResponse): Boolean { - return RESULT_PENDING == statusResponse.resultCode - } } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index e1207627bc..e22065623a 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -83,10 +83,7 @@ internal class DefaultTwintDelegate( private fun restoreState() { adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } - val action: SdkAction? = action - if (action != null) { - initState(action) - } + action?.let { initState(it) } } override fun observe( diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt index e992ae06df..4192756b53 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/TwintFragment.kt @@ -33,7 +33,7 @@ internal class TwintFragment : Fragment() { private var twint: Twint? = Twint(this, ::onTwintResult) - private var twintResultQueue: TwintPayResult? = null + private var queuedTwintResult: TwintPayResult? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentTwintBinding.inflate(inflater, container, false) @@ -50,7 +50,7 @@ internal class TwintFragment : Fragment() { .onEach { twint?.payWithCode(it) } .launchIn(viewLifecycleOwner.lifecycleScope) - twintResultQueue?.let { + queuedTwintResult?.let { adyenLog(AdyenLogLevel.DEBUG) { "initialize: executing queue" } onTwintResult(it) } @@ -62,10 +62,10 @@ internal class TwintFragment : Fragment() { ?.handleTwintResult(result) ?.also { adyenLog(AdyenLogLevel.DEBUG) { "onTwintResult: clearing queue" } - twintResultQueue = null + queuedTwintResult = null } ?: run { adyenLog(AdyenLogLevel.DEBUG) { "onTwintResult: setting queue" } - twintResultQueue = result + queuedTwintResult = result } } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt index 600bbc4ddb..8988f179d0 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/AdyenComponentView.kt @@ -157,6 +157,11 @@ class AdyenComponentView @JvmOverloads constructor( } } + /** + * Returns the [LayoutInflater] of the parent activity or fragment. Using `LayoutInflater.from(context)` when the + * view's parent is a fragment will return the [LayoutInflater] of the fragment's activity. This causes issues with + * nested fragment's. + */ @Suppress("SwallowedException") private fun getLayoutInflater(): LayoutInflater = try { findFragment().layoutInflater From f5430eaa37455a58cf54db1e86c956029adfaa9a Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 23 May 2024 14:42:05 +0200 Subject: [PATCH 150/272] Update ActionHandlingMethod explanation COAND-806 Co-authored-by: Ararat Mnatsakanyan --- .../java/com/adyen/checkout/instant/ActionHandlingMethod.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt b/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt index 193dd52c00..24684e28d5 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/ActionHandlingMethod.kt @@ -20,7 +20,7 @@ enum class ActionHandlingMethod { /** * The action will be handled with a web flow. **If** there is no way to handle the action with a web flow, then - * other methods might be used. + * native method will be used. */ PREFER_WEB, } From 0b337d2c0cccb91e621419b9b034c9ce247dfdd3 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 13:47:00 +0200 Subject: [PATCH 151/272] Add secondary text color COAND-912 --- docs/UI_CUSTOMIZATION.md | 2 +- example-app/src/main/res/color/text_color_secondary.xml | 4 ++++ example-app/src/main/res/values/styles.xml | 2 +- ui-core/src/main/res/color/text_color_secondary.xml | 4 ++++ ui-core/src/main/res/values/styles.xml | 2 +- 5 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 example-app/src/main/res/color/text_color_secondary.xml create mode 100644 ui-core/src/main/res/color/text_color_secondary.xml diff --git a/docs/UI_CUSTOMIZATION.md b/docs/UI_CUSTOMIZATION.md index 3b35896062..18dc55f6a0 100644 --- a/docs/UI_CUSTOMIZATION.md +++ b/docs/UI_CUSTOMIZATION.md @@ -95,7 +95,7 @@ Out of the box the SDK doesn’t support dark mode, but you can easily add this @color/text_color_primary @color/text_color_primary - @color/text_color_primary + @color/text_color_secondary @color/text_color_primary @color/text_color_link @style/AdyenCheckout.BottomSheetDialogTheme diff --git a/example-app/src/main/res/color/text_color_secondary.xml b/example-app/src/main/res/color/text_color_secondary.xml new file mode 100644 index 0000000000..495d5097da --- /dev/null +++ b/example-app/src/main/res/color/text_color_secondary.xml @@ -0,0 +1,4 @@ + + + + diff --git a/example-app/src/main/res/values/styles.xml b/example-app/src/main/res/values/styles.xml index 82f8242747..ef0d1e9681 100644 --- a/example-app/src/main/res/values/styles.xml +++ b/example-app/src/main/res/values/styles.xml @@ -18,7 +18,7 @@ @color/text_color_primary @color/text_color_primary - @color/text_color_primary + @color/text_color_secondary @color/text_color_primary @color/textColorLink @color/color_background diff --git a/ui-core/src/main/res/color/text_color_secondary.xml b/ui-core/src/main/res/color/text_color_secondary.xml new file mode 100644 index 0000000000..4da8c00e99 --- /dev/null +++ b/ui-core/src/main/res/color/text_color_secondary.xml @@ -0,0 +1,4 @@ + + + + diff --git a/ui-core/src/main/res/values/styles.xml b/ui-core/src/main/res/values/styles.xml index 15a44d880b..f1afd8eb11 100644 --- a/ui-core/src/main/res/values/styles.xml +++ b/ui-core/src/main/res/values/styles.xml @@ -23,7 +23,7 @@ @color/text_color_primary @color/text_color_primary - @color/text_color_primary + @color/text_color_secondary @color/text_color_primary @color/textColorLink @style/AdyenCheckout.BottomSheetDialogTheme From de9b80ba1f3a73390a40202b30d97c85ef0ad97d Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 20:05:44 +0200 Subject: [PATCH 152/272] Display country picker entries correctly COAND-912 --- .../ui/core/internal/ui/CountryViewHolder.kt | 8 ++---- ui-core/src/main/res/layout/country_view.xml | 25 ++++++++++--------- .../main/res/template/values/strings.xml.tt | 3 --- ui-core/src/main/res/values/strings.xml | 5 +--- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt index d800c9b8aa..250b35aac7 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt @@ -17,12 +17,8 @@ internal class CountryViewHolder(private val binding: CountryViewBinding) : Recy fun bindItem(country: CountryModel) { with(binding) { - textViewCountryCode.text = country.isoCode - textViewCountry.text = root.context.getString( - R.string.checkout_country_name_format, - country.countryName, - country.callingCode, - ) + textViewCountryCode.text = country.callingCode + textViewCountry.text = country.countryName } } } diff --git a/ui-core/src/main/res/layout/country_view.xml b/ui-core/src/main/res/layout/country_view.xml index 073dde3d45..d3582eec62 100644 --- a/ui-core/src/main/res/layout/country_view.xml +++ b/ui-core/src/main/res/layout/country_view.xml @@ -10,25 +10,26 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:padding="@dimen/standard_three_quarters_margin"> + android:orientation="vertical" + android:paddingHorizontal="@dimen/standard_margin" + android:paddingVertical="@dimen/standard_three_quarters_margin"> - + tools:text="+1234" /> - + android:textColor="?android:attr/textColorSecondary" + android:lines="1" + android:textSize="14sp" + tools:text="The Netherlands" /> diff --git a/ui-core/src/main/res/template/values/strings.xml.tt b/ui-core/src/main/res/template/values/strings.xml.tt index c384464cc7..518685147b 100644 --- a/ui-core/src/main/res/template/values/strings.xml.tt +++ b/ui-core/src/main/res/template/values/strings.xml.tt @@ -55,7 +55,4 @@ %%address.enterManually%% %%address.lookup.submit%% %%address.lookup.item.validationFailureMessage.empty%% - - - %1$s (%2$s) diff --git a/ui-core/src/main/res/values/strings.xml b/ui-core/src/main/res/values/strings.xml index d8ca6d248c..f4d78d9e87 100644 --- a/ui-core/src/main/res/values/strings.xml +++ b/ui-core/src/main/res/values/strings.xml @@ -55,7 +55,4 @@ Enter address manually Use this address Address required - - - %1$s (%2$s) - \ No newline at end of file + From adb0a5297eeae90561eb5657a17cee38a7ffd23b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 17 May 2024 12:09:09 +0200 Subject: [PATCH 153/272] Preselect Portuguese country code for MBWay COAND-912 --- .../checkout/mbway/internal/ui/DefaultMBWayDelegate.kt | 2 +- .../adyen/checkout/mbway/internal/ui/view/MbWayView.kt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 1070f294a6..8375467cb5 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -177,7 +177,7 @@ internal class DefaultMBWayDelegate( } companion object { - private const val ISO_CODE_PORTUGAL = "PT" + internal const val ISO_CODE_PORTUGAL = "PT" private const val ISO_CODE_SPAIN = "ES" private val SUPPORTED_COUNTRIES = listOf(ISO_CODE_PORTUGAL, ISO_CODE_SPAIN) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index c5d15edb29..350da22863 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.internal.util.CountryUtils import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.mbway.databinding.MbwayViewBinding +import com.adyen.checkout.mbway.internal.ui.DefaultMBWayDelegate import com.adyen.checkout.mbway.internal.ui.MBWayDelegate import com.adyen.checkout.ui.core.internal.ui.ComponentView import com.adyen.checkout.ui.core.internal.ui.CountryAdapter @@ -92,10 +93,11 @@ internal class MbWayView @JvmOverloads constructor( onCountrySelected(country) } } - val firstCountry = countries.firstOrNull() - if (firstCountry != null) { - binding.autoCompleteTextViewCountry.setText(firstCountry.toShortString()) - onCountrySelected(firstCountry) + val initialCountry = + countries.firstOrNull { it.isoCode == DefaultMBWayDelegate.ISO_CODE_PORTUGAL } ?: countries.firstOrNull() + initialCountry?.let { + binding.autoCompleteTextViewCountry.setText(it.toShortString()) + onCountrySelected(it) } } From 6c782edac109bb481b2c17f98ea960fe105d01a9 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 13:52:39 +0200 Subject: [PATCH 154/272] Align dropdown arrow correctly in MBWay view COAND-912 --- mbway/src/main/res/layout/mbway_view.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mbway/src/main/res/layout/mbway_view.xml b/mbway/src/main/res/layout/mbway_view.xml index 28ffdef409..6649df4d85 100644 --- a/mbway/src/main/res/layout/mbway_view.xml +++ b/mbway/src/main/res/layout/mbway_view.xml @@ -1,5 +1,4 @@ - - - @@ -19,8 +17,8 @@ android:id="@+id/layout_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - tools:ignore="DisableBaselineAlignment"> + android:baselineAligned="false" + android:orientation="horizontal"> + android:layout_height="match_parent" + android:dropDownAnchor="@id/layout_container" /> + + + From 9372060e2a111c7bffa3a34a275a86ad9d72e19e Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 14:00:28 +0200 Subject: [PATCH 155/272] Align dropdown arrow correctly in EContext view COAND-912 --- econtext/src/main/res/layout/econtext_view.xml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/econtext/src/main/res/layout/econtext_view.xml b/econtext/src/main/res/layout/econtext_view.xml index b620a336fd..8610342b9f 100644 --- a/econtext/src/main/res/layout/econtext_view.xml +++ b/econtext/src/main/res/layout/econtext_view.xml @@ -9,7 +9,7 @@ @@ -22,6 +22,7 @@ + + + + android:baselineAligned="false" + android:orientation="horizontal"> + android:layout_height="match_parent" + android:dropDownAnchor="@id/layout_container" /> + + + From 7fa5f62099b10b17c70a3105aa47d59b07c8a1ee Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 22 May 2024 14:59:42 +0200 Subject: [PATCH 156/272] Sort country list by localized country name COAND-912 --- .../internal/ui/DefaultEContextDelegate.kt | 5 ++ .../econtext/internal/ui/EContextDelegate.kt | 3 + .../econtext/internal/ui/view/EContextView.kt | 9 +-- .../mbway/internal/ui/DefaultMBWayDelegate.kt | 7 ++- .../mbway/internal/ui/MBWayDelegate.kt | 4 +- .../mbway/internal/ui/view/MbWayView.kt | 12 +--- .../ui/core/internal/ui/CountryViewHolder.kt | 1 - .../ui/core/internal/util/CountryUtils.kt | 34 ++++++++++++ .../ui/core/internal/util/CountryUtilsTest.kt | 55 +++++++++++++++++++ 9 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CountryUtils.kt create mode 100644 ui-core/src/test/java/com/adyen/checkout/ui/core/internal/util/CountryUtilsTest.kt diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index d820f87b40..fee8241776 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -33,6 +33,8 @@ import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIEvent import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIState import com.adyen.checkout.ui.core.internal.ui.SubmitHandler +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel +import com.adyen.checkout.ui.core.internal.util.CountryUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -202,6 +204,9 @@ internal class DefaultEContextDelegate< submitHandler.onSubmit(state) } + override fun getSupportedCountries(): List = + CountryUtils.getLocalizedCountries(componentParams.shopperLocale) + override fun isConfirmationRequired(): Boolean { return _viewFlow.value is ButtonComponentViewType } diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt index 635f3587f2..084b0d00bf 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt @@ -17,6 +17,7 @@ import com.adyen.checkout.econtext.internal.ui.model.EContextOutputData import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate import com.adyen.checkout.ui.core.internal.ui.UIStateDelegate import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel import kotlinx.coroutines.flow.Flow @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -38,4 +39,6 @@ interface EContextDelegate< fun updateInputData(update: EContextInputData.() -> Unit) fun setInteractionBlocked(isInteractionBlocked: Boolean) + + fun getSupportedCountries(): List } diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt index 02c4a08fcd..74e7aa4329 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt @@ -17,7 +17,6 @@ import android.widget.AdapterView import android.widget.LinearLayout import com.adyen.checkout.components.core.internal.ui.ComponentDelegate import com.adyen.checkout.components.core.internal.ui.model.Validation -import com.adyen.checkout.components.core.internal.util.CountryUtils import com.adyen.checkout.econtext.R import com.adyen.checkout.econtext.databinding.EcontextViewBinding import com.adyen.checkout.econtext.internal.ui.EContextDelegate @@ -159,13 +158,7 @@ internal class EContextView @JvmOverloads constructor( private fun initCountryCodeInput() { val countryAutoCompleteTextView = binding.autoCompleteTextViewCountry - val countries = CountryUtils.getCountries().map { - CountryModel( - isoCode = it.isoCode, - countryName = CountryUtils.getCountryName(it.isoCode, delegate.componentParams.shopperLocale), - callingCode = it.callingCode, - ) - } + val countries = delegate.getSupportedCountries() countryAdapter = CountryAdapter(context, localizedContext).apply { setItems(countries) } diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 8375467cb5..e8edc6aa8c 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -18,8 +18,6 @@ import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams -import com.adyen.checkout.components.core.internal.util.CountryInfo -import com.adyen.checkout.components.core.internal.util.CountryUtils import com.adyen.checkout.components.core.paymentmethod.MBWayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @@ -31,6 +29,8 @@ import com.adyen.checkout.ui.core.internal.ui.ComponentViewType import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIEvent import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIState import com.adyen.checkout.ui.core.internal.ui.SubmitHandler +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel +import com.adyen.checkout.ui.core.internal.util.CountryUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -157,7 +157,8 @@ internal class DefaultMBWayDelegate( _componentStateFlow.tryEmit(componentState) } - override fun getSupportedCountries(): List = CountryUtils.getCountries(SUPPORTED_COUNTRIES) + override fun getSupportedCountries(): List = + CountryUtils.getLocalizedCountries(componentParams.shopperLocale, SUPPORTED_COUNTRIES) override fun onSubmit() { val state = _componentStateFlow.value diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt index 11ad631185..effe2ff4cf 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt @@ -9,13 +9,13 @@ package com.adyen.checkout.mbway.internal.ui import com.adyen.checkout.components.core.internal.ui.PaymentComponentDelegate -import com.adyen.checkout.components.core.internal.util.CountryInfo import com.adyen.checkout.mbway.MBWayComponentState import com.adyen.checkout.mbway.internal.ui.model.MBWayInputData import com.adyen.checkout.mbway.internal.ui.model.MBWayOutputData import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate import com.adyen.checkout.ui.core.internal.ui.UIStateDelegate import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel import kotlinx.coroutines.flow.Flow internal interface MBWayDelegate : @@ -30,7 +30,7 @@ internal interface MBWayDelegate : val componentStateFlow: Flow - fun getSupportedCountries(): List + fun getSupportedCountries(): List fun updateInputData(update: MBWayInputData.() -> Unit) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index 350da22863..3e5d035473 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -16,8 +16,6 @@ import android.view.View.OnFocusChangeListener import android.widget.LinearLayout import com.adyen.checkout.components.core.internal.ui.ComponentDelegate import com.adyen.checkout.components.core.internal.ui.model.Validation -import com.adyen.checkout.components.core.internal.util.CountryInfo -import com.adyen.checkout.components.core.internal.util.CountryUtils import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.mbway.databinding.MbwayViewBinding @@ -81,7 +79,7 @@ internal class MbWayView @JvmOverloads constructor( } private fun initCountryInput() { - val countries = delegate.getSupportedCountries().mapToCountryModel() + val countries = delegate.getSupportedCountries() val adapter = CountryAdapter(context, localizedContext) adapter.setItems(countries) binding.autoCompleteTextViewCountry.apply { @@ -118,12 +116,4 @@ internal class MbWayView @JvmOverloads constructor( countryCode = countryModel.callingCode } } - - private fun List.mapToCountryModel() = map { - CountryModel( - isoCode = it.isoCode, - countryName = CountryUtils.getCountryName(it.isoCode, delegate.componentParams.shopperLocale), - callingCode = it.callingCode, - ) - } } diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt index 250b35aac7..85b83a8eb2 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/CountryViewHolder.kt @@ -9,7 +9,6 @@ package com.adyen.checkout.ui.core.internal.ui import androidx.recyclerview.widget.RecyclerView -import com.adyen.checkout.ui.core.R import com.adyen.checkout.ui.core.databinding.CountryViewBinding import com.adyen.checkout.ui.core.internal.ui.model.CountryModel diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CountryUtils.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CountryUtils.kt new file mode 100644 index 0000000000..b4a7f9133a --- /dev/null +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/util/CountryUtils.kt @@ -0,0 +1,34 @@ +/* + * 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 22/5/2024. + */ + +package com.adyen.checkout.ui.core.internal.util + +import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.internal.util.CountryUtils +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel +import java.util.Locale + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +object CountryUtils { + + fun getLocalizedCountries( + shopperLocale: Locale, + allowedISOCodes: List? = null, + comparator: Comparator = compareBy { it.countryName }, + ): List { + return CountryUtils.getCountries(allowedISOCodes) + .map { + CountryModel( + isoCode = it.isoCode, + countryName = CountryUtils.getCountryName(it.isoCode, shopperLocale), + callingCode = it.callingCode, + ) + } + .sortedWith(comparator) + } +} diff --git a/ui-core/src/test/java/com/adyen/checkout/ui/core/internal/util/CountryUtilsTest.kt b/ui-core/src/test/java/com/adyen/checkout/ui/core/internal/util/CountryUtilsTest.kt new file mode 100644 index 0000000000..0e1242b29a --- /dev/null +++ b/ui-core/src/test/java/com/adyen/checkout/ui/core/internal/util/CountryUtilsTest.kt @@ -0,0 +1,55 @@ +package com.adyen.checkout.ui.core.internal.util + +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.util.Locale +import com.adyen.checkout.components.core.internal.util.CountryUtils as CoreCountryUtils + +internal class CountryUtilsTest { + + @Test + fun `when passing nothing, then all countries are returned`() { + val actual = CountryUtils.getLocalizedCountries(Locale.US) + + val expected = CoreCountryUtils.getCountries().map { + CountryModel(it.isoCode, CoreCountryUtils.getCountryName(it.isoCode, Locale.US), it.callingCode) + }.sortedBy { it.countryName } + assertEquals(expected, actual) + } + + @Test + fun `when passing list of countries, then only specified countries are returned`() { + val specifiedCountries = listOf( + "NL", + "US", + "DE", + ) + val actual = CountryUtils.getLocalizedCountries(Locale.US, specifiedCountries) + + val expected = listOf( + CountryModel("DE", "Germany", "+49"), + CountryModel("NL", "Netherlands", "+31"), + CountryModel("US", "United States", "+1"), + ) + assertEquals(expected, actual) + } + + @Test + fun `when passing sorting, then result is sorted correctly`() { + val specifiedCountries = listOf( + "NL", + "US", + "DE", + ) + val actual = + CountryUtils.getLocalizedCountries(Locale.US, specifiedCountries, compareByDescending { it.isoCode }) + + val expected = listOf( + CountryModel("US", "United States", "+1"), + CountryModel("NL", "Netherlands", "+31"), + CountryModel("DE", "Germany", "+49"), + ) + assertEquals(expected, actual) + } +} From d17d0a38bad0af99625d7036c8622752ad112014 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 May 2024 08:53:39 +0200 Subject: [PATCH 157/272] Move initially selected country logic to delegate for MBWay COAND-912 --- .../checkout/mbway/internal/ui/DefaultMBWayDelegate.kt | 7 ++++++- .../com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt | 2 ++ .../com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt | 4 +--- .../checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt | 7 +++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index e8edc6aa8c..cad08ae381 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -160,6 +160,11 @@ internal class DefaultMBWayDelegate( override fun getSupportedCountries(): List = CountryUtils.getLocalizedCountries(componentParams.shopperLocale, SUPPORTED_COUNTRIES) + override fun getInitiallySelectedCountry(): CountryModel? { + val countries = getSupportedCountries() + return countries.firstOrNull { it.isoCode == ISO_CODE_PORTUGAL } ?: countries.firstOrNull() + } + override fun onSubmit() { val state = _componentStateFlow.value submitHandler.onSubmit(state) @@ -178,7 +183,7 @@ internal class DefaultMBWayDelegate( } companion object { - internal const val ISO_CODE_PORTUGAL = "PT" + private const val ISO_CODE_PORTUGAL = "PT" private const val ISO_CODE_SPAIN = "ES" private val SUPPORTED_COUNTRIES = listOf(ISO_CODE_PORTUGAL, ISO_CODE_SPAIN) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt index effe2ff4cf..0bcfbf8015 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt @@ -32,6 +32,8 @@ internal interface MBWayDelegate : fun getSupportedCountries(): List + fun getInitiallySelectedCountry(): CountryModel? + fun updateInputData(update: MBWayInputData.() -> Unit) fun setInteractionBlocked(isInteractionBlocked: Boolean) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index 3e5d035473..bfbd0f8232 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -91,9 +91,7 @@ internal class MbWayView @JvmOverloads constructor( onCountrySelected(country) } } - val initialCountry = - countries.firstOrNull { it.isoCode == DefaultMBWayDelegate.ISO_CODE_PORTUGAL } ?: countries.firstOrNull() - initialCountry?.let { + delegate.getInitiallySelectedCountry()?.let { binding.autoCompleteTextViewCountry.setText(it.toShortString()) onCountrySelected(it) } diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 3f18312f7f..ae9c8a1714 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -276,6 +276,13 @@ internal class DefaultMBWayDelegateTest( } } + @Test + fun `when getting initially selected country, then Portugal should be returned`() { + val result = delegate.getInitiallySelectedCountry() + + assertEquals("PT", result?.isoCode) + } + private fun createMBWayDelegate( configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultMBWayDelegate( From 83ee09e06a3b8fc2ad353cb48ae1b2223cbdd945 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 May 2024 08:56:07 +0200 Subject: [PATCH 158/272] Move initially selected country logic to delegate for EContext COAND-912 --- .../econtext/internal/ui/DefaultEContextDelegate.kt | 6 ++++++ .../checkout/econtext/internal/ui/EContextDelegate.kt | 2 ++ .../checkout/econtext/internal/ui/view/EContextView.kt | 4 +--- .../econtext/internal/ui/DefaultEContextDelegateTest.kt | 7 +++++++ .../com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt | 1 - 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index fee8241776..e2f50e7900 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -39,6 +39,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import java.util.Locale @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultEContextDelegate< @@ -207,6 +208,11 @@ internal class DefaultEContextDelegate< override fun getSupportedCountries(): List = CountryUtils.getLocalizedCountries(componentParams.shopperLocale) + override fun getInitiallySelectedCountry(): CountryModel? { + val countries = getSupportedCountries() + return countries.firstOrNull { it.isoCode == Locale.JAPAN.country } ?: countries.firstOrNull() + } + override fun isConfirmationRequired(): Boolean { return _viewFlow.value is ButtonComponentViewType } diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt index 084b0d00bf..ba6b35578d 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/EContextDelegate.kt @@ -41,4 +41,6 @@ interface EContextDelegate< fun setInteractionBlocked(isInteractionBlocked: Boolean) fun getSupportedCountries(): List + + fun getInitiallySelectedCountry(): CountryModel? } diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt index 74e7aa4329..f85100c305 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/view/EContextView.kt @@ -26,7 +26,6 @@ import com.adyen.checkout.ui.core.internal.ui.model.CountryModel import com.adyen.checkout.ui.core.internal.ui.view.AdyenTextInputEditText import com.adyen.checkout.ui.core.internal.util.setLocalizedHintFromStyle import kotlinx.coroutines.CoroutineScope -import java.util.Locale import com.adyen.checkout.ui.core.R as UICoreR @Suppress("TooManyFunctions") @@ -169,8 +168,7 @@ internal class EContextView @JvmOverloads constructor( val country = countryAdapter?.getItem(position) ?: return@OnItemClickListener onCountrySelected(country) } - val initialCountry = countries.firstOrNull { it.isoCode == Locale.JAPAN.country } ?: countries.firstOrNull() - initialCountry?.let { + delegate.getInitiallySelectedCountry()?.let { setText(it.toShortString()) onCountrySelected(it) } diff --git a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt index d28cdfa034..9e44355b85 100644 --- a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt +++ b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt @@ -279,6 +279,13 @@ internal class DefaultEContextDelegateTest( } } + @Test + fun `when getting initially selected country, then Japan should be returned`() { + val result = delegate.getInitiallySelectedCountry() + + assertEquals("JP", result?.isoCode) + } + private fun createEContextDelegate( configuration: CheckoutConfiguration = createCheckoutConfiguration(), order: Order = TEST_ORDER diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index bfbd0f8232..0343cdef4f 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -19,7 +19,6 @@ import com.adyen.checkout.components.core.internal.ui.model.Validation import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog import com.adyen.checkout.mbway.databinding.MbwayViewBinding -import com.adyen.checkout.mbway.internal.ui.DefaultMBWayDelegate import com.adyen.checkout.mbway.internal.ui.MBWayDelegate import com.adyen.checkout.ui.core.internal.ui.ComponentView import com.adyen.checkout.ui.core.internal.ui.CountryAdapter From d1d9cb0b6a59cfdda07cc2dde89aafc2523eeea1 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 28 May 2024 12:59:37 +0200 Subject: [PATCH 159/272] Use getBooleanOrNull instead of optBoolean for enableStoreDetails and showRemovePaymentMethodButton COAND-913 --- RELEASE_NOTES.md | 1 + .../checkout/sessions/core/SessionSetupConfiguration.kt | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 09274c325a..9c1cced4ca 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -32,6 +32,7 @@ CheckoutConfiguration( - Actions no longer crash when your app uses obfuscation. - Drop-in no longer throws an error while handling a 3DS2 challenge on API 66 and below. - When the app process dies during action handling, then the state will now be restored and the payment can be continued. +- Fixed ignoring `setEnableRemovingStoredPaymentMethods` flag set in Drop-in configuration for sessions. ## Changed - Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). 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 336950b982..74cfa2f6be 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 @@ -10,6 +10,7 @@ package com.adyen.checkout.sessions.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.jsonToMap import kotlinx.parcelize.Parcelize import org.json.JSONException @@ -20,7 +21,7 @@ data class SessionSetupConfiguration( val enableStoreDetails: Boolean? = null, val showInstallmentAmount: Boolean = false, val installmentOptions: Map? = null, - val showRemovePaymentMethodButton: Boolean = false, + val showRemovePaymentMethodButton: Boolean? = null, ) : ModelObject() { companion object { @@ -50,11 +51,11 @@ data class SessionSetupConfiguration( override fun deserialize(jsonObject: JSONObject): SessionSetupConfiguration { return try { SessionSetupConfiguration( - enableStoreDetails = jsonObject.optBoolean(ENABLE_STORE_DETAILS), + enableStoreDetails = jsonObject.getBooleanOrNull(ENABLE_STORE_DETAILS), showInstallmentAmount = jsonObject.optBoolean(SHOW_INSTALLMENT_AMOUNT), installmentOptions = jsonObject.optJSONObject(INSTALLMENT_OPTIONS) ?.jsonToMap(SessionSetupInstallmentOptions.SERIALIZER), - showRemovePaymentMethodButton = jsonObject.optBoolean(SHOW_REMOVE_PAYMENT_METHOD_BUTTON), + showRemovePaymentMethodButton = jsonObject.getBooleanOrNull(SHOW_REMOVE_PAYMENT_METHOD_BUTTON), ) } catch (e: JSONException) { throw ModelSerializationException(SessionSetupConfiguration::class.java, e) From d852fcd04cafbc5255f5b6f930058855cdff1aed Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 6 Feb 2024 18:18:15 +0100 Subject: [PATCH 160/272] Setup analytics base --- .../core/internal/analytics/AdyenAnalytics.kt | 24 ++ .../internal/analytics/AnalyticsEventApi.kt | 16 + .../internal/analytics/AnalyticsEvents.kt | 63 ++++ .../core/internal/analytics/GenericEvents.kt | 128 ++++++++ .../internal/analytics/AnalyticsProvider.kt | 87 +++++ .../internal/analytics/AnalyticsSource.kt | 22 ++ .../core/internal/data/api/AnalyticsMapper.kt | 156 ++++----- .../data/api/AnalyticsRepositoryData.kt | 2 +- .../data/model/AnalyticsSetupRequest.kt | 5 +- .../internal/data/model/AnalyticsSource.kt | 34 -- .../internal/data/api/AnalyticsMapperTest.kt | 302 +++++++++--------- .../api/DefaultAnalyticsRepositoryTest.kt | 4 +- .../internal/ui/DropInViewModelFactory.kt | 4 +- 13 files changed, 577 insertions(+), 270 deletions(-) create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSource.kt diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt new file mode 100644 index 0000000000..4106710eae --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt @@ -0,0 +1,24 @@ +/* + * 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 6/2/2024. + */ + +package com.adyen.checkout.core.internal.analytics + +import androidx.annotation.RestrictTo + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AdyenAnalytics { + + fun setup() { + // See DefaultAnalyticsRepository.setupAnalytics + } + + fun track(event: AnalyticsEvent) { + // Queue the event + // Send it + } +} diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt new file mode 100644 index 0000000000..8a98d58f62 --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt @@ -0,0 +1,16 @@ +/* + * 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 7/2/2024. + */ + +package com.adyen.checkout.core.internal.analytics + +import androidx.annotation.RestrictTo + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +@RequiresOptIn("Avoid using AnalyticsEvent directly") +@Target(AnnotationTarget.CONSTRUCTOR) +annotation class AnalyticsEventApi diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt new file mode 100644 index 0000000000..28027debc3 --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt @@ -0,0 +1,63 @@ +/* + * 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 7/2/2024. + */ + +package com.adyen.checkout.core.internal.analytics + +import androidx.annotation.RestrictTo +import java.util.Date + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +sealed interface AnalyticsEvent { + + val timestamp: Long + val component: String + + data class Info @AnalyticsEventApi constructor( + override val timestamp: Long = Date().time, + override val component: String, + val type: Type? = null, + val target: String? = null, + // TODO: Should this be false or null by default? + val isStoredPaymentMethod: Boolean? = null, + val brand: String? = null, + val issuer: String? = null, + val validationErrorCode: String? = null, + val validationErrorMessage: String? = null, + ) : AnalyticsEvent { + enum class Type(val value: String) { + DISPLAYED("displayed"), + DOWNLOAD("download"), + FOCUS("focus"), + INPUT("input"), + RENDERED("rendered"), + SELECTED("selected"), + UNFOCUS("unfocus"), + VALIDATION_ERROR("validationError"), + } + } + + data class Log @AnalyticsEventApi constructor( + override val timestamp: Long = Date().time, + override val component: String, + val type: Type? = null, + val subType: String? = null, + val target: String? = null, + val message: String? = null, + ) : AnalyticsEvent { + enum class Type(val value: String) { + ACTION("action"), + SUBMIT("submit"), + THREEDS2("ThreeDS2"), + } + } +} + +// TODO: Does it inherit the restrictTo from the Type? +typealias InfoEventType = AnalyticsEvent.Info.Type + +typealias LogEventType = AnalyticsEvent.Log.Type diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt new file mode 100644 index 0000000000..f9e46dd022 --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt @@ -0,0 +1,128 @@ +/* + * 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 7/2/2024. + */ + +package com.adyen.checkout.core.internal.analytics + +import androidx.annotation.RestrictTo + +@OptIn(AnalyticsEventApi::class) +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +object GenericEvents { + + // Info events + fun rendered( + component: String, + // Check if this should be null or false by default + isStoredPaymentMethod: Boolean, + brand: String? = null, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.RENDERED, + isStoredPaymentMethod = isStoredPaymentMethod, + brand = brand, + ) + + fun displayed( + component: String, + target: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.DISPLAYED, + target = target, + ) + + fun selected( + component: String, + target: String, + issuer: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.SELECTED, + target = target, + issuer = issuer, + ) + + fun input( + component: String, + target: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.INPUT, + target = target, + ) + + // TODO: This might move to the Input fields itself and not be bound to any component. But we should find a way to define targets. + // TODO: We could create an enum for target per module + fun focus( + component: String, + target: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.FOCUS, + target = target, + ) + + fun unFocus( + component: String, + target: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.UNFOCUS, + target = target, + ) + + fun download( + component: String, + target: String, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.DOWNLOAD, + target = target, + ) + + fun invalidField( + component: String, + target: String, + validationErrorCode: String?, + validationErrorMessage: String?, + ) = AnalyticsEvent.Info( + component = component, + type = InfoEventType.VALIDATION_ERROR, + target = target, + validationErrorCode = validationErrorCode, + validationErrorMessage = validationErrorMessage, + ) + + // Log events + fun submit( + component: String, + ) = AnalyticsEvent.Log( + component = component, + type = LogEventType.SUBMIT, + ) + + fun threeDS2( + component: String, + message: String, + ) = AnalyticsEvent.Log( + component = component, + type = LogEventType.THREEDS2, + message = message, + ) + + fun action( + component: String, + subType: String, + message: String, + ) = AnalyticsEvent.Log( + component = component, + type = LogEventType.ACTION, + subType = subType, + message = message, + ) +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt new file mode 100644 index 0000000000..fd6f27593f --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by josephj on 25/11/2022. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import android.app.Application +import android.os.Build +import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.BuildConfig +import com.adyen.checkout.components.core.internal.data.api.AnalyticsPlatform +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest +import com.adyen.checkout.components.core.internal.ui.model.ComponentParams + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AnalyticsProvider( + val application: Application, + val componentParams: ComponentParams, + // drop-in or txVariant + val source: AnalyticsSource, + val sessionId: String?, +) { + internal fun provide(): AnalyticsSetupRequest { + return AnalyticsSetupRequest( + version = actualVersion, + channel = ANDROID_CHANNEL, + platform = actualPlatform, + locale = componentParams.shopperLocale.toString(), + component = getComponentQueryParameter(source), + flavor = getFlavorQueryParameter(componentParams.isCreatedByDropIn), + deviceBrand = Build.BRAND, + deviceModel = Build.MODEL, + referrer = application.packageName, + systemVersion = Build.VERSION.SDK_INT.toString(), + screenWidth = application.resources.displayMetrics.widthPixels, + paymentMethods = source.getPaymentMethods(), + amount = componentParams.amount, + // unused for Android + containerWidth = null, + sessionId = sessionId, + ) + } + + @VisibleForTesting + internal fun getFlavorQueryParameter(isCreatedByDropIn: Boolean) = if (isCreatedByDropIn) { + DROP_IN + } else { + COMPONENTS + } + + @VisibleForTesting + internal fun getComponentQueryParameter(source: AnalyticsSource) = when (source) { + is AnalyticsSource.DropIn -> DROP_IN + is AnalyticsSource.PaymentComponent -> source.paymentMethodType + } + + companion object { + private const val DROP_IN = "dropin" + private const val COMPONENTS = "components" + private const val ANDROID_CHANNEL = "android" + + // these params are prefixed with actual because cross platform SDKs will override them so they are not + // technically constants + private var actualPlatform = AnalyticsPlatform.ANDROID.value + private var actualVersion = BuildConfig.CHECKOUT_VERSION + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + fun overrideForCrossPlatform( + platform: AnalyticsPlatform, + version: String, + ) { + this.actualPlatform = platform.value + this.actualVersion = version + } + + @VisibleForTesting + internal fun resetToDefaults() { + actualPlatform = AnalyticsPlatform.ANDROID.value + actualVersion = BuildConfig.CHECKOUT_VERSION + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt new file mode 100644 index 0000000000..4b4c58185e --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by ararat on 9/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import androidx.annotation.RestrictTo + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +sealed class AnalyticsSource { + data class DropIn(val paymentMethods: List) : AnalyticsSource() + data class PaymentComponent(val paymentMethodType: String) : AnalyticsSource() + + fun getPaymentMethods(): List = when(this) { + is DropIn -> paymentMethods + is PaymentComponent -> listOf(paymentMethodType) + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt index 138dcd8c7d..fcbd9fc489 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt @@ -14,86 +14,86 @@ import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.BuildConfig import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import java.util.Locale @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsMapper { - @Suppress("LongParameterList") - internal fun getAnalyticsSetupRequest( - packageName: String, - locale: Locale, - source: AnalyticsSource, - amount: Amount?, - screenWidth: Long, - paymentMethods: List, - sessionId: String?, - ): AnalyticsSetupRequest { - return AnalyticsSetupRequest( - version = actualVersion, - channel = ANDROID_CHANNEL, - platform = actualPlatform, - locale = locale.toString(), - component = getComponentQueryParameter(source), - flavor = getFlavorQueryParameter(source), - deviceBrand = Build.BRAND, - deviceModel = Build.MODEL, - referrer = packageName, - systemVersion = Build.VERSION.SDK_INT.toString(), - screenWidth = screenWidth, - paymentMethods = paymentMethods, - amount = amount, - // unused for Android - containerWidth = null, - sessionId = sessionId, - ) - } - - @VisibleForTesting - internal fun getFlavorQueryParameter(source: AnalyticsSource): String { - return when (source) { - is AnalyticsSource.DropIn -> Flavor.DROP_IN - is AnalyticsSource.PaymentComponent -> { - if (source.isCreatedByDropIn) Flavor.DROP_IN else Flavor.COMPONENTS - } - }.value - } - - @VisibleForTesting - internal fun getComponentQueryParameter(source: AnalyticsSource): String { - return when (source) { - is AnalyticsSource.DropIn -> DROP_IN_COMPONENT - is AnalyticsSource.PaymentComponent -> source.paymentMethodType - } - } - - private enum class Flavor(val value: String) { - DROP_IN("dropin"), - COMPONENTS("components") - } - - companion object { - private const val DROP_IN_COMPONENT = "dropin" - private const val ANDROID_CHANNEL = "android" - - // these params are prefixed with actual because cross platform SDKs will override them so they are not - // technically constants - private var actualPlatform = AnalyticsPlatform.ANDROID.value - private var actualVersion = BuildConfig.CHECKOUT_VERSION - - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - fun overrideForCrossPlatform( - platform: AnalyticsPlatform, - version: String, - ) { - this.actualPlatform = platform.value - this.actualVersion = version - } - - @VisibleForTesting - internal fun resetToDefaults() { - actualPlatform = AnalyticsPlatform.ANDROID.value - actualVersion = BuildConfig.CHECKOUT_VERSION - } - } +// @Suppress("LongParameterList") +// internal fun getAnalyticsSetupRequest( +// packageName: String, +// locale: Locale, +// source: AnalyticsSource, +// amount: Amount?, +// screenWidth: Long, +// paymentMethods: List, +// sessionId: String?, +// ): AnalyticsSetupRequest { +// return AnalyticsSetupRequest( +// version = actualVersion, +// channel = ANDROID_CHANNEL, +// platform = actualPlatform, +// locale = locale.toString(), +// component = getComponentQueryParameter(source), +// flavor = getFlavorQueryParameter(source), +// deviceBrand = Build.BRAND, +// deviceModel = Build.MODEL, +// referrer = packageName, +// systemVersion = Build.VERSION.SDK_INT.toString(), +// screenWidth = screenWidth, +// paymentMethods = paymentMethods, +// amount = amount, +// // unused for Android +// containerWidth = null, +// sessionId = sessionId, +// ) +// } +// +// @VisibleForTesting +// internal fun getFlavorQueryParameter(source: AnalyticsSource): String { +// return when (source) { +// is AnalyticsSource.DropIn -> Flavor.DROP_IN +// is AnalyticsSource.PaymentComponent -> { +// if (source.isCreatedByDropIn) Flavor.DROP_IN else Flavor.COMPONENTS +// } +// }.value +// } +// +// @VisibleForTesting +// internal fun getComponentQueryParameter(source: AnalyticsSource): String { +// return when (source) { +// is AnalyticsSource.DropIn -> DROP_IN_COMPONENT +// is AnalyticsSource.PaymentComponent -> source.paymentMethodType +// } +// } +// +// private enum class Flavor(val value: String) { +// DROP_IN("dropin"), +// COMPONENTS("components") +// } +// +// companion object { +// private const val DROP_IN_COMPONENT = "dropin" +// private const val ANDROID_CHANNEL = "android" +// +// // these params are prefixed with actual because cross platform SDKs will override them so they are not +// // technically constants +// private var actualPlatform = AnalyticsPlatform.ANDROID.value +// private var actualVersion = BuildConfig.CHECKOUT_VERSION +// +// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +// fun overrideForCrossPlatform( +// platform: AnalyticsPlatform, +// version: String, +// ) { +// this.actualPlatform = platform.value +// this.actualVersion = version +// } +// +// @VisibleForTesting +// internal fun resetToDefaults() { +// actualPlatform = AnalyticsPlatform.ANDROID.value +// actualVersion = BuildConfig.CHECKOUT_VERSION +// } +// } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt index c6717301e3..5d215f7ea8 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt @@ -13,7 +13,7 @@ import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.components.core.internal.ui.model.ComponentParams import com.adyen.checkout.components.core.internal.util.screenWidthPixels diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequest.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequest.kt index 7136498a4e..cfedef7b1d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequest.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequest.kt @@ -13,6 +13,7 @@ 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.ModelUtils +import com.adyen.checkout.core.internal.data.model.getIntOrNull import com.adyen.checkout.core.internal.data.model.getLongOrNull import com.adyen.checkout.core.internal.data.model.getStringOrNull import com.adyen.checkout.core.internal.data.model.optStringList @@ -33,7 +34,7 @@ internal data class AnalyticsSetupRequest( val referrer: String?, val systemVersion: String?, val containerWidth: Long?, - val screenWidth: Long?, + val screenWidth: Int?, val paymentMethods: List?, val amount: Amount?, val sessionId: String?, @@ -97,7 +98,7 @@ internal data class AnalyticsSetupRequest( referrer = getStringOrNull(REFERRER), systemVersion = getStringOrNull(SYSTEM_VERSION), containerWidth = getLongOrNull(CONTAINER_WIDTH), - screenWidth = getLongOrNull(SCREEN_WIDTH), + screenWidth = getIntOrNull(SCREEN_WIDTH), paymentMethods = optStringList(PAYMENT_METHODS), amount = ModelUtils.deserializeOpt(optJSONObject(AMOUNT), Amount.SERIALIZER), sessionId = getStringOrNull(SESSION_ID), diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSource.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSource.kt deleted file mode 100644 index 5d29d42cf8..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSource.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by josephj on 24/11/2022. - */ - -package com.adyen.checkout.components.core.internal.data.model - -import androidx.annotation.RestrictTo -import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.StoredPaymentMethod - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -sealed class AnalyticsSource { - - class DropIn : AnalyticsSource() - - class PaymentComponent internal constructor( - internal val isCreatedByDropIn: Boolean, - internal val paymentMethodType: String - ) : AnalyticsSource() { - constructor(isCreatedByDropIn: Boolean, paymentMethod: PaymentMethod) : this( - isCreatedByDropIn, - paymentMethod.type.orEmpty() - ) - - constructor(isCreatedByDropIn: Boolean, storedPaymentMethod: StoredPaymentMethod) : this( - isCreatedByDropIn, - storedPaymentMethod.type.orEmpty() - ) - } -} diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt index 6f39100d6e..e3ac4f941c 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt @@ -13,7 +13,7 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -26,154 +26,154 @@ import java.util.Locale @ExtendWith(MockitoExtension::class) internal class AnalyticsMapperTest { - private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() - - @BeforeEach - fun beforeEach() { - AnalyticsMapper.resetToDefaults() - } - - @Nested - @DisplayName("when getFlavorQueryParameter is called and") - inner class GetFlavorQueryParameterTest { - - @Test - fun `source is drop-in then returned value is dropin`() { - val actual = analyticsMapper.getFlavorQueryParameter(AnalyticsSource.DropIn()) - assertEquals("dropin", actual) - } - - @Test - fun `source is a component created from drop-in then returned value is dropin`() { - val actual = analyticsMapper.getFlavorQueryParameter( - AnalyticsSource.PaymentComponent( - isCreatedByDropIn = true, - paymentMethod = PaymentMethod(), - ) - ) - assertEquals("dropin", actual) - } - - @Test - fun `source is a component not created from drop-in then returned value is components`() { - val actual = analyticsMapper.getFlavorQueryParameter( - AnalyticsSource.PaymentComponent( - isCreatedByDropIn = false, - paymentMethod = PaymentMethod(), - ) - ) - assertEquals("components", actual) - } - } - - @Nested - @DisplayName("when getComponentQueryParameter is called and") - inner class GetComponentQueryParameterTest { - - @Test - fun `source is drop-in then returned value is dropin`() { - val actual = analyticsMapper.getComponentQueryParameter(AnalyticsSource.DropIn()) - assertEquals("dropin", actual) - } - - @Test - fun `source is a component with a payment method then returned value is the payment method type`() { - val actual = analyticsMapper.getComponentQueryParameter( - AnalyticsSource.PaymentComponent( - isCreatedByDropIn = true, - paymentMethod = PaymentMethod(type = "PAYMENT_METHOD_TYPE"), - ) - ) - assertEquals("PAYMENT_METHOD_TYPE", actual) - } - - @Test - fun `source is a component with a stored payment method then returned value is the stored payment method type`() { - val actual = analyticsMapper.getComponentQueryParameter( - AnalyticsSource.PaymentComponent( - isCreatedByDropIn = false, - storedPaymentMethod = StoredPaymentMethod(type = "STORED_PAYMENT_METHOD_TYPE"), - ) - ) - assertEquals("STORED_PAYMENT_METHOD_TYPE", actual) - } - } - - @Nested - @DisplayName("when getQueryParameters is called") - inner class GetQueryParametersTest { - - @Test - fun `then returned values should match expected`() { - val actual = analyticsMapper.getAnalyticsSetupRequest( - packageName = "PACKAGE_NAME", - locale = Locale("en", "US"), - source = AnalyticsSource.PaymentComponent( - isCreatedByDropIn = false, - PaymentMethod(type = "PAYMENT_METHOD_TYPE") - ), - amount = Amount("USD", 1337), - screenWidth = 1286, - paymentMethods = listOf("scheme", "googlepay"), - sessionId = "SESSION_ID", - ) - - val expected = AnalyticsSetupRequest( - version = "5.3.1", - channel = "android", - platform = "android", - locale = "en_US", - component = "PAYMENT_METHOD_TYPE", - flavor = "components", - deviceBrand = "null", - deviceModel = "null", - referrer = "PACKAGE_NAME", - systemVersion = Build.VERSION.SDK_INT.toString(), - containerWidth = null, - screenWidth = 1286, - paymentMethods = listOf("scheme", "googlepay"), - amount = Amount("USD", 1337), - sessionId = "SESSION_ID", - ) - - assertEquals(expected.toString(), actual.toString()) - } - } - - @Test - fun `when cross platform parameters are overridden, then returned values should match expected`() { - AnalyticsMapper.overrideForCrossPlatform(AnalyticsPlatform.FLUTTER, "some test version") - val actual = analyticsMapper.getAnalyticsSetupRequest( - packageName = "PACKAGE_NAME", - locale = Locale("en", "US"), - source = AnalyticsSource.PaymentComponent( - isCreatedByDropIn = false, - PaymentMethod(type = "PAYMENT_METHOD_TYPE") - ), - amount = Amount("USD", 1337), - screenWidth = 1286, - paymentMethods = listOf("scheme", "googlepay"), - sessionId = "SESSION_ID", - ) - - val expected = AnalyticsSetupRequest( - version = "some test version", - channel = "android", - platform = "flutter", - locale = "en_US", - component = "PAYMENT_METHOD_TYPE", - flavor = "components", - deviceBrand = "null", - deviceModel = "null", - referrer = "PACKAGE_NAME", - systemVersion = Build.VERSION.SDK_INT.toString(), - containerWidth = null, - screenWidth = 1286, - paymentMethods = listOf("scheme", "googlepay"), - amount = Amount("USD", 1337), - sessionId = "SESSION_ID", - ) - - assertEquals(expected.toString(), actual.toString()) - } +// private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() +// +// @BeforeEach +// fun beforeEach() { +// AnalyticsMapper.resetToDefaults() +// } +// +// @Nested +// @DisplayName("when getFlavorQueryParameter is called and") +// inner class GetFlavorQueryParameterTest { +// +// @Test +// fun `source is drop-in then returned value is dropin`() { +// val actual = analyticsMapper.getFlavorQueryParameter(AnalyticsSource.DropIn) +// assertEquals("dropin", actual) +// } +// +// @Test +// fun `source is a component created from drop-in then returned value is dropin`() { +// val actual = analyticsMapper.getFlavorQueryParameter( +// AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = true, +// paymentMethod = PaymentMethod(), +// ) +// ) +// assertEquals("dropin", actual) +// } +// +// @Test +// fun `source is a component not created from drop-in then returned value is components`() { +// val actual = analyticsMapper.getFlavorQueryParameter( +// AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = false, +// paymentMethod = PaymentMethod(), +// ) +// ) +// assertEquals("components", actual) +// } +// } +// +// @Nested +// @DisplayName("when getComponentQueryParameter is called and") +// inner class GetComponentQueryParameterTest { +// +// @Test +// fun `source is drop-in then returned value is dropin`() { +// val actual = analyticsMapper.getComponentQueryParameter(AnalyticsSource.DropIn) +// assertEquals("dropin", actual) +// } +// +// @Test +// fun `source is a component with a payment method then returned value is the payment method type`() { +// val actual = analyticsMapper.getComponentQueryParameter( +// AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = true, +// paymentMethod = PaymentMethod(type = "PAYMENT_METHOD_TYPE"), +// ) +// ) +// assertEquals("PAYMENT_METHOD_TYPE", actual) +// } +// +// @Test +// fun `source is a component with a stored payment method then returned value is the stored payment method type`() { +// val actual = analyticsMapper.getComponentQueryParameter( +// AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = false, +// storedPaymentMethod = StoredPaymentMethod(type = "STORED_PAYMENT_METHOD_TYPE"), +// ) +// ) +// assertEquals("STORED_PAYMENT_METHOD_TYPE", actual) +// } +// } +// +// @Nested +// @DisplayName("when getQueryParameters is called") +// inner class GetQueryParametersTest { +// +// @Test +// fun `then returned values should match expected`() { +// val actual = analyticsMapper.getAnalyticsSetupRequest( +// packageName = "PACKAGE_NAME", +// locale = Locale("en", "US"), +// source = AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = false, +// PaymentMethod(type = "PAYMENT_METHOD_TYPE") +// ), +// amount = Amount("USD", 1337), +// screenWidth = 1286, +// paymentMethods = listOf("scheme", "googlepay"), +// sessionId = "SESSION_ID", +// ) +// +// val expected = AnalyticsSetupRequest( +// version = "5.3.1", +// channel = "android", +// platform = "android", +// locale = "en_US", +// component = "PAYMENT_METHOD_TYPE", +// flavor = "components", +// deviceBrand = "null", +// deviceModel = "null", +// referrer = "PACKAGE_NAME", +// systemVersion = Build.VERSION.SDK_INT.toString(), +// containerWidth = null, +// screenWidth = 1286, +// paymentMethods = listOf("scheme", "googlepay"), +// amount = Amount("USD", 1337), +// sessionId = "SESSION_ID", +// ) +// +// assertEquals(expected.toString(), actual.toString()) +// } +// } +// +// @Test +// fun `when cross platform parameters are overridden, then returned values should match expected`() { +// AnalyticsMapper.overrideForCrossPlatform(AnalyticsPlatform.FLUTTER, "some test version") +// val actual = analyticsMapper.getAnalyticsSetupRequest( +// packageName = "PACKAGE_NAME", +// locale = Locale("en", "US"), +// source = AnalyticsSource.PaymentComponent( +// isCreatedByDropIn = false, +// PaymentMethod(type = "PAYMENT_METHOD_TYPE") +// ), +// amount = Amount("USD", 1337), +// screenWidth = 1286, +// paymentMethods = listOf("scheme", "googlepay"), +// sessionId = "SESSION_ID", +// ) +// +// val expected = AnalyticsSetupRequest( +// version = "some test version", +// channel = "android", +// platform = "flutter", +// locale = "en_US", +// component = "PAYMENT_METHOD_TYPE", +// flavor = "components", +// deviceBrand = "null", +// deviceModel = "null", +// referrer = "PACKAGE_NAME", +// systemVersion = Build.VERSION.SDK_INT.toString(), +// containerWidth = null, +// screenWidth = 1286, +// paymentMethods = listOf("scheme", "googlepay"), +// amount = Amount("USD", 1337), +// sessionId = "SESSION_ID", +// ) +// +// assertEquals(expected.toString(), actual.toString()) +// } } diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt index 0d24ac5405..91b470debc 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.data.api import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.exception.HttpException import com.adyen.checkout.test.LoggingExtension @@ -192,7 +192,7 @@ internal class DefaultAnalyticsRepositoryTest( companion object { private const val PACKAGE_NAME = "com.adyen.checkout.test" private val LOCALE = Locale.US - private val ANALYTICS_SOURCE = AnalyticsSource.DropIn() + private val ANALYTICS_SOURCE = AnalyticsSource.DropIn private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_AMOUNT = Amount("USD", 1337) private const val SCREEN_WIDTH = 1080 diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index a3e87505b4..f652930233 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusService -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.util.screenWidthPixels import com.adyen.checkout.core.internal.data.api.HttpClientFactory import com.adyen.checkout.core.internal.util.LocaleProvider @@ -59,7 +59,7 @@ internal class DropInViewModelFactory( level = dropInParams.analyticsParams.level, packageName = packageName, locale = dropInParams.shopperLocale, - source = AnalyticsSource.DropIn(), + source = AnalyticsSource.DropIn(paymentMethods), clientKey = dropInParams.clientKey, amount = dropInParams.amount, screenWidth = screenWidth, From 321a627117965535553f6ba3133f22a31ba7e578 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 12 Feb 2024 12:27:26 +0100 Subject: [PATCH 161/272] Implement analytics setup call COAND-844 diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt deleted file mode 100644 index 4106710ea..000000000 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 6/2/2024. - */ - -package com.adyen.checkout.core.internal.analytics - -import androidx.annotation.RestrictTo - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AdyenAnalytics { - - fun setup() { - // See DefaultAnalyticsRepository.setupAnalytics - } - - fun track(event: AnalyticsEvent) { - // Queue the event - // Send it - } -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt new file mode 100644 index 000000000..d012b5721 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt @@ -0,0 +1,92 @@ +/* + * 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 12/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runSuspendCatching +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AdyenAnalytics( + private val analyticsProvider: AnalyticsProvider, + private val analyticsParams: AnalyticsParams, + private val analyticsService: AnalyticsService, + coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, +) { + + // TODO: Check if Job or SupervisorJob is better for us + private val coroutineScope = CoroutineScope(coroutineDispatcher + SupervisorJob()) + + @Volatile + var checkoutAttemptId: String? = null + private set + + @Volatile + private var state: State = State.Uninitialized + + fun setup() { + if (cannotSendEvent()) { + checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS + return + } + + if (state != State.Uninitialized) return + state = State.InProgress + adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } + + coroutineScope.launch { + runSuspendCatching { + val analyticsSetupRequest = analyticsProvider.provide() + val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsParams.clientKey) + checkoutAttemptId = response.checkoutAttemptId + state = State.Ready + adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } + }.onFailure { e -> + state = State.Failed + adyenLog(AdyenLogLevel.ERROR) { + "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" + } + } + } + } + + fun track(event: AnalyticsEvent) { + // TODO: Check if we can send events anyway, because attempt id is anonymous already + if (cannotSendEvent()) return + + // Queue the event + // Send it + } + + private fun cannotSendEvent(): Boolean { + return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority + } + + companion object { + private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + } + + @VisibleForTesting + internal sealed class State { + data object Uninitialized : State() + data object InProgress : State() + data object Ready : State() + data object Failed : State() + } +} diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt similarity index 78% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt index 8a98d58f6..d3775c469 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 7/2/2024. + * Created by oscars on 12/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt similarity index 96% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt index 28027debc..d90089e3e 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt @@ -6,7 +6,7 @@ * Created by oscars on 7/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo import java.util.Date diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt index fd6f27593..70a792048 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt @@ -21,7 +21,6 @@ import com.adyen.checkout.components.core.internal.ui.model.ComponentParams class AnalyticsProvider( val application: Application, val componentParams: ComponentParams, - // drop-in or txVariant val source: AnalyticsSource, val sessionId: String?, ) { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt index 4b4c58185..7938e93c7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt @@ -12,11 +12,12 @@ import androidx.annotation.RestrictTo @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) sealed class AnalyticsSource { - data class DropIn(val paymentMethods: List) : AnalyticsSource() + data class DropIn(val paymentMethodList: List) : AnalyticsSource() data class PaymentComponent(val paymentMethodType: String) : AnalyticsSource() + // TODO: Check if we can rename paymentMethodList and not make it clash with this function fun getPaymentMethods(): List = when(this) { - is DropIn -> paymentMethods + is DropIn -> paymentMethodList is PaymentComponent -> listOf(paymentMethodType) } } diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt similarity index 80% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index f9e46dd02..b99a56ce0 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 7/2/2024. + * Created by oscars on 12/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo @@ -22,7 +22,7 @@ object GenericEvents { brand: String? = null, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.RENDERED, + type = AnalyticsEvent.Info.Type.RENDERED, isStoredPaymentMethod = isStoredPaymentMethod, brand = brand, ) @@ -32,7 +32,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.DISPLAYED, + type = AnalyticsEvent.Info.Type.DISPLAYED, target = target, ) @@ -42,7 +42,7 @@ object GenericEvents { issuer: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.SELECTED, + type = AnalyticsEvent.Info.Type.SELECTED, target = target, issuer = issuer, ) @@ -52,7 +52,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.INPUT, + type = AnalyticsEvent.Info.Type.INPUT, target = target, ) @@ -63,7 +63,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.FOCUS, + type = AnalyticsEvent.Info.Type.FOCUS, target = target, ) @@ -72,7 +72,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.UNFOCUS, + type = AnalyticsEvent.Info.Type.UNFOCUS, target = target, ) @@ -81,7 +81,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.DOWNLOAD, + type = AnalyticsEvent.Info.Type.DOWNLOAD, target = target, ) @@ -92,7 +92,7 @@ object GenericEvents { validationErrorMessage: String?, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.VALIDATION_ERROR, + type = AnalyticsEvent.Info.Type.VALIDATION_ERROR, target = target, validationErrorCode = validationErrorCode, validationErrorMessage = validationErrorMessage, @@ -103,7 +103,7 @@ object GenericEvents { component: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.SUBMIT, + type = AnalyticsEvent.Log.Type.SUBMIT, ) fun threeDS2( @@ -111,7 +111,7 @@ object GenericEvents { message: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.THREEDS2, + type = AnalyticsEvent.Log.Type.THREEDS2, message = message, ) @@ -121,7 +121,7 @@ object GenericEvents { message: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.ACTION, + type = AnalyticsEvent.Log.Type.ACTION, subType = subType, message = message, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt index 5d215f7ea..f5fea4d92 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt @@ -40,7 +40,7 @@ data class AnalyticsRepositoryData( ) : this( application = application, componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(componentParams.isCreatedByDropIn, paymentMethod), + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), paymentMethodType = paymentMethod.type, sessionId = sessionId, ) @@ -53,7 +53,7 @@ data class AnalyticsRepositoryData( ) : this( application = application, componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(componentParams.isCreatedByDropIn, storedPaymentMethod), + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), paymentMethodType = storedPaymentMethod.type, sessionId = sessionId, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt index b9557b82a..fbd5e7918 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt @@ -40,33 +40,33 @@ class DefaultAnalyticsRepository( state = State.InProgress adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } - runSuspendCatching { - val analyticsSetupRequest = with(analyticsRepositoryData) { - analyticsMapper.getAnalyticsSetupRequest( - packageName = packageName, - locale = locale, - source = source, - amount = amount, - screenWidth = screenWidth.toLong(), - paymentMethods = paymentMethods, - sessionId = sessionId, - ) - } - val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsRepositoryData.clientKey) - checkoutAttemptId = response.checkoutAttemptId - state = State.Ready - adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } - }.onFailure { e -> - state = State.Failed - adyenLog(AdyenLogLevel.ERROR) { - "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" - } - } +// runSuspendCatching { +// val analyticsSetupRequest = with(analyticsRepositoryData) { +// analyticsMapper.getAnalyticsSetupRequest( +// packageName = packageName, +// locale = locale, +// source = source, +// amount = amount, +// screenWidth = screenWidth.toLong(), +// paymentMethods = paymentMethods, +// sessionId = sessionId, +// ) +// } +// val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsRepositoryData.clientKey) +// checkoutAttemptId = response.checkoutAttemptId +// state = State.Ready +// adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } +// }.onFailure { e -> +// state = State.Failed +// adyenLog(AdyenLogLevel.ERROR) { +// "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" +// } +// } } private fun canSendAnalytics(requiredLevel: AnalyticsParamsLevel): Boolean { require(requiredLevel != NONE) { "Analytics are not allowed with level NONE" } - return !analyticsRepositoryData.level.hasHigherPriorityThan(requiredLevel) + return true } companion object { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt index c95ed163f..8214b77f5 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt @@ -15,20 +15,19 @@ import com.adyen.checkout.components.core.AnalyticsLevel @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class AnalyticsParams( val level: AnalyticsParamsLevel, + val clientKey: String, ) { - constructor(analyticsConfiguration: AnalyticsConfiguration?) : - this(level = getLevel(analyticsConfiguration)) + constructor( + analyticsConfiguration: AnalyticsConfiguration?, + clientKey: String, + ) : this(level = getLevel(analyticsConfiguration), clientKey = clientKey) } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -enum class AnalyticsParamsLevel(private val priority: Int) { - ALL(1), - NONE(2); - - internal fun hasHigherPriorityThan(level: AnalyticsParamsLevel): Boolean { - return priority > level.priority - } +enum class AnalyticsParamsLevel(val priority: Int) { + NONE(1), + ALL(2), } private fun getLevel(analyticsConfiguration: AnalyticsConfiguration?): AnalyticsParamsLevel { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index f65293023..fd98091ce 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -13,6 +13,7 @@ import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.data.api.AnalyticsService --- .../core/internal/analytics/AdyenAnalytics.kt | 24 ----- .../core/internal/analytics/AdyenAnalytics.kt | 92 +++++++++++++++++++ .../internal/analytics/AnalyticsEventApi.kt | 4 +- .../internal/analytics/AnalyticsEvents.kt | 2 +- .../internal/analytics/AnalyticsProvider.kt | 1 - .../internal/analytics/AnalyticsSource.kt | 5 +- .../core/internal/analytics/GenericEvents.kt | 26 +++--- .../data/api/AnalyticsRepositoryData.kt | 4 +- .../data/api/DefaultAnalyticsRepository.kt | 46 +++++----- .../core/internal/ui/model/AnalyticsParams.kt | 17 ++-- .../internal/ui/DropInViewModelFactory.kt | 1 + 11 files changed, 145 insertions(+), 77 deletions(-) delete mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt rename {checkout-core/src/main/java/com/adyen/checkout => components-core/src/main/java/com/adyen/checkout/components}/core/internal/analytics/AnalyticsEventApi.kt (78%) rename {checkout-core/src/main/java/com/adyen/checkout => components-core/src/main/java/com/adyen/checkout/components}/core/internal/analytics/AnalyticsEvents.kt (96%) rename {checkout-core/src/main/java/com/adyen/checkout => components-core/src/main/java/com/adyen/checkout/components}/core/internal/analytics/GenericEvents.kt (80%) diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt deleted file mode 100644 index 4106710eae..0000000000 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AdyenAnalytics.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 6/2/2024. - */ - -package com.adyen.checkout.core.internal.analytics - -import androidx.annotation.RestrictTo - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AdyenAnalytics { - - fun setup() { - // See DefaultAnalyticsRepository.setupAnalytics - } - - fun track(event: AnalyticsEvent) { - // Queue the event - // Send it - } -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt new file mode 100644 index 0000000000..d012b57219 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt @@ -0,0 +1,92 @@ +/* + * 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 12/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runSuspendCatching +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AdyenAnalytics( + private val analyticsProvider: AnalyticsProvider, + private val analyticsParams: AnalyticsParams, + private val analyticsService: AnalyticsService, + coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, +) { + + // TODO: Check if Job or SupervisorJob is better for us + private val coroutineScope = CoroutineScope(coroutineDispatcher + SupervisorJob()) + + @Volatile + var checkoutAttemptId: String? = null + private set + + @Volatile + private var state: State = State.Uninitialized + + fun setup() { + if (cannotSendEvent()) { + checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS + return + } + + if (state != State.Uninitialized) return + state = State.InProgress + adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } + + coroutineScope.launch { + runSuspendCatching { + val analyticsSetupRequest = analyticsProvider.provide() + val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsParams.clientKey) + checkoutAttemptId = response.checkoutAttemptId + state = State.Ready + adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } + }.onFailure { e -> + state = State.Failed + adyenLog(AdyenLogLevel.ERROR) { + "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" + } + } + } + } + + fun track(event: AnalyticsEvent) { + // TODO: Check if we can send events anyway, because attempt id is anonymous already + if (cannotSendEvent()) return + + // Queue the event + // Send it + } + + private fun cannotSendEvent(): Boolean { + return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority + } + + companion object { + private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + } + + @VisibleForTesting + internal sealed class State { + data object Uninitialized : State() + data object InProgress : State() + data object Ready : State() + data object Failed : State() + } +} diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt similarity index 78% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt index 8a98d58f62..d3775c469c 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEventApi.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 7/2/2024. + * Created by oscars on 12/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt similarity index 96% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt index 28027debc3..d90089e3e9 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt @@ -6,7 +6,7 @@ * Created by oscars on 7/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo import java.util.Date diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt index fd6f27593f..70a7920480 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt @@ -21,7 +21,6 @@ import com.adyen.checkout.components.core.internal.ui.model.ComponentParams class AnalyticsProvider( val application: Application, val componentParams: ComponentParams, - // drop-in or txVariant val source: AnalyticsSource, val sessionId: String?, ) { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt index 4b4c58185e..7938e93c70 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt @@ -12,11 +12,12 @@ import androidx.annotation.RestrictTo @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) sealed class AnalyticsSource { - data class DropIn(val paymentMethods: List) : AnalyticsSource() + data class DropIn(val paymentMethodList: List) : AnalyticsSource() data class PaymentComponent(val paymentMethodType: String) : AnalyticsSource() + // TODO: Check if we can rename paymentMethodList and not make it clash with this function fun getPaymentMethods(): List = when(this) { - is DropIn -> paymentMethods + is DropIn -> paymentMethodList is PaymentComponent -> listOf(paymentMethodType) } } diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt similarity index 80% rename from checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index f9e46dd022..b99a56ce03 100644 --- a/checkout-core/src/main/java/com/adyen/checkout/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 7/2/2024. + * Created by oscars on 12/2/2024. */ -package com.adyen.checkout.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo @@ -22,7 +22,7 @@ object GenericEvents { brand: String? = null, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.RENDERED, + type = AnalyticsEvent.Info.Type.RENDERED, isStoredPaymentMethod = isStoredPaymentMethod, brand = brand, ) @@ -32,7 +32,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.DISPLAYED, + type = AnalyticsEvent.Info.Type.DISPLAYED, target = target, ) @@ -42,7 +42,7 @@ object GenericEvents { issuer: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.SELECTED, + type = AnalyticsEvent.Info.Type.SELECTED, target = target, issuer = issuer, ) @@ -52,7 +52,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.INPUT, + type = AnalyticsEvent.Info.Type.INPUT, target = target, ) @@ -63,7 +63,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.FOCUS, + type = AnalyticsEvent.Info.Type.FOCUS, target = target, ) @@ -72,7 +72,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.UNFOCUS, + type = AnalyticsEvent.Info.Type.UNFOCUS, target = target, ) @@ -81,7 +81,7 @@ object GenericEvents { target: String, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.DOWNLOAD, + type = AnalyticsEvent.Info.Type.DOWNLOAD, target = target, ) @@ -92,7 +92,7 @@ object GenericEvents { validationErrorMessage: String?, ) = AnalyticsEvent.Info( component = component, - type = InfoEventType.VALIDATION_ERROR, + type = AnalyticsEvent.Info.Type.VALIDATION_ERROR, target = target, validationErrorCode = validationErrorCode, validationErrorMessage = validationErrorMessage, @@ -103,7 +103,7 @@ object GenericEvents { component: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.SUBMIT, + type = AnalyticsEvent.Log.Type.SUBMIT, ) fun threeDS2( @@ -111,7 +111,7 @@ object GenericEvents { message: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.THREEDS2, + type = AnalyticsEvent.Log.Type.THREEDS2, message = message, ) @@ -121,7 +121,7 @@ object GenericEvents { message: String, ) = AnalyticsEvent.Log( component = component, - type = LogEventType.ACTION, + type = AnalyticsEvent.Log.Type.ACTION, subType = subType, message = message, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt index 5d215f7ea8..f5fea4d92e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt @@ -40,7 +40,7 @@ data class AnalyticsRepositoryData( ) : this( application = application, componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(componentParams.isCreatedByDropIn, paymentMethod), + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), paymentMethodType = paymentMethod.type, sessionId = sessionId, ) @@ -53,7 +53,7 @@ data class AnalyticsRepositoryData( ) : this( application = application, componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(componentParams.isCreatedByDropIn, storedPaymentMethod), + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), paymentMethodType = storedPaymentMethod.type, sessionId = sessionId, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt index b9557b82a7..fbd5e79187 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt @@ -40,33 +40,33 @@ class DefaultAnalyticsRepository( state = State.InProgress adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } - runSuspendCatching { - val analyticsSetupRequest = with(analyticsRepositoryData) { - analyticsMapper.getAnalyticsSetupRequest( - packageName = packageName, - locale = locale, - source = source, - amount = amount, - screenWidth = screenWidth.toLong(), - paymentMethods = paymentMethods, - sessionId = sessionId, - ) - } - val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsRepositoryData.clientKey) - checkoutAttemptId = response.checkoutAttemptId - state = State.Ready - adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } - }.onFailure { e -> - state = State.Failed - adyenLog(AdyenLogLevel.ERROR) { - "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" - } - } +// runSuspendCatching { +// val analyticsSetupRequest = with(analyticsRepositoryData) { +// analyticsMapper.getAnalyticsSetupRequest( +// packageName = packageName, +// locale = locale, +// source = source, +// amount = amount, +// screenWidth = screenWidth.toLong(), +// paymentMethods = paymentMethods, +// sessionId = sessionId, +// ) +// } +// val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsRepositoryData.clientKey) +// checkoutAttemptId = response.checkoutAttemptId +// state = State.Ready +// adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } +// }.onFailure { e -> +// state = State.Failed +// adyenLog(AdyenLogLevel.ERROR) { +// "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" +// } +// } } private fun canSendAnalytics(requiredLevel: AnalyticsParamsLevel): Boolean { require(requiredLevel != NONE) { "Analytics are not allowed with level NONE" } - return !analyticsRepositoryData.level.hasHigherPriorityThan(requiredLevel) + return true } companion object { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt index c95ed163f5..8214b77f58 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/AnalyticsParams.kt @@ -15,20 +15,19 @@ import com.adyen.checkout.components.core.AnalyticsLevel @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class AnalyticsParams( val level: AnalyticsParamsLevel, + val clientKey: String, ) { - constructor(analyticsConfiguration: AnalyticsConfiguration?) : - this(level = getLevel(analyticsConfiguration)) + constructor( + analyticsConfiguration: AnalyticsConfiguration?, + clientKey: String, + ) : this(level = getLevel(analyticsConfiguration), clientKey = clientKey) } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -enum class AnalyticsParamsLevel(private val priority: Int) { - ALL(1), - NONE(2); - - internal fun hasHigherPriorityThan(level: AnalyticsParamsLevel): Boolean { - return priority > level.priority - } +enum class AnalyticsParamsLevel(val priority: Int) { + NONE(1), + ALL(2), } private fun getLevel(analyticsConfiguration: AnalyticsConfiguration?): AnalyticsParamsLevel { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index f652930233..fd98091ce5 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -13,6 +13,7 @@ import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.data.api.AnalyticsService From 405d2cb4dc088bd723a1a5dc29f9fe6e7585c5f0 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 12 Feb 2024 12:27:52 +0100 Subject: [PATCH 162/272] Implement AdyenAnalytics in MBWay COAND-844 --- .../provider/MBWayComponentProvider.kt | 59 +++++++++---------- .../mbway/internal/ui/DefaultMBWayDelegate.kt | 15 ++--- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index 8577f025d4..ac84ceb5c5 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.analytics.AdyenAnalytics +import com.adyen.checkout.components.core.internal.analytics.AnalyticsProvider +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -95,24 +95,24 @@ constructor( componentConfiguration = checkoutConfiguration.getMBWayConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), - ) - val mbWayDelegate = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + // TODO: Find out how to use analytics source with drop-in + adyenAnalytics = AdyenAnalytics( + analyticsProvider = AnalyticsProvider( + application, + componentParams, + AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + null, + ), + analyticsParams = componentParams.analyticsParams, + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + ), submitHandler = SubmitHandler(savedStateHandle), ) @@ -187,25 +187,24 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), - ) - val mbWayDelegate = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + // TODO: Find out how to use analytics source with drop-in + adyenAnalytics = AdyenAnalytics( + analyticsProvider = AnalyticsProvider( + application, + componentParams, + AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + checkoutSession.sessionSetupResponse.id, + ), + analyticsParams = componentParams.analyticsParams, + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + ), submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index cad08ae381..5b09933d4d 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AdyenAnalytics import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.MBWayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -34,7 +34,6 @@ import com.adyen.checkout.ui.core.internal.util.CountryUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultMBWayDelegate( @@ -42,7 +41,7 @@ internal class DefaultMBWayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: ButtonComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val adyenAnalytics: AdyenAnalytics, private val submitHandler: SubmitHandler, ) : MBWayDelegate { @@ -71,14 +70,12 @@ internal class DefaultMBWayDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + setupAnalytics() } - private fun setupAnalytics(coroutineScope: CoroutineScope) { + private fun setupAnalytics() { adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + adyenAnalytics.setup() } override fun observe( @@ -136,7 +133,7 @@ internal class DefaultMBWayDelegate( ): MBWayComponentState { val paymentMethod = MBWayPaymentMethod( type = MBWayPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = adyenAnalytics.checkoutAttemptId, telephoneNumber = outputData.mobilePhoneNumberFieldState.value, ) From 0d545baa6cf4446e68b921b5c3a58242442f31c8 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 12 Feb 2024 16:27:06 +0100 Subject: [PATCH 163/272] Add backend objects for event tracking COAND-844 --- .../core/internal/data/model/EmptyResponse.kt | 26 ++++++ .../core/internal/analytics/AdyenAnalytics.kt | 19 +++++ .../internal/analytics/AnalyticsEvents.kt | 1 - .../internal/data/api/AnalyticsService.kt | 17 ++++ .../internal/data/model/AnalyticsTrackInfo.kt | 85 +++++++++++++++++++ .../internal/data/model/AnalyticsTrackLog.kt | 72 ++++++++++++++++ .../data/model/AnalyticsTrackRequest.kt | 59 +++++++++++++ 7 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/EmptyResponse.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt diff --git a/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/EmptyResponse.kt b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/EmptyResponse.kt new file mode 100644 index 0000000000..b27f4da9fc --- /dev/null +++ b/checkout-core/src/main/java/com/adyen/checkout/core/internal/data/model/EmptyResponse.kt @@ -0,0 +1,26 @@ +/* + * 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 ararat on 13/2/2024. + */ + +package com.adyen.checkout.core.internal.data.model + +import androidx.annotation.RestrictTo +import kotlinx.parcelize.Parcelize +import org.json.JSONObject + +@Parcelize +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class EmptyResponse : ModelObject() { + companion object { + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: EmptyResponse) = JSONObject() + + override fun deserialize(jsonObject: JSONObject) = EmptyResponse() + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt index d012b57219..481de04cbd 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt @@ -21,6 +21,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import java.util.LinkedList @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AdyenAnalytics( @@ -33,6 +36,8 @@ class AdyenAnalytics( // TODO: Check if Job or SupervisorJob is better for us private val coroutineScope = CoroutineScope(coroutineDispatcher + SupervisorJob()) + private val eventQueue: LinkedList = LinkedList() + @Volatile var checkoutAttemptId: String? = null private set @@ -40,6 +45,8 @@ class AdyenAnalytics( @Volatile private var state: State = State.Uninitialized + val mutex = Mutex() + fun setup() { if (cannotSendEvent()) { checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS @@ -70,10 +77,22 @@ class AdyenAnalytics( // TODO: Check if we can send events anyway, because attempt id is anonymous already if (cannotSendEvent()) return + coroutineScope.launch { + mutex.withLock { + eventQueue.add(event) + track(event) + } + } + // Queue the event // Send it } + // TODO: Discuss if we need to use mappers before we send events to backend + private fun sendEvents() { + + } + private fun cannotSendEvent(): Boolean { return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt index d90089e3e9..1ed585d0cc 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt @@ -22,7 +22,6 @@ sealed interface AnalyticsEvent { override val component: String, val type: Type? = null, val target: String? = null, - // TODO: Should this be false or null by default? val isStoredPaymentMethod: Boolean? = null, val brand: String? = null, val issuer: String? = null, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt index 429b375b2f..3d9a6e2663 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt @@ -11,8 +11,10 @@ package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest import com.adyen.checkout.core.internal.data.api.HttpClient import com.adyen.checkout.core.internal.data.api.post +import com.adyen.checkout.core.internal.data.model.EmptyResponse import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -35,4 +37,19 @@ class AnalyticsService( responseSerializer = AnalyticsSetupResponse.SERIALIZER, ) } + + // TODO: Add tests + internal suspend fun trackEvents( + request: AnalyticsTrackRequest, + checkoutAttemptId: String, + clientKey: String, + ) { + httpClient.post( + path = "v3/analytics/${checkoutAttemptId}", + queryParameters = mapOf("clientKey" to clientKey), + body = request, + requestSerializer = AnalyticsTrackRequest.SERIALIZER, + responseSerializer = EmptyResponse.SERIALIZER, + ) + } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt new file mode 100644 index 0000000000..cb0e543b6f --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt @@ -0,0 +1,85 @@ +/* + * 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 7/2/2024. + */ + +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.getBooleanOrNull +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 +import org.json.JSONObject + +@Parcelize +internal data class AnalyticsTrackInfo( + val timestamp: Long?, + val component: String?, + val type: String?, + val target: String?, + val isStoredPaymentMethod: Boolean?, + val brand: String?, + val issuer: String?, + val validationErrorCode: String?, + val validationErrorMessage: String?, +) : ModelObject() { + + companion object { + private const val TIMESTAMP = "timestamp" + private const val COMPONENT = "component" + private const val TYPE = "type" + private const val TARGET = "target" + private const val IS_STORED_PAYMENT_METHOD = "isStoredPaymentMethod" + private const val BRAND = "brand" + private const val ISSUER = "issuer" + private const val VALIDATION_ERROR_CODE = "validationErrorCode" + private const val VALIDATION_ERROR_MESSAGE = "validationErrorMessage" + + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: AnalyticsTrackInfo): JSONObject { + try { + return JSONObject().apply { + putOpt(TIMESTAMP, modelObject.timestamp) + putOpt(COMPONENT, modelObject.component) + putOpt(TYPE, modelObject.type) + putOpt(TARGET, modelObject.target) + putOpt(IS_STORED_PAYMENT_METHOD, modelObject.isStoredPaymentMethod) + putOpt(BRAND, modelObject.brand) + putOpt(ISSUER, modelObject.issuer) + putOpt(VALIDATION_ERROR_CODE, modelObject.validationErrorCode) + putOpt(VALIDATION_ERROR_MESSAGE, modelObject.validationErrorMessage) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackInfo::class.java, e) + } + } + + override fun deserialize(jsonObject: JSONObject): AnalyticsTrackInfo { + return try { + with(jsonObject) { + AnalyticsTrackInfo( + timestamp = getLongOrNull(TIMESTAMP), + component = getStringOrNull(COMPONENT), + type = getStringOrNull(TYPE), + target = getStringOrNull(TARGET), + isStoredPaymentMethod = getBooleanOrNull(IS_STORED_PAYMENT_METHOD), + brand = getStringOrNull(BRAND), + issuer = getStringOrNull(ISSUER), + validationErrorCode = getStringOrNull(VALIDATION_ERROR_CODE), + validationErrorMessage = getStringOrNull(VALIDATION_ERROR_MESSAGE), + ) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackInfo::class.java, e) + } + } + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt new file mode 100644 index 0000000000..6df631b24e --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt @@ -0,0 +1,72 @@ +/* + * 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 7/2/2024. + */ + +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.getLongOrNull +import com.adyen.checkout.core.internal.data.model.getStringOrNull +import kotlinx.parcelize.Parcelize +import org.json.JSONException +import org.json.JSONObject + +@Parcelize +internal data class AnalyticsTrackLog( + val timestamp: Long?, + val component: String?, + val type: String?, + val subType: String?, + val target: String?, + val message: String?, +) : ModelObject() { + + companion object { + private const val TIMESTAMP = "timestamp" + private const val COMPONENT = "component" + private const val TYPE = "type" + private const val SUBTYPE = "subType" + private const val TARGET = "target" + private const val MESSAGE = "message" + + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: AnalyticsTrackLog): JSONObject { + try { + return JSONObject().apply { + putOpt(TIMESTAMP, modelObject.timestamp) + putOpt(COMPONENT, modelObject.component) + putOpt(TYPE, modelObject.type) + putOpt(SUBTYPE, modelObject.subType) + putOpt(TARGET, modelObject.target) + putOpt(MESSAGE, modelObject.message) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackLog::class.java, e) + } + } + + override fun deserialize(jsonObject: JSONObject): AnalyticsTrackLog { + return try { + with(jsonObject) { + AnalyticsTrackLog( + timestamp = getLongOrNull(TIMESTAMP), + component = getStringOrNull(COMPONENT), + type = getStringOrNull(TYPE), + subType = getStringOrNull(SUBTYPE), + target = getStringOrNull(TARGET), + message = getStringOrNull(MESSAGE), + ) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackLog::class.java, e) + } + } + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt new file mode 100644 index 0000000000..70fc6c074a --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt @@ -0,0 +1,59 @@ +/* + * 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 ararat on 12/2/2024. + */ + +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.ModelUtils.deserializeOptList +import com.adyen.checkout.core.internal.data.model.getStringOrNull +import kotlinx.parcelize.Parcelize +import org.json.JSONException +import org.json.JSONObject + +@Parcelize +internal data class AnalyticsTrackRequest( + val channel: String?, + val info: List?, + val logs: List?, +) : ModelObject() { + companion object { + private const val CHANNEL = "channel" + private const val INFO = "info" + private const val LOGS = "logs" + + @JvmField + val SERIALIZER: Serializer = object : Serializer { + override fun serialize(modelObject: AnalyticsTrackRequest): JSONObject { + try { + return JSONObject().apply { + putOpt(CHANNEL, modelObject.channel) + putOpt(INFO, modelObject.info) + putOpt(LOGS, modelObject.logs) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackRequest::class.java, e) + } + } + + override fun deserialize(jsonObject: JSONObject): AnalyticsTrackRequest { + return try { + with(jsonObject) { + AnalyticsTrackRequest( + channel = getStringOrNull(CHANNEL), + info = deserializeOptList(getJSONArray(INFO), AnalyticsTrackInfo.SERIALIZER), + logs = deserializeOptList(getJSONArray(LOGS), AnalyticsTrackLog.SERIALIZER), + ) + } + } catch (e: JSONException) { + throw ModelSerializationException(AnalyticsTrackRequest::class.java, e) + } + } + } + } +} From 2c236ec28753797526c268ae86411fc9ff4a9339 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 13 Feb 2024 16:59:05 +0100 Subject: [PATCH 164/272] Map events and send them to backend COAND-844 --- .../core/internal/analytics/AdyenAnalytics.kt | 93 ++++++++++++++----- .../data/api/AnalyticsTrackRequestMapper.kt | 65 +++++++++++++ 2 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt index 481de04cbd..7d86ec4176 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt @@ -11,6 +11,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.data.api.AnalyticsTrackRequestMapper import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -26,10 +27,12 @@ import kotlinx.coroutines.sync.withLock import java.util.LinkedList @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AdyenAnalytics( +// TODO: Create factory +class AdyenAnalytics internal constructor( private val analyticsProvider: AnalyticsProvider, private val analyticsParams: AnalyticsParams, private val analyticsService: AnalyticsService, + private val analyticsTrackRequestMapper: AnalyticsTrackRequestMapper, coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, ) { @@ -38,43 +41,56 @@ class AdyenAnalytics( private val eventQueue: LinkedList = LinkedList() - @Volatile - var checkoutAttemptId: String? = null - private set + val checkoutAttemptId: String? get() = (state as? State.Ready)?.checkoutAttemptId @Volatile private var state: State = State.Uninitialized - val mutex = Mutex() + private val mutex = Mutex() fun setup() { + coroutineScope.launch { + setupInternal() + } + } + + private suspend fun setupInternal() { if (cannotSendEvent()) { - checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS + state = State.Ready(CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS) return } - if (state != State.Uninitialized) return + if (!state.canInitialize()) return + state = State.InProgress + adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } - coroutineScope.launch { - runSuspendCatching { - val analyticsSetupRequest = analyticsProvider.provide() - val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsParams.clientKey) - checkoutAttemptId = response.checkoutAttemptId - state = State.Ready + runSuspendCatching { + adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } + state = fetchCheckoutAttemptId()?.let { adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } - }.onFailure { e -> - state = State.Failed - adyenLog(AdyenLogLevel.ERROR) { - "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" - } + State.Ready(it) + } ?: run { + adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId from response is null" } + State.Failed + } + }.onFailure { e -> + adyenLog(AdyenLogLevel.ERROR) { + "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" } + state = State.Failed } } + private suspend fun fetchCheckoutAttemptId(): String? { + val analyticsSetupRequest = analyticsProvider.provide() + val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsParams.clientKey) + return response.checkoutAttemptId + } + + fun track(event: AnalyticsEvent) { - // TODO: Check if we can send events anyway, because attempt id is anonymous already if (cannotSendEvent()) return coroutineScope.launch { @@ -83,13 +99,32 @@ class AdyenAnalytics( track(event) } } - - // Queue the event - // Send it } // TODO: Discuss if we need to use mappers before we send events to backend - private fun sendEvents() { + private suspend fun sendEvents() { + if (state.canInitialize()) { + setupInternal() + } + + val checkoutAttemptId = checkoutAttemptId ?: run { + adyenLog(AdyenLogLevel.WARN) { "Not sending events because checkoutAttemptId is null" } + return + } + + // TODO: Send correct channel + val request = analyticsTrackRequestMapper("", eventQueue.takeLast(BATCH_SIZE)) + + runSuspendCatching { + analyticsService.trackEvents(request, checkoutAttemptId, analyticsParams.clientKey) + }.fold( + onSuccess = { + // TODO: Remove events from the queue + }, + onFailure = { + // TODO: Handle error + }, + ) } @@ -99,13 +134,23 @@ class AdyenAnalytics( companion object { private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + + private const val BATCH_SIZE = 20 } @VisibleForTesting internal sealed class State { data object Uninitialized : State() data object InProgress : State() - data object Ready : State() + data class Ready(val checkoutAttemptId: String) : State() data object Failed : State() + + fun canInitialize(): Boolean = when (this) { + InProgress, + is Ready -> false + + Failed, + Uninitialized -> true + } } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt new file mode 100644 index 0000000000..1f7d967044 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt @@ -0,0 +1,65 @@ +/* + * 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 13/2/2024. + */ + +package com.adyen.checkout.components.core.internal.data.api + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackInfo +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackLog +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest + +internal class AnalyticsTrackRequestMapper { + + operator fun invoke( + channel: String, + events: List + ): AnalyticsTrackRequest { + val infoList = mutableListOf() + val logList = mutableListOf() + events.forEach { analyticsEvent -> + when (analyticsEvent) { + is AnalyticsEvent.Info -> { + val info = mapInfo(analyticsEvent) + infoList.add(info) + } + + is AnalyticsEvent.Log -> { + val log = mapLog(analyticsEvent) + logList.add(log) + } + } + } + + return AnalyticsTrackRequest( + channel = channel, + info = infoList, + logs = logList, + ) + } + + private fun mapInfo(analyticsEvent: AnalyticsEvent.Info) = AnalyticsTrackInfo( + timestamp = analyticsEvent.timestamp, + component = analyticsEvent.component, + type = analyticsEvent.type?.value, + target = analyticsEvent.target, + isStoredPaymentMethod = analyticsEvent.isStoredPaymentMethod, + brand = analyticsEvent.brand, + issuer = analyticsEvent.issuer, + validationErrorCode = analyticsEvent.validationErrorCode, + validationErrorMessage = analyticsEvent.validationErrorMessage, + ) + + private fun mapLog(analyticsEvent: AnalyticsEvent.Log) = AnalyticsTrackLog( + timestamp = analyticsEvent.timestamp, + component = analyticsEvent.component, + type = analyticsEvent.type?.value, + subType = analyticsEvent.subType, + target = analyticsEvent.target, + message = analyticsEvent.message, + ) +} From 9b49194e9d366a985303c05e116b9bf846592c45 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 27 Feb 2024 15:05:11 +0100 Subject: [PATCH 165/272] Setup interfaces for analytics architecture COAND-844 --- .../analytics/AnalyticsLocalDataStore.kt | 18 ++++++++ .../internal/analytics/AnalyticsManager.kt | 30 +++++++++++++ .../internal/analytics/AnalyticsProvider.kt | 17 ++++---- .../analytics/AnalyticsRemoteDataStore.kt | 16 +++++++ .../internal/analytics/AnalyticsRepository.kt | 42 +++++++++++++++++++ 5 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt new file mode 100644 index 0000000000..c7dba13f96 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt @@ -0,0 +1,18 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +internal interface AnalyticsLocalDataStore { + + fun storeEvent() + + fun fetchEvents() + + fun removeEvents() +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt new file mode 100644 index 0000000000..9e9dd72a80 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -0,0 +1,30 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import kotlinx.coroutines.CoroutineScope + +class AnalyticsManager internal constructor() { + + private var checkoutAttemptId: String? = null + + private var isInitialized: Boolean = false + + fun initialize(coroutineScope: CoroutineScope) { + + } + + fun trackEvent(event: AnalyticsEvent) { + + } + + fun getCheckoutAttemptId(): String? = checkoutAttemptId + + fun clear() {} +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt index 70a7920480..b1e30b0bb7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt @@ -17,14 +17,17 @@ import com.adyen.checkout.components.core.internal.data.api.AnalyticsPlatform import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.ui.model.ComponentParams -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AnalyticsProvider( +internal interface AnalyticsProvider { + fun provide(): AnalyticsSetupRequest +} + +internal class DefaultAnalyticsProvider( val application: Application, val componentParams: ComponentParams, val source: AnalyticsSource, val sessionId: String?, -) { - internal fun provide(): AnalyticsSetupRequest { +) : AnalyticsProvider { + override fun provide(): AnalyticsSetupRequest { return AnalyticsSetupRequest( version = actualVersion, channel = ANDROID_CHANNEL, @@ -45,15 +48,13 @@ class AnalyticsProvider( ) } - @VisibleForTesting - internal fun getFlavorQueryParameter(isCreatedByDropIn: Boolean) = if (isCreatedByDropIn) { + private fun getFlavorQueryParameter(isCreatedByDropIn: Boolean) = if (isCreatedByDropIn) { DROP_IN } else { COMPONENTS } - @VisibleForTesting - internal fun getComponentQueryParameter(source: AnalyticsSource) = when (source) { + private fun getComponentQueryParameter(source: AnalyticsSource) = when (source) { is AnalyticsSource.DropIn -> DROP_IN is AnalyticsSource.PaymentComponent -> source.paymentMethodType } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt new file mode 100644 index 0000000000..2d3de8a19f --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt @@ -0,0 +1,16 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +internal interface AnalyticsRemoteDataStore { + + suspend fun fetchCheckoutAttemptId(): String + + suspend fun sendEvents() +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt new file mode 100644 index 0000000000..ccee1fc6ce --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt @@ -0,0 +1,42 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +internal interface AnalyticsRepository { + + suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String + + fun storeEvent(event: AnalyticsEvent) + + fun getEvents(): List + + fun sendEvents() +} + +internal class DefaultAnalyticsRepository( + private val localDataStore: AnalyticsLocalDataStore, + private val remoteDataStore: AnalyticsRemoteDataStore, +) : AnalyticsRepository { + + override suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String { + TODO("Not yet implemented") + } + + override fun storeEvent(event: AnalyticsEvent) { + TODO("Not yet implemented") + } + + override fun getEvents(): List { + TODO("Not yet implemented") + } + + override fun sendEvents() { + TODO("Not yet implemented") + } +} From c725ba20b42c759b4e4d280c6e0eb71eb3d96794 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 27 Feb 2024 15:14:11 +0100 Subject: [PATCH 166/272] Implement fetch checkoutAttemptId flow COAND-844 --- .../internal/analytics/AnalyticsManager.kt | 40 ++++++++++++++++++- .../internal/analytics/AnalyticsProvider.kt | 8 ++-- .../analytics/AnalyticsRemoteDataStore.kt | 20 +++++++++- .../internal/analytics/AnalyticsRepository.kt | 7 ++-- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 9e9dd72a80..b26cdd00ad 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -8,16 +8,50 @@ package com.adyen.checkout.components.core.internal.analytics +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runSuspendCatching +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch -class AnalyticsManager internal constructor() { +class AnalyticsManager internal constructor( + private val analyticsRepository: AnalyticsRepository, + private val analyticsProvider: AnalyticsProvider, + private val analyticsParams: AnalyticsParams, + private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, +) { private var checkoutAttemptId: String? = null private var isInitialized: Boolean = false + // TODO: Check if we need to retry in case the request failed fun initialize(coroutineScope: CoroutineScope) { + if (isInitialized) return + isInitialized = true + + if (cannotSendEvent()) { + checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS + return + } + + coroutineScope.launch(coroutineDispatcher) { + runSuspendCatching { + analyticsRepository.fetchCheckoutAttemptId(analyticsProvider) + }.fold( + onSuccess = { checkoutAttemptId = it }, + onFailure = { adyenLog(AdyenLogLevel.WARN, it) { "Failed to fetch checkoutAttemptId." } }, + ) + } + } + + private fun cannotSendEvent(): Boolean { + return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority } fun trackEvent(event: AnalyticsEvent) { @@ -27,4 +61,8 @@ class AnalyticsManager internal constructor() { fun getCheckoutAttemptId(): String? = checkoutAttemptId fun clear() {} + + companion object { + private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt index b1e30b0bb7..9f7899b377 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt @@ -22,10 +22,10 @@ internal interface AnalyticsProvider { } internal class DefaultAnalyticsProvider( - val application: Application, - val componentParams: ComponentParams, - val source: AnalyticsSource, - val sessionId: String?, + private val application: Application, + private val componentParams: ComponentParams, + private val source: AnalyticsSource, + private val sessionId: String?, ) : AnalyticsProvider { override fun provide(): AnalyticsSetupRequest { return AnalyticsSetupRequest( diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt index 2d3de8a19f..20d8317025 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt @@ -8,9 +8,27 @@ package com.adyen.checkout.components.core.internal.analytics +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse + internal interface AnalyticsRemoteDataStore { - suspend fun fetchCheckoutAttemptId(): String + suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse suspend fun sendEvents() } + +internal class DefaultAnalyticsRemoteDataStore( + private val analyticsService: AnalyticsService, + private val clientKey: String, +) : AnalyticsRemoteDataStore { + + override suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse { + return analyticsService.setupAnalytics(request, clientKey) + } + + override suspend fun sendEvents() { + TODO("Not yet implemented") + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt index ccee1fc6ce..3c043fcd78 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics internal interface AnalyticsRepository { - suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String + suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String? fun storeEvent(event: AnalyticsEvent) @@ -24,8 +24,9 @@ internal class DefaultAnalyticsRepository( private val remoteDataStore: AnalyticsRemoteDataStore, ) : AnalyticsRepository { - override suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String { - TODO("Not yet implemented") + override suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String? { + val request = analyticsProvider.provide() + return remoteDataStore.fetchCheckoutAttemptId(request).checkoutAttemptId } override fun storeEvent(event: AnalyticsEvent) { From 18358c28a1790605251769b87a421dd930e7beab Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 27 Feb 2024 15:59:53 +0100 Subject: [PATCH 167/272] Implement send event flow COAND-844 --- .../internal/analytics/AnalyticsManager.kt | 8 ++-- .../analytics/AnalyticsPlatformParams.kt | 41 +++++++++++++++++++ .../analytics/AnalyticsRemoteDataStore.kt | 10 +++-- .../internal/analytics/AnalyticsRepository.kt | 19 ++++++--- ...sProvider.kt => AnalyticsSetupProvider.kt} | 38 ++++------------- .../internal/data/api/AnalyticsService.kt | 2 +- ...er.kt => AnalyticsTrackRequestProvider.kt} | 7 ++-- .../data/model/AnalyticsTrackRequest.kt | 4 ++ 8 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsProvider.kt => AnalyticsSetupProvider.kt} (61%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/{AnalyticsTrackRequestMapper.kt => AnalyticsTrackRequestProvider.kt} (90%) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index b26cdd00ad..c35c08d687 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.launch class AnalyticsManager internal constructor( private val analyticsRepository: AnalyticsRepository, - private val analyticsProvider: AnalyticsProvider, + private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { @@ -42,7 +42,7 @@ class AnalyticsManager internal constructor( coroutineScope.launch(coroutineDispatcher) { runSuspendCatching { - analyticsRepository.fetchCheckoutAttemptId(analyticsProvider) + analyticsRepository.fetchCheckoutAttemptId(analyticsSetupProvider) }.fold( onSuccess = { checkoutAttemptId = it }, onFailure = { adyenLog(AdyenLogLevel.WARN, it) { "Failed to fetch checkoutAttemptId." } }, @@ -60,7 +60,9 @@ class AnalyticsManager internal constructor( fun getCheckoutAttemptId(): String? = checkoutAttemptId - fun clear() {} + fun clear() { + checkoutAttemptId = null + } companion object { private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt new file mode 100644 index 0000000000..51e3a0fdd1 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -0,0 +1,41 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.BuildConfig +import com.adyen.checkout.components.core.internal.data.api.AnalyticsPlatform + +object AnalyticsPlatformParams { + + @Suppress("ConstPropertyName") + const val channel = "android" + + var platform = AnalyticsPlatform.ANDROID.value + private set + + var version = BuildConfig.CHECKOUT_VERSION + private set + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + fun overrideForCrossPlatform( + platform: AnalyticsPlatform, + version: String, + ) { + this.platform = platform.value + this.version = version + } + + @VisibleForTesting + internal fun resetToDefaults() { + platform = AnalyticsPlatform.ANDROID.value + version = BuildConfig.CHECKOUT_VERSION + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt index 20d8317025..d9fc8dcaa2 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt @@ -11,12 +11,13 @@ package com.adyen.checkout.components.core.internal.analytics import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest internal interface AnalyticsRemoteDataStore { suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse - suspend fun sendEvents() + suspend fun sendEvents(request: AnalyticsTrackRequest, checkoutAttemptId: String) } internal class DefaultAnalyticsRemoteDataStore( @@ -28,7 +29,10 @@ internal class DefaultAnalyticsRemoteDataStore( return analyticsService.setupAnalytics(request, clientKey) } - override suspend fun sendEvents() { - TODO("Not yet implemented") + override suspend fun sendEvents( + request: AnalyticsTrackRequest, + checkoutAttemptId: String, + ) { + analyticsService.sendEvents(request, checkoutAttemptId, clientKey) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt index 3c043fcd78..d33cd8f17f 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt @@ -8,24 +8,27 @@ package com.adyen.checkout.components.core.internal.analytics +import com.adyen.checkout.components.core.internal.data.api.AnalyticsTrackRequestProvider + internal interface AnalyticsRepository { - suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String? + suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? fun storeEvent(event: AnalyticsEvent) fun getEvents(): List - fun sendEvents() + suspend fun sendEvents(events: List, checkoutAttemptId: String) } internal class DefaultAnalyticsRepository( private val localDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, + private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, ) : AnalyticsRepository { - override suspend fun fetchCheckoutAttemptId(analyticsProvider: AnalyticsProvider): String? { - val request = analyticsProvider.provide() + override suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? { + val request = analyticsSetupProvider.provide() return remoteDataStore.fetchCheckoutAttemptId(request).checkoutAttemptId } @@ -37,7 +40,11 @@ internal class DefaultAnalyticsRepository( TODO("Not yet implemented") } - override fun sendEvents() { - TODO("Not yet implemented") + override suspend fun sendEvents( + events: List, + checkoutAttemptId: String, + ) { + val request = analyticsTrackRequestProvider(events) + remoteDataStore.sendEvents(request, checkoutAttemptId) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt similarity index 61% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt index 9f7899b377..75baefdef3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt @@ -10,28 +10,25 @@ package com.adyen.checkout.components.core.internal.analytics import android.app.Application import android.os.Build -import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.BuildConfig -import com.adyen.checkout.components.core.internal.data.api.AnalyticsPlatform import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.ui.model.ComponentParams -internal interface AnalyticsProvider { +internal interface AnalyticsSetupProvider { fun provide(): AnalyticsSetupRequest } -internal class DefaultAnalyticsProvider( +internal class DefaultAnalyticsSetupProvider( private val application: Application, private val componentParams: ComponentParams, private val source: AnalyticsSource, private val sessionId: String?, -) : AnalyticsProvider { +) : AnalyticsSetupProvider { + override fun provide(): AnalyticsSetupRequest { return AnalyticsSetupRequest( - version = actualVersion, - channel = ANDROID_CHANNEL, - platform = actualPlatform, + version = AnalyticsPlatformParams.version, + channel = AnalyticsPlatformParams.channel, + platform = AnalyticsPlatformParams.platform, locale = componentParams.shopperLocale.toString(), component = getComponentQueryParameter(source), flavor = getFlavorQueryParameter(componentParams.isCreatedByDropIn), @@ -62,26 +59,5 @@ internal class DefaultAnalyticsProvider( companion object { private const val DROP_IN = "dropin" private const val COMPONENTS = "components" - private const val ANDROID_CHANNEL = "android" - - // these params are prefixed with actual because cross platform SDKs will override them so they are not - // technically constants - private var actualPlatform = AnalyticsPlatform.ANDROID.value - private var actualVersion = BuildConfig.CHECKOUT_VERSION - - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - fun overrideForCrossPlatform( - platform: AnalyticsPlatform, - version: String, - ) { - this.actualPlatform = platform.value - this.actualVersion = version - } - - @VisibleForTesting - internal fun resetToDefaults() { - actualPlatform = AnalyticsPlatform.ANDROID.value - actualVersion = BuildConfig.CHECKOUT_VERSION - } } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt index 3d9a6e2663..6abbcf5ef5 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt @@ -39,7 +39,7 @@ class AnalyticsService( } // TODO: Add tests - internal suspend fun trackEvents( + internal suspend fun sendEvents( request: AnalyticsTrackRequest, checkoutAttemptId: String, clientKey: String, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt similarity index 90% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt index 1f7d967044..40347d90cd 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestMapper.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt @@ -9,14 +9,14 @@ package com.adyen.checkout.components.core.internal.data.api import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatformParams import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackInfo import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackLog import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest -internal class AnalyticsTrackRequestMapper { +internal class AnalyticsTrackRequestProvider { operator fun invoke( - channel: String, events: List ): AnalyticsTrackRequest { val infoList = mutableListOf() @@ -36,7 +36,8 @@ internal class AnalyticsTrackRequestMapper { } return AnalyticsTrackRequest( - channel = channel, + channel = AnalyticsPlatformParams.channel, + platform = AnalyticsPlatformParams.platform, info = infoList, logs = logList, ) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt index 70fc6c074a..84e843d6bc 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt @@ -19,11 +19,13 @@ import org.json.JSONObject @Parcelize internal data class AnalyticsTrackRequest( val channel: String?, + val platform: String?, val info: List?, val logs: List?, ) : ModelObject() { companion object { private const val CHANNEL = "channel" + private const val PLATFORM = "platform" private const val INFO = "info" private const val LOGS = "logs" @@ -33,6 +35,7 @@ internal data class AnalyticsTrackRequest( try { return JSONObject().apply { putOpt(CHANNEL, modelObject.channel) + putOpt(PLATFORM, modelObject.platform) putOpt(INFO, modelObject.info) putOpt(LOGS, modelObject.logs) } @@ -46,6 +49,7 @@ internal data class AnalyticsTrackRequest( with(jsonObject) { AnalyticsTrackRequest( channel = getStringOrNull(CHANNEL), + platform = getStringOrNull(PLATFORM), info = deserializeOptList(getJSONArray(INFO), AnalyticsTrackInfo.SERIALIZER), logs = deserializeOptList(getJSONArray(LOGS), AnalyticsTrackLog.SERIALIZER), ) From 77716eeb953ced5d72273eec33d0c7aca6aca880 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 28 Feb 2024 15:47:38 +0100 Subject: [PATCH 168/272] Add local data store logic COAND-844 --- .../analytics/AnalyticsLocalDataStore.kt | 18 ----- .../internal/analytics/AnalyticsManager.kt | 3 +- .../analytics/data/AnalyticsLocalDataStore.kt | 18 +++++ .../data/AnalyticsRemoteDataStore.kt | 23 +++++++ .../analytics/data/AnalyticsRepository.kt | 21 ++++++ .../DefaultAnalyticsRemoteDataStore.kt} | 13 ++-- .../DefaultAnalyticsRepository.kt} | 37 +++++------ .../data/InfoAnalyticsLocalDataStore.kt | 27 ++++++++ .../data/LogAnalyticsLocalDataStore.kt | 27 ++++++++ .../data/api/AnalyticsTrackRequestProvider.kt | 65 ++++++++----------- 10 files changed, 165 insertions(+), 87 deletions(-) delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsRemoteDataStore.kt => data/DefaultAnalyticsRemoteDataStore.kt} (75%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsRepository.kt => data/DefaultAnalyticsRepository.kt} (50%) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt deleted file mode 100644 index c7dba13f96..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsLocalDataStore.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 27/2/2024. - */ - -package com.adyen.checkout.components.core.internal.analytics - -internal interface AnalyticsLocalDataStore { - - fun storeEvent() - - fun fetchEvents() - - fun removeEvents() -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index c35c08d687..753c660592 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -8,6 +8,7 @@ package com.adyen.checkout.components.core.internal.analytics +import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -55,7 +56,7 @@ class AnalyticsManager internal constructor( } fun trackEvent(event: AnalyticsEvent) { - + analyticsRepository.storeEvent(event) } fun getCheckoutAttemptId(): String? = checkoutAttemptId diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt new file mode 100644 index 0000000000..f9a70a0ce6 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt @@ -0,0 +1,18 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data + +internal interface AnalyticsLocalDataStore { + + fun storeEvent(event: T) + + fun fetchEvents(size: Int): List + + fun clear() +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt new file mode 100644 index 0000000000..9dab4deef1 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt @@ -0,0 +1,23 @@ +/* + * 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 ararat on 28/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data + +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest + +internal interface AnalyticsRemoteDataStore { + + val infoSize: Int + val logSize: Int + + suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse + + suspend fun sendEvents(request: AnalyticsTrackRequest, checkoutAttemptId: String) +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt new file mode 100644 index 0000000000..7b2095e161 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -0,0 +1,21 @@ +/* + * 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 ararat on 28/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSetupProvider + +internal interface AnalyticsRepository { + + suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? + + fun storeEvent(event: AnalyticsEvent) + + suspend fun sendEvents(checkoutAttemptId: String) +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt similarity index 75% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt index d9fc8dcaa2..0cab8f8bf5 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt @@ -3,26 +3,21 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 27/2/2024. + * Created by ararat on 28/2/2024. */ -package com.adyen.checkout.components.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest -internal interface AnalyticsRemoteDataStore { - - suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse - - suspend fun sendEvents(request: AnalyticsTrackRequest, checkoutAttemptId: String) -} - internal class DefaultAnalyticsRemoteDataStore( private val analyticsService: AnalyticsService, private val clientKey: String, + override val infoSize: Int, + override val logSize: Int, ) : AnalyticsRemoteDataStore { override suspend fun fetchCheckoutAttemptId(request: AnalyticsSetupRequest): AnalyticsSetupResponse { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt similarity index 50% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index d33cd8f17f..d16044b2b8 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -3,26 +3,18 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 27/2/2024. + * Created by ararat on 28/2/2024. */ -package com.adyen.checkout.components.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics.data +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.data.api.AnalyticsTrackRequestProvider -internal interface AnalyticsRepository { - - suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? - - fun storeEvent(event: AnalyticsEvent) - - fun getEvents(): List - - suspend fun sendEvents(events: List, checkoutAttemptId: String) -} - internal class DefaultAnalyticsRepository( - private val localDataStore: AnalyticsLocalDataStore, + private val localInfoDataStore: AnalyticsLocalDataStore, + private val localLogDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, ) : AnalyticsRepository { @@ -33,18 +25,21 @@ internal class DefaultAnalyticsRepository( } override fun storeEvent(event: AnalyticsEvent) { - TODO("Not yet implemented") - } - - override fun getEvents(): List { - TODO("Not yet implemented") + when (event) { + is AnalyticsEvent.Info -> localInfoDataStore.storeEvent(event) + is AnalyticsEvent.Log -> localLogDataStore.storeEvent(event) + } } override suspend fun sendEvents( - events: List, checkoutAttemptId: String, ) { - val request = analyticsTrackRequestProvider(events) + val infoEvents = localInfoDataStore.fetchEvents(remoteDataStore.infoSize) + val logEvents = localLogDataStore.fetchEvents(remoteDataStore.logSize) + val request = analyticsTrackRequestProvider( + infoList = infoEvents, + logList = logEvents, + ) remoteDataStore.sendEvents(request, checkoutAttemptId) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt new file mode 100644 index 0000000000..9178a5e703 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt @@ -0,0 +1,27 @@ +/* + * 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 ararat on 28/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import java.util.LinkedList + +internal class InfoAnalyticsLocalDataStore : AnalyticsLocalDataStore { + + private val list = LinkedList() + + override fun storeEvent(event: AnalyticsEvent.Info) { + list.add(event) + } + + override fun fetchEvents(size: Int) = list.takeLast(size) + + override fun clear() { + list.clear() + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt new file mode 100644 index 0000000000..0474088282 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt @@ -0,0 +1,27 @@ +/* + * 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 ararat on 28/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import java.util.LinkedList + +internal class LogAnalyticsLocalDataStore : AnalyticsLocalDataStore { + + private val list = LinkedList() + + override fun storeEvent(event: AnalyticsEvent.Log) { + list.add(event) + } + + override fun fetchEvents(size: Int) = list.takeLast(size) + + override fun clear() { + list.clear() + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt index 40347d90cd..3b0f7df9db 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt @@ -17,50 +17,39 @@ import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequ internal class AnalyticsTrackRequestProvider { operator fun invoke( - events: List + infoList: List, + logList: List, ): AnalyticsTrackRequest { - val infoList = mutableListOf() - val logList = mutableListOf() - events.forEach { analyticsEvent -> - when (analyticsEvent) { - is AnalyticsEvent.Info -> { - val info = mapInfo(analyticsEvent) - infoList.add(info) - } - - is AnalyticsEvent.Log -> { - val log = mapLog(analyticsEvent) - logList.add(log) - } - } - } - return AnalyticsTrackRequest( channel = AnalyticsPlatformParams.channel, platform = AnalyticsPlatformParams.platform, - info = infoList, - logs = logList, + info = infoList.mapToTrackEvent(), + logs = logList.mapToTrackEvent(), ) } - private fun mapInfo(analyticsEvent: AnalyticsEvent.Info) = AnalyticsTrackInfo( - timestamp = analyticsEvent.timestamp, - component = analyticsEvent.component, - type = analyticsEvent.type?.value, - target = analyticsEvent.target, - isStoredPaymentMethod = analyticsEvent.isStoredPaymentMethod, - brand = analyticsEvent.brand, - issuer = analyticsEvent.issuer, - validationErrorCode = analyticsEvent.validationErrorCode, - validationErrorMessage = analyticsEvent.validationErrorMessage, - ) + private fun List.mapToTrackEvent() = map { event -> + AnalyticsTrackInfo( + timestamp = event.timestamp, + component = event.component, + type = event.type?.value, + target = event.target, + isStoredPaymentMethod = event.isStoredPaymentMethod, + brand = event.brand, + issuer = event.issuer, + validationErrorCode = event.validationErrorCode, + validationErrorMessage = event.validationErrorMessage, + ) + } - private fun mapLog(analyticsEvent: AnalyticsEvent.Log) = AnalyticsTrackLog( - timestamp = analyticsEvent.timestamp, - component = analyticsEvent.component, - type = analyticsEvent.type?.value, - subType = analyticsEvent.subType, - target = analyticsEvent.target, - message = analyticsEvent.message, - ) + private fun List.mapToTrackEvent() = map { event -> + AnalyticsTrackLog( + timestamp = event.timestamp, + component = event.component, + type = event.type?.value, + subType = event.subType, + target = event.target, + message = event.message, + ) + } } From d7c56a35cc6f2d7674df819e13f6f937674572e1 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 29 Feb 2024 09:15:11 +0100 Subject: [PATCH 169/272] Make reading and writing in data stores synchronized COAND-844 --- .../internal/analytics/AnalyticsManager.kt | 4 +++- .../analytics/data/AnalyticsLocalDataStore.kt | 8 ++++---- .../analytics/data/AnalyticsRepository.kt | 2 +- .../data/DefaultAnalyticsRepository.kt | 2 +- .../data/InfoAnalyticsLocalDataStore.kt | 20 ++++++++++++++----- .../data/LogAnalyticsLocalDataStore.kt | 20 ++++++++++++++----- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 753c660592..1705df3ab1 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -55,7 +55,9 @@ class AnalyticsManager internal constructor( return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority } - fun trackEvent(event: AnalyticsEvent) { + // TODO: Work on coroutineScope. Perhaps implement a new one in this class. + // TODO: This function should not be suspend, when coroutineScope is implemented. + suspend fun trackEvent(event: AnalyticsEvent) { analyticsRepository.storeEvent(event) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt index f9a70a0ce6..f858a822b6 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt @@ -3,16 +3,16 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 27/2/2024. + * Created by ararat on 28/2/2024. */ package com.adyen.checkout.components.core.internal.analytics.data internal interface AnalyticsLocalDataStore { - fun storeEvent(event: T) + suspend fun storeEvent(event: T) - fun fetchEvents(size: Int): List + suspend fun fetchEvents(size: Int): List - fun clear() + suspend fun clear() } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt index 7b2095e161..7d57e43761 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -15,7 +15,7 @@ internal interface AnalyticsRepository { suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? - fun storeEvent(event: AnalyticsEvent) + suspend fun storeEvent(event: AnalyticsEvent) suspend fun sendEvents(checkoutAttemptId: String) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index d16044b2b8..c1d8bdd1eb 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -24,7 +24,7 @@ internal class DefaultAnalyticsRepository( return remoteDataStore.fetchCheckoutAttemptId(request).checkoutAttemptId } - override fun storeEvent(event: AnalyticsEvent) { + override suspend fun storeEvent(event: AnalyticsEvent) { when (event) { is AnalyticsEvent.Info -> localInfoDataStore.storeEvent(event) is AnalyticsEvent.Log -> localLogDataStore.storeEvent(event) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt index 9178a5e703..b2251a237f 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt @@ -9,19 +9,29 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import java.util.LinkedList internal class InfoAnalyticsLocalDataStore : AnalyticsLocalDataStore { private val list = LinkedList() - override fun storeEvent(event: AnalyticsEvent.Info) { - list.add(event) + private val mutex = Mutex() + + override suspend fun storeEvent(event: AnalyticsEvent.Info) { + mutex.withLock { + list.add(event) + } } - override fun fetchEvents(size: Int) = list.takeLast(size) + override suspend fun fetchEvents(size: Int) = mutex.withLock { + list.takeLast(size) + } - override fun clear() { - list.clear() + override suspend fun clear() { + mutex.withLock { + list.clear() + } } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt index 0474088282..97c1c6b228 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt @@ -9,19 +9,29 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import java.util.LinkedList internal class LogAnalyticsLocalDataStore : AnalyticsLocalDataStore { private val list = LinkedList() - override fun storeEvent(event: AnalyticsEvent.Log) { - list.add(event) + private val mutex = Mutex() + + override suspend fun storeEvent(event: AnalyticsEvent.Log) { + mutex.withLock { + list.add(event) + } } - override fun fetchEvents(size: Int) = list.takeLast(size) + override suspend fun fetchEvents(size: Int) = mutex.withLock { + list.takeLast(size) + } - override fun clear() { - list.clear() + override suspend fun clear() { + mutex.withLock { + list.clear() + } } } From ee1c2c89de08511c9fa458391ed1e0e0bdd6c8e3 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 1 Mar 2024 17:01:27 +0100 Subject: [PATCH 170/272] Implement timer and send logic COAND-844 --- .../internal/analytics/AnalyticsEvents.kt | 3 + .../internal/analytics/AnalyticsManager.kt | 68 ++++++++++++++++--- .../data/DefaultAnalyticsRepository.kt | 2 + 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt index 1ed585d0cc..c3fb714b75 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt @@ -15,10 +15,12 @@ import java.util.Date sealed interface AnalyticsEvent { val timestamp: Long + val shouldForceSend: Boolean val component: String data class Info @AnalyticsEventApi constructor( override val timestamp: Long = Date().time, + override val shouldForceSend: Boolean = false, override val component: String, val type: Type? = null, val target: String? = null, @@ -42,6 +44,7 @@ sealed interface AnalyticsEvent { data class Log @AnalyticsEventApi constructor( override val timestamp: Long = Date().time, + override val shouldForceSend: Boolean = true, override val component: String, val type: Type? = null, val subType: String? = null, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 1705df3ab1..3569743dc7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -17,7 +17,11 @@ import com.adyen.checkout.core.internal.util.runSuspendCatching import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlin.time.Duration.Companion.seconds class AnalyticsManager internal constructor( private val analyticsRepository: AnalyticsRepository, @@ -30,13 +34,19 @@ class AnalyticsManager internal constructor( private var isInitialized: Boolean = false + private var _coroutineScope: CoroutineScope? = null + private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + + private var timerJob: Job? = null + // TODO: Check if we need to retry in case the request failed fun initialize(coroutineScope: CoroutineScope) { if (isInitialized) return - isInitialized = true - if (cannotSendEvent()) { + _coroutineScope = coroutineScope + + if (cannotSendEvents()) { checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS return } @@ -45,29 +55,69 @@ class AnalyticsManager internal constructor( runSuspendCatching { analyticsRepository.fetchCheckoutAttemptId(analyticsSetupProvider) }.fold( - onSuccess = { checkoutAttemptId = it }, + onSuccess = { attemptId -> + checkoutAttemptId = attemptId?.also { startTimer() } + }, onFailure = { adyenLog(AdyenLogLevel.WARN, it) { "Failed to fetch checkoutAttemptId." } }, ) } } - private fun cannotSendEvent(): Boolean { - return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority + fun trackEvent(event: AnalyticsEvent) { + if (cannotSendEvents()) return + coroutineScope.launch { + // TODO: Handle exceptions + analyticsRepository.storeEvent(event) + + if (event.shouldForceSend) { + stopTimer() + sendEvents() + startTimer() + } + } + } + + private fun startTimer() { + stopTimer() + timerJob = coroutineScope.launch { + while (isActive) { + delay(DISPATCH_INTERVAL_MILLIS) + sendEvents() + } + } } - // TODO: Work on coroutineScope. Perhaps implement a new one in this class. - // TODO: This function should not be suspend, when coroutineScope is implemented. - suspend fun trackEvent(event: AnalyticsEvent) { - analyticsRepository.storeEvent(event) + private fun stopTimer() { + timerJob?.cancel() + } + + private suspend fun sendEvents() { + if (cannotSendEvents()) return + + val checkoutAttemptId = checkoutAttemptId + if (checkoutAttemptId == null) { + adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId should not be null at this point." } + return + } + + // TODO: Handle exceptions + analyticsRepository.sendEvents(checkoutAttemptId) } fun getCheckoutAttemptId(): String? = checkoutAttemptId + private fun cannotSendEvents(): Boolean { + return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority + } + fun clear() { + _coroutineScope = null checkoutAttemptId = null + stopTimer() } companion object { private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + private val DISPATCH_INTERVAL_MILLIS = 10.seconds.inWholeMilliseconds } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index c1d8bdd1eb..f31ac7fbd4 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -41,5 +41,7 @@ internal class DefaultAnalyticsRepository( logList = logEvents, ) remoteDataStore.sendEvents(request, checkoutAttemptId) + localInfoDataStore.clear() + localLogDataStore.clear() } } From a9fa4f5016ddab78b0cefa1bf0b138cb009cf2e4 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 4 Mar 2024 11:32:39 +0100 Subject: [PATCH 171/272] Exception handling logic for AnalyticsManager COAND-844 --- .../internal/analytics/AnalyticsManager.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 3569743dc7..57f7109817 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -65,21 +65,25 @@ class AnalyticsManager internal constructor( fun trackEvent(event: AnalyticsEvent) { if (cannotSendEvents()) return - coroutineScope.launch { - // TODO: Handle exceptions - analyticsRepository.storeEvent(event) + coroutineScope.launch(coroutineDispatcher) { + runSuspendCatching { + analyticsRepository.storeEvent(event) - if (event.shouldForceSend) { - stopTimer() - sendEvents() - startTimer() - } + if (event.shouldForceSend) { + stopTimer() + sendEvents() + startTimer() + } + }.fold( + onSuccess = { /* Not necessary */ }, + onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Storing event failed" } }, + ) } } private fun startTimer() { stopTimer() - timerJob = coroutineScope.launch { + timerJob = coroutineScope.launch(coroutineDispatcher) { while (isActive) { delay(DISPATCH_INTERVAL_MILLIS) sendEvents() @@ -100,8 +104,12 @@ class AnalyticsManager internal constructor( return } - // TODO: Handle exceptions - analyticsRepository.sendEvents(checkoutAttemptId) + runSuspendCatching { + analyticsRepository.sendEvents(checkoutAttemptId) + }.fold( + onSuccess = { adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } }, + onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, + ) } fun getCheckoutAttemptId(): String? = checkoutAttemptId From 68daa1c7c9a3aecaad46eed3674ef72a49d2a856 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 4 Mar 2024 11:57:26 +0100 Subject: [PATCH 172/272] Rename and rearrange files COAND-844 --- .../ACHDirectDebitComponentProvider.kt | 24 +-- .../ui/DefaultACHDirectDebitDelegate.kt | 4 +- .../ui/StoredACHDirectDebitDelegate.kt | 4 +- .../ui/DefaultACHDirectDebitDelegateTest.kt | 6 +- .../ui/StoredACHDirectDebitDelegateTest.kt | 6 +- .../BacsDirectDebitComponentProvider.kt | 16 +- .../ui/DefaultBacsDirectDebitDelegate.kt | 4 +- .../DefaultBacsDirectDebitDelegateTest.kt | 4 +- .../provider/BcmcComponentProvider.kt | 16 +- .../provider/BlikComponentProvider.kt | 20 +-- .../blik/internal/ui/DefaultBlikDelegate.kt | 4 +- .../blik/internal/ui/StoredBlikDelegate.kt | 4 +- .../internal/ui/DefaultBlikDelegateTest.kt | 4 +- .../provider/BoletoComponentProvider.kt | 16 +- .../internal/ui/DefaultBoletoDelegate.kt | 4 +- .../internal/ui/DefaultBoletoDelegateTest.kt | 6 +- .../provider/CardComponentProvider.kt | 20 +-- .../card/internal/ui/DefaultCardDelegate.kt | 4 +- .../card/internal/ui/StoredCardDelegate.kt | 4 +- .../internal/ui/DefaultCardDelegateTest.kt | 6 +- .../internal/ui/StoredCardDelegateTest.kt | 6 +- .../provider/CashAppPayComponentProvider.kt | 20 +-- .../internal/ui/DefaultCashAppPayDelegate.kt | 4 +- .../internal/ui/StoredCashAppPayDelegate.kt | 4 +- .../ui/DefaultCashAppPayDelegateTest.kt | 4 +- .../ui/StoredCashAppPayDelegateTest.kt | 4 +- .../core/internal/analytics/AdyenAnalytics.kt | 156 ------------------ .../internal/analytics/AnalyticsEventApi.kt | 1 + .../internal/analytics/AnalyticsManager.kt | 1 + .../api => analytics}/AnalyticsPlatform.kt | 4 +- .../analytics/AnalyticsPlatformParams.kt | 1 - .../analytics/data/AnalyticsRepository.kt | 2 +- .../data/DefaultAnalyticsRepository.kt | 6 +- .../{ => local}/AnalyticsLocalDataStore.kt | 2 +- .../InfoAnalyticsLocalDataStore.kt | 4 +- .../{ => local}/LogAnalyticsLocalDataStore.kt | 4 +- .../data/old}/AnalyticsMapper.kt | 7 +- .../data/old}/AnalyticsRepositoryData.kt | 7 +- .../old/DefaultOldAnalyticsRepository.kt} | 14 +- .../{ => remote}/AnalyticsRemoteDataStore.kt | 2 +- .../data/remote}/AnalyticsService.kt | 6 +- .../data/remote/AnalyticsSetupProvider.kt | 15 ++ .../remote}/AnalyticsTrackRequestProvider.kt | 4 +- .../DefaultAnalyticsRemoteDataStore.kt | 5 +- .../remote/DefaultAnalyticsSetupProvider.kt} | 12 +- ...epository.kt => OldAnalyticsRepository.kt} | 3 +- ...t => DefaultOldAnalyticsRepositoryTest.kt} | 20 ++- .../ConvenienceStoresJPComponentProvider.kt | 4 +- .../provider/DotpayComponentProvider.kt | 4 +- .../internal/provider/ComponentProvider.kt | 6 +- .../dropin/internal/ui/DropInViewModel.kt | 4 +- .../internal/ui/DropInViewModelFactory.kt | 10 +- .../provider/EContextComponentProvider.kt | 16 +- .../internal/ui/DefaultEContextDelegate.kt | 4 +- .../ui/DefaultEContextDelegateTest.kt | 4 +- .../provider/EntercashComponentProvider.kt | 4 +- .../internal/provider/EPSComponentProvider.kt | 4 +- .../provider/GiftCardComponentProvider.kt | 16 +- .../internal/ui/DefaultGiftCardDelegate.kt | 4 +- .../ui/DefaultGiftCardDelegateTest.kt | 4 +- .../provider/GooglePayComponentProvider.kt | 16 +- .../internal/ui/DefaultGooglePayDelegate.kt | 4 +- .../ui/DefaultGooglePayDelegateTest.kt | 4 +- .../provider/IdealComponentProvider.kt | 4 +- .../InstantPaymentComponentProvider.kt | 16 +- .../ui/DefaultInstantPaymentDelegate.kt | 4 +- .../ui/DefaultInstantPaymentDelegateTest.kt | 4 +- .../provider/IssuerListComponentProvider.kt | 18 +- .../internal/ui/DefaultIssuerListDelegate.kt | 4 +- .../ui/DefaultIssuerListDelegateTest.kt | 4 +- .../provider/MBWayComponentProvider.kt | 7 +- .../mbway/internal/ui/DefaultMBWayDelegate.kt | 1 - .../internal/ui/DefaultMBWayDelegateTest.kt | 4 +- .../provider/MolpayComponentProvider.kt | 4 +- .../OnlineBankingComponentProvider.kt | 16 +- .../ui/DefaultOnlineBankingDelegate.kt | 4 +- .../ui/DefaultOnlineBankingDelegateTest.kt | 4 +- .../OnlineBankingCZComponentProvider.kt | 4 +- .../OnlineBankingJPComponentProvider.kt | 4 +- .../OnlineBankingPLComponentProvider.kt | 4 +- .../OnlineBankingSKComponentProvider.kt | 4 +- .../provider/OpenBankingComponentProvider.kt | 4 +- .../provider/PayByBankComponentProvider.kt | 16 +- .../internal/ui/DefaultPayByBankDelegate.kt | 4 +- .../ui/DefaultPayByBankDelegateTest.kt | 4 +- .../provider/PayEasyComponentProvider.kt | 4 +- .../provider/SepaComponentProvider.kt | 16 +- .../sepa/internal/ui/DefaultSepaDelegate.kt | 4 +- .../internal/ui/DefaultSepaDelegateTest.kt | 4 +- .../provider/SevenElevenComponentProvider.kt | 4 +- .../internal/provider/UPIComponentProvider.kt | 16 +- .../upi/internal/ui/DefaultUPIDelegate.kt | 4 +- .../upi/internal/ui/DefaultUPIDelegateTest.kt | 4 +- 93 files changed, 332 insertions(+), 464 deletions(-) delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api => analytics}/AnalyticsPlatform.kt (79%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{ => local}/AnalyticsLocalDataStore.kt (97%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{ => local}/InfoAnalyticsLocalDataStore.kt (95%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{ => local}/LogAnalyticsLocalDataStore.kt (95%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api => analytics/data/old}/AnalyticsMapper.kt (95%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api => analytics/data/old}/AnalyticsRepositoryData.kt (93%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api/DefaultAnalyticsRepository.kt => analytics/data/old/DefaultOldAnalyticsRepository.kt} (87%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{ => remote}/AnalyticsRemoteDataStore.kt (98%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api => analytics/data/remote}/AnalyticsService.kt (92%) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsSetupProvider.kt rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{data/api => analytics/data/remote}/AnalyticsTrackRequestProvider.kt (94%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{ => remote}/DefaultAnalyticsRemoteDataStore.kt (84%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsSetupProvider.kt => data/remote/DefaultAnalyticsSetupProvider.kt} (86%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/{AnalyticsRepository.kt => OldAnalyticsRepository.kt} (86%) rename components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/{DefaultAnalyticsRepositoryTest.kt => DefaultOldAnalyticsRepositoryTest.kt} (88%) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt index 410cadd5e3..f41482f88d 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt @@ -34,11 +34,11 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +71,7 @@ class ACHDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -121,7 +121,7 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -207,7 +207,7 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -285,7 +285,7 @@ constructor( paymentMethod: PaymentMethod, savedStateHandle: SavedStateHandle, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: AnalyticsRepository, + analyticsRepository: OldAnalyticsRepository, httpClient: HttpClient, order: Order?, ): DefaultACHDirectDebitDelegate { @@ -328,7 +328,7 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -414,7 +414,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -487,7 +487,7 @@ constructor( private fun createStoredDelegate( paymentMethod: StoredPaymentMethod, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: AnalyticsRepository, + analyticsRepository: OldAnalyticsRepository, order: Order? ): StoredACHDirectDebitDelegate { return StoredACHDirectDebitDelegate( diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt index b51d517f2a..f3aaf31ff9 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -62,7 +62,7 @@ import kotlinx.coroutines.launch internal class DefaultACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val publicKeyRepository: PublicKeyRepository, private val addressRepository: AddressRepository, private val submitHandler: SubmitHandler, diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt index 41bcbe2ed2..073b08f90e 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -48,7 +48,7 @@ import kotlinx.coroutines.launch internal class StoredACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val storedPaymentMethod: StoredPaymentMethod, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, override val componentParams: ACHDirectDebitComponentParams, private val order: OrderRequest?, ) : ACHDirectDebitDelegate { diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt index 3bcc431788..b3da5caae8 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt @@ -21,7 +21,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -69,7 +69,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler ) { @@ -683,7 +683,7 @@ internal class DefaultACHDirectDebitDelegateTest( @Suppress("LongParameterList") private fun createAchDelegate( paymentMethod: PaymentMethod = PaymentMethod(), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, publicKeyRepository: PublicKeyRepository = this.publicKeyRepository, addressRepository: AddressRepository = this.addressRepository, genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt index 27eea5d09a..a23b57bc90 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.test.TestDispatcherExtension @@ -45,7 +45,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository + @Mock private val analyticsRepository: OldAnalyticsRepository ) { private lateinit var delegate: ACHDirectDebitDelegate @@ -106,7 +106,7 @@ internal class StoredACHDirectDebitDelegateTest( private fun createAchDelegate( paymentMethod: StoredPaymentMethod = StoredPaymentMethod(id = STORED_ID), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, configuration: CheckoutConfiguration = createCheckoutConfiguration(), order: OrderRequest? = TEST_ORDER, ) = StoredACHDirectDebitDelegate( diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt index d3b02aea63..87334b0eec 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt @@ -28,11 +28,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class BacsDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -95,7 +95,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBacsDirectDebitConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -187,7 +187,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 9a1b0155dc..07d532b9c8 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BacsDirectDebitPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -41,7 +41,7 @@ internal class DefaultBacsDirectDebitDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, ) : BacsDirectDebitDelegate { diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index b980860d8c..a97ec3cf5b 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -23,7 +23,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -56,7 +56,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBacsDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt index 2eced9b09a..cce2f1e86e 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt @@ -31,11 +31,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -66,7 +66,7 @@ class BcmcComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -116,7 +116,7 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -225,7 +225,7 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt index b0e7cc1aa8..24e76637a5 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt @@ -30,11 +30,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper @@ -62,7 +62,7 @@ class BlikComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -112,7 +112,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -201,7 +201,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -293,7 +293,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -405,7 +405,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt index c1e640c110..41ac884a12 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -40,7 +40,7 @@ internal class DefaultBlikDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, ) : BlikDelegate { diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt index 20beadb7d8..d0a84b3e0b 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -39,7 +39,7 @@ internal class StoredBlikDelegate( override val componentParams: ButtonComponentParams, private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, ) : BlikDelegate { diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt index 7dd6b6feba..c061a91ed6 100644 --- a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -50,7 +50,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBlikDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt index 0c2fc1aa39..b15a11b4b8 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt @@ -28,11 +28,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -58,7 +58,7 @@ class BoletoComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -97,7 +97,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -192,7 +192,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt index a4c1164446..8cc723f9a1 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt @@ -22,7 +22,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.ShopperName import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -54,7 +54,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultBoletoDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt index 9e4e33f97d..a1e69c77ec 100644 --- a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt +++ b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -54,7 +54,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBoletoDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, ) { private lateinit var delegate: DefaultBoletoDelegate @@ -510,7 +510,7 @@ internal class DefaultBoletoDelegateTest( @Suppress("LongParameterList") private fun createBoletoDelegate( submitHandler: SubmitHandler = this.submitHandler, - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, paymentMethod: PaymentMethod = PaymentMethod(), addressRepository: TestAddressRepository = this.addressRepository, order: Order? = TEST_ORDER, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt index 9a72fc7a80..1ac9be9467 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt @@ -33,11 +33,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +71,7 @@ class CardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -136,7 +136,7 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -250,7 +250,7 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -376,7 +376,7 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -476,7 +476,7 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index 09525bf5be..2152c86d1a 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -44,7 +44,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -97,7 +97,7 @@ class DefaultCardDelegate( override val componentParams: CardComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val addressRepository: AddressRepository, private val detectCardTypeRepository: DetectCardTypeRepository, private val cardValidationMapper: CardValidationMapper, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt index 68b91590b1..f9964001cb 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt @@ -32,7 +32,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -72,7 +72,7 @@ internal class StoredCardDelegate( private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, override val componentParams: CardComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val cardEncryptor: BaseCardEncryptor, private val publicKeyRepository: PublicKeyRepository, private val submitHandler: SubmitHandler, diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index ff25ae7a26..a259182540 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -45,7 +45,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -98,7 +98,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, @Mock private val addressLookupDelegate: AddressLookupDelegate, ) { @@ -1210,7 +1210,7 @@ internal class DefaultCardDelegateTest( genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), paymentMethod: PaymentMethod = PaymentMethod(type = PaymentMethodTypes.SCHEME), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, addressLookupDelegate: AddressLookupDelegate = this.addressLookupDelegate diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index ad22d1a039..7efc73a8eb 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -78,7 +78,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler ) { @@ -451,7 +451,7 @@ internal class StoredCardDelegateTest( cardEncryptor: BaseCardEncryptor = this.cardEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), storedPaymentMethod: StoredPaymentMethod = getStoredPaymentMethod(), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, ): StoredCardDelegate { diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt index 3979798d38..b69f96709f 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt @@ -35,11 +35,11 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +67,7 @@ class CashAppPayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -118,7 +118,7 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -204,7 +204,7 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -291,7 +291,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -388,7 +388,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt index 6979720e40..bc6d5c6175 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt @@ -33,7 +33,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -58,7 +58,7 @@ internal class DefaultCashAppPayDelegate @Suppress("LongParameterList") constructor( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt index ccf9379d2d..4dc7c5f366 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -34,7 +34,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class StoredCashAppPayDelegate( - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: StoredPaymentMethod, private val order: OrderRequest?, diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt index 36743232a4..079f732c00 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -73,7 +73,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCashAppPayDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val cashAppPayFactory: CashAppPayFactory, @Mock private val cashAppPay: CashAppPay, ) { diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt index 370cbd1328..4aada9ae87 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -44,7 +44,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class StoredCashAppPayDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, ) { private lateinit var delegate: StoredCashAppPayDelegate diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt deleted file mode 100644 index 7d86ec4176..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AdyenAnalytics.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 12/2/2024. - */ - -package com.adyen.checkout.components.core.internal.analytics - -import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.AnalyticsTrackRequestMapper -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel -import com.adyen.checkout.core.AdyenLogLevel -import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.core.internal.util.runSuspendCatching -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import java.util.LinkedList - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -// TODO: Create factory -class AdyenAnalytics internal constructor( - private val analyticsProvider: AnalyticsProvider, - private val analyticsParams: AnalyticsParams, - private val analyticsService: AnalyticsService, - private val analyticsTrackRequestMapper: AnalyticsTrackRequestMapper, - coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default, -) { - - // TODO: Check if Job or SupervisorJob is better for us - private val coroutineScope = CoroutineScope(coroutineDispatcher + SupervisorJob()) - - private val eventQueue: LinkedList = LinkedList() - - val checkoutAttemptId: String? get() = (state as? State.Ready)?.checkoutAttemptId - - @Volatile - private var state: State = State.Uninitialized - - private val mutex = Mutex() - - fun setup() { - coroutineScope.launch { - setupInternal() - } - } - - private suspend fun setupInternal() { - if (cannotSendEvent()) { - state = State.Ready(CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS) - return - } - - if (!state.canInitialize()) return - - state = State.InProgress - - adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } - - runSuspendCatching { - adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } - state = fetchCheckoutAttemptId()?.let { - adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } - State.Ready(it) - } ?: run { - adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId from response is null" } - State.Failed - } - }.onFailure { e -> - adyenLog(AdyenLogLevel.ERROR) { - "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" - } - state = State.Failed - } - } - - private suspend fun fetchCheckoutAttemptId(): String? { - val analyticsSetupRequest = analyticsProvider.provide() - val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsParams.clientKey) - return response.checkoutAttemptId - } - - - fun track(event: AnalyticsEvent) { - if (cannotSendEvent()) return - - coroutineScope.launch { - mutex.withLock { - eventQueue.add(event) - track(event) - } - } - } - - // TODO: Discuss if we need to use mappers before we send events to backend - private suspend fun sendEvents() { - if (state.canInitialize()) { - setupInternal() - } - - val checkoutAttemptId = checkoutAttemptId ?: run { - adyenLog(AdyenLogLevel.WARN) { "Not sending events because checkoutAttemptId is null" } - return - } - - // TODO: Send correct channel - val request = analyticsTrackRequestMapper("", eventQueue.takeLast(BATCH_SIZE)) - - runSuspendCatching { - analyticsService.trackEvents(request, checkoutAttemptId, analyticsParams.clientKey) - }.fold( - onSuccess = { - // TODO: Remove events from the queue - }, - onFailure = { - // TODO: Handle error - }, - ) - - } - - private fun cannotSendEvent(): Boolean { - return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority - } - - companion object { - private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" - - private const val BATCH_SIZE = 20 - } - - @VisibleForTesting - internal sealed class State { - data object Uninitialized : State() - data object InProgress : State() - data class Ready(val checkoutAttemptId: String) : State() - data object Failed : State() - - fun canInitialize(): Boolean = when (this) { - InProgress, - is Ready -> false - - Failed, - Uninitialized -> true - } - } -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt index d3775c469c..db9b3568cd 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo +// TODO: Remove if not used @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @RequiresOptIn("Avoid using AnalyticsEvent directly") @Target(AnnotationTarget.CONSTRUCTOR) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 57f7109817..0249034da0 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -9,6 +9,7 @@ package com.adyen.checkout.components.core.internal.analytics import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsPlatform.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt similarity index 79% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsPlatform.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt index 4063fbed36..896debe554 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsPlatform.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 9/11/2023. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt index 51e3a0fdd1..9b2665ada3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -11,7 +11,6 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.BuildConfig -import com.adyen.checkout.components.core.internal.data.api.AnalyticsPlatform object AnalyticsPlatformParams { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt index 7d57e43761..eb15dd7754 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -9,7 +9,7 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider internal interface AnalyticsRepository { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index f31ac7fbd4..6222b38996 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -9,8 +9,10 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSetupProvider -import com.adyen.checkout.components.core.internal.data.api.AnalyticsTrackRequestProvider +import com.adyen.checkout.components.core.internal.analytics.data.local.AnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsRemoteDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider internal class DefaultAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt similarity index 97% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt index f858a822b6..9424ded07f 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt @@ -6,7 +6,7 @@ * Created by ararat on 28/2/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data +package com.adyen.checkout.components.core.internal.analytics.data.local internal interface AnalyticsLocalDataStore { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt similarity index 95% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt index b2251a237f..e52f0308b3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/InfoAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by ararat on 28/2/2024. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data +package com.adyen.checkout.components.core.internal.analytics.data.local import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent import kotlinx.coroutines.sync.Mutex diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt similarity index 95% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt index 97c1c6b228..6ade5e8ba9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/LogAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by ararat on 28/2/2024. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data +package com.adyen.checkout.components.core.internal.analytics.data.local import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent import kotlinx.coroutines.sync.Mutex diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt similarity index 95% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt index fcbd9fc489..2ee5d6486f 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt @@ -1,12 +1,12 @@ /* - * Copyright (c) 2022 Adyen N.V. + * 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 josephj on 25/11/2022. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics.data.old import android.os.Build import androidx.annotation.RestrictTo @@ -17,6 +17,7 @@ import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequ import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import java.util.Locale +// TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsMapper { // @Suppress("LongParameterList") diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt similarity index 93% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt index f5fea4d92e..1c7362e2ab 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt @@ -1,12 +1,12 @@ /* - * Copyright (c) 2023 Adyen N.V. + * 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 josephj on 25/7/2023. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics.data.old import android.app.Application import androidx.annotation.RestrictTo @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.internal.ui.model.ComponentParams import com.adyen.checkout.components.core.internal.util.screenWidthPixels import java.util.Locale +// TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class AnalyticsRepositoryData( val level: AnalyticsParamsLevel, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt similarity index 87% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt index fbd5e79187..7c6f4ea3a3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt @@ -1,28 +1,30 @@ /* - * Copyright (c) 2022 Adyen N.V. + * 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 josephj on 24/11/2022. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics.data.old import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.ALL import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.NONE import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.core.internal.util.runSuspendCatching +// TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class DefaultAnalyticsRepository( +class DefaultOldAnalyticsRepository( private val analyticsRepositoryData: AnalyticsRepositoryData, private val analyticsService: AnalyticsService, private val analyticsMapper: AnalyticsMapper, -) : AnalyticsRepository { +) : OldAnalyticsRepository { @VisibleForTesting internal var state: State = State.Uninitialized diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsRemoteDataStore.kt similarity index 98% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsRemoteDataStore.kt index 9dab4deef1..9db3742a4d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsRemoteDataStore.kt @@ -6,7 +6,7 @@ * Created by ararat on 28/2/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data +package com.adyen.checkout.components.core.internal.analytics.data.remote import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt similarity index 92% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt index 6abbcf5ef5..53de222df8 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt @@ -1,12 +1,12 @@ /* - * Copyright (c) 2022 Adyen N.V. + * 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 josephj on 24/11/2022. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics.data.remote import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsSetupProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsSetupProvider.kt new file mode 100644 index 0000000000..59eaee5b50 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsSetupProvider.kt @@ -0,0 +1,15 @@ +/* + * 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 ararat on 4/3/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics.data.remote + +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest + +internal interface AnalyticsSetupProvider { + fun provide(): AnalyticsSetupRequest +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt similarity index 94% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index 3b0f7df9db..39a97a8834 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -3,10 +3,10 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 13/2/2024. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.data.api +package com.adyen.checkout.components.core.internal.analytics.data.remote import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatformParams diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt similarity index 84% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt index 0cab8f8bf5..690cf17485 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt @@ -6,9 +6,10 @@ * Created by ararat on 28/2/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data +package com.adyen.checkout.components.core.internal.analytics.data.remote -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsRemoteDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt similarity index 86% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt index 75baefdef3..0c6b2e4a63 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSetupProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt @@ -1,22 +1,20 @@ /* - * Copyright (c) 2022 Adyen N.V. + * 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 josephj on 25/11/2022. + * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics +package com.adyen.checkout.components.core.internal.analytics.data.remote import android.app.Application import android.os.Build +import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatformParams +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.ui.model.ComponentParams -internal interface AnalyticsSetupProvider { - fun provide(): AnalyticsSetupRequest -} - internal class DefaultAnalyticsSetupProvider( private val application: Application, private val componentParams: ComponentParams, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt similarity index 86% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt index 5128a76a01..b278776052 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt @@ -10,8 +10,9 @@ package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo +// TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -interface AnalyticsRepository { +interface OldAnalyticsRepository { suspend fun setupAnalytics() fun getCheckoutAttemptId(): String? diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt similarity index 88% rename from components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt rename to components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt index 91b470debc..c951154b2a 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt @@ -11,6 +11,10 @@ package com.adyen.checkout.components.core.internal.data.api import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.exception.HttpException import com.adyen.checkout.test.LoggingExtension @@ -37,13 +41,13 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class, LoggingExtension::class) -internal class DefaultAnalyticsRepositoryTest( +internal class DefaultOldAnalyticsRepositoryTest( @Mock private val analyticsService: AnalyticsService, ) { private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() - private lateinit var analyticsRepository: DefaultAnalyticsRepository + private lateinit var analyticsRepository: DefaultOldAnalyticsRepository @BeforeEach fun before() = runTest { @@ -55,7 +59,7 @@ internal class DefaultAnalyticsRepositoryTest( @Test fun `when repository is only instantiated then state is set as uninitialized`() = runTest { - assertEquals(DefaultAnalyticsRepository.State.Uninitialized, analyticsRepository.state) + assertEquals(DefaultOldAnalyticsRepository.State.Uninitialized, analyticsRepository.state) } @Nested @@ -80,7 +84,7 @@ internal class DefaultAnalyticsRepositoryTest( @Test fun `and AnalyticsService is successful then state is set as initialized`() = runTest { analyticsRepository.setupAnalytics() - assertEquals(DefaultAnalyticsRepository.State.Ready, analyticsRepository.state) + assertEquals(DefaultOldAnalyticsRepository.State.Ready, analyticsRepository.state) } @Test @@ -93,7 +97,7 @@ internal class DefaultAnalyticsRepositoryTest( fun `and AnalyticsService returns an error then state is set as error`() = runTest { whenever(analyticsService.setupAnalytics(any(), any())) doThrow HttpException(1, "error_message", null) analyticsRepository.setupAnalytics() - assertEquals(DefaultAnalyticsRepository.State.Failed, analyticsRepository.state) + assertEquals(DefaultOldAnalyticsRepository.State.Failed, analyticsRepository.state) } @Test @@ -152,7 +156,7 @@ internal class DefaultAnalyticsRepositoryTest( analyticsRepository.setupAnalytics() assertEquals( - DefaultAnalyticsRepository.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, + DefaultOldAnalyticsRepository.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, analyticsRepository.getCheckoutAttemptId(), ) } @@ -171,8 +175,8 @@ internal class DefaultAnalyticsRepositoryTest( screenWidth: Int = SCREEN_WIDTH, paymentMethods: List = PAYMENT_METHODS, sessionId: String? = TEST_SESSION_ID, - ): DefaultAnalyticsRepository { - return DefaultAnalyticsRepository( + ): DefaultOldAnalyticsRepository { + return DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( level = level, packageName = packageName, diff --git a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt index 34f88479cd..b77cd45c23 100644 --- a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt +++ b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.ConvenienceStoresJPPaymentMethod import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -29,7 +29,7 @@ class ConvenienceStoresJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : EContextComponentProvider< ConvenienceStoresJPComponent, ConvenienceStoresJPConfiguration, diff --git a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt index d3e8c6bae9..07bd8d53d8 100644 --- a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt +++ b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.DotpayPaymentMethod import com.adyen.checkout.dotpay.DotpayComponent @@ -29,7 +29,7 @@ class DotpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = DotpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt index e3b2cc6aa0..442524cebf 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.ComponentCallback import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -115,7 +115,7 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: AnalyticsRepository, + analyticsRepository: OldAnalyticsRepository, onRedirect: () -> Unit, ): PaymentComponent { return when { @@ -181,7 +181,7 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: AnalyticsRepository, + analyticsRepository: OldAnalyticsRepository, onRedirect: () -> Unit, ): PaymentComponent { return when { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index 8ff6142543..5ad6cbfbf8 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -24,7 +24,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -58,7 +58,7 @@ import kotlinx.coroutines.launch internal class DropInViewModel( private val bundleHandler: DropInSavedStateHandleContainer, private val orderStatusRepository: OrderStatusRepository, - internal val analyticsRepository: AnalyticsRepository, + internal val analyticsRepository: OldAnalyticsRepository, private val initialDropInParams: DropInParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : ViewModel() { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index fd98091ce5..a51f7e5cfb 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -14,10 +14,10 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusService import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource @@ -55,7 +55,7 @@ internal class DropInViewModelFactory( val httpClient = HttpClientFactory.getHttpClient(dropInParams.environment) val orderStatusRepository = OrderStatusRepository(OrderStatusService(httpClient)) - val analyticsRepository = DefaultAnalyticsRepository( + val analyticsRepository = DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( level = dropInParams.analyticsParams.level, packageName = packageName, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt index d9dd909bda..a97dcd59b2 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt @@ -26,11 +26,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +67,7 @@ abstract class EContextComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsRepository: OldAnalyticsRepository?, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, SessionPaymentComponentProvider< @@ -100,7 +100,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -192,7 +192,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index e2f50e7900..c5b60cac91 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -50,7 +50,7 @@ internal class DefaultEContextDelegate< override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> EContextPaymentMethodT, private val componentStateFactory: ( diff --git a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt index 9e44355b85..25a4a212df 100644 --- a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt +++ b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultEContextDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt index 844cbdf7ed..fc903a7cb3 100644 --- a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt +++ b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EntercashPaymentMethod import com.adyen.checkout.entercash.EntercashComponent @@ -29,7 +29,7 @@ class EntercashComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider< EntercashComponent, EntercashConfiguration, diff --git a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt index f87ed9ccee..46f735b811 100644 --- a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt +++ b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EPSPaymentMethod import com.adyen.checkout.eps.EPSComponent @@ -29,7 +29,7 @@ class EPSComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = EPSComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt index 85ec671395..5a978f3c99 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt @@ -19,11 +19,11 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -59,7 +59,7 @@ class GiftCardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -100,7 +100,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -195,7 +195,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt index da2611d89e..9751fd3d33 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -60,7 +60,7 @@ internal class DefaultGiftCardDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val publicKeyRepository: PublicKeyRepository, override val componentParams: GiftCardComponentParams, private val cardEncryptor: BaseCardEncryptor, diff --git a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt index 0a6ad6e5e5..978381aaa4 100644 --- a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt +++ b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.OrderResponse import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -59,7 +59,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultGiftCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt index a308067728..0a0264f325 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt @@ -23,11 +23,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentMethodAvailabilityCheck import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -64,7 +64,7 @@ class GooglePayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -103,7 +103,7 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -194,7 +194,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt index 8ab1f14270..ca05f63caf 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -48,7 +48,7 @@ internal class DefaultGooglePayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: GooglePayComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, ) : GooglePayDelegate { private val _componentStateFlow = MutableStateFlow(createComponentState()) diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt index dff2e9fdf9..d48810d26f 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Configuration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.GooglePayPaymentMethod import com.adyen.checkout.core.Environment @@ -48,7 +48,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultGooglePayDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, ) { private lateinit var delegate: DefaultGooglePayDelegate diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt index 2c88bd6328..ec68570eb8 100644 --- a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt +++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.IdealPaymentMethod import com.adyen.checkout.ideal.IdealComponent @@ -29,7 +29,7 @@ class IdealComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = IdealComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index 19deb75e1e..3d596fb9d4 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -23,11 +23,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +56,7 @@ class InstantPaymentComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -94,7 +94,7 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -185,7 +185,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index ef05504d0b..d060320df5 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails @@ -37,7 +37,7 @@ internal class DefaultInstantPaymentDelegate( private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: InstantComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, ) : InstantPaymentDelegate { override val componentStateFlow: StateFlow = MutableStateFlow(createComponentState()) diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index 29dd8d9686..ed4572a4dc 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper @@ -42,7 +42,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) class DefaultInstantPaymentDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, ) { private lateinit var delegate: DefaultInstantPaymentDelegate diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt index 29eeae3fad..6a627f8168 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt @@ -27,11 +27,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -69,7 +69,7 @@ abstract class IssuerListComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsRepository: OldAnalyticsRepository?, private val hideIssuerLogosDefaultValue: Boolean = false, private val localeProvider: LocaleProvider = LocaleProvider(), ) : @@ -104,7 +104,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -195,7 +195,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -284,7 +284,7 @@ constructor( paymentMethod: PaymentMethod, order: Order?, savedStateHandle: SavedStateHandle, - analyticsRepository: AnalyticsRepository, + analyticsRepository: OldAnalyticsRepository, ): DefaultIssuerListDelegate { return DefaultIssuerListDelegate( observerRepository = PaymentObserverRepository(), diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index d4ff7685ea..7d203c7a41 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @@ -45,7 +45,7 @@ internal class DefaultIssuerListDelegate< override val componentParams: IssuerListComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> IssuerListPaymentMethodT, private val componentStateFactory: ( diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index b9d877d93b..d979ac1d70 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.issuerlist.IssuerListViewType @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultIssuerListDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index ac84ceb5c5..bd8b0eb374 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -22,11 +22,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AdyenAnalytics import com.adyen.checkout.components.core.internal.analytics.AnalyticsProvider import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +56,7 @@ class MBWayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 5b09933d4d..e92203a0d2 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -16,7 +16,6 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AdyenAnalytics import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.MBWayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index ae9c8a1714..1d82eaf807 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -49,7 +49,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultMBWayDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt index 45885eecb0..1d6a2ab9b6 100644 --- a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt +++ b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.MolpayPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class MolpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = MolpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt index 9ba3010645..579885f5fc 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt @@ -26,11 +26,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -68,7 +68,7 @@ abstract class OnlineBankingComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsRepository: OldAnalyticsRepository?, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -101,7 +101,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -199,7 +199,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt index d4633d09de..2cb14434d8 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod @@ -53,7 +53,7 @@ internal class DefaultOnlineBankingDelegate< private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: ButtonComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val termsAndConditionsUrl: String, private val submitHandler: SubmitHandler, private val paymentMethodFactory: () -> IssuerListPaymentMethodT, diff --git a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt index a6505d1887..ba85bc9894 100644 --- a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt +++ b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -54,7 +54,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultOnlineBankingDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val context: Context, @Mock private val pdfOpener: PdfOpener, @Mock private val submitHandler: SubmitHandler, diff --git a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt index 467851265b..856f05dc67 100644 --- a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt +++ b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingCZPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingCZComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : OnlineBankingComponentProvider< OnlineBankingCZComponent, OnlineBankingCZConfiguration, diff --git a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt index 3474600925..77de8f9e60 100644 --- a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt +++ b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingJPPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : EContextComponentProvider< OnlineBankingJPComponent, OnlineBankingJPConfiguration, diff --git a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt index 207acfc642..3367491e68 100644 --- a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt +++ b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingPLPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingPLComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider< OnlineBankingPLComponent, OnlineBankingPLConfiguration, diff --git a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt index 69dc9425aa..b67fad0811 100644 --- a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt +++ b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingSKPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingSKComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : OnlineBankingComponentProvider< OnlineBankingSKComponent, OnlineBankingSKConfiguration, diff --git a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt index c0e49f8f40..30b815a4f1 100644 --- a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt +++ b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OpenBankingPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class OpenBankingComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : IssuerListComponentProvider< OpenBankingComponent, OpenBankingConfiguration, diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt index ab01e7ffa8..ed164a3d07 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +56,7 @@ class PayByBankComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -93,7 +93,7 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -184,7 +184,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt index f199dc34c8..cf90f62f57 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.paymentmethod.PayByBankPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -42,7 +42,7 @@ internal class DefaultPayByBankDelegate( private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: GenericComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, ) : PayByBankDelegate { diff --git a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt index 7a3d149cba..18c9e948ae 100644 --- a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt +++ b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultPayByBankDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt index 7b4839a19b..de66ec1552 100644 --- a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt +++ b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.PayEasyPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class PayEasyComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : EContextComponentProvider( componentClass = PayEasyComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt index e2a4eba75f..64a7ec9dda 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class SepaComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -96,7 +96,7 @@ constructor( componentConfiguration = checkoutConfiguration.getSepaConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -188,7 +188,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt index ad521141d1..f8caca2d65 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -40,7 +40,7 @@ internal class DefaultSepaDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val submitHandler: SubmitHandler, ) : SepaDelegate { diff --git a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt index 2b7849ec65..37eb8d49c6 100644 --- a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt +++ b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod @@ -51,7 +51,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultSepaDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt index 871e3316c6..507c6906ee 100644 --- a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt +++ b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.SevenElevenPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class SevenElevenComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsRepository: OldAnalyticsRepository? = null, ) : EContextComponentProvider< SevenElevenComponent, SevenElevenConfiguration, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt index 5dba69e692..be90ec7ca2 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class UPIComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsRepository: OldAnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -90,7 +90,7 @@ constructor( componentConfiguration = checkoutConfiguration.getUPIConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -182,7 +182,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt index 5aa87e6805..e1a99ecdcd 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.UPIPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -38,7 +38,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultUPIDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: OldAnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index 7a3d27144b..2c66829f73 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -56,7 +56,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultUPIDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsRepository: OldAnalyticsRepository, ) { private lateinit var delegate: DefaultUPIDelegate From 7a5aea3f36f580514baccda3014d6933e56bfe50 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 5 Mar 2024 10:53:03 +0100 Subject: [PATCH 173/272] Create analytics manager factory and use it for MBWay component COAND-844 diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 0249034da..40fdf0839 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -8,8 +8,8 @@ package com.adyen.checkout.components.core.internal.analytics +import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -24,9 +24,9 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.seconds +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsManager internal constructor( private val analyticsRepository: AnalyticsRepository, - private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { @@ -54,7 +54,7 @@ class AnalyticsManager internal constructor( coroutineScope.launch(coroutineDispatcher) { runSuspendCatching { - analyticsRepository.fetchCheckoutAttemptId(analyticsSetupProvider) + analyticsRepository.fetchCheckoutAttemptId() }.fold( onSuccess = { attemptId -> checkoutAttemptId = attemptId?.also { startTimer() } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt new file mode 100644 index 000000000..e02614591 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -0,0 +1,57 @@ +/* + * 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 ararat on 5/3/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import android.app.Application +import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.internal.analytics.data.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider +import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsRemoteDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.core.internal.data.api.HttpClientFactory + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AnalyticsManagerFactory { + fun provide( + componentParams: ComponentParams, + application: Application, + source: AnalyticsSource, + sessionId: String? + ) = AnalyticsManager( + analyticsRepository = DefaultAnalyticsRepository( + localInfoDataStore = InfoAnalyticsLocalDataStore(), + localLogDataStore = LogAnalyticsLocalDataStore(), + remoteDataStore = DefaultAnalyticsRemoteDataStore( + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + clientKey = componentParams.clientKey, + infoSize = INFO_SIZE, + logSize = LOG_SIZE, + ), + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = application, + componentParams = componentParams, + source = source, + sessionId = sessionId, + ), + analyticsTrackRequestProvider = AnalyticsTrackRequestProvider(), + ), + analyticsParams = componentParams.analyticsParams, + ) + + companion object { + private const val INFO_SIZE = 50 + private const val LOG_SIZE = 5 + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt index eb15dd775..e4fd2e09e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -9,11 +9,10 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider internal interface AnalyticsRepository { - suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? + suspend fun fetchCheckoutAttemptId(): String? suspend fun storeEvent(event: AnalyticsEvent) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index 6222b3899..1a372c3d4 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -18,10 +18,11 @@ internal class DefaultAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, private val localLogDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, + private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, ) : AnalyticsRepository { - override suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? { + override suspend fun fetchCheckoutAttemptId(): String? { val request = analyticsSetupProvider.provide() return remoteDataStore.fetchCheckoutAttemptId(request).checkoutAttemptId } diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index bd8b0eb37..74688703c 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -22,10 +22,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsProvider +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -56,7 +55,7 @@ class MBWayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -99,18 +98,11 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - // TODO: Find out how to use analytics source with drop-in - adyenAnalytics = AdyenAnalytics( - analyticsProvider = AnalyticsProvider( - application, - componentParams, - AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - null, - ), - analyticsParams = componentParams.analyticsParams, - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), + analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ), submitHandler = SubmitHandler(savedStateHandle), ) @@ -191,18 +183,11 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - // TODO: Find out how to use analytics source with drop-in - adyenAnalytics = AdyenAnalytics( - analyticsProvider = AnalyticsProvider( - application, - componentParams, - AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - checkoutSession.sessionSetupResponse.id, - ), - analyticsParams = componentParams.analyticsParams, - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), + analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ), submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index cb9cc361e..fec5297ed 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -16,6 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.util.CountryInfo import com.adyen.checkout.components.core.internal.util.CountryUtils @@ -40,7 +41,7 @@ internal class DefaultMBWayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: ButtonComponentParams, - private val adyenAnalytics: AdyenAnalytics, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : MBWayDelegate { @@ -69,12 +70,12 @@ internal class DefaultMBWayDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics() + initializeAnalytics(coroutineScope) } - private fun setupAnalytics() { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - adyenAnalytics.setup() + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(coroutineScope) } override fun observe( @@ -132,7 +133,7 @@ internal class DefaultMBWayDelegate( ): MBWayComponentState { val paymentMethod = MBWayPaymentMethod( type = MBWayPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = adyenAnalytics.checkoutAttemptId, + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), telephoneNumber = outputData.mobilePhoneNumberFieldState.value, ) diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 6055c03f0..5303293f3 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -41,6 +41,7 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -49,7 +50,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultMBWayDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -198,9 +199,9 @@ internal class DefaultMBWayDelegateTest( } @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { + fun `when delegate is initialized then analytics manager is initialized`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() + verify(analyticsManager).initialize(any()) } @Nested @@ -261,7 +262,7 @@ internal class DefaultMBWayDelegateTest( @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -289,7 +290,7 @@ internal class DefaultMBWayDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getMBWayConfiguration(), ), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) --- .../internal/analytics/AnalyticsManager.kt | 6 +- .../analytics/AnalyticsManagerFactory.kt | 57 +++++++++++++++++++ .../analytics/data/AnalyticsRepository.kt | 3 +- .../data/DefaultAnalyticsRepository.kt | 3 +- .../provider/MBWayComponentProvider.kt | 41 +++++-------- .../mbway/internal/ui/DefaultMBWayDelegate.kt | 13 +++-- .../internal/ui/DefaultMBWayDelegateTest.kt | 13 +++-- 7 files changed, 90 insertions(+), 46 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 0249034da0..40fdf0839d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -8,8 +8,8 @@ package com.adyen.checkout.components.core.internal.analytics +import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -24,9 +24,9 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.seconds +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsManager internal constructor( private val analyticsRepository: AnalyticsRepository, - private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { @@ -54,7 +54,7 @@ class AnalyticsManager internal constructor( coroutineScope.launch(coroutineDispatcher) { runSuspendCatching { - analyticsRepository.fetchCheckoutAttemptId(analyticsSetupProvider) + analyticsRepository.fetchCheckoutAttemptId() }.fold( onSuccess = { attemptId -> checkoutAttemptId = attemptId?.also { startTimer() } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt new file mode 100644 index 0000000000..e026145919 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -0,0 +1,57 @@ +/* + * 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 ararat on 5/3/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import android.app.Application +import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.internal.analytics.data.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider +import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsRemoteDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.core.internal.data.api.HttpClientFactory + +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AnalyticsManagerFactory { + fun provide( + componentParams: ComponentParams, + application: Application, + source: AnalyticsSource, + sessionId: String? + ) = AnalyticsManager( + analyticsRepository = DefaultAnalyticsRepository( + localInfoDataStore = InfoAnalyticsLocalDataStore(), + localLogDataStore = LogAnalyticsLocalDataStore(), + remoteDataStore = DefaultAnalyticsRemoteDataStore( + analyticsService = AnalyticsService( + HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + ), + clientKey = componentParams.clientKey, + infoSize = INFO_SIZE, + logSize = LOG_SIZE, + ), + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = application, + componentParams = componentParams, + source = source, + sessionId = sessionId, + ), + analyticsTrackRequestProvider = AnalyticsTrackRequestProvider(), + ), + analyticsParams = componentParams.analyticsParams, + ) + + companion object { + private const val INFO_SIZE = 50 + private const val LOG_SIZE = 5 + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt index eb15dd7754..e4fd2e09e2 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -9,11 +9,10 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider internal interface AnalyticsRepository { - suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? + suspend fun fetchCheckoutAttemptId(): String? suspend fun storeEvent(event: AnalyticsEvent) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index 6222b38996..1a372c3d41 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -18,10 +18,11 @@ internal class DefaultAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, private val localLogDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, + private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, ) : AnalyticsRepository { - override suspend fun fetchCheckoutAttemptId(analyticsSetupProvider: AnalyticsSetupProvider): String? { + override suspend fun fetchCheckoutAttemptId(): String? { val request = analyticsSetupProvider.provide() return remoteDataStore.fetchCheckoutAttemptId(request).checkoutAttemptId } diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index bd8b0eb374..74688703c4 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -22,10 +22,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsProvider +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -56,7 +55,7 @@ class MBWayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -99,18 +98,11 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - // TODO: Find out how to use analytics source with drop-in - adyenAnalytics = AdyenAnalytics( - analyticsProvider = AnalyticsProvider( - application, - componentParams, - AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - null, - ), - analyticsParams = componentParams.analyticsParams, - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), + analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ), submitHandler = SubmitHandler(savedStateHandle), ) @@ -191,18 +183,11 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - // TODO: Find out how to use analytics source with drop-in - adyenAnalytics = AdyenAnalytics( - analyticsProvider = AnalyticsProvider( - application, - componentParams, - AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - checkoutSession.sessionSetupResponse.id, - ), - analyticsParams = componentParams.analyticsParams, - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), + analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ), submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index e92203a0d2..2efdbb082b 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -16,6 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.MBWayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -40,7 +41,7 @@ internal class DefaultMBWayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: ButtonComponentParams, - private val adyenAnalytics: AdyenAnalytics, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : MBWayDelegate { @@ -69,12 +70,12 @@ internal class DefaultMBWayDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics() + initializeAnalytics(coroutineScope) } - private fun setupAnalytics() { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - adyenAnalytics.setup() + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(coroutineScope) } override fun observe( @@ -132,7 +133,7 @@ internal class DefaultMBWayDelegate( ): MBWayComponentState { val paymentMethod = MBWayPaymentMethod( type = MBWayPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = adyenAnalytics.checkoutAttemptId, + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), telephoneNumber = outputData.mobilePhoneNumberFieldState.value, ) diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 1d82eaf807..d884903f65 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -41,6 +41,7 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -49,7 +50,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultMBWayDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -198,9 +199,9 @@ internal class DefaultMBWayDelegateTest( } @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { + fun `when delegate is initialized then analytics manager is initialized`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() + verify(analyticsManager).initialize(any()) } @Nested @@ -261,7 +262,7 @@ internal class DefaultMBWayDelegateTest( @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -296,7 +297,7 @@ internal class DefaultMBWayDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getMBWayConfiguration(), ), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) From d4b27ae81b0137403ade1fcfbcb08b6707a8e789 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 5 Mar 2024 11:07:38 +0100 Subject: [PATCH 174/272] Resolve some warnings COAND-844 --- .../components/core/internal/analytics/AnalyticsPlatform.kt | 1 + .../core/internal/analytics/AnalyticsPlatformParams.kt | 3 ++- .../analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt index 896debe554..547a944f4d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo +@Suppress("unused") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) enum class AnalyticsPlatform(val value: String) { ANDROID("android"), diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt index 9b2665ada3..d467edf6c2 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -14,7 +14,6 @@ import com.adyen.checkout.components.core.BuildConfig object AnalyticsPlatformParams { - @Suppress("ConstPropertyName") const val channel = "android" var platform = AnalyticsPlatform.ANDROID.value @@ -23,6 +22,7 @@ object AnalyticsPlatformParams { var version = BuildConfig.CHECKOUT_VERSION private set + @Suppress("unused") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) fun overrideForCrossPlatform( platform: AnalyticsPlatform, @@ -32,6 +32,7 @@ object AnalyticsPlatformParams { this.version = version } + @Suppress("unused") @VisibleForTesting internal fun resetToDefaults() { platform = AnalyticsPlatform.ANDROID.value diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt index 690cf17485..cd947a13ef 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt @@ -8,8 +8,6 @@ package com.adyen.checkout.components.core.internal.analytics.data.remote -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsRemoteDataStore -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest From c2f10a31a61b079a2ba81f4bf01661bd963c6dbf Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 6 Mar 2024 14:01:29 +0100 Subject: [PATCH 175/272] Fix some TODOs COAND-844 --- .../components/core/internal/analytics/AnalyticsEvents.kt | 4 ++-- .../components/core/internal/analytics/AnalyticsManager.kt | 1 - .../components/core/internal/analytics/AnalyticsSource.kt | 3 +-- .../{AnalyticsEventApi.kt => DirectAnalyticsEventCreation.kt} | 3 +-- .../components/core/internal/analytics/GenericEvents.kt | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsEventApi.kt => DirectAnalyticsEventCreation.kt} (86%) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt index c3fb714b75..0f8297f1d7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt @@ -18,7 +18,7 @@ sealed interface AnalyticsEvent { val shouldForceSend: Boolean val component: String - data class Info @AnalyticsEventApi constructor( + data class Info @DirectAnalyticsEventCreation constructor( override val timestamp: Long = Date().time, override val shouldForceSend: Boolean = false, override val component: String, @@ -42,7 +42,7 @@ sealed interface AnalyticsEvent { } } - data class Log @AnalyticsEventApi constructor( + data class Log @DirectAnalyticsEventCreation constructor( override val timestamp: Long = Date().time, override val shouldForceSend: Boolean = true, override val component: String, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 40fdf0839d..68270e6556 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -40,7 +40,6 @@ class AnalyticsManager internal constructor( private var timerJob: Job? = null - // TODO: Check if we need to retry in case the request failed fun initialize(coroutineScope: CoroutineScope) { if (isInitialized) return isInitialized = true diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt index 7938e93c70..0fd824329d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsSource.kt @@ -15,8 +15,7 @@ sealed class AnalyticsSource { data class DropIn(val paymentMethodList: List) : AnalyticsSource() data class PaymentComponent(val paymentMethodType: String) : AnalyticsSource() - // TODO: Check if we can rename paymentMethodList and not make it clash with this function - fun getPaymentMethods(): List = when(this) { + fun getPaymentMethods(): List = when (this) { is DropIn -> paymentMethodList is PaymentComponent -> listOf(paymentMethodType) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DirectAnalyticsEventCreation.kt similarity index 86% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DirectAnalyticsEventCreation.kt index db9b3568cd..07ec486355 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEventApi.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DirectAnalyticsEventCreation.kt @@ -10,8 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo -// TODO: Remove if not used @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @RequiresOptIn("Avoid using AnalyticsEvent directly") @Target(AnnotationTarget.CONSTRUCTOR) -annotation class AnalyticsEventApi +annotation class DirectAnalyticsEventCreation diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index b99a56ce03..7dea94c027 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo -@OptIn(AnalyticsEventApi::class) +@OptIn(DirectAnalyticsEventCreation::class) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object GenericEvents { From 6ded0f3aa5cf8865370ecfca2ad808047b00a680 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 6 Mar 2024 14:44:07 +0100 Subject: [PATCH 176/272] Fix some build errors COAND-844 --- .../analytics/AnalyticsPlatformParams.kt | 1 + .../remote/AnalyticsTrackRequestProvider.kt | 46 +++++++++---------- .../ui/model/CommonComponentParamsMapper.kt | 5 +- .../model/InstantComponentParamsMapperTest.kt | 4 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt index d467edf6c2..e8b450563c 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -12,6 +12,7 @@ import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.BuildConfig +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object AnalyticsPlatformParams { const val channel = "android" diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index 39a97a8834..f6b50c3af9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -23,33 +23,29 @@ internal class AnalyticsTrackRequestProvider { return AnalyticsTrackRequest( channel = AnalyticsPlatformParams.channel, platform = AnalyticsPlatformParams.platform, - info = infoList.mapToTrackEvent(), - logs = logList.mapToTrackEvent(), + info = infoList.map { event -> event.mapToTrackEvent() }, + logs = logList.map { event -> event.mapToTrackEvent() }, ) } - private fun List.mapToTrackEvent() = map { event -> - AnalyticsTrackInfo( - timestamp = event.timestamp, - component = event.component, - type = event.type?.value, - target = event.target, - isStoredPaymentMethod = event.isStoredPaymentMethod, - brand = event.brand, - issuer = event.issuer, - validationErrorCode = event.validationErrorCode, - validationErrorMessage = event.validationErrorMessage, - ) - } + private fun AnalyticsEvent.Info.mapToTrackEvent() = AnalyticsTrackInfo( + timestamp = timestamp, + component = component, + type = type?.value, + target = target, + isStoredPaymentMethod = isStoredPaymentMethod, + brand = brand, + issuer = issuer, + validationErrorCode = validationErrorCode, + validationErrorMessage = validationErrorMessage, + ) - private fun List.mapToTrackEvent() = map { event -> - AnalyticsTrackLog( - timestamp = event.timestamp, - component = event.component, - type = event.type?.value, - subType = event.subType, - target = event.target, - message = event.message, - ) - } + private fun AnalyticsEvent.Log.mapToTrackEvent() = AnalyticsTrackLog( + timestamp = timestamp, + component = component, + type = type?.value, + subType = subType, + target = target, + message = message, + ) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/CommonComponentParamsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/CommonComponentParamsMapper.kt index 25c456e0a8..36079d28ff 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/CommonComponentParamsMapper.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/CommonComponentParamsMapper.kt @@ -26,7 +26,10 @@ class CommonComponentParamsMapper { shopperLocale = checkoutConfiguration.shopperLocale ?: sessionParams?.shopperLocale ?: deviceLocale, environment = sessionParams?.environment ?: checkoutConfiguration.environment, clientKey = sessionParams?.clientKey ?: checkoutConfiguration.clientKey, - analyticsParams = AnalyticsParams(checkoutConfiguration.analyticsConfiguration), + analyticsParams = AnalyticsParams( + analyticsConfiguration = checkoutConfiguration.analyticsConfiguration, + clientKey = checkoutConfiguration.clientKey, + ), isCreatedByDropIn = dropInOverrideParams != null, amount = sessionParams?.amount ?: dropInOverrideParams?.amount diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt index 3029375788..31f7083226 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/model/InstantComponentParamsMapperTest.kt @@ -69,7 +69,7 @@ internal class InstantComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -171,7 +171,7 @@ internal class InstantComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, ): InstantComponentParams { From d16855d9591adeaad545d823be1550a04a15d9cf Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 10:56:49 +0100 Subject: [PATCH 177/272] Rename old classes back to previous and prefix new classes This is only for review purposes and should be reverted later. COAND-844 --- .../ACHDirectDebitComponentProvider.kt | 22 ++-- .../ui/DefaultACHDirectDebitDelegate.kt | 4 +- .../ui/StoredACHDirectDebitDelegate.kt | 4 +- .../ui/DefaultACHDirectDebitDelegateTest.kt | 6 +- .../ui/StoredACHDirectDebitDelegateTest.kt | 6 +- .../BacsDirectDebitComponentProvider.kt | 14 +-- .../ui/DefaultBacsDirectDebitDelegate.kt | 4 +- .../DefaultBacsDirectDebitDelegateTest.kt | 4 +- .../provider/BcmcComponentProvider.kt | 14 +-- .../provider/BlikComponentProvider.kt | 18 ++-- .../blik/internal/ui/DefaultBlikDelegate.kt | 4 +- .../blik/internal/ui/StoredBlikDelegate.kt | 4 +- .../internal/ui/DefaultBlikDelegateTest.kt | 4 +- .../provider/BoletoComponentProvider.kt | 14 +-- .../internal/ui/DefaultBoletoDelegate.kt | 4 +- .../internal/ui/DefaultBoletoDelegateTest.kt | 6 +- .../provider/CardComponentProvider.kt | 18 ++-- .../card/internal/ui/DefaultCardDelegate.kt | 4 +- .../card/internal/ui/StoredCardDelegate.kt | 4 +- .../internal/ui/DefaultCardDelegateTest.kt | 6 +- .../internal/ui/StoredCardDelegateTest.kt | 6 +- .../provider/CashAppPayComponentProvider.kt | 18 ++-- .../internal/ui/DefaultCashAppPayDelegate.kt | 4 +- .../internal/ui/StoredCashAppPayDelegate.kt | 4 +- .../ui/DefaultCashAppPayDelegateTest.kt | 4 +- .../ui/StoredCashAppPayDelegateTest.kt | 4 +- .../internal/analytics/AnalyticsManager.kt | 4 +- .../analytics/AnalyticsManagerFactory.kt | 4 +- ...ry.kt => DefaultNewAnalyticsRepository.kt} | 4 +- ...epository.kt => NewAnalyticsRepository.kt} | 2 +- .../analytics/data/old/AnalyticsMapper.kt | 100 ------------------ .../remote/AnalyticsTrackRequestProvider.kt | 2 +- .../core/internal/data/api/AnalyticsMapper.kt | 96 +++++++++++++++++ ...csRepository.kt => AnalyticsRepository.kt} | 2 +- .../api}/AnalyticsRepositoryData.kt | 2 +- .../api/DefaultAnalyticsRepository.kt} | 9 +- ...t.kt => DefaultAnalyticsRepositoryTest.kt} | 23 ++-- .../ConvenienceStoresJPComponentProvider.kt | 4 +- .../provider/DotpayComponentProvider.kt | 4 +- .../internal/provider/ComponentProvider.kt | 6 +- .../dropin/internal/ui/DropInViewModel.kt | 4 +- .../internal/ui/DropInViewModelFactory.kt | 9 +- .../provider/EContextComponentProvider.kt | 14 +-- .../internal/ui/DefaultEContextDelegate.kt | 4 +- .../ui/DefaultEContextDelegateTest.kt | 4 +- .../provider/EntercashComponentProvider.kt | 4 +- .../internal/provider/EPSComponentProvider.kt | 4 +- .../provider/GiftCardComponentProvider.kt | 14 +-- .../internal/ui/DefaultGiftCardDelegate.kt | 4 +- .../ui/DefaultGiftCardDelegateTest.kt | 4 +- .../provider/GooglePayComponentProvider.kt | 14 +-- .../internal/ui/DefaultGooglePayDelegate.kt | 4 +- .../ui/DefaultGooglePayDelegateTest.kt | 4 +- .../provider/IdealComponentProvider.kt | 4 +- .../InstantPaymentComponentProvider.kt | 14 +-- .../ui/DefaultInstantPaymentDelegate.kt | 4 +- .../ui/DefaultInstantPaymentDelegateTest.kt | 4 +- .../provider/IssuerListComponentProvider.kt | 16 +-- .../internal/ui/DefaultIssuerListDelegate.kt | 4 +- .../ui/DefaultIssuerListDelegateTest.kt | 4 +- .../provider/MolpayComponentProvider.kt | 4 +- .../OnlineBankingComponentProvider.kt | 14 +-- .../ui/DefaultOnlineBankingDelegate.kt | 4 +- .../ui/DefaultOnlineBankingDelegateTest.kt | 4 +- .../OnlineBankingCZComponentProvider.kt | 4 +- .../OnlineBankingJPComponentProvider.kt | 4 +- .../OnlineBankingPLComponentProvider.kt | 4 +- .../OnlineBankingSKComponentProvider.kt | 4 +- .../provider/OpenBankingComponentProvider.kt | 4 +- .../provider/PayByBankComponentProvider.kt | 14 +-- .../internal/ui/DefaultPayByBankDelegate.kt | 4 +- .../ui/DefaultPayByBankDelegateTest.kt | 4 +- .../provider/PayEasyComponentProvider.kt | 4 +- .../provider/SepaComponentProvider.kt | 14 +-- .../sepa/internal/ui/DefaultSepaDelegate.kt | 4 +- .../internal/ui/DefaultSepaDelegateTest.kt | 4 +- .../provider/SevenElevenComponentProvider.kt | 4 +- .../internal/provider/UPIComponentProvider.kt | 14 +-- .../upi/internal/ui/DefaultUPIDelegate.kt | 4 +- .../upi/internal/ui/DefaultUPIDelegateTest.kt | 4 +- 80 files changed, 357 insertions(+), 366 deletions(-) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{DefaultAnalyticsRepository.kt => DefaultNewAnalyticsRepository.kt} (96%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{AnalyticsRepository.kt => NewAnalyticsRepository.kt} (91%) delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt rename components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/{OldAnalyticsRepository.kt => AnalyticsRepository.kt} (92%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{analytics/data/old => data/api}/AnalyticsRepositoryData.kt (97%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{analytics/data/old/DefaultOldAnalyticsRepository.kt => data/api/DefaultAnalyticsRepository.kt} (92%) rename components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/{DefaultOldAnalyticsRepositoryTest.kt => DefaultAnalyticsRepositoryTest.kt} (89%) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt index f41482f88d..0a7fe67abf 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt @@ -34,11 +34,11 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +71,7 @@ class ACHDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -121,7 +121,7 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -207,7 +207,7 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -285,7 +285,7 @@ constructor( paymentMethod: PaymentMethod, savedStateHandle: SavedStateHandle, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: OldAnalyticsRepository, + analyticsRepository: AnalyticsRepository, httpClient: HttpClient, order: Order?, ): DefaultACHDirectDebitDelegate { @@ -328,7 +328,7 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -414,7 +414,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -487,7 +487,7 @@ constructor( private fun createStoredDelegate( paymentMethod: StoredPaymentMethod, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: OldAnalyticsRepository, + analyticsRepository: AnalyticsRepository, order: Order? ): StoredACHDirectDebitDelegate { return StoredACHDirectDebitDelegate( diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt index f3aaf31ff9..b51d517f2a 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -62,7 +62,7 @@ import kotlinx.coroutines.launch internal class DefaultACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val publicKeyRepository: PublicKeyRepository, private val addressRepository: AddressRepository, private val submitHandler: SubmitHandler, diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt index 073b08f90e..41bcbe2ed2 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -48,7 +48,7 @@ import kotlinx.coroutines.launch internal class StoredACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val storedPaymentMethod: StoredPaymentMethod, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, override val componentParams: ACHDirectDebitComponentParams, private val order: OrderRequest?, ) : ACHDirectDebitDelegate { diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt index b3da5caae8..3bcc431788 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt @@ -21,7 +21,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -69,7 +69,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler ) { @@ -683,7 +683,7 @@ internal class DefaultACHDirectDebitDelegateTest( @Suppress("LongParameterList") private fun createAchDelegate( paymentMethod: PaymentMethod = PaymentMethod(), - analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, + analyticsRepository: AnalyticsRepository = this.analyticsRepository, publicKeyRepository: PublicKeyRepository = this.publicKeyRepository, addressRepository: AddressRepository = this.addressRepository, genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt index a23b57bc90..27eea5d09a 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.test.TestDispatcherExtension @@ -45,7 +45,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository + @Mock private val analyticsRepository: AnalyticsRepository ) { private lateinit var delegate: ACHDirectDebitDelegate @@ -106,7 +106,7 @@ internal class StoredACHDirectDebitDelegateTest( private fun createAchDelegate( paymentMethod: StoredPaymentMethod = StoredPaymentMethod(id = STORED_ID), - analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, + analyticsRepository: AnalyticsRepository = this.analyticsRepository, configuration: CheckoutConfiguration = createCheckoutConfiguration(), order: OrderRequest? = TEST_ORDER, ) = StoredACHDirectDebitDelegate( diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt index 87334b0eec..1cd07058fc 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt @@ -28,11 +28,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class BacsDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -95,7 +95,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBacsDirectDebitConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -187,7 +187,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 07d532b9c8..9a1b0155dc 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BacsDirectDebitPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -41,7 +41,7 @@ internal class DefaultBacsDirectDebitDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, ) : BacsDirectDebitDelegate { diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index a97ec3cf5b..b980860d8c 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -23,7 +23,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -56,7 +56,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBacsDirectDebitDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt index cce2f1e86e..708fb80a44 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt @@ -31,11 +31,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -66,7 +66,7 @@ class BcmcComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -116,7 +116,7 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -225,7 +225,7 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt index 24e76637a5..b6394ff12a 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt @@ -30,11 +30,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper @@ -62,7 +62,7 @@ class BlikComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -112,7 +112,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -201,7 +201,7 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -293,7 +293,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -405,7 +405,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt index 41ac884a12..c1e640c110 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -40,7 +40,7 @@ internal class DefaultBlikDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, ) : BlikDelegate { diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt index d0a84b3e0b..20beadb7d8 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -39,7 +39,7 @@ internal class StoredBlikDelegate( override val componentParams: ButtonComponentParams, private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, ) : BlikDelegate { diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt index c061a91ed6..7dd6b6feba 100644 --- a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -50,7 +50,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBlikDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt index b15a11b4b8..fa3431e42f 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt @@ -28,11 +28,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -58,7 +58,7 @@ class BoletoComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -97,7 +97,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -192,7 +192,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt index 8cc723f9a1..a4c1164446 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt @@ -22,7 +22,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.ShopperName import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -54,7 +54,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultBoletoDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt index a1e69c77ec..9e4e33f97d 100644 --- a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt +++ b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -54,7 +54,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBoletoDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, ) { private lateinit var delegate: DefaultBoletoDelegate @@ -510,7 +510,7 @@ internal class DefaultBoletoDelegateTest( @Suppress("LongParameterList") private fun createBoletoDelegate( submitHandler: SubmitHandler = this.submitHandler, - analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, + analyticsRepository: AnalyticsRepository = this.analyticsRepository, paymentMethod: PaymentMethod = PaymentMethod(), addressRepository: TestAddressRepository = this.addressRepository, order: Order? = TEST_ORDER, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt index 1ac9be9467..242bfe39d5 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt @@ -33,11 +33,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +71,7 @@ class CardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -136,7 +136,7 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -250,7 +250,7 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -376,7 +376,7 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -476,7 +476,7 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index 2152c86d1a..09525bf5be 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -44,7 +44,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -97,7 +97,7 @@ class DefaultCardDelegate( override val componentParams: CardComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val addressRepository: AddressRepository, private val detectCardTypeRepository: DetectCardTypeRepository, private val cardValidationMapper: CardValidationMapper, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt index f9964001cb..68b91590b1 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt @@ -32,7 +32,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -72,7 +72,7 @@ internal class StoredCardDelegate( private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, override val componentParams: CardComponentParams, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val cardEncryptor: BaseCardEncryptor, private val publicKeyRepository: PublicKeyRepository, private val submitHandler: SubmitHandler, diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index a259182540..ff25ae7a26 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -45,7 +45,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -98,7 +98,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCardDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, @Mock private val addressLookupDelegate: AddressLookupDelegate, ) { @@ -1210,7 +1210,7 @@ internal class DefaultCardDelegateTest( genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), paymentMethod: PaymentMethod = PaymentMethod(type = PaymentMethodTypes.SCHEME), - analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, + analyticsRepository: AnalyticsRepository = this.analyticsRepository, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, addressLookupDelegate: AddressLookupDelegate = this.addressLookupDelegate diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index 7efc73a8eb..ad22d1a039 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -78,7 +78,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredCardDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler ) { @@ -451,7 +451,7 @@ internal class StoredCardDelegateTest( cardEncryptor: BaseCardEncryptor = this.cardEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), storedPaymentMethod: StoredPaymentMethod = getStoredPaymentMethod(), - analyticsRepository: OldAnalyticsRepository = this.analyticsRepository, + analyticsRepository: AnalyticsRepository = this.analyticsRepository, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, ): StoredCardDelegate { diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt index b69f96709f..6b26588fd1 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt @@ -35,11 +35,11 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +67,7 @@ class CashAppPayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -118,7 +118,7 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -204,7 +204,7 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -291,7 +291,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -388,7 +388,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt index bc6d5c6175..6979720e40 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt @@ -33,7 +33,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -58,7 +58,7 @@ internal class DefaultCashAppPayDelegate @Suppress("LongParameterList") constructor( private val submitHandler: SubmitHandler, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt index 4dc7c5f366..ccf9379d2d 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -34,7 +34,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class StoredCashAppPayDelegate( - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: StoredPaymentMethod, private val order: OrderRequest?, diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt index 079f732c00..36743232a4 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -73,7 +73,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCashAppPayDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val cashAppPayFactory: CashAppPayFactory, @Mock private val cashAppPay: CashAppPay, ) { diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt index 4aada9ae87..370cbd1328 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -44,7 +44,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class StoredCashAppPayDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, ) { private lateinit var delegate: StoredCashAppPayDelegate diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 68270e6556..7661cf2960 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -9,7 +9,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo -import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.NewAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -26,7 +26,7 @@ import kotlin.time.Duration.Companion.seconds @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsManager internal constructor( - private val analyticsRepository: AnalyticsRepository, + private val analyticsRepository: NewAnalyticsRepository, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt index e026145919..5a2849bee2 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import android.app.Application import androidx.annotation.RestrictTo -import com.adyen.checkout.components.core.internal.analytics.data.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.DefaultNewAnalyticsRepository import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService @@ -28,7 +28,7 @@ class AnalyticsManagerFactory { source: AnalyticsSource, sessionId: String? ) = AnalyticsManager( - analyticsRepository = DefaultAnalyticsRepository( + analyticsRepository = DefaultNewAnalyticsRepository( localInfoDataStore = InfoAnalyticsLocalDataStore(), localLogDataStore = LogAnalyticsLocalDataStore(), remoteDataStore = DefaultAnalyticsRemoteDataStore( diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt similarity index 96% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt index 1a372c3d41..ed3b5da9a7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt @@ -14,13 +14,13 @@ import com.adyen.checkout.components.core.internal.analytics.data.remote.Analyti import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider -internal class DefaultAnalyticsRepository( +internal class DefaultNewAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, private val localLogDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, -) : AnalyticsRepository { +) : NewAnalyticsRepository { override suspend fun fetchCheckoutAttemptId(): String? { val request = analyticsSetupProvider.provide() diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt similarity index 91% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt index e4fd2e09e2..6ffd10f182 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -internal interface AnalyticsRepository { +internal interface NewAnalyticsRepository { suspend fun fetchCheckoutAttemptId(): String? diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt deleted file mode 100644 index 2ee5d6486f..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsMapper.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 ararat on 4/3/2024. - */ - -package com.adyen.checkout.components.core.internal.analytics.data.old - -import android.os.Build -import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.BuildConfig -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import java.util.Locale - -// TODO: Remove this file -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AnalyticsMapper { -// @Suppress("LongParameterList") -// internal fun getAnalyticsSetupRequest( -// packageName: String, -// locale: Locale, -// source: AnalyticsSource, -// amount: Amount?, -// screenWidth: Long, -// paymentMethods: List, -// sessionId: String?, -// ): AnalyticsSetupRequest { -// return AnalyticsSetupRequest( -// version = actualVersion, -// channel = ANDROID_CHANNEL, -// platform = actualPlatform, -// locale = locale.toString(), -// component = getComponentQueryParameter(source), -// flavor = getFlavorQueryParameter(source), -// deviceBrand = Build.BRAND, -// deviceModel = Build.MODEL, -// referrer = packageName, -// systemVersion = Build.VERSION.SDK_INT.toString(), -// screenWidth = screenWidth, -// paymentMethods = paymentMethods, -// amount = amount, -// // unused for Android -// containerWidth = null, -// sessionId = sessionId, -// ) -// } -// -// @VisibleForTesting -// internal fun getFlavorQueryParameter(source: AnalyticsSource): String { -// return when (source) { -// is AnalyticsSource.DropIn -> Flavor.DROP_IN -// is AnalyticsSource.PaymentComponent -> { -// if (source.isCreatedByDropIn) Flavor.DROP_IN else Flavor.COMPONENTS -// } -// }.value -// } -// -// @VisibleForTesting -// internal fun getComponentQueryParameter(source: AnalyticsSource): String { -// return when (source) { -// is AnalyticsSource.DropIn -> DROP_IN_COMPONENT -// is AnalyticsSource.PaymentComponent -> source.paymentMethodType -// } -// } -// -// private enum class Flavor(val value: String) { -// DROP_IN("dropin"), -// COMPONENTS("components") -// } -// -// companion object { -// private const val DROP_IN_COMPONENT = "dropin" -// private const val ANDROID_CHANNEL = "android" -// -// // these params are prefixed with actual because cross platform SDKs will override them so they are not -// // technically constants -// private var actualPlatform = AnalyticsPlatform.ANDROID.value -// private var actualVersion = BuildConfig.CHECKOUT_VERSION -// -// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -// fun overrideForCrossPlatform( -// platform: AnalyticsPlatform, -// version: String, -// ) { -// this.actualPlatform = platform.value -// this.actualVersion = version -// } -// -// @VisibleForTesting -// internal fun resetToDefaults() { -// actualPlatform = AnalyticsPlatform.ANDROID.value -// actualVersion = BuildConfig.CHECKOUT_VERSION -// } -// } -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index f6b50c3af9..379bf4aa39 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -21,7 +21,7 @@ internal class AnalyticsTrackRequestProvider { logList: List, ): AnalyticsTrackRequest { return AnalyticsTrackRequest( - channel = AnalyticsPlatformParams.channel, + channel = AnalyticsPlatformParams.CHANNEL, platform = AnalyticsPlatformParams.platform, info = infoList.map { event -> event.mapToTrackEvent() }, logs = logList.map { event -> event.mapToTrackEvent() }, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt new file mode 100644 index 0000000000..40f05dc634 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt @@ -0,0 +1,96 @@ +/* + * 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 6/3/2024. + */ + +package com.adyen.checkout.components.core.internal.data.api + +import android.os.Build +import androidx.annotation.RestrictTo +import androidx.annotation.VisibleForTesting +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.BuildConfig +import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatform +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource +import java.util.Locale + +// TODO: Remove this file +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class AnalyticsMapper { + @Suppress("LongParameterList") + internal fun getAnalyticsSetupRequest( + packageName: String, + locale: Locale, + source: AnalyticsSource, + amount: Amount?, + screenWidth: Long, + paymentMethods: List, + sessionId: String?, + ): AnalyticsSetupRequest { + return AnalyticsSetupRequest( + version = actualVersion, + channel = ANDROID_CHANNEL, + platform = actualPlatform, + locale = locale.toString(), + component = getComponentQueryParameter(source), + flavor = getFlavorQueryParameter(source), + deviceBrand = Build.BRAND, + deviceModel = Build.MODEL, + referrer = packageName, + systemVersion = Build.VERSION.SDK_INT.toString(), + screenWidth = screenWidth.toInt(), + paymentMethods = paymentMethods, + amount = amount, + // unused for Android + containerWidth = null, + sessionId = sessionId, + ) + } + + @VisibleForTesting + internal fun getFlavorQueryParameter(source: AnalyticsSource): String { + return "stub - this class will be deleted" + } + + @VisibleForTesting + internal fun getComponentQueryParameter(source: AnalyticsSource): String { + return when (source) { + is AnalyticsSource.DropIn -> DROP_IN_COMPONENT + is AnalyticsSource.PaymentComponent -> source.paymentMethodType + } + } + + private enum class Flavor(val value: String) { + DROP_IN("dropin"), + COMPONENTS("components") + } + + companion object { + private const val DROP_IN_COMPONENT = "dropin" + private const val ANDROID_CHANNEL = "android" + + // these params are prefixed with actual because cross platform SDKs will override them so they are not + // technically constants + private var actualPlatform = AnalyticsPlatform.ANDROID.value + private var actualVersion = BuildConfig.CHECKOUT_VERSION + + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + fun overrideForCrossPlatform( + platform: AnalyticsPlatform, + version: String, + ) { + this.actualPlatform = platform.value + this.actualVersion = version + } + + @VisibleForTesting + internal fun resetToDefaults() { + actualPlatform = AnalyticsPlatform.ANDROID.value + actualVersion = BuildConfig.CHECKOUT_VERSION + } + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt similarity index 92% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt index b278776052..06b54ba712 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/OldAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt @@ -12,7 +12,7 @@ import androidx.annotation.RestrictTo // TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -interface OldAnalyticsRepository { +interface AnalyticsRepository { suspend fun setupAnalytics() fun getCheckoutAttemptId(): String? diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt similarity index 97% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt index 1c7362e2ab..773f7c7025 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt @@ -6,7 +6,7 @@ * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data.old +package com.adyen.checkout.components.core.internal.data.api import android.app.Application import androidx.annotation.RestrictTo diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt similarity index 92% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt index 7c6f4ea3a3..a7531495e5 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/old/DefaultOldAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt @@ -3,15 +3,14 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by ararat on 4/3/2024. + * Created by oscars on 6/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data.old +package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.ALL import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.NONE @@ -20,11 +19,11 @@ import com.adyen.checkout.core.internal.util.adyenLog // TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class DefaultOldAnalyticsRepository( +class DefaultAnalyticsRepository( private val analyticsRepositoryData: AnalyticsRepositoryData, private val analyticsService: AnalyticsService, private val analyticsMapper: AnalyticsMapper, -) : OldAnalyticsRepository { +) : AnalyticsRepository { @VisibleForTesting internal var state: State = State.Uninitialized diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt similarity index 89% rename from components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt rename to components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt index c951154b2a..ab3cb03f56 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultOldAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt @@ -9,12 +9,9 @@ package com.adyen.checkout.components.core.internal.data.api import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.exception.HttpException import com.adyen.checkout.test.LoggingExtension @@ -41,13 +38,13 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class, LoggingExtension::class) -internal class DefaultOldAnalyticsRepositoryTest( +internal class DefaultAnalyticsRepositoryTest( @Mock private val analyticsService: AnalyticsService, ) { private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() - private lateinit var analyticsRepository: DefaultOldAnalyticsRepository + private lateinit var analyticsRepository: DefaultAnalyticsRepository @BeforeEach fun before() = runTest { @@ -59,7 +56,7 @@ internal class DefaultOldAnalyticsRepositoryTest( @Test fun `when repository is only instantiated then state is set as uninitialized`() = runTest { - assertEquals(DefaultOldAnalyticsRepository.State.Uninitialized, analyticsRepository.state) + assertEquals(DefaultAnalyticsRepository.State.Uninitialized, analyticsRepository.state) } @Nested @@ -84,7 +81,7 @@ internal class DefaultOldAnalyticsRepositoryTest( @Test fun `and AnalyticsService is successful then state is set as initialized`() = runTest { analyticsRepository.setupAnalytics() - assertEquals(DefaultOldAnalyticsRepository.State.Ready, analyticsRepository.state) + assertEquals(DefaultAnalyticsRepository.State.Ready, analyticsRepository.state) } @Test @@ -97,7 +94,7 @@ internal class DefaultOldAnalyticsRepositoryTest( fun `and AnalyticsService returns an error then state is set as error`() = runTest { whenever(analyticsService.setupAnalytics(any(), any())) doThrow HttpException(1, "error_message", null) analyticsRepository.setupAnalytics() - assertEquals(DefaultOldAnalyticsRepository.State.Failed, analyticsRepository.state) + assertEquals(DefaultAnalyticsRepository.State.Failed, analyticsRepository.state) } @Test @@ -156,7 +153,7 @@ internal class DefaultOldAnalyticsRepositoryTest( analyticsRepository.setupAnalytics() assertEquals( - DefaultOldAnalyticsRepository.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, + DefaultAnalyticsRepository.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, analyticsRepository.getCheckoutAttemptId(), ) } @@ -175,8 +172,8 @@ internal class DefaultOldAnalyticsRepositoryTest( screenWidth: Int = SCREEN_WIDTH, paymentMethods: List = PAYMENT_METHODS, sessionId: String? = TEST_SESSION_ID, - ): DefaultOldAnalyticsRepository { - return DefaultOldAnalyticsRepository( + ): DefaultAnalyticsRepository { + return DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( level = level, packageName = packageName, @@ -196,7 +193,7 @@ internal class DefaultOldAnalyticsRepositoryTest( companion object { private const val PACKAGE_NAME = "com.adyen.checkout.test" private val LOCALE = Locale.US - private val ANALYTICS_SOURCE = AnalyticsSource.DropIn + private val ANALYTICS_SOURCE = AnalyticsSource.DropIn(emptyList()) private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_AMOUNT = Amount("USD", 1337) private const val SCREEN_WIDTH = 1080 diff --git a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt index b77cd45c23..34f88479cd 100644 --- a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt +++ b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.ConvenienceStoresJPPaymentMethod import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -29,7 +29,7 @@ class ConvenienceStoresJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : EContextComponentProvider< ConvenienceStoresJPComponent, ConvenienceStoresJPConfiguration, diff --git a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt index 07bd8d53d8..d3e8c6bae9 100644 --- a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt +++ b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.DotpayPaymentMethod import com.adyen.checkout.dotpay.DotpayComponent @@ -29,7 +29,7 @@ class DotpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = DotpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt index 442524cebf..e3b2cc6aa0 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.ComponentCallback import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -115,7 +115,7 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: OldAnalyticsRepository, + analyticsRepository: AnalyticsRepository, onRedirect: () -> Unit, ): PaymentComponent { return when { @@ -181,7 +181,7 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: OldAnalyticsRepository, + analyticsRepository: AnalyticsRepository, onRedirect: () -> Unit, ): PaymentComponent { return when { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index 5ad6cbfbf8..8ff6142543 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -24,7 +24,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -58,7 +58,7 @@ import kotlinx.coroutines.launch internal class DropInViewModel( private val bundleHandler: DropInSavedStateHandleContainer, private val orderStatusRepository: OrderStatusRepository, - internal val analyticsRepository: OldAnalyticsRepository, + internal val analyticsRepository: AnalyticsRepository, private val initialDropInParams: DropInParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : ViewModel() { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index a51f7e5cfb..0f5372f47f 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -14,13 +14,12 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusService -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.util.screenWidthPixels import com.adyen.checkout.core.internal.data.api.HttpClientFactory import com.adyen.checkout.core.internal.util.LocaleProvider @@ -55,7 +54,7 @@ internal class DropInViewModelFactory( val httpClient = HttpClientFactory.getHttpClient(dropInParams.environment) val orderStatusRepository = OrderStatusRepository(OrderStatusService(httpClient)) - val analyticsRepository = DefaultOldAnalyticsRepository( + val analyticsRepository = DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( level = dropInParams.analyticsParams.level, packageName = packageName, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt index a97dcd59b2..599a315f90 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt @@ -26,11 +26,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +67,7 @@ abstract class EContextComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: OldAnalyticsRepository?, + private val analyticsRepository: AnalyticsRepository?, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, SessionPaymentComponentProvider< @@ -100,7 +100,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -192,7 +192,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index c5b60cac91..e2f50e7900 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -50,7 +50,7 @@ internal class DefaultEContextDelegate< override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> EContextPaymentMethodT, private val componentStateFactory: ( diff --git a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt index 25a4a212df..9e44355b85 100644 --- a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt +++ b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultEContextDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt index fc903a7cb3..844cbdf7ed 100644 --- a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt +++ b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EntercashPaymentMethod import com.adyen.checkout.entercash.EntercashComponent @@ -29,7 +29,7 @@ class EntercashComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider< EntercashComponent, EntercashConfiguration, diff --git a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt index 46f735b811..f87ed9ccee 100644 --- a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt +++ b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EPSPaymentMethod import com.adyen.checkout.eps.EPSComponent @@ -29,7 +29,7 @@ class EPSComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = EPSComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt index 5a978f3c99..6bb51f184f 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt @@ -19,11 +19,11 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -59,7 +59,7 @@ class GiftCardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -100,7 +100,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -195,7 +195,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt index 9751fd3d33..da2611d89e 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -60,7 +60,7 @@ internal class DefaultGiftCardDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val publicKeyRepository: PublicKeyRepository, override val componentParams: GiftCardComponentParams, private val cardEncryptor: BaseCardEncryptor, diff --git a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt index 978381aaa4..0a6ad6e5e5 100644 --- a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt +++ b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.OrderResponse import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -59,7 +59,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultGiftCardDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt index 0a0264f325..197a453d5c 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt @@ -23,11 +23,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentMethodAvailabilityCheck import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -64,7 +64,7 @@ class GooglePayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -103,7 +103,7 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -194,7 +194,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt index ca05f63caf..8ab1f14270 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -48,7 +48,7 @@ internal class DefaultGooglePayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: GooglePayComponentParams, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, ) : GooglePayDelegate { private val _componentStateFlow = MutableStateFlow(createComponentState()) diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt index d48810d26f..dff2e9fdf9 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Configuration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.GooglePayPaymentMethod import com.adyen.checkout.core.Environment @@ -48,7 +48,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultGooglePayDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, ) { private lateinit var delegate: DefaultGooglePayDelegate diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt index ec68570eb8..2c88bd6328 100644 --- a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt +++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.IdealPaymentMethod import com.adyen.checkout.ideal.IdealComponent @@ -29,7 +29,7 @@ class IdealComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = IdealComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index 3d596fb9d4..b746c2185f 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -23,11 +23,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +56,7 @@ class InstantPaymentComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -94,7 +94,7 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -185,7 +185,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index d060320df5..ef05504d0b 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails @@ -37,7 +37,7 @@ internal class DefaultInstantPaymentDelegate( private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: InstantComponentParams, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, ) : InstantPaymentDelegate { override val componentStateFlow: StateFlow = MutableStateFlow(createComponentState()) diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index ed4572a4dc..29dd8d9686 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper @@ -42,7 +42,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) class DefaultInstantPaymentDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, ) { private lateinit var delegate: DefaultInstantPaymentDelegate diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt index 6a627f8168..ef4d249ceb 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt @@ -27,11 +27,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -69,7 +69,7 @@ abstract class IssuerListComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: OldAnalyticsRepository?, + private val analyticsRepository: AnalyticsRepository?, private val hideIssuerLogosDefaultValue: Boolean = false, private val localeProvider: LocaleProvider = LocaleProvider(), ) : @@ -104,7 +104,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -195,7 +195,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -284,7 +284,7 @@ constructor( paymentMethod: PaymentMethod, order: Order?, savedStateHandle: SavedStateHandle, - analyticsRepository: OldAnalyticsRepository, + analyticsRepository: AnalyticsRepository, ): DefaultIssuerListDelegate { return DefaultIssuerListDelegate( observerRepository = PaymentObserverRepository(), diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index 7d203c7a41..d4ff7685ea 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @@ -45,7 +45,7 @@ internal class DefaultIssuerListDelegate< override val componentParams: IssuerListComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> IssuerListPaymentMethodT, private val componentStateFactory: ( diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index d979ac1d70..b9d877d93b 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.issuerlist.IssuerListViewType @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultIssuerListDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt index 1d6a2ab9b6..45885eecb0 100644 --- a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt +++ b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.MolpayPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class MolpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider( componentClass = MolpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt index 579885f5fc..22f092db16 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt @@ -26,11 +26,11 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -68,7 +68,7 @@ abstract class OnlineBankingComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: OldAnalyticsRepository?, + private val analyticsRepository: AnalyticsRepository?, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -101,7 +101,7 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -199,7 +199,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt index 2cb14434d8..d4633d09de 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod @@ -53,7 +53,7 @@ internal class DefaultOnlineBankingDelegate< private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: ButtonComponentParams, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val termsAndConditionsUrl: String, private val submitHandler: SubmitHandler, private val paymentMethodFactory: () -> IssuerListPaymentMethodT, diff --git a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt index ba85bc9894..a6505d1887 100644 --- a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt +++ b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -54,7 +54,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultOnlineBankingDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val context: Context, @Mock private val pdfOpener: PdfOpener, @Mock private val submitHandler: SubmitHandler, diff --git a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt index 856f05dc67..467851265b 100644 --- a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt +++ b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingCZPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingCZComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : OnlineBankingComponentProvider< OnlineBankingCZComponent, OnlineBankingCZConfiguration, diff --git a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt index 77de8f9e60..3474600925 100644 --- a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt +++ b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingJPPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : EContextComponentProvider< OnlineBankingJPComponent, OnlineBankingJPConfiguration, diff --git a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt index 3367491e68..207acfc642 100644 --- a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt +++ b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingPLPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingPLComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider< OnlineBankingPLComponent, OnlineBankingPLConfiguration, diff --git a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt index b67fad0811..69dc9425aa 100644 --- a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt +++ b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingSKPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingSKComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : OnlineBankingComponentProvider< OnlineBankingSKComponent, OnlineBankingSKConfiguration, diff --git a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt index 30b815a4f1..c0e49f8f40 100644 --- a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt +++ b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OpenBankingPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class OpenBankingComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : IssuerListComponentProvider< OpenBankingComponent, OpenBankingConfiguration, diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt index ed164a3d07..957bc58e1e 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +56,7 @@ class PayByBankComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -93,7 +93,7 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -184,7 +184,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt index cf90f62f57..f199dc34c8 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.paymentmethod.PayByBankPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -42,7 +42,7 @@ internal class DefaultPayByBankDelegate( private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: GenericComponentParams, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, ) : PayByBankDelegate { diff --git a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt index 18c9e948ae..7a3d149cba 100644 --- a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt +++ b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -52,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultPayByBankDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt index de66ec1552..7b4839a19b 100644 --- a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt +++ b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.PayEasyPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class PayEasyComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : EContextComponentProvider( componentClass = PayEasyComponent::class.java, dropInOverrideParams = dropInOverrideParams, diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt index 64a7ec9dda..074da2cc33 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class SepaComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -96,7 +96,7 @@ constructor( componentConfiguration = checkoutConfiguration.getSepaConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -188,7 +188,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt index f8caca2d65..ad521141d1 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -40,7 +40,7 @@ internal class DefaultSepaDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val submitHandler: SubmitHandler, ) : SepaDelegate { diff --git a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt index 37eb8d49c6..2b7849ec65 100644 --- a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt +++ b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod @@ -51,7 +51,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultSepaDelegateTest( - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, @Mock private val submitHandler: SubmitHandler, ) { diff --git a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt index 507c6906ee..871e3316c6 100644 --- a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt +++ b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.SevenElevenPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class SevenElevenComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: OldAnalyticsRepository? = null, + analyticsRepository: AnalyticsRepository? = null, ) : EContextComponentProvider< SevenElevenComponent, SevenElevenConfiguration, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt index be90ec7ca2..b7dde005df 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt @@ -22,11 +22,11 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository -import com.adyen.checkout.components.core.internal.analytics.data.old.AnalyticsRepositoryData import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.analytics.data.old.DefaultOldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +57,7 @@ class UPIComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: OldAnalyticsRepository? = null, + private val analyticsRepository: AnalyticsRepository? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -90,7 +90,7 @@ constructor( componentConfiguration = checkoutConfiguration.getUPIConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, @@ -182,7 +182,7 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultOldAnalyticsRepository( + val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( analyticsRepositoryData = AnalyticsRepositoryData( application = application, componentParams = componentParams, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt index e1a99ecdcd..5aa87e6805 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.UPIPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -38,7 +38,7 @@ import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultUPIDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: OldAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index 2c66829f73..7a3d27144b 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.OldAnalyticsRepository +import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -56,7 +56,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultUPIDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: OldAnalyticsRepository, + @Mock private val analyticsRepository: AnalyticsRepository, ) { private lateinit var delegate: DefaultUPIDelegate From 85cc709dff573d058f8b22b616e2241878127f19 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 11:26:47 +0100 Subject: [PATCH 178/272] Fix unit tests and lint errors COAND-844 --- .../Adyen3DS2ComponentParamsMapperTest.kt | 4 +- ...ACHDirectDebitComponentParamsMapperTest.kt | 4 +- .../ui/model/BcmcComponentParamsMapperTest.kt | 4 +- .../model/BoletoComponentParamsMapperTest.kt | 4 +- .../ui/model/CardComponentParamsMapperTest.kt | 5 +- .../CashAppPayComponentParamsMapperTest.kt | 5 +- .../{AnalyticsEvents.kt => AnalyticsEvent.kt} | 1 + .../analytics/AnalyticsPlatformParams.kt | 2 +- .../core/internal/analytics/GenericEvents.kt | 6 +- .../analytics/data/remote/AnalyticsService.kt | 3 +- .../remote/DefaultAnalyticsSetupProvider.kt | 2 +- .../core/internal/data/api/AnalyticsMapper.kt | 5 +- .../internal/data/api/AnalyticsRepository.kt | 1 + .../data/api/AnalyticsRepositoryData.kt | 1 + .../data/api/DefaultAnalyticsRepository.kt | 1 + .../internal/data/api/AnalyticsMapperTest.kt | 179 --------------- .../api/DefaultAnalyticsRepositoryTest.kt | 204 ------------------ .../model/ButtonComponentParamsMapperTest.kt | 4 +- .../model/GenericComponentParamsMapperTest.kt | 4 +- .../internal/ui/model/DropInParamsMapper.kt | 5 +- .../ui/model/DropInParamsMapperTest.kt | 4 +- .../GiftCardComponentParamsMapperTest.kt | 4 +- .../GooglePayComponentParamsMapperTest.kt | 10 +- .../internal/util/GooglePayUtilsTest.kt | 4 +- .../IssuerListComponentParamsMapperTest.kt | 6 +- 25 files changed, 53 insertions(+), 419 deletions(-) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/{AnalyticsEvents.kt => AnalyticsEvent.kt} (98%) delete mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt delete mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/model/Adyen3DS2ComponentParamsMapperTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/model/Adyen3DS2ComponentParamsMapperTest.kt index 7b4bd50998..545255bf63 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/model/Adyen3DS2ComponentParamsMapperTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/model/Adyen3DS2ComponentParamsMapperTest.kt @@ -91,7 +91,7 @@ internal class Adyen3DS2ComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -223,7 +223,7 @@ internal class Adyen3DS2ComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, uiCustomization: UiCustomization? = null, diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/model/ACHDirectDebitComponentParamsMapperTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/model/ACHDirectDebitComponentParamsMapperTest.kt index 19c05660f3..632e32b591 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/model/ACHDirectDebitComponentParamsMapperTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/model/ACHDirectDebitComponentParamsMapperTest.kt @@ -88,7 +88,7 @@ internal class ACHDirectDebitComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "EUR", @@ -347,7 +347,7 @@ internal class ACHDirectDebitComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, diff --git a/bcmc/src/test/java/com/adyen/checkout/bcmc/internal/ui/model/BcmcComponentParamsMapperTest.kt b/bcmc/src/test/java/com/adyen/checkout/bcmc/internal/ui/model/BcmcComponentParamsMapperTest.kt index 46704b9fc5..009c3e3777 100644 --- a/bcmc/src/test/java/com/adyen/checkout/bcmc/internal/ui/model/BcmcComponentParamsMapperTest.kt +++ b/bcmc/src/test/java/com/adyen/checkout/bcmc/internal/ui/model/BcmcComponentParamsMapperTest.kt @@ -108,7 +108,7 @@ internal class BcmcComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -289,7 +289,7 @@ internal class BcmcComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, diff --git a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/model/BoletoComponentParamsMapperTest.kt b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/model/BoletoComponentParamsMapperTest.kt index 57e3b68190..07a38106f9 100644 --- a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/model/BoletoComponentParamsMapperTest.kt +++ b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/model/BoletoComponentParamsMapperTest.kt @@ -90,7 +90,7 @@ internal class BoletoComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "EUR", @@ -257,7 +257,7 @@ internal class BoletoComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, addressParams: AddressParams = AddressParams.FullAddress( diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/model/CardComponentParamsMapperTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/model/CardComponentParamsMapperTest.kt index ede86baf05..742e787f4c 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/model/CardComponentParamsMapperTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/model/CardComponentParamsMapperTest.kt @@ -112,6 +112,7 @@ internal class CardComponentParamsMapperTest { shopperLocale = Locale.FRANCE, environment = Environment.APSE, clientKey = TEST_CLIENT_KEY_2, + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2), isHolderNameRequired = true, supportedCardBrands = listOf( CardBrand(cardType = CardType.DINERS), @@ -162,7 +163,7 @@ internal class CardComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "EUR", @@ -560,7 +561,7 @@ internal class CardComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isHolderNameRequired: Boolean = false, diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/model/CashAppPayComponentParamsMapperTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/model/CashAppPayComponentParamsMapperTest.kt index e17aa3766a..7a6634e458 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/model/CashAppPayComponentParamsMapperTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/model/CashAppPayComponentParamsMapperTest.kt @@ -91,6 +91,7 @@ internal class CashAppPayComponentParamsMapperTest { shopperLocale = Locale.FRANCE, environment = Environment.APSE, clientKey = TEST_CLIENT_KEY_2, + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2), cashAppPayEnvironment = CashAppPayEnvironment.PRODUCTION, returnUrl = "https://google.com", showStorePaymentField = false, @@ -135,7 +136,7 @@ internal class CashAppPayComponentParamsMapperTest { environment = Environment.EUROPE, cashAppPayEnvironment = CashAppPayEnvironment.PRODUCTION, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "EUR", @@ -446,7 +447,7 @@ internal class CashAppPayComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt similarity index 98% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt index 0f8297f1d7..f8e11eec9e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt @@ -60,6 +60,7 @@ sealed interface AnalyticsEvent { } // TODO: Does it inherit the restrictTo from the Type? +@Suppress("ForbiddenComment") typealias InfoEventType = AnalyticsEvent.Info.Type typealias LogEventType = AnalyticsEvent.Log.Type diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt index e8b450563c..d019061979 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.BuildConfig @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object AnalyticsPlatformParams { - const val channel = "android" + const val CHANNEL = "android" var platform = AnalyticsPlatform.ANDROID.value private set diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index 7dea94c027..2a06ecfce4 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo +@Suppress("TooManyFunctions") @OptIn(DirectAnalyticsEventCreation::class) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object GenericEvents { @@ -56,8 +57,9 @@ object GenericEvents { target = target, ) - // TODO: This might move to the Input fields itself and not be bound to any component. But we should find a way to define targets. - // TODO: We could create an enum for target per module + // TODO: This might move to the Input fields itself and not be bound to any component. But we should find a way to + // define targets. We could create an enum for target per module + @Suppress("ForbiddenComment") fun focus( component: String, target: String, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt index 53de222df8..ed6e99e1ef 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt @@ -38,6 +38,7 @@ class AnalyticsService( ) } + @Suppress("ForbiddenComment") // TODO: Add tests internal suspend fun sendEvents( request: AnalyticsTrackRequest, @@ -45,7 +46,7 @@ class AnalyticsService( clientKey: String, ) { httpClient.post( - path = "v3/analytics/${checkoutAttemptId}", + path = "v3/analytics/$checkoutAttemptId", queryParameters = mapOf("clientKey" to clientKey), body = request, requestSerializer = AnalyticsTrackRequest.SERIALIZER, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt index 0c6b2e4a63..f2e0f982b9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt @@ -25,7 +25,7 @@ internal class DefaultAnalyticsSetupProvider( override fun provide(): AnalyticsSetupRequest { return AnalyticsSetupRequest( version = AnalyticsPlatformParams.version, - channel = AnalyticsPlatformParams.channel, + channel = AnalyticsPlatformParams.CHANNEL, platform = AnalyticsPlatformParams.platform, locale = componentParams.shopperLocale.toString(), component = getComponentQueryParameter(source), diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt index 40f05dc634..f91d02a42a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt @@ -14,10 +14,11 @@ import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.BuildConfig import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatform -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import java.util.Locale +@Suppress("ForbiddenComment") // TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsMapper { @@ -51,6 +52,7 @@ class AnalyticsMapper { ) } + @Suppress("FunctionOnlyReturningConstant", "UnusedParameter") @VisibleForTesting internal fun getFlavorQueryParameter(source: AnalyticsSource): String { return "stub - this class will be deleted" @@ -64,6 +66,7 @@ class AnalyticsMapper { } } + @Suppress("unused") private enum class Flavor(val value: String) { DROP_IN("dropin"), COMPONENTS("components") diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt index 06b54ba712..0c2ff7a090 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo +@Suppress("ForbiddenComment") // TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) interface AnalyticsRepository { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt index 773f7c7025..8b47022e33 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.internal.ui.model.ComponentParams import com.adyen.checkout.components.core.internal.util.screenWidthPixels import java.util.Locale +@Suppress("ForbiddenComment") // TODO: Remove this file @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class AnalyticsRepositoryData( diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt index a7531495e5..0e8100bc13 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt @@ -18,6 +18,7 @@ import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog // TODO: Remove this file +@Suppress("unused", "ForbiddenComment") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class DefaultAnalyticsRepository( private val analyticsRepositoryData: AnalyticsRepositoryData, diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt deleted file mode 100644 index e3ac4f941c..0000000000 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapperTest.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2022 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by josephj on 25/11/2022. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import android.os.Build -import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.junit.jupiter.MockitoExtension -import java.util.Locale - -@ExtendWith(MockitoExtension::class) -internal class AnalyticsMapperTest { - -// private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() -// -// @BeforeEach -// fun beforeEach() { -// AnalyticsMapper.resetToDefaults() -// } -// -// @Nested -// @DisplayName("when getFlavorQueryParameter is called and") -// inner class GetFlavorQueryParameterTest { -// -// @Test -// fun `source is drop-in then returned value is dropin`() { -// val actual = analyticsMapper.getFlavorQueryParameter(AnalyticsSource.DropIn) -// assertEquals("dropin", actual) -// } -// -// @Test -// fun `source is a component created from drop-in then returned value is dropin`() { -// val actual = analyticsMapper.getFlavorQueryParameter( -// AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = true, -// paymentMethod = PaymentMethod(), -// ) -// ) -// assertEquals("dropin", actual) -// } -// -// @Test -// fun `source is a component not created from drop-in then returned value is components`() { -// val actual = analyticsMapper.getFlavorQueryParameter( -// AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = false, -// paymentMethod = PaymentMethod(), -// ) -// ) -// assertEquals("components", actual) -// } -// } -// -// @Nested -// @DisplayName("when getComponentQueryParameter is called and") -// inner class GetComponentQueryParameterTest { -// -// @Test -// fun `source is drop-in then returned value is dropin`() { -// val actual = analyticsMapper.getComponentQueryParameter(AnalyticsSource.DropIn) -// assertEquals("dropin", actual) -// } -// -// @Test -// fun `source is a component with a payment method then returned value is the payment method type`() { -// val actual = analyticsMapper.getComponentQueryParameter( -// AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = true, -// paymentMethod = PaymentMethod(type = "PAYMENT_METHOD_TYPE"), -// ) -// ) -// assertEquals("PAYMENT_METHOD_TYPE", actual) -// } -// -// @Test -// fun `source is a component with a stored payment method then returned value is the stored payment method type`() { -// val actual = analyticsMapper.getComponentQueryParameter( -// AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = false, -// storedPaymentMethod = StoredPaymentMethod(type = "STORED_PAYMENT_METHOD_TYPE"), -// ) -// ) -// assertEquals("STORED_PAYMENT_METHOD_TYPE", actual) -// } -// } -// -// @Nested -// @DisplayName("when getQueryParameters is called") -// inner class GetQueryParametersTest { -// -// @Test -// fun `then returned values should match expected`() { -// val actual = analyticsMapper.getAnalyticsSetupRequest( -// packageName = "PACKAGE_NAME", -// locale = Locale("en", "US"), -// source = AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = false, -// PaymentMethod(type = "PAYMENT_METHOD_TYPE") -// ), -// amount = Amount("USD", 1337), -// screenWidth = 1286, -// paymentMethods = listOf("scheme", "googlepay"), -// sessionId = "SESSION_ID", -// ) -// -// val expected = AnalyticsSetupRequest( -// version = "5.3.1", -// channel = "android", -// platform = "android", -// locale = "en_US", -// component = "PAYMENT_METHOD_TYPE", -// flavor = "components", -// deviceBrand = "null", -// deviceModel = "null", -// referrer = "PACKAGE_NAME", -// systemVersion = Build.VERSION.SDK_INT.toString(), -// containerWidth = null, -// screenWidth = 1286, -// paymentMethods = listOf("scheme", "googlepay"), -// amount = Amount("USD", 1337), -// sessionId = "SESSION_ID", -// ) -// -// assertEquals(expected.toString(), actual.toString()) -// } -// } -// -// @Test -// fun `when cross platform parameters are overridden, then returned values should match expected`() { -// AnalyticsMapper.overrideForCrossPlatform(AnalyticsPlatform.FLUTTER, "some test version") -// val actual = analyticsMapper.getAnalyticsSetupRequest( -// packageName = "PACKAGE_NAME", -// locale = Locale("en", "US"), -// source = AnalyticsSource.PaymentComponent( -// isCreatedByDropIn = false, -// PaymentMethod(type = "PAYMENT_METHOD_TYPE") -// ), -// amount = Amount("USD", 1337), -// screenWidth = 1286, -// paymentMethods = listOf("scheme", "googlepay"), -// sessionId = "SESSION_ID", -// ) -// -// val expected = AnalyticsSetupRequest( -// version = "some test version", -// channel = "android", -// platform = "flutter", -// locale = "en_US", -// component = "PAYMENT_METHOD_TYPE", -// flavor = "components", -// deviceBrand = "null", -// deviceModel = "null", -// referrer = "PACKAGE_NAME", -// systemVersion = Build.VERSION.SDK_INT.toString(), -// containerWidth = null, -// screenWidth = 1286, -// paymentMethods = listOf("scheme", "googlepay"), -// amount = Amount("USD", 1337), -// sessionId = "SESSION_ID", -// ) -// -// assertEquals(expected.toString(), actual.toString()) -// } -} diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt deleted file mode 100644 index ab3cb03f56..0000000000 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepositoryTest.kt +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2023 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by josephj on 18/7/2023. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel -import com.adyen.checkout.core.exception.HttpException -import com.adyen.checkout.test.LoggingExtension -import com.adyen.checkout.test.TestDispatcherExtension -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.Mock -import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.never -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import java.util.Locale - -@OptIn(ExperimentalCoroutinesApi::class) -@ExtendWith(MockitoExtension::class, TestDispatcherExtension::class, LoggingExtension::class) -internal class DefaultAnalyticsRepositoryTest( - @Mock private val analyticsService: AnalyticsService, -) { - - private val analyticsMapper: AnalyticsMapper = AnalyticsMapper() - - private lateinit var analyticsRepository: DefaultAnalyticsRepository - - @BeforeEach - fun before() = runTest { - analyticsRepository = getDefaultAnalyticsRepository() - whenever( - analyticsService.setupAnalytics(any(), any()), - ) doReturn AnalyticsSetupResponse(checkoutAttemptId = TEST_CHECKOUT_ATTEMPT_ID) - } - - @Test - fun `when repository is only instantiated then state is set as uninitialized`() = runTest { - assertEquals(DefaultAnalyticsRepository.State.Uninitialized, analyticsRepository.state) - } - - @Nested - @DisplayName("when setupAnalytics is called") - inner class InputDataChangedTest { - - @Test - fun `then AnalyticsService is called`() = runTest { - analyticsRepository.setupAnalytics() - val analyticsSetupRequest = analyticsMapper.getAnalyticsSetupRequest( - packageName = PACKAGE_NAME, - locale = LOCALE, - source = ANALYTICS_SOURCE, - amount = TEST_AMOUNT, - screenWidth = SCREEN_WIDTH.toLong(), - paymentMethods = PAYMENT_METHODS, - sessionId = TEST_SESSION_ID, - ) - verify(analyticsService).setupAnalytics(analyticsSetupRequest, TEST_CLIENT_KEY) - } - - @Test - fun `and AnalyticsService is successful then state is set as initialized`() = runTest { - analyticsRepository.setupAnalytics() - assertEquals(DefaultAnalyticsRepository.State.Ready, analyticsRepository.state) - } - - @Test - fun `and AnalyticsService is successful then checkoutAttemptId is set`() = runTest { - analyticsRepository.setupAnalytics() - assertEquals(TEST_CHECKOUT_ATTEMPT_ID, analyticsRepository.getCheckoutAttemptId()) - } - - @Test - fun `and AnalyticsService returns an error then state is set as error`() = runTest { - whenever(analyticsService.setupAnalytics(any(), any())) doThrow HttpException(1, "error_message", null) - analyticsRepository.setupAnalytics() - assertEquals(DefaultAnalyticsRepository.State.Failed, analyticsRepository.state) - } - - @Test - fun `and AnalyticsService returns an error then checkoutAttemptId is not set`() = runTest { - whenever(analyticsService.setupAnalytics(any(), any())) doThrow HttpException(1, "error_message", null) - analyticsRepository.setupAnalytics() - assertNull(analyticsRepository.getCheckoutAttemptId()) - } - - @Test - fun `multiple times then AnalyticsService is only called once`() = runTest { - analyticsRepository.setupAnalytics() - analyticsRepository.setupAnalytics() - analyticsRepository.setupAnalytics() - verify(analyticsService, times(1)).setupAnalytics(any(), any()) - } - - @Test - fun `multiple times and AnalyticsService returns an error then AnalyticsService is only called once`() = - runTest { - whenever(analyticsService.setupAnalytics(any(), any())) doThrow HttpException(1, "error_message", null) - analyticsRepository.setupAnalytics() - analyticsRepository.setupAnalytics() - analyticsRepository.setupAnalytics() - verify(analyticsService, times(1)).setupAnalytics(any(), any()) - } - - @Test - fun `and level is set to ALL then call is made`() = runTest { - analyticsRepository = getDefaultAnalyticsRepository( - level = AnalyticsParamsLevel.ALL, - ) - - analyticsRepository.setupAnalytics() - - verify(analyticsService, times(1)).setupAnalytics(any(), any()) - } - - @Test - fun `and level is set to NONE then call is not made`() = runTest { - analyticsRepository = getDefaultAnalyticsRepository( - level = AnalyticsParamsLevel.NONE, - ) - - analyticsRepository.setupAnalytics() - - verify(analyticsService, never()).setupAnalytics(any(), any()) - } - - @Test - fun `and level is set to NONE then checkoutAttemptId is not set`() = runTest { - analyticsRepository = getDefaultAnalyticsRepository( - level = AnalyticsParamsLevel.NONE, - ) - - analyticsRepository.setupAnalytics() - - assertEquals( - DefaultAnalyticsRepository.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, - analyticsRepository.getCheckoutAttemptId(), - ) - } - } - - @Suppress("LongParameterList") - private fun getDefaultAnalyticsRepository( - level: AnalyticsParamsLevel = AnalyticsParamsLevel.ALL, - packageName: String = PACKAGE_NAME, - locale: Locale = LOCALE, - source: AnalyticsSource = ANALYTICS_SOURCE, - analyticsService: AnalyticsService = this.analyticsService, - analyticsMapper: AnalyticsMapper = this.analyticsMapper, - clientKey: String = TEST_CLIENT_KEY, - amount: Amount = TEST_AMOUNT, - screenWidth: Int = SCREEN_WIDTH, - paymentMethods: List = PAYMENT_METHODS, - sessionId: String? = TEST_SESSION_ID, - ): DefaultAnalyticsRepository { - return DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - level = level, - packageName = packageName, - locale = locale, - source = source, - clientKey = clientKey, - amount = amount, - screenWidth = screenWidth, - paymentMethods = paymentMethods, - sessionId = sessionId, - ), - analyticsService = analyticsService, - analyticsMapper = analyticsMapper, - ) - } - - companion object { - private const val PACKAGE_NAME = "com.adyen.checkout.test" - private val LOCALE = Locale.US - private val ANALYTICS_SOURCE = AnalyticsSource.DropIn(emptyList()) - private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" - private val TEST_AMOUNT = Amount("USD", 1337) - private const val SCREEN_WIDTH = 1080 - private val PAYMENT_METHODS = listOf("bcmc", "blik", "boletobancario") - private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" - private const val TEST_SESSION_ID = "TEST_SESSION_ID" - } -} diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/ButtonComponentParamsMapperTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/ButtonComponentParamsMapperTest.kt index c76dc05813..dc725315d8 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/ButtonComponentParamsMapperTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/ButtonComponentParamsMapperTest.kt @@ -66,7 +66,7 @@ internal class ButtonComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "USD", @@ -228,7 +228,7 @@ internal class ButtonComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/GenericComponentParamsMapperTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/GenericComponentParamsMapperTest.kt index bb86aa2bbb..bce1246f83 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/GenericComponentParamsMapperTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/ui/model/GenericComponentParamsMapperTest.kt @@ -66,7 +66,7 @@ internal class GenericComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -200,7 +200,7 @@ internal class GenericComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, ): GenericComponentParams { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapper.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapper.kt index 1d74b86cd2..288e51cb07 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapper.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapper.kt @@ -27,7 +27,10 @@ internal class DropInParamsMapper { shopperLocale = getShopperLocale(checkoutConfiguration, sessionParams) ?: deviceLocale, environment = sessionParams?.environment ?: checkoutConfiguration.environment, clientKey = sessionParams?.clientKey ?: checkoutConfiguration.clientKey, - analyticsParams = AnalyticsParams(checkoutConfiguration.analyticsConfiguration), + analyticsParams = AnalyticsParams( + analyticsConfiguration = checkoutConfiguration.analyticsConfiguration, + clientKey = checkoutConfiguration.clientKey, + ), amount = sessionParams?.amount ?: checkoutConfiguration.amount, showPreselectedStoredPaymentMethod = dropInConfiguration?.showPreselectedStoredPaymentMethod ?: true, skipListWhenSinglePaymentMethod = dropInConfiguration?.skipListWhenSinglePaymentMethod ?: false, diff --git a/drop-in/src/test/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapperTest.kt b/drop-in/src/test/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapperTest.kt index 7b7179c765..7181f45c37 100644 --- a/drop-in/src/test/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapperTest.kt +++ b/drop-in/src/test/java/com/adyen/checkout/dropin/internal/ui/model/DropInParamsMapperTest.kt @@ -97,7 +97,7 @@ internal class DropInParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), amount = Amount( currency = "EUR", value = 49_00L, @@ -246,7 +246,7 @@ internal class DropInParamsMapperTest { environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, shopperLocale: Locale = DEVICE_LOCALE, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), amount: Amount? = null, showPreselectedStoredPaymentMethod: Boolean = true, skipListWhenSinglePaymentMethod: Boolean = false, diff --git a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/model/GiftCardComponentParamsMapperTest.kt b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/model/GiftCardComponentParamsMapperTest.kt index bb9213e757..6abfbead7b 100644 --- a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/model/GiftCardComponentParamsMapperTest.kt +++ b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/model/GiftCardComponentParamsMapperTest.kt @@ -67,7 +67,7 @@ internal class GiftCardComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -215,7 +215,7 @@ internal class GiftCardComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt index 25bf35d10c..244d2b9558 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/model/GooglePayComponentParamsMapperTest.kt @@ -110,6 +110,7 @@ internal class GooglePayComponentParamsMapperTest { shopperLocale = Locale.FRANCE, environment = Environment.APSE, clientKey = TEST_CLIENT_KEY_2, + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2), gatewayMerchantId = "MERCHANT_ACCOUNT", googlePayEnvironment = WalletConstants.ENVIRONMENT_PRODUCTION, amount = amount, @@ -165,7 +166,7 @@ internal class GooglePayComponentParamsMapperTest { environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, googlePayEnvironment = WalletConstants.ENVIRONMENT_PRODUCTION, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, amount = Amount( currency = "CAD", @@ -340,6 +341,7 @@ internal class GooglePayComponentParamsMapperTest { shopperLocale = Locale.CHINA, environment = Environment.UNITED_STATES, clientKey = TEST_CLIENT_KEY_2, + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_2), googlePayEnvironment = WalletConstants.ENVIRONMENT_PRODUCTION, ) @@ -361,7 +363,7 @@ internal class GooglePayComponentParamsMapperTest { val expected = getGooglePayComponentParams( environment = Environment.TEST, clientKey = TEST_CLIENT_KEY_1, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn = false, ) @@ -386,7 +388,7 @@ internal class GooglePayComponentParamsMapperTest { val expected = getGooglePayComponentParams( environment = Environment.TEST, clientKey = TEST_CLIENT_KEY_1, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn = false, ) @@ -520,7 +522,7 @@ internal class GooglePayComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, gatewayMerchantId: String = TEST_GATEWAY_MERCHANT_ID, googlePayEnvironment: Int = WalletConstants.ENVIRONMENT_TEST, diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt index 3e245d4110..a5f45707d4 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/util/GooglePayUtilsTest.kt @@ -243,7 +243,7 @@ internal class GooglePayUtilsTest { shopperLocale = Locale.US, environment = Environment.TEST, clientKey = "CLIENT_KEY", - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, "CLIENT_KEY"), isCreatedByDropIn = false, amount = null, ), @@ -273,7 +273,7 @@ internal class GooglePayUtilsTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = "CLIENT_KEY_CUSTOM", - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, "CLIENT_KEY_CUSTOM"), isCreatedByDropIn = true, amount = Amount("EUR", 13_37), ), diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/model/IssuerListComponentParamsMapperTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/model/IssuerListComponentParamsMapperTest.kt index 7adec2928b..35b4ea78c5 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/model/IssuerListComponentParamsMapperTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/model/IssuerListComponentParamsMapperTest.kt @@ -72,7 +72,7 @@ internal class IssuerListComponentParamsMapperTest { shopperLocale = DEVICE_LOCALE, environment = Environment.TEST, clientKey = TEST_CLIENT_KEY_1, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn = false, viewType = IssuerListViewType.SPINNER_VIEW, hideIssuerLogos = true, @@ -118,7 +118,7 @@ internal class IssuerListComponentParamsMapperTest { shopperLocale = Locale.GERMAN, environment = Environment.EUROPE, clientKey = TEST_CLIENT_KEY_2, - analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE), + analyticsParams = AnalyticsParams(AnalyticsParamsLevel.NONE, TEST_CLIENT_KEY_2), isCreatedByDropIn = true, viewType = IssuerListViewType.SPINNER_VIEW, hideIssuerLogos = true, @@ -286,7 +286,7 @@ internal class IssuerListComponentParamsMapperTest { shopperLocale: Locale = DEVICE_LOCALE, environment: Environment = Environment.TEST, clientKey: String = TEST_CLIENT_KEY_1, - analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL), + analyticsParams: AnalyticsParams = AnalyticsParams(AnalyticsParamsLevel.ALL, TEST_CLIENT_KEY_1), isCreatedByDropIn: Boolean = false, amount: Amount? = null, isSubmitButtonVisible: Boolean = true, From 4d5c7cf7a57c1b585131db41a0d4ce512044d5a1 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 12:05:57 +0100 Subject: [PATCH 179/272] Create AnalyticsManager and pass it to components in drop-in COAND-844 --- .../analytics/AnalyticsManagerFactory.kt | 40 ++++++++++++++++--- .../remote/DefaultAnalyticsSetupProvider.kt | 13 +++--- .../internal/provider/ComponentProvider.kt | 4 +- .../ui/BaseComponentDialogFragment.kt | 1 + .../dropin/internal/ui/DropInViewModel.kt | 9 +++-- .../internal/ui/DropInViewModelFactory.kt | 22 +++++++++- .../ui/GiftCardComponentDialogFragment.kt | 1 + .../ui/GooglePayComponentDialogFragment.kt | 1 + 8 files changed, 76 insertions(+), 15 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt index 5a2849bee2..0f3a7e14c9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -10,6 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics import android.app.Application import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.internal.analytics.data.DefaultNewAnalyticsRepository import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore @@ -17,37 +18,66 @@ import com.adyen.checkout.components.core.internal.analytics.data.remote.Analyti import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsRemoteDataStore import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import com.adyen.checkout.core.Environment import com.adyen.checkout.core.internal.data.api.HttpClientFactory +import java.util.Locale @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class AnalyticsManagerFactory { + fun provide( componentParams: ComponentParams, application: Application, source: AnalyticsSource, sessionId: String? - ) = AnalyticsManager( + ): AnalyticsManager = provide( + shopperLocale = componentParams.shopperLocale, + environment = componentParams.environment, + clientKey = componentParams.clientKey, + analyticsParams = componentParams.analyticsParams, + isCreatedByDropIn = componentParams.isCreatedByDropIn, + amount = componentParams.amount, + application = application, + source = source, + sessionId = sessionId, + ) + + @Suppress("LongParameterList") + fun provide( + shopperLocale: Locale, + environment: Environment, + clientKey: String, + analyticsParams: AnalyticsParams, + isCreatedByDropIn: Boolean, + amount: Amount?, + application: Application, + source: AnalyticsSource, + sessionId: String? + ): AnalyticsManager = AnalyticsManager( analyticsRepository = DefaultNewAnalyticsRepository( localInfoDataStore = InfoAnalyticsLocalDataStore(), localLogDataStore = LogAnalyticsLocalDataStore(), remoteDataStore = DefaultAnalyticsRemoteDataStore( analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), + HttpClientFactory.getAnalyticsHttpClient(environment), ), - clientKey = componentParams.clientKey, + clientKey = clientKey, infoSize = INFO_SIZE, logSize = LOG_SIZE, ), analyticsSetupProvider = DefaultAnalyticsSetupProvider( application = application, - componentParams = componentParams, + shopperLocale = shopperLocale, + isCreatedByDropIn = isCreatedByDropIn, + amount = amount, source = source, sessionId = sessionId, ), analyticsTrackRequestProvider = AnalyticsTrackRequestProvider(), ), - analyticsParams = componentParams.analyticsParams, + analyticsParams = analyticsParams, ) companion object { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt index f2e0f982b9..8e7e72db18 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt @@ -10,14 +10,17 @@ package com.adyen.checkout.components.core.internal.analytics.data.remote import android.app.Application import android.os.Build +import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatformParams import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import com.adyen.checkout.components.core.internal.ui.model.ComponentParams +import java.util.Locale internal class DefaultAnalyticsSetupProvider( private val application: Application, - private val componentParams: ComponentParams, + private val shopperLocale: Locale, + private val isCreatedByDropIn: Boolean, + private val amount: Amount?, private val source: AnalyticsSource, private val sessionId: String?, ) : AnalyticsSetupProvider { @@ -27,16 +30,16 @@ internal class DefaultAnalyticsSetupProvider( version = AnalyticsPlatformParams.version, channel = AnalyticsPlatformParams.CHANNEL, platform = AnalyticsPlatformParams.platform, - locale = componentParams.shopperLocale.toString(), + locale = shopperLocale.toString(), component = getComponentQueryParameter(source), - flavor = getFlavorQueryParameter(componentParams.isCreatedByDropIn), + flavor = getFlavorQueryParameter(isCreatedByDropIn), deviceBrand = Build.BRAND, deviceModel = Build.MODEL, referrer = application.packageName, systemVersion = Build.VERSION.SDK_INT.toString(), screenWidth = application.resources.displayMetrics.widthPixels, paymentMethods = source.getPaymentMethods(), - amount = componentParams.amount, + amount = amount, // unused for Android containerWidth = null, sessionId = sessionId, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt index e3b2cc6aa0..1f94f6e76f 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt @@ -35,6 +35,7 @@ import com.adyen.checkout.components.core.ComponentCallback import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -182,6 +183,7 @@ internal fun getComponentFor( dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, analyticsRepository: AnalyticsRepository, + analyticsManager: AnalyticsManager, onRedirect: () -> Unit, ): PaymentComponent { return when { @@ -312,7 +314,7 @@ internal fun getComponentFor( } checkCompileOnly { MBWayComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - MBWayComponentProvider(dropInOverrideParams, analyticsRepository).get( + MBWayComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt index a6b783f516..ea52177cec 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt @@ -120,6 +120,7 @@ internal abstract class BaseComponentDialogFragment : dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) } diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index 8ff6142543..7924bcd9bf 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -24,6 +24,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.components.core.StoredPaymentMethod +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -59,6 +60,7 @@ internal class DropInViewModel( private val bundleHandler: DropInSavedStateHandleContainer, private val orderStatusRepository: OrderStatusRepository, internal val analyticsRepository: AnalyticsRepository, + internal val analyticsManager: AnalyticsManager, private val initialDropInParams: DropInParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : ViewModel() { @@ -186,7 +188,7 @@ internal class DropInViewModel( fun onCreated() { navigateToInitialDestination() - setupAnalytics() + initializeAnalytics() } fun onDropInServiceConnected() { @@ -230,11 +232,12 @@ internal class DropInViewModel( sendEvent(DropInActivityEvent.NavigateTo(destination)) } - private fun setupAnalytics() { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } + private fun initializeAnalytics() { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } viewModelScope.launch { analyticsRepository.setupAnalytics() } + analyticsManager.initialize(viewModelScope) } /** diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index 0f5372f47f..240c65bdde 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -8,11 +8,13 @@ package com.adyen.checkout.dropin.internal.ui +import android.app.Application import androidx.activity.ComponentActivity import androidx.lifecycle.AbstractSavedStateViewModelFactory import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper @@ -39,6 +41,7 @@ internal class DropInViewModelFactory( private val packageName: String = activity.packageName private val screenWidth: Int = activity.screenWidthPixels private val deviceLocale: Locale = localeProvider.getLocale(activity) + private val application: Application = activity.application override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T { val bundleHandler = DropInSavedStateHandleContainer(handle) @@ -71,9 +74,26 @@ internal class DropInViewModelFactory( ), analyticsMapper = AnalyticsMapper(), ) + val analyticsManager = AnalyticsManagerFactory().provide( + shopperLocale = dropInParams.shopperLocale, + environment = dropInParams.environment, + clientKey = dropInParams.clientKey, + analyticsParams = dropInParams.analyticsParams, + isCreatedByDropIn = true, + amount = dropInParams.amount, + application = application, + source = AnalyticsSource.DropIn(paymentMethods), + sessionId = bundleHandler.sessionDetails?.id, + ) @Suppress("UNCHECKED_CAST") - return DropInViewModel(bundleHandler, orderStatusRepository, analyticsRepository, dropInParams) as T + return DropInViewModel( + bundleHandler, + orderStatusRepository, + analyticsRepository, + analyticsManager, + dropInParams, + ) as T } private fun getDropInParams( diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt index 7527299979..bf1d9346c9 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt @@ -74,6 +74,7 @@ internal class GiftCardComponentDialogFragment : DropInBottomSheetDialogFragment dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) as GiftCardComponent } catch (e: CheckoutException) { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt index 1c3b7fbee7..17fb46d466 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt @@ -77,6 +77,7 @@ internal class GooglePayComponentDialogFragment : dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) as GooglePayComponent } catch (e: CheckoutException) { From 12d14515785c48aedfc9f9e3f361ebf48722440c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 12:11:20 +0100 Subject: [PATCH 180/272] Move AnalyticsService to it's original place This is for review purposes and should be reverted later. COAND-844 --- .../ach/internal/provider/ACHDirectDebitComponentProvider.kt | 2 +- .../bacs/internal/provider/BacsDirectDebitComponentProvider.kt | 2 +- .../checkout/bcmc/internal/provider/BcmcComponentProvider.kt | 2 +- .../checkout/blik/internal/provider/BlikComponentProvider.kt | 2 +- .../boleto/internal/provider/BoletoComponentProvider.kt | 2 +- .../checkout/card/internal/provider/CardComponentProvider.kt | 2 +- .../cashapppay/internal/provider/CashAppPayComponentProvider.kt | 2 +- .../core/internal/analytics/AnalyticsManagerFactory.kt | 2 +- .../analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt | 1 + .../{analytics/data/remote => data/api}/AnalyticsService.kt | 2 +- .../core/internal/data/api/DefaultAnalyticsRepository.kt | 1 - .../adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt | 2 +- .../econtext/internal/provider/EContextComponentProvider.kt | 2 +- .../giftcard/internal/provider/GiftCardComponentProvider.kt | 2 +- .../googlepay/internal/provider/GooglePayComponentProvider.kt | 2 +- .../internal/provider/InstantPaymentComponentProvider.kt | 2 +- .../issuerlist/internal/provider/IssuerListComponentProvider.kt | 2 +- .../internal/provider/OnlineBankingComponentProvider.kt | 2 +- .../paybybank/internal/provider/PayByBankComponentProvider.kt | 2 +- .../checkout/sepa/internal/provider/SepaComponentProvider.kt | 2 +- .../checkout/upi/internal/provider/UPIComponentProvider.kt | 2 +- 21 files changed, 20 insertions(+), 20 deletions(-) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/{analytics/data/remote => data/api}/AnalyticsService.kt (96%) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt index 0a7fe67abf..410cadd5e3 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt @@ -34,10 +34,10 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt index 1cd07058fc..d3b02aea63 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt @@ -28,10 +28,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt index 708fb80a44..2eced9b09a 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt @@ -31,10 +31,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt index b6394ff12a..b0e7cc1aa8 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt @@ -30,10 +30,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt index fa3431e42f..0c2fc1aa39 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt @@ -28,10 +28,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper diff --git a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt index 242bfe39d5..9a72fc7a80 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt @@ -33,10 +33,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt index 6b26588fd1..3979798d38 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt @@ -35,10 +35,10 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt index 0f3a7e14c9..c1f68964d3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -14,10 +14,10 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.internal.analytics.data.DefaultNewAnalyticsRepository import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsRemoteDataStore import com.adyen.checkout.components.core.internal.analytics.data.remote.DefaultAnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.ComponentParams import com.adyen.checkout.core.Environment diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt index cd947a13ef..969c1cae62 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsRemoteDataStore.kt @@ -8,6 +8,7 @@ package com.adyen.checkout.components.core.internal.analytics.data.remote +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt similarity index 96% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt index ed6e99e1ef..34a569d786 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt @@ -6,7 +6,7 @@ * Created by ararat on 4/3/2024. */ -package com.adyen.checkout.components.core.internal.analytics.data.remote +package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt index 0e8100bc13..e55a2e039c 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt @@ -10,7 +10,6 @@ package com.adyen.checkout.components.core.internal.data.api import androidx.annotation.RestrictTo import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.ALL import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.NONE diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index 240c65bdde..5cd92220a5 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -16,9 +16,9 @@ import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusService diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt index 599a315f90..d9dd909bda 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt @@ -26,10 +26,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt index 6bb51f184f..85ec671395 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt @@ -19,10 +19,10 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt index 197a453d5c..a308067728 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt @@ -23,10 +23,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentMethodAvailabilityCheck import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index b746c2185f..19deb75e1e 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -23,10 +23,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt index ef4d249ceb..29eeae3fad 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt @@ -27,10 +27,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt index 22f092db16..9ba3010645 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt @@ -26,10 +26,10 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt index 957bc58e1e..ab01e7ffa8 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt @@ -22,10 +22,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt index 074da2cc33..e2a4eba75f 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt @@ -22,10 +22,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt index b7dde005df..5dba69e692 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt @@ -22,10 +22,10 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData +import com.adyen.checkout.components.core.internal.data.api.AnalyticsService import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper From 45eac916a10d51efd09e28a09a880337c5106579 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 17:49:57 +0100 Subject: [PATCH 181/272] Serialize AnalyticsTrackRequest correctly COAND-844 --- .../core/internal/data/model/AnalyticsTrackRequest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt index 84e843d6bc..fb1129a0f3 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequest.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.ModelUtils import com.adyen.checkout.core.internal.data.model.ModelUtils.deserializeOptList import com.adyen.checkout.core.internal.data.model.getStringOrNull import kotlinx.parcelize.Parcelize @@ -36,8 +37,8 @@ internal data class AnalyticsTrackRequest( return JSONObject().apply { putOpt(CHANNEL, modelObject.channel) putOpt(PLATFORM, modelObject.platform) - putOpt(INFO, modelObject.info) - putOpt(LOGS, modelObject.logs) + putOpt(INFO, ModelUtils.serializeOptList(modelObject.info, AnalyticsTrackInfo.SERIALIZER)) + putOpt(LOGS, ModelUtils.serializeOptList(modelObject.logs, AnalyticsTrackLog.SERIALIZER)) } } catch (e: JSONException) { throw ModelSerializationException(AnalyticsTrackRequest::class.java, e) From 2093f4b791fcf227db16bbc9db1fe0645057ea04 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 17:50:52 +0100 Subject: [PATCH 182/272] Generate id for analytics events COAND-844 --- .../components/core/internal/analytics/AnalyticsEvent.kt | 4 ++++ .../analytics/data/remote/AnalyticsTrackRequestProvider.kt | 2 ++ .../components/core/internal/data/model/AnalyticsTrackInfo.kt | 4 ++++ .../components/core/internal/data/model/AnalyticsTrackLog.kt | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt index f8e11eec9e..def1988672 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt @@ -10,15 +10,18 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo import java.util.Date +import java.util.UUID @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) sealed interface AnalyticsEvent { + val id: String val timestamp: Long val shouldForceSend: Boolean val component: String data class Info @DirectAnalyticsEventCreation constructor( + override val id: String = UUID.randomUUID().toString(), override val timestamp: Long = Date().time, override val shouldForceSend: Boolean = false, override val component: String, @@ -43,6 +46,7 @@ sealed interface AnalyticsEvent { } data class Log @DirectAnalyticsEventCreation constructor( + override val id: String = UUID.randomUUID().toString(), override val timestamp: Long = Date().time, override val shouldForceSend: Boolean = true, override val component: String, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index 379bf4aa39..d077d8f796 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -29,6 +29,7 @@ internal class AnalyticsTrackRequestProvider { } private fun AnalyticsEvent.Info.mapToTrackEvent() = AnalyticsTrackInfo( + id = id, timestamp = timestamp, component = component, type = type?.value, @@ -41,6 +42,7 @@ internal class AnalyticsTrackRequestProvider { ) private fun AnalyticsEvent.Log.mapToTrackEvent() = AnalyticsTrackLog( + id = id, timestamp = timestamp, component = component, type = type?.value, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt index cb0e543b6f..b3a83a534a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfo.kt @@ -19,6 +19,7 @@ import org.json.JSONObject @Parcelize internal data class AnalyticsTrackInfo( + val id: String, val timestamp: Long?, val component: String?, val type: String?, @@ -31,6 +32,7 @@ internal data class AnalyticsTrackInfo( ) : ModelObject() { companion object { + private const val ID = "id" private const val TIMESTAMP = "timestamp" private const val COMPONENT = "component" private const val TYPE = "type" @@ -46,6 +48,7 @@ internal data class AnalyticsTrackInfo( override fun serialize(modelObject: AnalyticsTrackInfo): JSONObject { try { return JSONObject().apply { + put(ID, modelObject.id) putOpt(TIMESTAMP, modelObject.timestamp) putOpt(COMPONENT, modelObject.component) putOpt(TYPE, modelObject.type) @@ -65,6 +68,7 @@ internal data class AnalyticsTrackInfo( return try { with(jsonObject) { AnalyticsTrackInfo( + id = getString(ID), timestamp = getLongOrNull(TIMESTAMP), component = getStringOrNull(COMPONENT), type = getStringOrNull(TYPE), diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt index 6df631b24e..7ce26e238a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt @@ -18,6 +18,7 @@ import org.json.JSONObject @Parcelize internal data class AnalyticsTrackLog( + val id: String, val timestamp: Long?, val component: String?, val type: String?, @@ -27,6 +28,7 @@ internal data class AnalyticsTrackLog( ) : ModelObject() { companion object { + private const val ID = "id" private const val TIMESTAMP = "timestamp" private const val COMPONENT = "component" private const val TYPE = "type" @@ -39,6 +41,7 @@ internal data class AnalyticsTrackLog( override fun serialize(modelObject: AnalyticsTrackLog): JSONObject { try { return JSONObject().apply { + put(ID, modelObject.id) putOpt(TIMESTAMP, modelObject.timestamp) putOpt(COMPONENT, modelObject.component) putOpt(TYPE, modelObject.type) @@ -55,6 +58,7 @@ internal data class AnalyticsTrackLog( return try { with(jsonObject) { AnalyticsTrackLog( + id = getString(ID), timestamp = getLongOrNull(TIMESTAMP), component = getStringOrNull(COMPONENT), type = getStringOrNull(TYPE), From b6aa7b9238b01601be03f9d5545f7c4d1d2e65c8 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Mar 2024 17:51:35 +0100 Subject: [PATCH 183/272] Return an empty JSONObject when response is empty COAND-844 --- .../com/adyen/checkout/core/internal/data/api/HttpClientExt.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3b2b5908b4..37da117734 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 @@ -66,7 +66,7 @@ suspend fun HttpClient.post( adyenLog(AdyenLogLevel.VERBOSE) { "request - ${requestJson.toStringPretty()}" } val result = runAndLogHttpException { post(path, requestJson.toString(), queryParameters) } - val resultJson = JSONObject(String(result, Charsets.UTF_8)) + val resultJson = JSONObject(String(result, Charsets.UTF_8).ifBlank { "{}" }) adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } From cbd9820bda7dd4d17bcd7269df3dffea150ffb95 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 8 Mar 2024 10:52:48 +0100 Subject: [PATCH 184/272] Don't send events if there are none COAND-844 --- .../internal/analytics/data/DefaultNewAnalyticsRepository.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt index ed3b5da9a7..5869fa57e4 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt @@ -39,6 +39,9 @@ internal class DefaultNewAnalyticsRepository( ) { val infoEvents = localInfoDataStore.fetchEvents(remoteDataStore.infoSize) val logEvents = localLogDataStore.fetchEvents(remoteDataStore.logSize) + + if (infoEvents.isEmpty() && logEvents.isEmpty()) return + val request = analyticsTrackRequestProvider( infoList = infoEvents, logList = logEvents, From 33e0d537c0ce8744cd3c1958ea870d123c50a881 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 8 Mar 2024 13:18:23 +0100 Subject: [PATCH 185/272] Only clean up AnalyticsManager by the class that initialized it COAND-844 --- .../core/internal/analytics/AnalyticsManager.kt | 12 ++++++++++-- .../checkout/dropin/internal/ui/DropInViewModel.kt | 7 ++++++- .../mbway/internal/ui/DefaultMBWayDelegate.kt | 3 ++- .../mbway/internal/ui/DefaultMBWayDelegateTest.kt | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 7661cf2960..2c04701fdb 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -40,10 +40,14 @@ class AnalyticsManager internal constructor( private var timerJob: Job? = null - fun initialize(coroutineScope: CoroutineScope) { + private var ownerReference: String? = null + + fun initialize(owner: Any, coroutineScope: CoroutineScope) { if (isInitialized) return isInitialized = true + ownerReference = owner::class.qualifiedName + _coroutineScope = coroutineScope if (cannotSendEvents()) { @@ -118,10 +122,14 @@ class AnalyticsManager internal constructor( return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority } - fun clear() { + fun clear(owner: Any) { + if (ownerReference != owner::class.qualifiedName) return _coroutineScope = null checkoutAttemptId = null stopTimer() + timerJob = null + ownerReference = null + isInitialized = false } companion object { diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index 7924bcd9bf..c7fd1f2670 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -237,7 +237,7 @@ internal class DropInViewModel( viewModelScope.launch { analyticsRepository.setupAnalytics() } - analyticsManager.initialize(viewModelScope) + analyticsManager.initialize(this, viewModelScope) } /** @@ -471,4 +471,9 @@ internal class DropInViewModel( eventChannel.send(event) } } + + override fun onCleared() { + super.onCleared() + analyticsManager.clear(this) + } } diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 2efdbb082b..acdd69f0c1 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -75,7 +75,7 @@ internal class DefaultMBWayDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } - analyticsManager.initialize(coroutineScope) + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -177,6 +177,7 @@ internal class DefaultMBWayDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } companion object { diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index d884903f65..b3f50899f9 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -201,7 +201,7 @@ internal class DefaultMBWayDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(any()) + verify(analyticsManager).initialize(any(), any()) } @Nested From a7c7a61eea5dc227357a92907839e5666157bc0d Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 8 Mar 2024 14:31:55 +0100 Subject: [PATCH 186/272] Create an interface for AnalyticsManager This will help with testing later. COAND-844 --- .../internal/analytics/AnalyticsManager.kt | 127 +--------------- .../analytics/AnalyticsManagerFactory.kt | 2 +- .../analytics/DefaultAnalyticsManager.kt | 137 ++++++++++++++++++ 3 files changed, 144 insertions(+), 122 deletions(-) create mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt index 2c04701fdb..391ad934b8 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManager.kt @@ -3,137 +3,22 @@ * * This file is open source and available under the MIT license. See the LICENSE file for more info. * - * Created by oscars on 27/2/2024. + * Created by oscars on 8/3/2024. */ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo -import com.adyen.checkout.components.core.internal.analytics.data.NewAnalyticsRepository -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel -import com.adyen.checkout.core.AdyenLogLevel -import com.adyen.checkout.core.internal.util.adyenLog -import com.adyen.checkout.core.internal.util.runSuspendCatching -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlin.time.Duration.Companion.seconds @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AnalyticsManager internal constructor( - private val analyticsRepository: NewAnalyticsRepository, - private val analyticsParams: AnalyticsParams, - private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, -) { +interface AnalyticsManager { - private var checkoutAttemptId: String? = null + fun initialize(owner: Any, coroutineScope: CoroutineScope) - private var isInitialized: Boolean = false + fun trackEvent(event: AnalyticsEvent) - private var _coroutineScope: CoroutineScope? = null - private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + fun getCheckoutAttemptId(): String? - private var timerJob: Job? = null - - private var ownerReference: String? = null - - fun initialize(owner: Any, coroutineScope: CoroutineScope) { - if (isInitialized) return - isInitialized = true - - ownerReference = owner::class.qualifiedName - - _coroutineScope = coroutineScope - - if (cannotSendEvents()) { - checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS - return - } - - coroutineScope.launch(coroutineDispatcher) { - runSuspendCatching { - analyticsRepository.fetchCheckoutAttemptId() - }.fold( - onSuccess = { attemptId -> - checkoutAttemptId = attemptId?.also { startTimer() } - }, - onFailure = { adyenLog(AdyenLogLevel.WARN, it) { "Failed to fetch checkoutAttemptId." } }, - ) - } - } - - fun trackEvent(event: AnalyticsEvent) { - if (cannotSendEvents()) return - coroutineScope.launch(coroutineDispatcher) { - runSuspendCatching { - analyticsRepository.storeEvent(event) - - if (event.shouldForceSend) { - stopTimer() - sendEvents() - startTimer() - } - }.fold( - onSuccess = { /* Not necessary */ }, - onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Storing event failed" } }, - ) - } - } - - private fun startTimer() { - stopTimer() - timerJob = coroutineScope.launch(coroutineDispatcher) { - while (isActive) { - delay(DISPATCH_INTERVAL_MILLIS) - sendEvents() - } - } - } - - private fun stopTimer() { - timerJob?.cancel() - } - - private suspend fun sendEvents() { - if (cannotSendEvents()) return - - val checkoutAttemptId = checkoutAttemptId - if (checkoutAttemptId == null) { - adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId should not be null at this point." } - return - } - - runSuspendCatching { - analyticsRepository.sendEvents(checkoutAttemptId) - }.fold( - onSuccess = { adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } }, - onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, - ) - } - - fun getCheckoutAttemptId(): String? = checkoutAttemptId - - private fun cannotSendEvents(): Boolean { - return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority - } - - fun clear(owner: Any) { - if (ownerReference != owner::class.qualifiedName) return - _coroutineScope = null - checkoutAttemptId = null - stopTimer() - timerJob = null - ownerReference = null - isInitialized = false - } - - companion object { - private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" - private val DISPATCH_INTERVAL_MILLIS = 10.seconds.inWholeMilliseconds - } + fun clear(owner: Any) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt index c1f68964d3..cabee5d3bc 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -55,7 +55,7 @@ class AnalyticsManagerFactory { application: Application, source: AnalyticsSource, sessionId: String? - ): AnalyticsManager = AnalyticsManager( + ): AnalyticsManager = DefaultAnalyticsManager( analyticsRepository = DefaultNewAnalyticsRepository( localInfoDataStore = InfoAnalyticsLocalDataStore(), localLogDataStore = LogAnalyticsLocalDataStore(), diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt new file mode 100644 index 0000000000..8aa95af596 --- /dev/null +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -0,0 +1,137 @@ +/* + * 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 27/2/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics + +import com.adyen.checkout.components.core.internal.analytics.data.NewAnalyticsRepository +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog +import com.adyen.checkout.core.internal.util.runSuspendCatching +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlin.time.Duration.Companion.seconds + +internal class DefaultAnalyticsManager internal constructor( + private val analyticsRepository: NewAnalyticsRepository, + private val analyticsParams: AnalyticsParams, + private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, +) : AnalyticsManager { + + private var checkoutAttemptId: String? = null + + private var isInitialized: Boolean = false + + private var _coroutineScope: CoroutineScope? = null + private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + + private var timerJob: Job? = null + + private var ownerReference: String? = null + + override fun initialize(owner: Any, coroutineScope: CoroutineScope) { + if (isInitialized) return + isInitialized = true + + ownerReference = owner::class.qualifiedName + + _coroutineScope = coroutineScope + + if (cannotSendEvents()) { + checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS + return + } + + coroutineScope.launch(coroutineDispatcher) { + runSuspendCatching { + analyticsRepository.fetchCheckoutAttemptId() + }.fold( + onSuccess = { attemptId -> + checkoutAttemptId = attemptId?.also { startTimer() } + }, + onFailure = { adyenLog(AdyenLogLevel.WARN, it) { "Failed to fetch checkoutAttemptId." } }, + ) + } + } + + override fun trackEvent(event: AnalyticsEvent) { + if (cannotSendEvents()) return + coroutineScope.launch(coroutineDispatcher) { + runSuspendCatching { + analyticsRepository.storeEvent(event) + + if (event.shouldForceSend) { + stopTimer() + sendEvents() + startTimer() + } + }.fold( + onSuccess = { /* Not necessary */ }, + onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Storing event failed" } }, + ) + } + } + + private fun startTimer() { + stopTimer() + timerJob = coroutineScope.launch(coroutineDispatcher) { + while (isActive) { + delay(DISPATCH_INTERVAL_MILLIS) + sendEvents() + } + } + } + + private fun stopTimer() { + timerJob?.cancel() + } + + private suspend fun sendEvents() { + if (cannotSendEvents()) return + + val checkoutAttemptId = checkoutAttemptId + if (checkoutAttemptId == null) { + adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId should not be null at this point." } + return + } + + runSuspendCatching { + analyticsRepository.sendEvents(checkoutAttemptId) + }.fold( + onSuccess = { adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } }, + onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, + ) + } + + override fun getCheckoutAttemptId(): String? = checkoutAttemptId + + private fun cannotSendEvents(): Boolean { + return analyticsParams.level.priority <= AnalyticsParamsLevel.NONE.priority + } + + override fun clear(owner: Any) { + if (ownerReference != owner::class.qualifiedName) return + _coroutineScope = null + checkoutAttemptId = null + stopTimer() + timerJob = null + ownerReference = null + isInitialized = false + } + + companion object { + private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + private val DISPATCH_INTERVAL_MILLIS = 10.seconds.inWholeMilliseconds + } +} From e92ce9ba438145a6921bbbc5556609eacb5c268c Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 13 Mar 2024 16:30:28 +0100 Subject: [PATCH 187/272] Alignments with iOS COAND-844 --- .../core/internal/analytics/DefaultAnalyticsManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index 8aa95af596..2a6340c4b9 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -72,7 +72,6 @@ internal class DefaultAnalyticsManager internal constructor( analyticsRepository.storeEvent(event) if (event.shouldForceSend) { - stopTimer() sendEvents() startTimer() } @@ -122,12 +121,13 @@ internal class DefaultAnalyticsManager internal constructor( override fun clear(owner: Any) { if (ownerReference != owner::class.qualifiedName) return + // TODO: Log here if there are still events which are not sent when clearing or if possible send events _coroutineScope = null checkoutAttemptId = null - stopTimer() - timerJob = null ownerReference = null isInitialized = false + stopTimer() + timerJob = null } companion object { From 45b9dd4a3855d7e49fde283f78cf61c5165cc57a Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 14 Mar 2024 10:57:43 +0100 Subject: [PATCH 188/272] Log when exiting a function early COAND-844 --- .../analytics/DefaultAnalyticsManager.kt | 20 +++++++++++++++---- .../core/internal/analytics/GenericEvents.kt | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index 2a6340c4b9..283eb35de8 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -41,7 +41,10 @@ internal class DefaultAnalyticsManager internal constructor( private var ownerReference: String? = null override fun initialize(owner: Any, coroutineScope: CoroutineScope) { - if (isInitialized) return + if (isInitialized) { + adyenLog(AdyenLogLevel.DEBUG) { "Already initialized, ignoring." } + return + } isInitialized = true ownerReference = owner::class.qualifiedName @@ -66,7 +69,10 @@ internal class DefaultAnalyticsManager internal constructor( } override fun trackEvent(event: AnalyticsEvent) { - if (cannotSendEvents()) return + if (cannotSendEvents()) { + adyenLog(AdyenLogLevel.DEBUG) { "Not allowed to track events, ignoring." } + return + } coroutineScope.launch(coroutineDispatcher) { runSuspendCatching { analyticsRepository.storeEvent(event) @@ -97,7 +103,10 @@ internal class DefaultAnalyticsManager internal constructor( } private suspend fun sendEvents() { - if (cannotSendEvents()) return + if (cannotSendEvents()) { + adyenLog(AdyenLogLevel.DEBUG) { "Not allowed to send events, ignoring." } + return + } val checkoutAttemptId = checkoutAttemptId if (checkoutAttemptId == null) { @@ -120,7 +129,10 @@ internal class DefaultAnalyticsManager internal constructor( } override fun clear(owner: Any) { - if (ownerReference != owner::class.qualifiedName) return + if (ownerReference != owner::class.qualifiedName) { + adyenLog(AdyenLogLevel.DEBUG) { "Clear called by not the original owner, ignoring." } + return + } // TODO: Log here if there are still events which are not sent when clearing or if possible send events _coroutineScope = null checkoutAttemptId = null diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index 2a06ecfce4..b38d154c8a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -69,7 +69,7 @@ object GenericEvents { target = target, ) - fun unFocus( + fun unfocus( component: String, target: String, ) = AnalyticsEvent.Info( From 64ca1511146dd6df77d18130d90a5fbf36f9e7da Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 14 Mar 2024 11:27:49 +0100 Subject: [PATCH 189/272] Log how many events are stored when clearing DefaultAnalyticsManager COAND-844 --- .../internal/analytics/DefaultAnalyticsManager.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index 283eb35de8..ea482d98ec 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import java.util.concurrent.atomic.AtomicInteger import kotlin.time.Duration.Companion.seconds internal class DefaultAnalyticsManager internal constructor( @@ -40,6 +41,8 @@ internal class DefaultAnalyticsManager internal constructor( private var ownerReference: String? = null + private var storedEventCount: AtomicInteger = AtomicInteger(0) + override fun initialize(owner: Any, coroutineScope: CoroutineScope) { if (isInitialized) { adyenLog(AdyenLogLevel.DEBUG) { "Already initialized, ignoring." } @@ -82,7 +85,7 @@ internal class DefaultAnalyticsManager internal constructor( startTimer() } }.fold( - onSuccess = { /* Not necessary */ }, + onSuccess = { storedEventCount.incrementAndGet() }, onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Storing event failed" } }, ) } @@ -117,7 +120,10 @@ internal class DefaultAnalyticsManager internal constructor( runSuspendCatching { analyticsRepository.sendEvents(checkoutAttemptId) }.fold( - onSuccess = { adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } }, + onSuccess = { + adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } + storedEventCount.set(0) + }, onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, ) } @@ -133,7 +139,9 @@ internal class DefaultAnalyticsManager internal constructor( adyenLog(AdyenLogLevel.DEBUG) { "Clear called by not the original owner, ignoring." } return } - // TODO: Log here if there are still events which are not sent when clearing or if possible send events + + adyenLog(AdyenLogLevel.DEBUG) { "Clear called while there are ${storedEventCount.get()} events stored." } + _coroutineScope = null checkoutAttemptId = null ownerReference = null From 4b64fce0aa5217ba9d8151be6300af37071ab6f9 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 18 Mar 2024 15:20:03 +0100 Subject: [PATCH 190/272] Improve empty response handling COAND-844 --- .../checkout/core/internal/data/api/HttpClientExt.kt | 12 ++++++++++-- .../internal/analytics/AnalyticsPlatformParams.kt | 3 ++- .../data/remote/AnalyticsTrackRequestProvider.kt | 2 +- .../data/remote/DefaultAnalyticsSetupProvider.kt | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) 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 37da117734..327f2eac43 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 @@ -28,7 +28,7 @@ suspend fun HttpClient.get( adyenLog(AdyenLogLevel.DEBUG) { "GET - $path" } val result = runAndLogHttpException { get(path, queryParameters) } - val resultJson = JSONObject(String(result, Charsets.UTF_8)) + val resultJson = result.toJSONObject() adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } @@ -66,7 +66,7 @@ suspend fun HttpClient.post( adyenLog(AdyenLogLevel.VERBOSE) { "request - ${requestJson.toStringPretty()}" } val result = runAndLogHttpException { post(path, requestJson.toString(), queryParameters) } - val resultJson = JSONObject(String(result, Charsets.UTF_8).ifBlank { "{}" }) + val resultJson = result.toJSONObject() adyenLog(AdyenLogLevel.VERBOSE) { "response - ${resultJson.toStringPretty()}" } @@ -89,3 +89,11 @@ private fun HttpException.getLogMessage(): String { "[$code] $message" } } + +private fun ByteArray.toJSONObject(): JSONObject { + return if (isEmpty()) { + JSONObject() + } else { + JSONObject(String(this, Charsets.UTF_8)) + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt index d019061979..156cf74127 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParams.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.BuildConfig @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object AnalyticsPlatformParams { - const val CHANNEL = "android" + @Suppress("ConstPropertyName", "ktlint:standard:property-naming") + const val channel = "android" var platform = AnalyticsPlatform.ANDROID.value private set diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index d077d8f796..04d09ef941 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -21,7 +21,7 @@ internal class AnalyticsTrackRequestProvider { logList: List, ): AnalyticsTrackRequest { return AnalyticsTrackRequest( - channel = AnalyticsPlatformParams.CHANNEL, + channel = AnalyticsPlatformParams.channel, platform = AnalyticsPlatformParams.platform, info = infoList.map { event -> event.mapToTrackEvent() }, logs = logList.map { event -> event.mapToTrackEvent() }, diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt index 8e7e72db18..8ef42aaf53 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProvider.kt @@ -28,7 +28,7 @@ internal class DefaultAnalyticsSetupProvider( override fun provide(): AnalyticsSetupRequest { return AnalyticsSetupRequest( version = AnalyticsPlatformParams.version, - channel = AnalyticsPlatformParams.CHANNEL, + channel = AnalyticsPlatformParams.channel, platform = AnalyticsPlatformParams.platform, locale = shopperLocale.toString(), component = getComponentQueryParameter(source), From ab6291c0ec211f8f2a848ec1a07cc47eb0cce078 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 13:59:35 +0100 Subject: [PATCH 191/272] Change analytics implementation for StoredACHDirectDebitDelegate and DefaultACHDirectDebitDelegate COAND-844 --- .../ACHDirectDebitComponentProvider.kt | 90 +++++++------------ .../ui/DefaultACHDirectDebitDelegate.kt | 21 +++-- .../ui/StoredACHDirectDebitDelegate.kt | 22 +++-- .../ui/DefaultACHDirectDebitDelegateTest.kt | 24 +++-- .../ui/StoredACHDirectDebitDelegateTest.kt | 25 ++++-- 5 files changed, 93 insertions(+), 89 deletions(-) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt index 410cadd5e3..1d7c6c7c84 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt @@ -34,11 +34,9 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +69,7 @@ class ACHDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -121,23 +119,19 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val achDelegate = createDefaultDelegate( paymentMethod = paymentMethod, savedStateHandle = savedStateHandle, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, httpClient = httpClient, order = order, ) @@ -207,24 +201,19 @@ constructor( ) val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val achDelegate = createDefaultDelegate( paymentMethod = paymentMethod, savedStateHandle = savedStateHandle, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, httpClient = httpClient, order = checkoutSession.order, ) @@ -285,7 +274,7 @@ constructor( paymentMethod: PaymentMethod, savedStateHandle: SavedStateHandle, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: AnalyticsRepository, + analyticsManager: AnalyticsManager, httpClient: HttpClient, order: Order?, ): DefaultACHDirectDebitDelegate { @@ -297,7 +286,7 @@ constructor( return DefaultACHDirectDebitDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, publicKeyRepository = publicKeyRepository, addressRepository = addressRepository, submitHandler = SubmitHandler(savedStateHandle), @@ -328,22 +317,17 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = null, ) val achDelegate = createStoredDelegate( paymentMethod = storedPaymentMethod, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, order = order, ) @@ -414,22 +398,16 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val achDelegate = createStoredDelegate( paymentMethod = storedPaymentMethod, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, componentParams = componentParams, order = checkoutSession.order, ) @@ -487,13 +465,13 @@ constructor( private fun createStoredDelegate( paymentMethod: StoredPaymentMethod, componentParams: ACHDirectDebitComponentParams, - analyticsRepository: AnalyticsRepository, + analyticsManager: AnalyticsManager, order: Order? ): StoredACHDirectDebitDelegate { return StoredACHDirectDebitDelegate( observerRepository = PaymentObserverRepository(), storedPaymentMethod = paymentMethod, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, componentParams = componentParams, order = order, ) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt index b51d517f2a..200862a81a 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -62,7 +62,7 @@ import kotlinx.coroutines.launch internal class DefaultACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val publicKeyRepository: PublicKeyRepository, private val addressRepository: AddressRepository, private val submitHandler: SubmitHandler, @@ -110,7 +110,7 @@ internal class DefaultACHDirectDebitDelegate( _coroutineScope = coroutineScope submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) fetchPublicKey(coroutineScope) if (componentParams.addressParams is AddressParams.FullAddress) { @@ -120,6 +120,11 @@ internal class DefaultACHDirectDebitDelegate( } } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) + } + override fun updateAddressInputData(update: AddressInputModel.() -> Unit) { updateInputData { this.address.update() @@ -250,13 +255,6 @@ internal class DefaultACHDirectDebitDelegate( ) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } - } - private fun updateComponentState(outputData: ACHDirectDebitOutputData) { adyenLog(AdyenLogLevel.VERBOSE) { "updateComponentState" } val componentState = createComponentState(outputData) @@ -290,7 +288,7 @@ internal class DefaultACHDirectDebitDelegate( val achPaymentMethod = ACHDirectDebitPaymentMethod( type = ACHDirectDebitPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), encryptedBankAccountNumber = encryptedBankAccountNumber, encryptedBankLocationId = encryptedBankLocationId, ownerName = outputData.ownerName.value, @@ -354,6 +352,7 @@ internal class DefaultACHDirectDebitDelegate( override fun onCleared() { removeObserver() _coroutineScope = null + analyticsManager.clear(this) } override fun onSubmit() { diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt index 41bcbe2ed2..bcea14fb7b 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -42,13 +42,12 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class StoredACHDirectDebitDelegate( private val observerRepository: PaymentObserverRepository, private val storedPaymentMethod: StoredPaymentMethod, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, override val componentParams: ACHDirectDebitComponentParams, private val order: OrderRequest?, ) : ACHDirectDebitDelegate { @@ -87,13 +86,18 @@ internal class StoredACHDirectDebitDelegate( override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) componentStateFlow.onEach { onState(it) }.launchIn(coroutineScope) } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } + analyticsManager.initialize(this, coroutineScope) + } + private fun onState(achDirectDebitComponentState: ACHDirectDebitComponentState) { if (achDirectDebitComponentState.isValid) { submitChannel.trySend(achDirectDebitComponentState) @@ -104,17 +108,10 @@ internal class StoredACHDirectDebitDelegate( adyenLog(AdyenLogLevel.ERROR) { "updateInputData should not be called in StoredACHDirectDebitDelegate" } } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } - } - private fun createComponentState(): ACHDirectDebitComponentState { val paymentMethod = ACHDirectDebitPaymentMethod( type = ACHDirectDebitPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), storedPaymentMethodId = storedPaymentMethod.id, ) @@ -146,6 +143,7 @@ internal class StoredACHDirectDebitDelegate( override fun onCleared() { removeObserver() _coroutineScope = null + analyticsManager.clear(this) } override fun getPaymentMethodType(): String { diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt index 3bcc431788..0605f3203a 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt @@ -21,7 +21,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -61,7 +61,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -69,7 +71,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler ) { @@ -634,9 +636,15 @@ internal class DefaultACHDirectDebitDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -651,6 +659,12 @@ internal class DefaultACHDirectDebitDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, componentState.data.paymentMethod?.checkoutAttemptId) } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Suppress("LongParameterList") @@ -683,7 +697,7 @@ internal class DefaultACHDirectDebitDelegateTest( @Suppress("LongParameterList") private fun createAchDelegate( paymentMethod: PaymentMethod = PaymentMethod(), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsManager: AnalyticsManager = this.analyticsManager, publicKeyRepository: PublicKeyRepository = this.publicKeyRepository, addressRepository: AddressRepository = this.addressRepository, genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, @@ -693,7 +707,7 @@ internal class DefaultACHDirectDebitDelegateTest( ) = DefaultACHDirectDebitDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, publicKeyRepository = publicKeyRepository, addressRepository = addressRepository, submitHandler = submitHandler, diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt index 27eea5d09a..0d4f063977 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.test.TestDispatcherExtension @@ -38,14 +38,17 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredACHDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: ACHDirectDebitDelegate @@ -92,9 +95,15 @@ internal class StoredACHDirectDebitDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate = createAchDelegate() @@ -102,17 +111,23 @@ internal class StoredACHDirectDebitDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createAchDelegate( paymentMethod: StoredPaymentMethod = StoredPaymentMethod(id = STORED_ID), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsManager: AnalyticsManager = this.analyticsManager, configuration: CheckoutConfiguration = createCheckoutConfiguration(), order: OrderRequest? = TEST_ORDER, ) = StoredACHDirectDebitDelegate( observerRepository = PaymentObserverRepository(), storedPaymentMethod = paymentMethod, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, componentParams = ACHDirectDebitComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, DEVICE_LOCALE, null, null), order = order, From 9e5f43145fbca92707da5e9731047983a81c1790 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 14:32:41 +0100 Subject: [PATCH 192/272] Change analytics implementation for DefaultBacsDirectDebitDelegate COAND-844 --- .../BacsDirectDebitComponentProvider.kt | 45 +++++++------------ .../ui/DefaultBacsDirectDebitDelegate.kt | 18 ++++---- .../DefaultBacsDirectDebitDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt index d3b02aea63..6be4c43889 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt @@ -28,11 +28,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +55,7 @@ class BacsDirectDebitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -95,16 +93,11 @@ constructor( componentConfiguration = checkoutConfiguration.getBacsDirectDebitConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val bacsDelegate = DefaultBacsDirectDebitDelegate( @@ -112,7 +105,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -187,17 +180,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val bacsDelegate = DefaultBacsDirectDebitDelegate( @@ -205,7 +192,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 9a1b0155dc..3f7fe7cb04 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -20,7 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BacsDirectDebitPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -33,7 +33,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultBacsDirectDebitDelegate( @@ -41,7 +40,7 @@ internal class DefaultBacsDirectDebitDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : BacsDirectDebitDelegate { @@ -64,14 +63,12 @@ internal class DefaultBacsDirectDebitDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -189,7 +186,7 @@ internal class DefaultBacsDirectDebitDelegate( ): BacsDirectDebitComponentState { val bacsDirectDebitPaymentMethod = BacsDirectDebitPaymentMethod( type = BacsDirectDebitPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), holderName = outputData.holderNameState.value, bankAccountNumber = outputData.bankAccountNumberState.value, bankLocationId = outputData.sortCodeState.value, @@ -220,5 +217,6 @@ internal class DefaultBacsDirectDebitDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index b980860d8c..89b6b396ba 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -23,7 +23,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -48,7 +48,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -56,7 +58,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBacsDirectDebitDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -451,12 +453,6 @@ internal class DefaultBacsDirectDebitDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -556,9 +552,15 @@ internal class DefaultBacsDirectDebitDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -575,6 +577,12 @@ internal class DefaultBacsDirectDebitDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createBacsDelegate( @@ -591,7 +599,7 @@ internal class DefaultBacsDirectDebitDelegateTest( ), paymentMethod = PaymentMethod(), order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) From 9b74556760fd6f881e40ac99446c2f3c98ebb601 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 14:48:19 +0100 Subject: [PATCH 193/272] Change analytics implementation for DefaultCardDelegate and StoredCardDelegate COAND-844 --- .../provider/BcmcComponentProvider.kt | 45 ++++------- .../provider/CardComponentProvider.kt | 80 +++++++------------ .../card/internal/ui/DefaultCardDelegate.kt | 17 ++-- .../card/internal/ui/StoredCardDelegate.kt | 17 ++-- .../internal/ui/DefaultCardDelegateTest.kt | 30 ++++--- .../internal/ui/StoredCardDelegateTest.kt | 30 ++++--- 6 files changed, 98 insertions(+), 121 deletions(-) diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt index 2eced9b09a..f6ae0dbd0f 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt @@ -31,11 +31,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -66,7 +64,7 @@ class BcmcComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -116,16 +114,11 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val cardDelegate = DefaultCardDelegate( @@ -134,7 +127,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, addressRepository = addressRepository, detectCardTypeRepository = detectCardTypeRepository, cardValidationMapper = cardValidationMapper, @@ -225,17 +218,11 @@ constructor( val binLookupService = BinLookupService(httpClient) val detectCardTypeRepository = DefaultDetectCardTypeRepository(cardEncryptor, binLookupService) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val cardDelegate = DefaultCardDelegate( @@ -244,7 +231,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, addressRepository = addressRepository, detectCardTypeRepository = detectCardTypeRepository, cardValidationMapper = cardValidationMapper, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt index 9a72fc7a80..e5d06843c7 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt @@ -33,11 +33,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -71,7 +69,7 @@ class CardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -136,16 +134,11 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val cardDelegate = DefaultCardDelegate( @@ -154,7 +147,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, addressRepository = addressRepository, detectCardTypeRepository = detectCardTypeRepository, cardValidationMapper = cardValidationMapper, @@ -250,17 +243,11 @@ constructor( val addressRepository = DefaultAddressRepository(addressService) val cardValidationMapper = CardValidationMapper() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val cardDelegate = DefaultCardDelegate( @@ -269,7 +256,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, addressRepository = addressRepository, detectCardTypeRepository = detectCardTypeRepository, cardValidationMapper = cardValidationMapper, @@ -376,16 +363,11 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = null, ) val cardDelegate = StoredCardDelegate( @@ -393,7 +375,7 @@ constructor( storedPaymentMethod = storedPaymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, cardEncryptor = cardEncryptor, publicKeyRepository = publicKeyRepository, submitHandler = SubmitHandler(savedStateHandle), @@ -476,17 +458,11 @@ constructor( val publicKeyRepository = DefaultPublicKeyRepository(publicKeyService) val cardEncryptor = CardEncryptorFactory.provide() - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val cardDelegate = StoredCardDelegate( @@ -494,7 +470,7 @@ constructor( storedPaymentMethod = storedPaymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, cardEncryptor = cardEncryptor, publicKeyRepository = publicKeyRepository, submitHandler = SubmitHandler(savedStateHandle), diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index 09525bf5be..87268d06a5 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -44,7 +44,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -97,7 +97,7 @@ class DefaultCardDelegate( override val componentParams: CardComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val addressRepository: AddressRepository, private val detectCardTypeRepository: DetectCardTypeRepository, private val cardValidationMapper: CardValidationMapper, @@ -151,7 +151,7 @@ class DefaultCardDelegate( submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) fetchPublicKey() subscribeToDetectedCardTypes() @@ -171,11 +171,9 @@ class DefaultCardDelegate( .launchIn(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -688,7 +686,7 @@ class DefaultCardDelegate( ): CardComponentState { val cardPaymentMethod = CardPaymentMethod( type = CardPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), ).apply { encryptedCardNumber = encryptedCard.encryptedCardNumber encryptedExpiryMonth = encryptedCard.encryptedExpiryMonth @@ -834,6 +832,7 @@ class DefaultCardDelegate( onBinValueListener = null onBinLookupListener = null addressLookupDelegate.clear() + analyticsManager.clear(this) } companion object { diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt index 68b91590b1..1391b880a1 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt @@ -32,7 +32,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -72,7 +72,7 @@ internal class StoredCardDelegate( private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, override val componentParams: CardComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val cardEncryptor: BaseCardEncryptor, private val publicKeyRepository: PublicKeyRepository, private val submitHandler: SubmitHandler, @@ -132,7 +132,7 @@ internal class StoredCardDelegate( submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) initializeInputData() fetchPublicKey() @@ -151,11 +151,9 @@ internal class StoredCardDelegate( } } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -334,7 +332,7 @@ internal class StoredCardDelegate( ): CardComponentState { val cardPaymentMethod = CardPaymentMethod( type = CardPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), ).apply { storedPaymentMethodId = getPaymentMethodId() @@ -430,6 +428,7 @@ internal class StoredCardDelegate( override fun onCleared() { removeObserver() coroutineScope = null + analyticsManager.clear(this) } companion object { diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index ff25ae7a26..8057210b3e 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -45,7 +45,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -90,7 +90,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -98,7 +100,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, @Mock private val addressLookupDelegate: AddressLookupDelegate, ) { @@ -978,12 +980,6 @@ internal class DefaultCardDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -1040,9 +1036,15 @@ internal class DefaultCardDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -1056,6 +1058,12 @@ internal class DefaultCardDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Nested @@ -1210,7 +1218,7 @@ internal class DefaultCardDelegateTest( genericEncryptor: BaseGenericEncryptor = this.genericEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), paymentMethod: PaymentMethod = PaymentMethod(type = PaymentMethodTypes.SCHEME), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsManager: AnalyticsManager = this.analyticsManager, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, addressLookupDelegate: AddressLookupDelegate = this.addressLookupDelegate @@ -1237,7 +1245,7 @@ internal class DefaultCardDelegateTest( detectCardTypeRepository = detectCardTypeRepository, cardValidationMapper = cardValidationMapper, genericEncryptor = genericEncryptor, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, addressLookupDelegate = addressLookupDelegate, ) diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index ad22d1a039..bbbf769bec 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -70,7 +70,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -78,7 +80,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler ) { @@ -365,12 +367,6 @@ internal class StoredCardDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -429,9 +425,15 @@ internal class StoredCardDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -443,6 +445,12 @@ internal class StoredCardDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Suppress("LongParameterList") @@ -451,7 +459,7 @@ internal class StoredCardDelegateTest( cardEncryptor: BaseCardEncryptor = this.cardEncryptor, configuration: CheckoutConfiguration = createCheckoutConfiguration(), storedPaymentMethod: StoredPaymentMethod = getStoredPaymentMethod(), - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsManager: AnalyticsManager = this.analyticsManager, submitHandler: SubmitHandler = this.submitHandler, order: OrderRequest? = TEST_ORDER, ): StoredCardDelegate { @@ -472,7 +480,7 @@ internal class StoredCardDelegateTest( publicKeyRepository = publicKeyRepository, componentParams = componentParams, cardEncryptor = cardEncryptor, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, order = order, ) From e4e9b07faeea1502a2777ef8f1fed526116251d2 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 15:14:37 +0100 Subject: [PATCH 194/272] Change analytics implementation for DefaultBlikDelegate and StoredBlikDelegate COAND-844 --- .../provider/BlikComponentProvider.kt | 80 +++++--------- .../blik/internal/ui/DefaultBlikDelegate.kt | 18 ++- .../blik/internal/ui/StoredBlikDelegate.kt | 21 ++-- .../internal/ui/DefaultBlikDelegateTest.kt | 28 +++-- .../internal/ui/StoredBlikDelegateTest.kt | 104 ++++++++++++++++++ 5 files changed, 169 insertions(+), 82 deletions(-) create mode 100644 blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt index b0e7cc1aa8..75e047b0d2 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt @@ -30,11 +30,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper @@ -62,7 +60,7 @@ class BlikComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -112,16 +110,11 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val blikDelegate = DefaultBlikDelegate( @@ -129,7 +122,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -201,16 +194,11 @@ constructor( componentConfiguration = checkoutConfiguration.getBlikConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = null, ) val blikDelegate = StoredBlikDelegate( @@ -218,7 +206,7 @@ constructor( componentParams = componentParams, storedPaymentMethod = storedPaymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -293,17 +281,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val blikDelegate = DefaultBlikDelegate( @@ -311,7 +293,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -405,17 +387,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val blikDelegate = StoredBlikDelegate( @@ -423,7 +399,7 @@ constructor( componentParams = componentParams, storedPaymentMethod = storedPaymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt index c1e640c110..b2f0bd08e0 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -32,7 +32,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultBlikDelegate( @@ -40,7 +39,7 @@ internal class DefaultBlikDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : BlikDelegate { @@ -70,14 +69,12 @@ internal class DefaultBlikDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -132,7 +129,7 @@ internal class DefaultBlikDelegate( ): BlikComponentState { val paymentMethod = BlikPaymentMethod( type = BlikPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), blikCode = outputData.blikCodeField.value, ) @@ -164,5 +161,6 @@ internal class DefaultBlikDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt index 20beadb7d8..a13b2dd723 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -31,7 +31,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class StoredBlikDelegate( @@ -39,7 +38,7 @@ internal class StoredBlikDelegate( override val componentParams: ButtonComponentParams, private val storedPaymentMethod: StoredPaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : BlikDelegate { @@ -63,14 +62,12 @@ internal class StoredBlikDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -107,10 +104,13 @@ internal class StoredBlikDelegate( private fun createOutputData() = BlikOutputData(blikCode = "") + @Suppress("ForbiddenComment") + // TODO: Here we only call this method on initialization. The checkoutAttemptId will only be available if it is + // passed by drop-in. This should be fixed as part of state refactoring. private fun createComponentState(): BlikComponentState { val paymentMethod = BlikPaymentMethod( type = BlikPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), storedPaymentMethodId = storedPaymentMethod.id, ) @@ -137,5 +137,6 @@ internal class StoredBlikDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt index 7dd6b6feba..8f5b760f4d 100644 --- a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -42,7 +42,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -50,7 +52,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBlikDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -192,12 +194,6 @@ internal class DefaultBlikDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -254,9 +250,15 @@ internal class DefaultBlikDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -268,6 +270,12 @@ internal class DefaultBlikDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createBlikDelegate( @@ -283,7 +291,7 @@ internal class DefaultBlikDelegateTest( ), paymentMethod = PaymentMethod(), order = TEST_ORDER, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt new file mode 100644 index 0000000000..29dbb66a2d --- /dev/null +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt @@ -0,0 +1,104 @@ +/* + * 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 ararat on 18/3/2024. + */ + +package com.adyen.checkout.blik.internal.ui + +import com.adyen.checkout.blik.BlikComponentState +import com.adyen.checkout.blik.BlikConfiguration +import com.adyen.checkout.blik.blik +import com.adyen.checkout.blik.getBlikConfiguration +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.OrderRequest +import com.adyen.checkout.components.core.StoredPaymentMethod +import com.adyen.checkout.components.core.internal.PaymentObserverRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.core.Environment +import com.adyen.checkout.test.LoggingExtension +import com.adyen.checkout.ui.core.internal.ui.SubmitHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.verify +import java.util.Locale + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockitoExtension::class, LoggingExtension::class) +class StoredBlikDelegateTest( + @Mock private val analyticsManager: AnalyticsManager, + @Mock private val submitHandler: SubmitHandler, +) { + + private lateinit var delegate: StoredBlikDelegate + + @BeforeEach + fun beforeEach() { + delegate = createStoredBlikDelegate() + } + + @Nested + inner class AnalyticsTest { + + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } + } + + private fun createStoredBlikDelegate( + configuration: CheckoutConfiguration = createCheckoutConfiguration() + ) = StoredBlikDelegate( + observerRepository = PaymentObserverRepository(), + componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = configuration, + deviceLocale = Locale.US, + dropInOverrideParams = null, + componentSessionParams = null, + componentConfiguration = configuration.getBlikConfiguration(), + ), + storedPaymentMethod = StoredPaymentMethod(id = STORED_ID), + order = TEST_ORDER, + analyticsManager = analyticsManager, + submitHandler = submitHandler, + ) + + private fun createCheckoutConfiguration( + amount: Amount? = null, + configuration: BlikConfiguration.Builder.() -> Unit = {} + ) = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + amount = amount, + ) { + blik(configuration) + } + + companion object { + private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") + private const val STORED_ID = "Stored_id" + } +} From 4bcb0b5aecdf9178c8f989aa0c1ca3c8fcf3b8c8 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 15:18:37 +0100 Subject: [PATCH 195/272] Change analytics implementation for DefaultBoletoDelegate COAND-844 --- .../provider/BoletoComponentProvider.kt | 45 +++++++------------ .../internal/ui/DefaultBoletoDelegate.kt | 18 ++++---- .../internal/ui/DefaultBoletoDelegateTest.kt | 31 ++++++++----- 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt index 0c2fc1aa39..51cb876156 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt @@ -28,11 +28,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -58,7 +56,7 @@ class BoletoComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -97,16 +95,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val addressService = AddressService(httpClient) @@ -114,7 +107,7 @@ constructor( val boletoDelegate = DefaultBoletoDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, @@ -192,24 +185,18 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val addressService = AddressService(httpClient) val addressRepository = DefaultAddressRepository(addressService) val boletoDelegate = DefaultBoletoDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt index a4c1164446..4da71ec85b 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt @@ -22,7 +22,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.ShopperName import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -49,12 +49,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultBoletoDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, @@ -94,7 +93,7 @@ internal class DefaultBoletoDelegate( _coroutineScope = coroutineScope submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) if (componentParams.addressParams is AddressParams.FullAddress) { subscribeToStatesList() @@ -103,11 +102,9 @@ internal class DefaultBoletoDelegate( } } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } private fun subscribeToStatesList() { @@ -234,7 +231,7 @@ internal class DefaultBoletoDelegate( val paymentComponentData = PaymentComponentData( paymentMethod = GenericPaymentMethod( type = paymentMethod.type, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), subtype = null, ), order = order, @@ -301,5 +298,6 @@ internal class DefaultBoletoDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt index 9e4e33f97d..c14ca297c7 100644 --- a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt +++ b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -45,7 +45,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -54,7 +56,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBoletoDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: DefaultBoletoDelegate @@ -67,13 +69,6 @@ internal class DefaultBoletoDelegateTest( delegate = createBoletoDelegate() } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - - verify(analyticsRepository).setupAnalytics() - } - @Nested @DisplayName("when input data changes and") inner class InputDataChangedTest { @@ -491,9 +486,15 @@ internal class DefaultBoletoDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -505,19 +506,25 @@ internal class DefaultBoletoDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Suppress("LongParameterList") private fun createBoletoDelegate( submitHandler: SubmitHandler = this.submitHandler, - analyticsRepository: AnalyticsRepository = this.analyticsRepository, + analyticsManager: AnalyticsManager = this.analyticsManager, paymentMethod: PaymentMethod = PaymentMethod(), addressRepository: TestAddressRepository = this.addressRepository, order: Order? = TEST_ORDER, configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultBoletoDelegate( submitHandler = submitHandler, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, From 92c7201929cc966abb7c42ae1c0abeaabb6c0682 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 16:07:42 +0100 Subject: [PATCH 196/272] Change analytics implementation for DefaultIssuerListDelegate COAND-844 --- .../provider/DotpayComponentProvider.kt | 6 +-- .../provider/EntercashComponentProvider.kt | 6 +-- .../internal/provider/EPSComponentProvider.kt | 6 +-- .../provider/IdealComponentProvider.kt | 6 +-- .../provider/IssuerListComponentProvider.kt | 50 +++++++------------ .../internal/ui/DefaultIssuerListDelegate.kt | 18 +++---- .../ui/DefaultIssuerListDelegateTest.kt | 32 +++++++----- .../provider/MolpayComponentProvider.kt | 6 +-- .../OnlineBankingPLComponentProvider.kt | 8 +-- .../provider/OpenBankingComponentProvider.kt | 6 +-- 10 files changed, 69 insertions(+), 75 deletions(-) diff --git a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt index d3e8c6bae9..a4a939731a 100644 --- a/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt +++ b/dotpay/src/main/java/com/adyen/checkout/dotpay/internal/provider/DotpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.DotpayPaymentMethod import com.adyen.checkout.dotpay.DotpayComponent @@ -29,11 +29,11 @@ class DotpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider( componentClass = DotpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( diff --git a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt index 844cbdf7ed..865375ffac 100644 --- a/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt +++ b/entercash/src/main/java/com/adyen/checkout/entercash/internal/provider/EntercashComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EntercashPaymentMethod import com.adyen.checkout.entercash.EntercashComponent @@ -29,7 +29,7 @@ class EntercashComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider< EntercashComponent, EntercashConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = EntercashComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( diff --git a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt index f87ed9ccee..11dfb6cc55 100644 --- a/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt +++ b/eps/src/main/java/com/adyen/checkout/eps/internal/provider/EPSComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.EPSPaymentMethod import com.adyen.checkout.eps.EPSComponent @@ -29,11 +29,11 @@ class EPSComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider( componentClass = EPSComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, hideIssuerLogosDefaultValue = true, ) { diff --git a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt index 2c88bd6328..a919c8b124 100644 --- a/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt +++ b/ideal/src/main/java/com/adyen/checkout/ideal/internal/provider/IdealComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.IdealPaymentMethod import com.adyen.checkout.ideal.IdealComponent @@ -29,11 +29,11 @@ class IdealComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider( componentClass = IdealComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt index 29eeae3fad..ef8c497fb7 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt @@ -27,11 +27,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -69,7 +67,7 @@ abstract class IssuerListComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsManager: AnalyticsManager? = null, private val hideIssuerLogosDefaultValue: Boolean = false, private val localeProvider: LocaleProvider = LocaleProvider(), ) : @@ -104,23 +102,19 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) + val issuerListDelegate = createDefaultDelegate( componentParams = componentParams, paymentMethod = paymentMethod, order = order, savedStateHandle = savedStateHandle, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( @@ -195,24 +189,18 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val issuerListDelegate = createDefaultDelegate( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, savedStateHandle = savedStateHandle, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( @@ -284,14 +272,14 @@ constructor( paymentMethod: PaymentMethod, order: Order?, savedStateHandle: SavedStateHandle, - analyticsRepository: AnalyticsRepository, + analyticsManager: AnalyticsManager, ): DefaultIssuerListDelegate { return DefaultIssuerListDelegate( observerRepository = PaymentObserverRepository(), componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), typedPaymentMethodFactory = ::createPaymentMethod, componentStateFactory = ::createComponentState, diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index d4ff7685ea..a4d9d46e6e 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @@ -34,7 +34,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultIssuerListDelegate< @@ -45,7 +44,7 @@ internal class DefaultIssuerListDelegate< override val componentParams: IssuerListComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> IssuerListPaymentMethodT, private val componentStateFactory: ( @@ -74,14 +73,12 @@ internal class DefaultIssuerListDelegate< override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -146,7 +143,7 @@ internal class DefaultIssuerListDelegate< ): ComponentStateT { val issuerListPaymentMethod = typedPaymentMethodFactory().apply { type = getPaymentMethodType() - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId() + checkoutAttemptId = analyticsManager.getCheckoutAttemptId() issuer = outputData.selectedIssuer?.id.orEmpty() } @@ -173,5 +170,6 @@ internal class DefaultIssuerListDelegate< override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index b9d877d93b..fc125f9ac2 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.issuerlist.IssuerListViewType @@ -44,7 +44,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -52,7 +54,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultIssuerListDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -180,7 +182,7 @@ internal class DefaultIssuerListDelegateTest( ), paymentMethod = PaymentMethod(), order = TEST_ORDER, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, typedPaymentMethodFactory = { TestIssuerPaymentMethod() }, componentStateFactory = { data, isInputValid, isReady -> @@ -215,7 +217,7 @@ internal class DefaultIssuerListDelegateTest( ), paymentMethod = PaymentMethod(), order = TEST_ORDER, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, typedPaymentMethodFactory = { TestIssuerPaymentMethod() }, componentStateFactory = { data, isInputValid, isReady -> @@ -231,12 +233,6 @@ internal class DefaultIssuerListDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -295,9 +291,15 @@ internal class DefaultIssuerListDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -309,6 +311,12 @@ internal class DefaultIssuerListDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createIssuerListDelegate( @@ -325,7 +333,7 @@ internal class DefaultIssuerListDelegateTest( ), paymentMethod = PaymentMethod(), order = TEST_ORDER, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, typedPaymentMethodFactory = { TestIssuerPaymentMethod() }, componentStateFactory = { data, isInputValid, isReady -> diff --git a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt index 45885eecb0..1b1e9e4fc8 100644 --- a/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt +++ b/molpay/src/main/java/com/adyen/checkout/molpay/internal/provider/MolpayComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.MolpayPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,11 +29,11 @@ class MolpayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider( componentClass = MolpayComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( diff --git a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt index 207acfc642..158accf2f6 100644 --- a/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt +++ b/online-banking-pl/src/main/java/com/adyen/checkout/onlinebankingpl/internal/provider/OnlineBankingPLComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingPLPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,16 +29,16 @@ class OnlineBankingPLComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider< OnlineBankingPLComponent, OnlineBankingPLConfiguration, OnlineBankingPLPaymentMethod, - OnlineBankingPLComponentState + OnlineBankingPLComponentState, >( componentClass = OnlineBankingPLComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( diff --git a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt index c0e49f8f40..8f8d515e05 100644 --- a/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt +++ b/openbanking/src/main/java/com/adyen/checkout/openbanking/internal/provider/OpenBankingComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OpenBankingPaymentMethod import com.adyen.checkout.issuerlist.internal.provider.IssuerListComponentProvider @@ -29,7 +29,7 @@ class OpenBankingComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : IssuerListComponentProvider< OpenBankingComponent, OpenBankingConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = OpenBankingComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponent( From 72eadf168d9e1f46b90cf70805f8fccce8845ca9 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 16:15:18 +0100 Subject: [PATCH 197/272] Change analytics implementation for DefaultEContextDelegate COAND-844 --- .../ConvenienceStoresJPComponentProvider.kt | 6 +-- .../provider/EContextComponentProvider.kt | 47 +++++++------------ .../internal/ui/DefaultEContextDelegate.kt | 18 ++++--- .../ui/DefaultEContextDelegateTest.kt | 28 +++++++---- .../OnlineBankingJPComponentProvider.kt | 6 +-- .../provider/PayEasyComponentProvider.kt | 6 +-- .../provider/SevenElevenComponentProvider.kt | 6 +-- 7 files changed, 56 insertions(+), 61 deletions(-) diff --git a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt index 34f88479cd..bed48924f3 100644 --- a/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt +++ b/convenience-stores-jp/src/main/java/com/adyen/checkout/conveniencestoresjp/internal/provider/ConvenienceStoresJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.ConvenienceStoresJPPaymentMethod import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -29,7 +29,7 @@ class ConvenienceStoresJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : EContextComponentProvider< ConvenienceStoresJPComponent, ConvenienceStoresJPConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = ConvenienceStoresJPComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponentState( diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt index d9dd909bda..0aa660187b 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt @@ -26,11 +26,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +65,7 @@ abstract class EContextComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsManager: AnalyticsManager?, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, SessionPaymentComponentProvider< @@ -100,23 +98,19 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) + val eContextDelegate = DefaultEContextDelegate( observerRepository = PaymentObserverRepository(), componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), typedPaymentMethodFactory = { createPaymentMethod() }, componentStateFactory = ::createComponentState, @@ -192,24 +186,19 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) + val eContextDelegate = DefaultEContextDelegate( observerRepository = PaymentObserverRepository(), componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), typedPaymentMethodFactory = { createPaymentMethod() }, componentStateFactory = ::createComponentState, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index e2f50e7900..80d7c6a61b 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -17,7 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -38,7 +38,6 @@ import com.adyen.checkout.ui.core.internal.util.CountryUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch import java.util.Locale @Suppress("TooManyFunctions", "LongParameterList") @@ -50,7 +49,7 @@ internal class DefaultEContextDelegate< override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, private val typedPaymentMethodFactory: () -> EContextPaymentMethodT, private val componentStateFactory: ( @@ -79,14 +78,12 @@ internal class DefaultEContextDelegate< override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun updateInputData(update: EContextInputData.() -> Unit) { @@ -120,7 +117,7 @@ internal class DefaultEContextDelegate< ): EContextComponentStateT { val eContextPaymentMethod = typedPaymentMethodFactory().apply { type = getPaymentMethodType() - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId() + checkoutAttemptId = analyticsManager.getCheckoutAttemptId() firstName = outputData.firstNameState.value lastName = outputData.lastNameState.value telephoneNumber = outputData.phoneNumberState.value @@ -198,6 +195,7 @@ internal class DefaultEContextDelegate< override fun onCleared() { removeObserver() + analyticsManager.clear(this) } override fun onSubmit() { diff --git a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt index 9e44355b85..83b7c543bb 100644 --- a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt +++ b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -45,14 +45,16 @@ import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultEContextDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -197,12 +199,6 @@ internal class DefaultEContextDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -259,9 +255,15 @@ internal class DefaultEContextDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -277,6 +279,12 @@ internal class DefaultEContextDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + org.mockito.kotlin.verify(analyticsManager).clear(eq(delegate)) + } } @Test @@ -300,7 +308,7 @@ internal class DefaultEContextDelegateTest( ), paymentMethod = PaymentMethod(), order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, typedPaymentMethodFactory = { TestEContextPaymentMethod() }, componentStateFactory = { data, isInputValid, isReady -> diff --git a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt index 3474600925..106fbab491 100644 --- a/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt +++ b/online-banking-jp/src/main/java/com/adyen/checkout/onlinebankingjp/internal/provider/OnlineBankingJPComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingJPPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingJPComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : EContextComponentProvider< OnlineBankingJPComponent, OnlineBankingJPConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = OnlineBankingJPComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponentState( diff --git a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt index 7b4839a19b..5b04a5835d 100644 --- a/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt +++ b/payeasy/src/main/java/com/adyen/checkout/payeasy/internal/provider/PayEasyComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.PayEasyPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,11 +29,11 @@ class PayEasyComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : EContextComponentProvider( componentClass = PayEasyComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponentState( diff --git a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt index 871e3316c6..4abd5212bd 100644 --- a/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt +++ b/seven-eleven/src/main/java/com/adyen/checkout/seveneleven/internal/provider/SevenElevenComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.SevenElevenPaymentMethod import com.adyen.checkout.econtext.internal.provider.EContextComponentProvider @@ -29,7 +29,7 @@ class SevenElevenComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : EContextComponentProvider< SevenElevenComponent, SevenElevenConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = SevenElevenComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createComponentState( From d43f1cca4dbc4a7653574b1ed334bc379bab366f Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 16:48:04 +0100 Subject: [PATCH 198/272] Change analytics implementation for DefaultGiftCardDelegate COAND-844 --- .../provider/GiftCardComponentProvider.kt | 45 +++++++------------ .../internal/ui/DefaultGiftCardDelegate.kt | 17 ++++--- .../ui/DefaultGiftCardDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 48 deletions(-) diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt index 85ec671395..77dddd2a62 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt @@ -19,11 +19,9 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.data.api.DefaultPublicKeyRepository import com.adyen.checkout.components.core.internal.data.api.PublicKeyService import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider @@ -59,7 +57,7 @@ class GiftCardComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -100,23 +98,18 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val giftCardDelegate = DefaultGiftCardDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, publicKeyRepository = DefaultPublicKeyRepository(publicKeyService), componentParams = componentParams, cardEncryptor = cardEncryptor, @@ -195,24 +188,18 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) val publicKeyService = PublicKeyService(httpClient) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val giftCardDelegate = DefaultGiftCardDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, publicKeyRepository = DefaultPublicKeyRepository(publicKeyService), componentParams = componentParams, cardEncryptor = cardEncryptor, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt index da2611d89e..1b358bdb88 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt @@ -19,7 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -60,7 +60,7 @@ internal class DefaultGiftCardDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val publicKeyRepository: PublicKeyRepository, override val componentParams: GiftCardComponentParams, private val cardEncryptor: BaseCardEncryptor, @@ -95,15 +95,13 @@ internal class DefaultGiftCardDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) fetchPublicKey(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } private fun fetchPublicKey(coroutineScope: CoroutineScope) { @@ -209,7 +207,7 @@ internal class DefaultGiftCardDelegate( val giftCardPaymentMethod = GiftCardPaymentMethod( type = GiftCardPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), encryptedCardNumber = encryptedCard.encryptedCardNumber, encryptedSecurityCode = encryptedCard.encryptedSecurityCode, brand = paymentMethod.brand, @@ -346,6 +344,7 @@ internal class DefaultGiftCardDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } companion object { diff --git a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt index 0a6ad6e5e5..0230a63cf7 100644 --- a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt +++ b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.OrderResponse import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -51,7 +51,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -59,7 +61,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultGiftCardDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -179,12 +181,6 @@ internal class DefaultGiftCardDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -360,9 +356,15 @@ internal class DefaultGiftCardDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -375,6 +377,12 @@ internal class DefaultGiftCardDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Test @@ -408,7 +416,7 @@ internal class DefaultGiftCardDelegateTest( componentParams = GiftCardComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), cardEncryptor = cardEncryptor, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) From fc82ec6360f18bebdd35cf42bf6b5695b49afa06 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 16:58:54 +0100 Subject: [PATCH 199/272] Change analytics implementation for DefaultGooglePayDelegate COAND-844 --- .../provider/GooglePayComponentProvider.kt | 45 +++++++------------ .../internal/ui/DefaultGooglePayDelegate.kt | 18 ++++---- .../ui/DefaultGooglePayDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt index a308067728..bc915cb12e 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt @@ -23,11 +23,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentMethodAvailabilityCheck import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -64,7 +62,7 @@ class GooglePayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -103,16 +101,11 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val googlePayDelegate = DefaultGooglePayDelegate( @@ -120,7 +113,7 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( @@ -194,17 +187,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val googlePayDelegate = DefaultGooglePayDelegate( @@ -212,7 +199,7 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt index 8ab1f14270..1ed3720e37 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -40,7 +40,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultGooglePayDelegate( @@ -48,7 +47,7 @@ internal class DefaultGooglePayDelegate( private val paymentMethod: PaymentMethod, private val order: OrderRequest?, override val componentParams: GooglePayComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, ) : GooglePayDelegate { private val _componentStateFlow = MutableStateFlow(createComponentState()) @@ -61,18 +60,16 @@ internal class DefaultGooglePayDelegate( override val submitFlow: Flow = submitChannel.receiveAsFlow() override fun initialize(coroutineScope: CoroutineScope) { - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) componentStateFlow.onEach { onState(it) }.launchIn(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } private fun onState(state: GooglePayComponentState) { @@ -115,7 +112,7 @@ internal class DefaultGooglePayDelegate( val paymentMethod = GooglePayUtils.createGooglePayPaymentMethod( paymentData = paymentData, paymentMethodType = paymentMethod.type, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), ) val paymentComponentData = PaymentComponentData( paymentMethod = paymentMethod, @@ -181,5 +178,6 @@ internal class DefaultGooglePayDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt index dff2e9fdf9..e3666f0d4a 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Configuration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.GooglePayPaymentMethod import com.adyen.checkout.core.Environment @@ -40,7 +40,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -48,7 +50,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultGooglePayDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: DefaultGooglePayDelegate @@ -129,18 +131,18 @@ internal class DefaultGooglePayDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -150,6 +152,12 @@ internal class DefaultGooglePayDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createCheckoutConfiguration( @@ -176,7 +184,7 @@ internal class DefaultGooglePayDelegateTest( order = TEST_ORDER, componentParams = GooglePayComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null, paymentMethod), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) } From c957a1c196894b945fa162563330a4bd2301d3ac Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:02:26 +0100 Subject: [PATCH 200/272] Change analytics implementation for DefaultInstantPaymentDelegate COAND-844 --- .../InstantPaymentComponentProvider.kt | 45 +++++++------------ .../ui/DefaultInstantPaymentDelegate.kt | 18 ++++---- .../ui/DefaultInstantPaymentDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index 19deb75e1e..3ed2f8f6d2 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -23,11 +23,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +54,7 @@ class InstantPaymentComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -94,16 +92,11 @@ constructor( paymentMethod = paymentMethod, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val instantPaymentDelegate = DefaultInstantPaymentDelegate( @@ -111,7 +104,7 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( @@ -185,17 +178,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val instantPaymentDelegate = DefaultInstantPaymentDelegate( @@ -203,7 +190,7 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index ef05504d0b..a44d2ac784 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails @@ -30,14 +30,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch internal class DefaultInstantPaymentDelegate( private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: InstantComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, ) : InstantPaymentDelegate { override val componentStateFlow: StateFlow = MutableStateFlow(createComponentState()) @@ -55,7 +54,7 @@ internal class DefaultInstantPaymentDelegate( val paymentComponentData = PaymentComponentData( paymentMethod = GenericPaymentMethod( type = paymentMethod.type, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), subtype = getSubtype(paymentMethod), ), order = order, @@ -78,14 +77,12 @@ internal class DefaultInstantPaymentDelegate( } override fun initialize(coroutineScope: CoroutineScope) { - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -109,6 +106,7 @@ internal class DefaultInstantPaymentDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } companion object { diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index 29dd8d9686..8f51098b93 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper @@ -34,7 +34,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -42,7 +44,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) class DefaultInstantPaymentDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: DefaultInstantPaymentDelegate @@ -82,18 +84,18 @@ class DefaultInstantPaymentDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate = createInstantPaymentDelegate() @@ -101,6 +103,12 @@ class DefaultInstantPaymentDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createCheckoutConfiguration( @@ -121,7 +129,7 @@ class DefaultInstantPaymentDelegateTest( order = TEST_ORDER, componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null, PaymentMethod(type = "paypal")), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) } From 3ae4e78ba6e0c9053ad443306dd9a8aa22f9e8f0 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:06:38 +0100 Subject: [PATCH 201/272] Change analytics implementation for DefaultOnlineBankingDelegate COAND-844 --- .../OnlineBankingComponentProvider.kt | 45 +++++++------------ .../ui/DefaultOnlineBankingDelegate.kt | 18 ++++---- .../ui/DefaultOnlineBankingDelegateTest.kt | 28 +++++++----- .../OnlineBankingCZComponentProvider.kt | 6 +-- .../OnlineBankingSKComponentProvider.kt | 6 +-- 5 files changed, 48 insertions(+), 55 deletions(-) diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt index 9ba3010645..a35d80713a 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt @@ -26,11 +26,9 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -68,7 +66,7 @@ abstract class OnlineBankingComponentProvider< constructor( private val componentClass: Class, private val dropInOverrideParams: DropInOverrideParams?, - private val analyticsRepository: AnalyticsRepository?, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -101,16 +99,11 @@ constructor( componentConfiguration = getConfiguration(checkoutConfiguration), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val onlineBankingDelegate = DefaultOnlineBankingDelegate( @@ -119,7 +112,7 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, termsAndConditionsUrl = getTermsAndConditionsUrl(), submitHandler = SubmitHandler(savedStateHandle), paymentMethodFactory = { createPaymentMethod() }, @@ -199,17 +192,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val onlineBankingDelegate = DefaultOnlineBankingDelegate( @@ -218,7 +205,7 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, termsAndConditionsUrl = getTermsAndConditionsUrl(), submitHandler = SubmitHandler(savedStateHandle), paymentMethodFactory = { createPaymentMethod() }, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt index d4633d09de..d2a64f6fd5 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod @@ -41,7 +41,6 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions", "LongParameterList") internal class DefaultOnlineBankingDelegate< @@ -53,7 +52,7 @@ internal class DefaultOnlineBankingDelegate< private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: ButtonComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val termsAndConditionsUrl: String, private val submitHandler: SubmitHandler, private val paymentMethodFactory: () -> IssuerListPaymentMethodT, @@ -92,14 +91,12 @@ internal class DefaultOnlineBankingDelegate< override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -154,7 +151,7 @@ internal class DefaultOnlineBankingDelegate< ): ComponentStateT { val issuerListPaymentMethod = paymentMethodFactory().apply { type = getPaymentMethodType() - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId() + checkoutAttemptId = analyticsManager.getCheckoutAttemptId() issuer = outputData.selectedIssuer?.id } @@ -190,5 +187,6 @@ internal class DefaultOnlineBankingDelegate< override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt index a6505d1887..ff7c841c53 100644 --- a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt +++ b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -45,8 +45,10 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -54,7 +56,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultOnlineBankingDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val context: Context, @Mock private val pdfOpener: PdfOpener, @Mock private val submitHandler: SubmitHandler, @@ -177,12 +179,6 @@ internal class DefaultOnlineBankingDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -239,9 +235,15 @@ internal class DefaultOnlineBankingDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -253,6 +255,12 @@ internal class DefaultOnlineBankingDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createOnlineBankingDelegate( @@ -263,7 +271,7 @@ internal class DefaultOnlineBankingDelegateTest( pdfOpener = pdfOpener, paymentMethod = PaymentMethod(), order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams( checkoutConfiguration = configuration, diff --git a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt index 467851265b..2f971e0eac 100644 --- a/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt +++ b/online-banking-cz/src/main/java/com/adyen/checkout/onlinebankingcz/internal/provider/OnlineBankingCZComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingCZPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingCZComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : OnlineBankingComponentProvider< OnlineBankingCZComponent, OnlineBankingCZConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = OnlineBankingCZComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createPaymentMethod(): OnlineBankingCZPaymentMethod { diff --git a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt index 69dc9425aa..63e2ca76fd 100644 --- a/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt +++ b/online-banking-sk/src/main/java/com/adyen/checkout/onlinebankingsk/internal/provider/OnlineBankingSKComponentProvider.kt @@ -14,7 +14,7 @@ import com.adyen.checkout.action.core.internal.ui.GenericActionDelegate import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.internal.ComponentEventHandler -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.paymentmethod.OnlineBankingSKPaymentMethod import com.adyen.checkout.onlinebankingcore.internal.provider.OnlineBankingComponentProvider @@ -29,7 +29,7 @@ class OnlineBankingSKComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( dropInOverrideParams: DropInOverrideParams? = null, - analyticsRepository: AnalyticsRepository? = null, + analyticsManager: AnalyticsManager? = null, ) : OnlineBankingComponentProvider< OnlineBankingSKComponent, OnlineBankingSKConfiguration, @@ -38,7 +38,7 @@ constructor( >( componentClass = OnlineBankingSKComponent::class.java, dropInOverrideParams = dropInOverrideParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, ) { override fun createPaymentMethod(): OnlineBankingSKPaymentMethod { From 542088c5c70611499d78247324f6c9ffbf299a77 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:19:59 +0100 Subject: [PATCH 202/272] Change analytics implementation for DefaultPayByBankDelegate COAND-844 --- .../provider/PayByBankComponentProvider.kt | 45 +++++++------------ .../internal/ui/DefaultPayByBankDelegate.kt | 18 ++++---- .../ui/DefaultPayByBankDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt index ab01e7ffa8..f4cc1d56a7 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt @@ -22,11 +22,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -56,7 +54,7 @@ class PayByBankComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -93,16 +91,11 @@ constructor( componentSessionParams = null, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val payByBankDelegate = DefaultPayByBankDelegate( @@ -110,7 +103,7 @@ constructor( paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -184,17 +177,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val payByBankDelegate = DefaultPayByBankDelegate( @@ -202,7 +189,7 @@ constructor( paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt index f199dc34c8..563bc61ba5 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.paymentmethod.PayByBankPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -34,7 +34,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultPayByBankDelegate( @@ -42,7 +41,7 @@ internal class DefaultPayByBankDelegate( private val paymentMethod: PaymentMethod, private val order: Order?, override val componentParams: GenericComponentParams, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : PayByBankDelegate { @@ -76,14 +75,12 @@ internal class DefaultPayByBankDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -144,7 +141,7 @@ internal class DefaultPayByBankDelegate( ): PayByBankComponentState { val payByBankPaymentMethod = PayByBankPaymentMethod( type = getPaymentMethodType(), - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), issuer = outputData?.selectedIssuer?.id, ) @@ -196,5 +193,6 @@ internal class DefaultPayByBankDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt index 7a3d149cba..20a9dc37a3 100644 --- a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt +++ b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -44,7 +44,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -52,7 +54,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultPayByBankDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -206,12 +208,6 @@ internal class DefaultPayByBankDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitHandlerTest { @@ -252,9 +248,15 @@ internal class DefaultPayByBankDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -266,6 +268,12 @@ internal class DefaultPayByBankDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createCheckoutConfiguration( @@ -292,7 +300,7 @@ internal class DefaultPayByBankDelegateTest( issuers = issuers, ), order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) } From 74487903d7ea9535b3179b5a821add1e0d051247 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:22:38 +0100 Subject: [PATCH 203/272] Change analytics implementation for DefaultSepaDelegate COAND-844 --- .../provider/SepaComponentProvider.kt | 45 +++++++------------ .../sepa/internal/ui/DefaultSepaDelegate.kt | 18 ++++---- .../internal/ui/DefaultSepaDelegateTest.kt | 28 +++++++----- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt index e2a4eba75f..6264a60df6 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt @@ -22,11 +22,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +55,7 @@ class SepaComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -96,16 +94,11 @@ constructor( componentConfiguration = checkoutConfiguration.getSepaConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val sepaDelegate = DefaultSepaDelegate( @@ -113,7 +106,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -188,17 +181,11 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val sepaDelegate = DefaultSepaDelegate( @@ -206,7 +193,7 @@ constructor( componentParams = componentParams, paymentMethod = paymentMethod, order = checkoutSession.order, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt index ad521141d1..402a21bfc9 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -32,7 +32,6 @@ import com.adyen.checkout.ui.core.internal.ui.SubmitHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultSepaDelegate( @@ -40,7 +39,7 @@ internal class DefaultSepaDelegate( override val componentParams: ButtonComponentParams, private val paymentMethod: PaymentMethod, private val order: Order?, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val submitHandler: SubmitHandler, ) : SepaDelegate { @@ -63,14 +62,12 @@ internal class DefaultSepaDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -123,7 +120,7 @@ internal class DefaultSepaDelegate( ): SepaComponentState { val paymentMethod = SepaPaymentMethod( type = SepaPaymentMethod.PAYMENT_METHOD_TYPE, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), ownerName = outputData.ownerNameField.value, iban = outputData.ibanNumberField.value, ) @@ -154,5 +151,6 @@ internal class DefaultSepaDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt index 2b7849ec65..8db292c6d2 100644 --- a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt +++ b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt @@ -15,7 +15,7 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod @@ -43,7 +43,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -51,7 +53,7 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultSepaDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { @@ -124,12 +126,6 @@ internal class DefaultSepaDelegateTest( } } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Nested inner class SubmitButtonVisibilityTest { @@ -186,9 +182,15 @@ internal class DefaultSepaDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -201,6 +203,12 @@ internal class DefaultSepaDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createSepaDelegate( @@ -217,7 +225,7 @@ internal class DefaultSepaDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getSepaConfiguration(), ), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, submitHandler = submitHandler, ) From a4c1030798e47fb93063eb71a4006cfc2f39c3cd Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:25:11 +0100 Subject: [PATCH 204/272] Change analytics implementation for DefaultUPIDelegate COAND-844 --- .../internal/provider/UPIComponentProvider.kt | 45 +++++++------------ .../upi/internal/ui/DefaultUPIDelegate.kt | 18 ++++---- .../upi/internal/ui/DefaultUPIDelegateTest.kt | 29 +++++++----- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt index 5dba69e692..477e593bad 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt @@ -22,11 +22,9 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -57,7 +55,7 @@ class UPIComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider>, @@ -90,21 +88,16 @@ constructor( componentConfiguration = checkoutConfiguration.getUPIConfiguration(), ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val upiDelegate = DefaultUPIDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, @@ -182,22 +175,16 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val upiDelegate = DefaultUPIDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt index 5aa87e6805..c3c2d674e5 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.UPIPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -33,12 +33,11 @@ import com.adyen.checkout.upi.internal.ui.model.UPIOutputData import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class DefaultUPIDelegate( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, @@ -66,14 +65,12 @@ internal class DefaultUPIDelegate( override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -129,7 +126,7 @@ internal class DefaultUPIDelegate( ): UPIComponentState { val paymentMethod = UPIPaymentMethod( type = if (outputData.mode == UPIMode.VPA) PaymentMethodTypes.UPI_COLLECT else PaymentMethodTypes.UPI_QR, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), virtualPaymentAddress = if (outputData.mode == UPIMode.VPA) { outputData.virtualPaymentAddressFieldState.value } else { @@ -172,5 +169,6 @@ internal class DefaultUPIDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index 7a3d27144b..a265c12502 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -16,7 +16,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -47,7 +47,9 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -56,7 +58,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultUPIDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: DefaultUPIDelegate @@ -66,13 +68,6 @@ internal class DefaultUPIDelegateTest( delegate = createUPIDelegate() } - @Test - fun `when delegate is initialized then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - - verify(analyticsRepository).setupAnalytics() - } - @Nested @DisplayName("when input data changes and") inner class InputDataChangedTest { @@ -217,9 +212,15 @@ internal class DefaultUPIDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -231,6 +232,12 @@ internal class DefaultUPIDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createCheckoutConfiguration( @@ -249,7 +256,7 @@ internal class DefaultUPIDelegateTest( configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultUPIDelegate( submitHandler = submitHandler, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = PaymentMethod(), order = order, From 854d9153423b8484c768eb1033db6dd1c9a89eb6 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:27:36 +0100 Subject: [PATCH 205/272] Change analytics implementation for DefaultCashAppPayDelegate and StoredCashAppPayDelegate COAND-844 --- .../provider/CashAppPayComponentProvider.kt | 80 +++++++------------ .../internal/ui/DefaultCashAppPayDelegate.kt | 17 ++-- .../internal/ui/StoredCashAppPayDelegate.kt | 21 ++--- .../ui/DefaultCashAppPayDelegateTest.kt | 28 ++++--- .../ui/StoredCashAppPayDelegateTest.kt | 31 ++++--- 5 files changed, 87 insertions(+), 90 deletions(-) diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt index 3979798d38..21805c02fb 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt @@ -35,11 +35,9 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.ComponentEventHandler import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.provider.StoredPaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -67,7 +65,7 @@ class CashAppPayComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( private val dropInOverrideParams: DropInOverrideParams? = null, - private val analyticsRepository: AnalyticsRepository? = null, + private val analyticsManager: AnalyticsManager? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : PaymentComponentProvider< @@ -118,21 +116,16 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, ) val cashAppPayDelegate = DefaultCashAppPayDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, @@ -204,20 +197,15 @@ constructor( context = application, ) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = null, ) val cashAppPayDelegate = StoredCashAppPayDelegate( - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = storedPaymentMethod, order = order, @@ -291,22 +279,16 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - paymentMethod = paymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val cashAppPayDelegate = DefaultCashAppPayDelegate( submitHandler = SubmitHandler(savedStateHandle), - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, @@ -388,21 +370,15 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) - val analyticsRepository = analyticsRepository ?: DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - application = application, - componentParams = componentParams, - storedPaymentMethod = storedPaymentMethod, - sessionId = checkoutSession.sessionSetupResponse.id, - ), - analyticsService = AnalyticsService( - HttpClientFactory.getAnalyticsHttpClient(componentParams.environment), - ), - analyticsMapper = AnalyticsMapper(), + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, ) val cashAppPayDelegate = StoredCashAppPayDelegate( - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = storedPaymentMethod, order = checkoutSession.order, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt index 6979720e40..5dbc29150c 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt @@ -33,7 +33,7 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -58,7 +58,7 @@ internal class DefaultCashAppPayDelegate @Suppress("LongParameterList") constructor( private val submitHandler: SubmitHandler, - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val observerRepository: PaymentObserverRepository, private val paymentMethod: PaymentMethod, private val order: OrderRequest?, @@ -93,7 +93,7 @@ constructor( cashAppPay = initCashAppPay() - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) if (!isConfirmationRequired()) { initiatePayment() @@ -110,11 +110,9 @@ constructor( } } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } override fun observe( @@ -168,7 +166,7 @@ constructor( val cashAppPayPaymentMethod = CashAppPayPaymentMethod( type = paymentMethod.type, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), grantId = oneTimeData?.grantId, customerId = onFileData?.customerId ?: oneTimeData?.customerId, onFileGrantId = onFileData?.grantId, @@ -324,5 +322,6 @@ constructor( _coroutineScope = null removeObserver() cashAppPay.unregisterFromStateUpdates() + analyticsManager.clear(this) } } diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt index ccf9379d2d..07a39c5715 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -30,11 +30,10 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch @Suppress("TooManyFunctions") internal class StoredCashAppPayDelegate( - private val analyticsRepository: AnalyticsRepository, + private val analyticsManager: AnalyticsManager, private val observerRepository: PaymentObserverRepository, private val paymentMethod: StoredPaymentMethod, private val order: OrderRequest?, @@ -51,18 +50,16 @@ internal class StoredCashAppPayDelegate( override val submitFlow: Flow = submitChannel.receiveAsFlow() override fun initialize(coroutineScope: CoroutineScope) { - setupAnalytics(coroutineScope) + initializeAnalytics(coroutineScope) componentStateFlow.onEach { onState(it) }.launchIn(coroutineScope) } - private fun setupAnalytics(coroutineScope: CoroutineScope) { - adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } - coroutineScope.launch { - analyticsRepository.setupAnalytics() - } + private fun initializeAnalytics(coroutineScope: CoroutineScope) { + adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } + analyticsManager.initialize(this, coroutineScope) } private fun onState(componentState: CashAppPayComponentState) { @@ -94,10 +91,13 @@ internal class StoredCashAppPayDelegate( adyenLog(AdyenLogLevel.WARN) { "updateInputData should not be called for stored Cash App Pay" } } + @Suppress("ForbiddenComment") + // TODO: Here we only call this method on initialization. The checkoutAttemptId will only be available if it is + // passed by drop-in. This should be fixed as part of state refactoring. private fun createComponentState(): CashAppPayComponentState { val cashAppPayPaymentMethod = CashAppPayPaymentMethod( type = paymentMethod.type, - checkoutAttemptId = analyticsRepository.getCheckoutAttemptId(), + checkoutAttemptId = analyticsManager.getCheckoutAttemptId(), storedPaymentMethodId = paymentMethod.id, ) @@ -120,5 +120,6 @@ internal class StoredCashAppPayDelegate( override fun onCleared() { removeObserver() + analyticsManager.clear(this) } } diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt index 36743232a4..0ba5870dd0 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt @@ -35,7 +35,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -63,6 +63,7 @@ import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -73,7 +74,7 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCashAppPayDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, @Mock private val cashAppPayFactory: CashAppPayFactory, @Mock private val cashAppPay: CashAppPay, ) { @@ -91,12 +92,6 @@ internal class DefaultCashAppPayDelegateTest( @DisplayName("when delegate is initialized") inner class InitializeTest { - @Test - fun `then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @Test fun `no confirmation is required, then payment should be initiated`() = runTest { delegate = createDefaultCashAppPayDelegate( @@ -452,9 +447,15 @@ internal class DefaultCashAppPayDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsRepository.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID val testFlow = delegate.componentStateFlow.test(testScheduler) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -468,13 +469,20 @@ internal class DefaultCashAppPayDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, testFlow.latestValue.data.paymentMethod?.checkoutAttemptId) } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } private fun createDefaultCashAppPayDelegate( configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultCashAppPayDelegate( submitHandler = submitHandler, - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = getPaymentMethod(), order = TEST_ORDER, diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt index 370cbd1328..3efdb4d64f 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -31,6 +31,7 @@ import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest @@ -38,13 +39,15 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class StoredCashAppPayDelegateTest( - @Mock private val analyticsRepository: AnalyticsRepository, + @Mock private val analyticsManager: AnalyticsManager, ) { private lateinit var delegate: StoredCashAppPayDelegate @@ -64,12 +67,6 @@ internal class StoredCashAppPayDelegateTest( } } - @Test - fun `when delegate is initialized, then analytics event is sent`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsRepository).setupAnalytics() - } - @ParameterizedTest @MethodSource("amountSource") fun `when input data is valid, then amount is propagated in component state if set`( @@ -118,10 +115,26 @@ internal class StoredCashAppPayDelegateTest( assertEquals(expected, testFlow.latestValue) } + @Nested + inner class AnalyticsTest { + + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } + } + private fun createStoredCashAppPayDelegate( configuration: CheckoutConfiguration = createCheckoutConfiguration() ) = StoredCashAppPayDelegate( - analyticsRepository = analyticsRepository, + analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), paymentMethod = StoredPaymentMethod( id = TEST_PAYMENT_METHOD_ID, From fc3cb0740a37711c2d65e4e9cbdc0176ca2a2ea2 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:36:49 +0100 Subject: [PATCH 206/272] Change analytics implementation for ComponentProvider COAND-844 --- .../internal/provider/ComponentProvider.kt | 60 +++++++++---------- .../ui/BaseComponentDialogFragment.kt | 3 +- .../ui/GiftCardComponentDialogFragment.kt | 1 - .../ui/GooglePayComponentDialogFragment.kt | 1 - .../ui/PaymentMethodListDialogFragment.kt | 2 +- .../PreselectedStoredPaymentMethodFragment.kt | 2 +- 6 files changed, 32 insertions(+), 37 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt index 1f94f6e76f..9a3e2aec13 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt @@ -36,7 +36,6 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponent import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.conveniencestoresjp.ConvenienceStoresJPComponent @@ -116,12 +115,12 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: AnalyticsRepository, + analyticsManager: AnalyticsManager, onRedirect: () -> Unit, ): PaymentComponent { return when { checkCompileOnly { ACHDirectDebitComponent.PROVIDER.isPaymentMethodSupported(storedPaymentMethod) } -> { - ACHDirectDebitComponentProvider(dropInOverrideParams, analyticsRepository).get( + ACHDirectDebitComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, storedPaymentMethod = storedPaymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -131,7 +130,7 @@ internal fun getComponentFor( } checkCompileOnly { BlikComponent.PROVIDER.isPaymentMethodSupported(storedPaymentMethod) } -> { - BlikComponentProvider(dropInOverrideParams, analyticsRepository).get( + BlikComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, storedPaymentMethod = storedPaymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -151,7 +150,7 @@ internal fun getComponentFor( } checkCompileOnly { CardComponent.PROVIDER.isPaymentMethodSupported(storedPaymentMethod) } -> { - CardComponentProvider(dropInOverrideParams, analyticsRepository).get( + CardComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, storedPaymentMethod = storedPaymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -182,13 +181,12 @@ internal fun getComponentFor( checkoutConfiguration: CheckoutConfiguration, dropInOverrideParams: DropInOverrideParams, componentCallback: ComponentCallback<*>, - analyticsRepository: AnalyticsRepository, analyticsManager: AnalyticsManager, onRedirect: () -> Unit, ): PaymentComponent { return when { checkCompileOnly { ACHDirectDebitComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - ACHDirectDebitComponentProvider(dropInOverrideParams, analyticsRepository).get( + ACHDirectDebitComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -197,7 +195,7 @@ internal fun getComponentFor( } checkCompileOnly { BacsDirectDebitComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - BacsDirectDebitComponentProvider(dropInOverrideParams, analyticsRepository).get( + BacsDirectDebitComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -206,7 +204,7 @@ internal fun getComponentFor( } checkCompileOnly { BcmcComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - BcmcComponentProvider(dropInOverrideParams, analyticsRepository).get( + BcmcComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -215,7 +213,7 @@ internal fun getComponentFor( } checkCompileOnly { BlikComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - BlikComponentProvider(dropInOverrideParams, analyticsRepository).get( + BlikComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -224,7 +222,7 @@ internal fun getComponentFor( } checkCompileOnly { BoletoComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - BoletoComponentProvider(dropInOverrideParams, analyticsRepository).get( + BoletoComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -233,7 +231,7 @@ internal fun getComponentFor( } checkCompileOnly { CardComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - CardComponentProvider(dropInOverrideParams, analyticsRepository).get( + CardComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -251,7 +249,7 @@ internal fun getComponentFor( } checkCompileOnly { ConvenienceStoresJPComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - ConvenienceStoresJPComponentProvider(dropInOverrideParams, analyticsRepository).get( + ConvenienceStoresJPComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -260,7 +258,7 @@ internal fun getComponentFor( } checkCompileOnly { DotpayComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - DotpayComponentProvider(dropInOverrideParams, analyticsRepository).get( + DotpayComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -269,7 +267,7 @@ internal fun getComponentFor( } checkCompileOnly { EntercashComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - EntercashComponentProvider(dropInOverrideParams, analyticsRepository).get( + EntercashComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -278,7 +276,7 @@ internal fun getComponentFor( } checkCompileOnly { EPSComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - EPSComponentProvider(dropInOverrideParams, analyticsRepository).get( + EPSComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -287,7 +285,7 @@ internal fun getComponentFor( } checkCompileOnly { GiftCardComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - GiftCardComponentProvider(dropInOverrideParams, analyticsRepository).get( + GiftCardComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -296,7 +294,7 @@ internal fun getComponentFor( } checkCompileOnly { GooglePayComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - GooglePayComponentProvider(dropInOverrideParams, analyticsRepository).get( + GooglePayComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -305,7 +303,7 @@ internal fun getComponentFor( } checkCompileOnly { IdealComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - IdealComponentProvider(dropInOverrideParams, analyticsRepository).get( + IdealComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -323,7 +321,7 @@ internal fun getComponentFor( } checkCompileOnly { MolpayComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - MolpayComponentProvider(dropInOverrideParams, analyticsRepository).get( + MolpayComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -332,7 +330,7 @@ internal fun getComponentFor( } checkCompileOnly { OnlineBankingCZComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - OnlineBankingCZComponentProvider(dropInOverrideParams, analyticsRepository).get( + OnlineBankingCZComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -341,7 +339,7 @@ internal fun getComponentFor( } checkCompileOnly { OnlineBankingJPComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - OnlineBankingJPComponentProvider(dropInOverrideParams, analyticsRepository).get( + OnlineBankingJPComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -350,7 +348,7 @@ internal fun getComponentFor( } checkCompileOnly { OnlineBankingPLComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - OnlineBankingPLComponentProvider(dropInOverrideParams, analyticsRepository).get( + OnlineBankingPLComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -359,7 +357,7 @@ internal fun getComponentFor( } checkCompileOnly { OnlineBankingSKComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - OnlineBankingSKComponentProvider(dropInOverrideParams, analyticsRepository).get( + OnlineBankingSKComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -368,7 +366,7 @@ internal fun getComponentFor( } checkCompileOnly { OpenBankingComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - OpenBankingComponentProvider(dropInOverrideParams, analyticsRepository).get( + OpenBankingComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -377,7 +375,7 @@ internal fun getComponentFor( } checkCompileOnly { PayByBankComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - PayByBankComponentProvider(dropInOverrideParams, analyticsRepository).get( + PayByBankComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -386,7 +384,7 @@ internal fun getComponentFor( } checkCompileOnly { PayEasyComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - PayEasyComponentProvider(dropInOverrideParams, analyticsRepository).get( + PayEasyComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -395,7 +393,7 @@ internal fun getComponentFor( } checkCompileOnly { SepaComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - SepaComponentProvider(dropInOverrideParams, analyticsRepository).get( + SepaComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -404,7 +402,7 @@ internal fun getComponentFor( } checkCompileOnly { SevenElevenComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - SevenElevenComponentProvider(dropInOverrideParams, analyticsRepository).get( + SevenElevenComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -413,7 +411,7 @@ internal fun getComponentFor( } checkCompileOnly { UPIComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - UPIComponentProvider(dropInOverrideParams, analyticsRepository).get( + UPIComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, @@ -425,7 +423,7 @@ internal fun getComponentFor( // which payment methods it supports. Meaning it could take over a payment method that should be handled by // it's dedicated component. checkCompileOnly { InstantPaymentComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { - InstantPaymentComponentProvider(dropInOverrideParams, analyticsRepository).get( + InstantPaymentComponentProvider(dropInOverrideParams, analyticsManager).get( fragment = fragment, paymentMethod = paymentMethod, checkoutConfiguration = checkoutConfiguration, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt index ea52177cec..8f4da7b370 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/BaseComponentDialogFragment.kt @@ -109,7 +109,7 @@ internal abstract class BaseComponentDialogFragment : checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, - analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) } else { @@ -119,7 +119,6 @@ internal abstract class BaseComponentDialogFragment : checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, - analyticsRepository = dropInViewModel.analyticsRepository, analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt index bf1d9346c9..8eda0c50b3 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GiftCardComponentDialogFragment.kt @@ -73,7 +73,6 @@ internal class GiftCardComponentDialogFragment : DropInBottomSheetDialogFragment checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, - analyticsRepository = dropInViewModel.analyticsRepository, analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) as GiftCardComponent diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt index 17fb46d466..e3f2365467 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/GooglePayComponentDialogFragment.kt @@ -76,7 +76,6 @@ internal class GooglePayComponentDialogFragment : checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = this, - analyticsRepository = dropInViewModel.analyticsRepository, analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) as GooglePayComponent diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt index 658375218f..681f967e9c 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PaymentMethodListDialogFragment.kt @@ -162,7 +162,7 @@ internal class PaymentMethodListDialogFragment : checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = paymentMethodsListViewModel, - analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) paymentMethodsListViewModel.onClickStoredItem(storedPaymentMethod, storedPaymentMethodModel) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt index 2d38ba7023..e352d594e5 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/PreselectedStoredPaymentMethodFragment.kt @@ -81,7 +81,7 @@ internal class PreselectedStoredPaymentMethodFragment : DropInBottomSheetDialogF checkoutConfiguration = dropInViewModel.checkoutConfiguration, dropInOverrideParams = dropInViewModel.getDropInOverrideParams(), componentCallback = storedPaymentViewModel, - analyticsRepository = dropInViewModel.analyticsRepository, + analyticsManager = dropInViewModel.analyticsManager, onRedirect = protocol::onRedirect, ) } catch (e: CheckoutException) { From 069b828e8158a0c9d043646f806af50dd4f88c42 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:38:22 +0100 Subject: [PATCH 207/272] Change analytics implementation for DropInViewModel COAND-844 --- .../dropin/internal/ui/DropInViewModel.kt | 5 ---- .../internal/ui/DropInViewModelFactory.kt | 25 ------------------- 2 files changed, 30 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index c7fd1f2670..f393096ac5 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -25,7 +25,6 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.PaymentMethodsApiResponse import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -59,7 +58,6 @@ import kotlinx.coroutines.launch internal class DropInViewModel( private val bundleHandler: DropInSavedStateHandleContainer, private val orderStatusRepository: OrderStatusRepository, - internal val analyticsRepository: AnalyticsRepository, internal val analyticsManager: AnalyticsManager, private val initialDropInParams: DropInParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, @@ -234,9 +232,6 @@ internal class DropInViewModel( private fun initializeAnalytics() { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } - viewModelScope.launch { - analyticsRepository.setupAnalytics() - } analyticsManager.initialize(this, viewModelScope) } diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt index 5cd92220a5..752f575ac2 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModelFactory.kt @@ -16,13 +16,8 @@ import androidx.lifecycle.ViewModel import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.internal.analytics.AnalyticsManagerFactory import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.api.AnalyticsMapper -import com.adyen.checkout.components.core.internal.data.api.AnalyticsRepositoryData -import com.adyen.checkout.components.core.internal.data.api.AnalyticsService -import com.adyen.checkout.components.core.internal.data.api.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusRepository import com.adyen.checkout.components.core.internal.data.api.OrderStatusService -import com.adyen.checkout.components.core.internal.util.screenWidthPixels import com.adyen.checkout.core.internal.data.api.HttpClientFactory import com.adyen.checkout.core.internal.util.LocaleProvider import com.adyen.checkout.dropin.internal.ui.model.DropInParams @@ -38,8 +33,6 @@ internal class DropInViewModelFactory( localeProvider: LocaleProvider = LocaleProvider(), ) : AbstractSavedStateViewModelFactory(activity, activity.intent.extras) { - private val packageName: String = activity.packageName - private val screenWidth: Int = activity.screenWidthPixels private val deviceLocale: Locale = localeProvider.getLocale(activity) private val application: Application = activity.application @@ -57,23 +50,6 @@ internal class DropInViewModelFactory( val httpClient = HttpClientFactory.getHttpClient(dropInParams.environment) val orderStatusRepository = OrderStatusRepository(OrderStatusService(httpClient)) - val analyticsRepository = DefaultAnalyticsRepository( - analyticsRepositoryData = AnalyticsRepositoryData( - level = dropInParams.analyticsParams.level, - packageName = packageName, - locale = dropInParams.shopperLocale, - source = AnalyticsSource.DropIn(paymentMethods), - clientKey = dropInParams.clientKey, - amount = dropInParams.amount, - screenWidth = screenWidth, - paymentMethods = paymentMethods, - sessionId = bundleHandler.sessionDetails?.id, - ), - analyticsService = AnalyticsService( - httpClient = HttpClientFactory.getAnalyticsHttpClient(dropInParams.environment), - ), - analyticsMapper = AnalyticsMapper(), - ) val analyticsManager = AnalyticsManagerFactory().provide( shopperLocale = dropInParams.shopperLocale, environment = dropInParams.environment, @@ -90,7 +66,6 @@ internal class DropInViewModelFactory( return DropInViewModel( bundleHandler, orderStatusRepository, - analyticsRepository, analyticsManager, dropInParams, ) as T From ecb34c013d65786c62090ab33da47880d2fa9692 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:39:23 +0100 Subject: [PATCH 208/272] Make improvements for MBWay analytics implementation COAND-844 --- .../provider/MBWayComponentProvider.kt | 28 +++++++++++-------- .../internal/ui/DefaultMBWayDelegateTest.kt | 19 +++++++++---- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index 74688703c4..8b4d812c87 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -93,17 +93,19 @@ constructor( componentConfiguration = checkoutConfiguration.getMBWayConfiguration(), ) + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, + ) + val mbWayDelegate = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = order, componentParams = componentParams, - analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( - componentParams = componentParams, - application = application, - source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - sessionId = null, - ), + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) @@ -178,17 +180,19 @@ constructor( val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, + ) + val mbWayDelegate = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), paymentMethod = paymentMethod, order = checkoutSession.order, componentParams = componentParams, - analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( - componentParams = componentParams, - application = application, - source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - sessionId = checkoutSession.sessionSetupResponse.id, - ), + analyticsManager = analyticsManager, submitHandler = SubmitHandler(savedStateHandle), ) diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index b3f50899f9..9dc49aa7ca 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -43,6 +43,7 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -198,12 +199,6 @@ internal class DefaultMBWayDelegateTest( } } - @Test - fun `when delegate is initialized then analytics manager is initialized`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(any(), any()) - } - @Nested inner class SubmitButtonVisibilityTest { @@ -260,6 +255,12 @@ internal class DefaultMBWayDelegateTest( @Nested inner class AnalyticsTest { + @Test + fun `when delegate is initialized then analytics manager is initialized`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + verify(analyticsManager).initialize(eq(delegate), any()) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID @@ -275,6 +276,12 @@ internal class DefaultMBWayDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) } } + + @Test + fun `when delegate is cleared then analytics manager is cleared`() { + delegate.onCleared() + verify(analyticsManager).clear(eq(delegate)) + } } @Test From 451533013b95689475454c8dd69d61f6e945c326 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:45:46 +0100 Subject: [PATCH 209/272] Remove old analytics files COAND-844 --- .../core/internal/data/api/AnalyticsMapper.kt | 99 ------------------- .../internal/data/api/AnalyticsRepository.kt | 20 ---- .../data/api/AnalyticsRepositoryData.kt | 80 --------------- .../data/api/DefaultAnalyticsRepository.kt | 86 ---------------- 4 files changed, 285 deletions(-) delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt delete mode 100644 components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt deleted file mode 100644 index f91d02a42a..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsMapper.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 6/3/2024. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import android.os.Build -import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.BuildConfig -import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatform -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest -import java.util.Locale - -@Suppress("ForbiddenComment") -// TODO: Remove this file -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class AnalyticsMapper { - @Suppress("LongParameterList") - internal fun getAnalyticsSetupRequest( - packageName: String, - locale: Locale, - source: AnalyticsSource, - amount: Amount?, - screenWidth: Long, - paymentMethods: List, - sessionId: String?, - ): AnalyticsSetupRequest { - return AnalyticsSetupRequest( - version = actualVersion, - channel = ANDROID_CHANNEL, - platform = actualPlatform, - locale = locale.toString(), - component = getComponentQueryParameter(source), - flavor = getFlavorQueryParameter(source), - deviceBrand = Build.BRAND, - deviceModel = Build.MODEL, - referrer = packageName, - systemVersion = Build.VERSION.SDK_INT.toString(), - screenWidth = screenWidth.toInt(), - paymentMethods = paymentMethods, - amount = amount, - // unused for Android - containerWidth = null, - sessionId = sessionId, - ) - } - - @Suppress("FunctionOnlyReturningConstant", "UnusedParameter") - @VisibleForTesting - internal fun getFlavorQueryParameter(source: AnalyticsSource): String { - return "stub - this class will be deleted" - } - - @VisibleForTesting - internal fun getComponentQueryParameter(source: AnalyticsSource): String { - return when (source) { - is AnalyticsSource.DropIn -> DROP_IN_COMPONENT - is AnalyticsSource.PaymentComponent -> source.paymentMethodType - } - } - - @Suppress("unused") - private enum class Flavor(val value: String) { - DROP_IN("dropin"), - COMPONENTS("components") - } - - companion object { - private const val DROP_IN_COMPONENT = "dropin" - private const val ANDROID_CHANNEL = "android" - - // these params are prefixed with actual because cross platform SDKs will override them so they are not - // technically constants - private var actualPlatform = AnalyticsPlatform.ANDROID.value - private var actualVersion = BuildConfig.CHECKOUT_VERSION - - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - fun overrideForCrossPlatform( - platform: AnalyticsPlatform, - version: String, - ) { - this.actualPlatform = platform.value - this.actualVersion = version - } - - @VisibleForTesting - internal fun resetToDefaults() { - actualPlatform = AnalyticsPlatform.ANDROID.value - actualVersion = BuildConfig.CHECKOUT_VERSION - } - } -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt deleted file mode 100644 index 0c2ff7a090..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepository.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2022 Adyen N.V. - * - * This file is open source and available under the MIT license. See the LICENSE file for more info. - * - * Created by josephj on 24/11/2022. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import androidx.annotation.RestrictTo - -@Suppress("ForbiddenComment") -// TODO: Remove this file -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -interface AnalyticsRepository { - suspend fun setupAnalytics() - - fun getCheckoutAttemptId(): String? -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt deleted file mode 100644 index 8b47022e33..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsRepositoryData.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 ararat on 4/3/2024. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import android.app.Application -import androidx.annotation.RestrictTo -import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.StoredPaymentMethod -import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel -import com.adyen.checkout.components.core.internal.ui.model.ComponentParams -import com.adyen.checkout.components.core.internal.util.screenWidthPixels -import java.util.Locale - -@Suppress("ForbiddenComment") -// TODO: Remove this file -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -data class AnalyticsRepositoryData( - val level: AnalyticsParamsLevel, - val packageName: String, - val locale: Locale, - val source: AnalyticsSource, - val clientKey: String, - val amount: Amount?, - val screenWidth: Int, - val paymentMethods: List, - val sessionId: String?, -) { - - constructor( - application: Application, - componentParams: ComponentParams, - paymentMethod: PaymentMethod, - sessionId: String? = null, - ) : this( - application = application, - componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), - paymentMethodType = paymentMethod.type, - sessionId = sessionId, - ) - - constructor( - application: Application, - componentParams: ComponentParams, - storedPaymentMethod: StoredPaymentMethod, - sessionId: String? = null, - ) : this( - application = application, - componentParams = componentParams, - source = AnalyticsSource.PaymentComponent(storedPaymentMethod.type.orEmpty()), - paymentMethodType = storedPaymentMethod.type, - sessionId = sessionId, - ) - - private constructor( - application: Application, - componentParams: ComponentParams, - source: AnalyticsSource, - paymentMethodType: String?, - sessionId: String?, - ) : this( - level = componentParams.analyticsParams.level, - packageName = application.packageName, - locale = componentParams.shopperLocale, - source = source, - clientKey = componentParams.clientKey, - amount = componentParams.amount, - screenWidth = application.screenWidthPixels, - paymentMethods = listOfNotNull(paymentMethodType), - sessionId = sessionId, - ) -} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt deleted file mode 100644 index e55a2e039c..0000000000 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/DefaultAnalyticsRepository.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 6/3/2024. - */ - -package com.adyen.checkout.components.core.internal.data.api - -import androidx.annotation.RestrictTo -import androidx.annotation.VisibleForTesting -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.ALL -import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel.NONE -import com.adyen.checkout.core.AdyenLogLevel -import com.adyen.checkout.core.internal.util.adyenLog - -// TODO: Remove this file -@Suppress("unused", "ForbiddenComment") -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -class DefaultAnalyticsRepository( - private val analyticsRepositoryData: AnalyticsRepositoryData, - private val analyticsService: AnalyticsService, - private val analyticsMapper: AnalyticsMapper, -) : AnalyticsRepository { - - @VisibleForTesting - internal var state: State = State.Uninitialized - private set - - private var checkoutAttemptId: String? = null - override fun getCheckoutAttemptId(): String? = checkoutAttemptId - - override suspend fun setupAnalytics() { - if (!canSendAnalytics(requiredLevel = ALL)) { - checkoutAttemptId = CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS - return - } - if (state != State.Uninitialized) return - state = State.InProgress - adyenLog(AdyenLogLevel.VERBOSE) { "Setting up analytics" } - -// runSuspendCatching { -// val analyticsSetupRequest = with(analyticsRepositoryData) { -// analyticsMapper.getAnalyticsSetupRequest( -// packageName = packageName, -// locale = locale, -// source = source, -// amount = amount, -// screenWidth = screenWidth.toLong(), -// paymentMethods = paymentMethods, -// sessionId = sessionId, -// ) -// } -// val response = analyticsService.setupAnalytics(analyticsSetupRequest, analyticsRepositoryData.clientKey) -// checkoutAttemptId = response.checkoutAttemptId -// state = State.Ready -// adyenLog(AdyenLogLevel.VERBOSE) { "Analytics setup call successful" } -// }.onFailure { e -> -// state = State.Failed -// adyenLog(AdyenLogLevel.ERROR) { -// "Failed to send analytics setup call - ${e::class.simpleName}: ${e.message}" -// } -// } - } - - private fun canSendAnalytics(requiredLevel: AnalyticsParamsLevel): Boolean { - require(requiredLevel != NONE) { "Analytics are not allowed with level NONE" } - return true - } - - companion object { - - @VisibleForTesting - internal const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" - } - - @VisibleForTesting - internal sealed class State { - object Uninitialized : State() - object InProgress : State() - object Ready : State() - object Failed : State() - } -} From 824f7e53fb5d89b7a933a6c86bf11c246f53fead Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 18 Mar 2024 17:46:24 +0100 Subject: [PATCH 210/272] Change NewAnalyticsRepository to AnalyticsRepository COAND-844 --- .../core/internal/analytics/AnalyticsManagerFactory.kt | 4 ++-- .../core/internal/analytics/DefaultAnalyticsManager.kt | 4 ++-- .../{NewAnalyticsRepository.kt => AnalyticsRepository.kt} | 2 +- ...ewAnalyticsRepository.kt => DefaultAnalyticsRepository.kt} | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{NewAnalyticsRepository.kt => AnalyticsRepository.kt} (91%) rename components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/{DefaultNewAnalyticsRepository.kt => DefaultAnalyticsRepository.kt} (96%) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt index cabee5d3bc..4498d7cb8c 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsManagerFactory.kt @@ -11,7 +11,7 @@ package com.adyen.checkout.components.core.internal.analytics import android.app.Application import androidx.annotation.RestrictTo import com.adyen.checkout.components.core.Amount -import com.adyen.checkout.components.core.internal.analytics.data.DefaultNewAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.DefaultAnalyticsRepository import com.adyen.checkout.components.core.internal.analytics.data.local.InfoAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.local.LogAnalyticsLocalDataStore import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider @@ -56,7 +56,7 @@ class AnalyticsManagerFactory { source: AnalyticsSource, sessionId: String? ): AnalyticsManager = DefaultAnalyticsManager( - analyticsRepository = DefaultNewAnalyticsRepository( + analyticsRepository = DefaultAnalyticsRepository( localInfoDataStore = InfoAnalyticsLocalDataStore(), localLogDataStore = LogAnalyticsLocalDataStore(), remoteDataStore = DefaultAnalyticsRemoteDataStore( diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index ea482d98ec..c679dfa43d 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -8,7 +8,7 @@ package com.adyen.checkout.components.core.internal.analytics -import com.adyen.checkout.components.core.internal.analytics.data.NewAnalyticsRepository +import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel import com.adyen.checkout.core.AdyenLogLevel @@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicInteger import kotlin.time.Duration.Companion.seconds internal class DefaultAnalyticsManager internal constructor( - private val analyticsRepository: NewAnalyticsRepository, + private val analyticsRepository: AnalyticsRepository, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, ) : AnalyticsManager { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt similarity index 91% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt index 6ffd10f182..e4fd2e09e2 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/NewAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/AnalyticsRepository.kt @@ -10,7 +10,7 @@ package com.adyen.checkout.components.core.internal.analytics.data import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent -internal interface NewAnalyticsRepository { +internal interface AnalyticsRepository { suspend fun fetchCheckoutAttemptId(): String? diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt similarity index 96% rename from components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt rename to components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index 5869fa57e4..6ece9c056e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultNewAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -14,13 +14,13 @@ import com.adyen.checkout.components.core.internal.analytics.data.remote.Analyti import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider -internal class DefaultNewAnalyticsRepository( +internal class DefaultAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, private val localLogDataStore: AnalyticsLocalDataStore, private val remoteDataStore: AnalyticsRemoteDataStore, private val analyticsSetupProvider: AnalyticsSetupProvider, private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, -) : NewAnalyticsRepository { +) : AnalyticsRepository { override suspend fun fetchCheckoutAttemptId(): String? { val request = analyticsSetupProvider.provide() From 877ab1452693861e96780492748bd562a7a10bed Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 14 Mar 2024 11:58:27 +0100 Subject: [PATCH 211/272] Add tests for AnalyticsPlatformParams COAND-844 --- .../analytics/AnalyticsPlatformParamsTest.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt new file mode 100644 index 0000000000..dfe44f1861 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt @@ -0,0 +1,31 @@ +package com.adyen.checkout.components.core.internal.analytics + +import com.adyen.checkout.components.core.BuildConfig +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class AnalyticsPlatformParamsTest { + + @BeforeEach + fun setup() { + AnalyticsPlatformParams.resetToDefaults() + } + + @Test + fun `when no overriding, then default are returned`() { + assertEquals(AnalyticsPlatform.ANDROID.value, AnalyticsPlatformParams.platform) + assertEquals(BuildConfig.CHECKOUT_VERSION, AnalyticsPlatformParams.version) + } + + @Test + fun `when overriding, then set values are returned`() { + AnalyticsPlatformParams.overrideForCrossPlatform( + AnalyticsPlatform.FLUTTER, + "test version", + ) + + assertEquals(AnalyticsPlatform.FLUTTER.value, AnalyticsPlatformParams.platform) + assertEquals("test version", AnalyticsPlatformParams.version) + } +} From 7c8e0403b40561e146b9fa41e07563bc5ccae94e Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 14 Mar 2024 12:14:54 +0100 Subject: [PATCH 212/272] Add test for AnalyticsService COAND-844 --- .../internal/data/api/AnalyticsService.kt | 4 +- .../internal/data/api/AnalyticsServiceTest.kt | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt index 34a569d786..b42d84fa39 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsService.kt @@ -38,13 +38,11 @@ class AnalyticsService( ) } - @Suppress("ForbiddenComment") - // TODO: Add tests internal suspend fun sendEvents( request: AnalyticsTrackRequest, checkoutAttemptId: String, clientKey: String, - ) { + ): EmptyResponse = withContext(coroutineDispatcher) { httpClient.post( path = "v3/analytics/$checkoutAttemptId", queryParameters = mapOf("clientKey" to clientKey), 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 new file mode 100644 index 0000000000..2f322559ca --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/api/AnalyticsServiceTest.kt @@ -0,0 +1,55 @@ +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.HttpClient +import com.adyen.checkout.core.internal.data.model.EmptyResponse +import com.adyen.checkout.test.LoggingExtension +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockitoExtension::class, LoggingExtension::class) +internal class AnalyticsServiceTest( + @Mock private val httpClient: HttpClient +) { + + private lateinit var analyticsService: AnalyticsService + + @BeforeEach + fun setup() { + analyticsService = AnalyticsService( + httpClient = httpClient, + ) + } + + @Test + fun `when sending events, then an empty response is expected`() = runTest { + val request = AnalyticsTrackRequest( + channel = "android", + platform = "android", + info = emptyList(), + logs = emptyList(), + ) + val checkoutAttemptId = "testtest" + whenever(httpClient.post(eq("v3/analytics/$checkoutAttemptId"), any(), any(), any())) + .doReturn(ByteArray(0)) + + val response = analyticsService.sendEvents(request, checkoutAttemptId, TEST_CLIENT_KEY) + + assertInstanceOf(EmptyResponse::class.java, response) + } + + companion object { + private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + } +} From f0286c3e4e76864ae6dd17fd23e483eb3385d3b0 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 14 Mar 2024 14:59:13 +0100 Subject: [PATCH 213/272] Add tests for AnalyticsSetupRequest COAND-844 --- .../data/model/AnalyticsSetupRequestTest.kt | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequestTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequestTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequestTest.kt new file mode 100644 index 0000000000..b533f02035 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupRequestTest.kt @@ -0,0 +1,94 @@ +package com.adyen.checkout.components.core.internal.data.model + +import com.adyen.checkout.components.core.Amount +import org.json.JSONArray +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class AnalyticsSetupRequestTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val request = AnalyticsSetupRequest( + version = "1.0.0", + channel = "android", + platform = "android", + locale = "en-US", + component = "dropin", + flavor = "dropin", + deviceBrand = "Google", + deviceModel = "Pixel", + referrer = "unknown", + systemVersion = "15", + containerWidth = 50, + screenWidth = 100, + paymentMethods = listOf("ideal", "scheme"), + amount = Amount("EUR", 1337), + sessionId = "session", + ) + + val actual = AnalyticsSetupRequest.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("version", "1.0.0") + .put("channel", "android") + .put("platform", "android") + .put("locale", "en-US") + .put("component", "dropin") + .put("flavor", "dropin") + .put("deviceBrand", "Google") + .put("deviceModel", "Pixel") + .put("referrer", "unknown") + .put("systemVersion", "15") + .put("containerWidth", 50) + .put("screenWidth", 100) + .put("paymentMethods", JSONArray(listOf("ideal", "scheme"))) + .put("amount", JSONObject().put("currency", "EUR").put("value", 1337)) + .put("sessionId", "session") + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val response = JSONObject() + .put("version", "1.0.0") + .put("channel", "android") + .put("platform", "android") + .put("locale", "en-US") + .put("component", "dropin") + .put("flavor", "dropin") + .put("deviceBrand", "Google") + .put("deviceModel", "Pixel") + .put("referrer", "unknown") + .put("systemVersion", "15") + .put("containerWidth", 50) + .put("screenWidth", 100) + .put("paymentMethods", JSONArray(listOf("ideal", "scheme"))) + .put("amount", JSONObject().put("currency", "EUR").put("value", 1337)) + .put("sessionId", "session") + + val actual = AnalyticsSetupRequest.SERIALIZER.deserialize(response) + + val expected = AnalyticsSetupRequest( + version = "1.0.0", + channel = "android", + platform = "android", + locale = "en-US", + component = "dropin", + flavor = "dropin", + deviceBrand = "Google", + deviceModel = "Pixel", + referrer = "unknown", + systemVersion = "15", + containerWidth = 50, + screenWidth = 100, + paymentMethods = listOf("ideal", "scheme"), + amount = Amount("EUR", 1337), + sessionId = "session", + ) + + assertEquals(expected, actual) + } +} From a864bf89d5c20abfcc5e1da9491d1f2e048de6da Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 15 Mar 2024 09:21:14 +0100 Subject: [PATCH 214/272] Add tests for AnalyticsSetupResponse COAND-844 --- .../data/model/AnalyticsSetupResponseTest.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupResponseTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupResponseTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupResponseTest.kt new file mode 100644 index 0000000000..c6c471bbd9 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsSetupResponseTest.kt @@ -0,0 +1,36 @@ +package com.adyen.checkout.components.core.internal.data.model + +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class AnalyticsSetupResponseTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val request = AnalyticsSetupResponse( + checkoutAttemptId = "random-id", + ) + + val actual = AnalyticsSetupResponse.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("checkoutAttemptId", "random-id") + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val response = JSONObject() + .put("checkoutAttemptId", "random-id") + + val actual = AnalyticsSetupResponse.SERIALIZER.deserialize(response) + + val expected = AnalyticsSetupResponse( + checkoutAttemptId = "random-id", + ) + + assertEquals(expected, actual) + } +} From 37769da7e4bf53df9e9848882962807ee63c51ce Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 15 Mar 2024 09:32:44 +0100 Subject: [PATCH 215/272] Add tests for AnalyticsTrackRequest COAND-844 --- .../data/model/AnalyticsTrackRequestTest.kt | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt new file mode 100644 index 0000000000..b10ce86354 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt @@ -0,0 +1,99 @@ +package com.adyen.checkout.components.core.internal.data.model + +import com.adyen.checkout.core.internal.data.model.ModelUtils +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class AnalyticsTrackRequestTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val info = listOf( + AnalyticsTrackInfo( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + target = null, + isStoredPaymentMethod = null, + brand = null, + issuer = null, + validationErrorCode = null, + validationErrorMessage = null, + ), + ) + val logs = listOf( + AnalyticsTrackLog( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + subType = null, + target = null, + message = null, + ), + ) + val request = AnalyticsTrackRequest( + channel = "android", + platform = "android", + info = info, + logs = logs, + ) + + val actual = AnalyticsTrackRequest.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("channel", "android") + .put("platform", "android") + .put("info", ModelUtils.serializeOptList(info, AnalyticsTrackInfo.SERIALIZER)) + .put("logs", ModelUtils.serializeOptList(logs, AnalyticsTrackLog.SERIALIZER)) + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val info = listOf( + AnalyticsTrackInfo( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + target = null, + isStoredPaymentMethod = null, + brand = null, + issuer = null, + validationErrorCode = null, + validationErrorMessage = null, + ), + ) + val logs = listOf( + AnalyticsTrackLog( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + subType = null, + target = null, + message = null, + ), + ) + val response = JSONObject() + .put("channel", "android") + .put("platform", "android") + .put("info", ModelUtils.serializeOptList(info, AnalyticsTrackInfo.SERIALIZER)) + .put("logs", ModelUtils.serializeOptList(logs, AnalyticsTrackLog.SERIALIZER)) + + val actual = AnalyticsTrackRequest.SERIALIZER.deserialize(response) + + val expected = AnalyticsTrackRequest( + channel = "android", + platform = "android", + info = info, + logs = logs, + ) + + assertEquals(expected, actual) + } +} From 0b6fd8c02005bd54d2210bd283788904e174fa40 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 15 Mar 2024 10:41:01 +0100 Subject: [PATCH 216/272] Add tests for AnalyticsTrackInfo COAND-844 --- .../data/model/AnalyticsTrackInfoTest.kt | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfoTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfoTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfoTest.kt new file mode 100644 index 0000000000..dbf8d1336d --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackInfoTest.kt @@ -0,0 +1,92 @@ +package com.adyen.checkout.components.core.internal.data.model + +import com.adyen.checkout.core.exception.ModelSerializationException +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +internal class AnalyticsTrackInfoTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val request = AnalyticsTrackInfo( + id = "id", + timestamp = 12345L, + component = "dropin", + type = "test", + target = "field", + isStoredPaymentMethod = true, + brand = "nike", + issuer = "ing", + validationErrorCode = "418", + validationErrorMessage = "I'm a teapot", + ) + + val actual = AnalyticsTrackInfo.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("id", "id") + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("target", "field") + .put("isStoredPaymentMethod", true) + .put("brand", "nike") + .put("issuer", "ing") + .put("validationErrorCode", "418") + .put("validationErrorMessage", "I'm a teapot") + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val response = JSONObject() + .put("id", "id") + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("target", "field") + .put("isStoredPaymentMethod", true) + .put("brand", "nike") + .put("issuer", "ing") + .put("validationErrorCode", "418") + .put("validationErrorMessage", "I'm a teapot") + + val actual = AnalyticsTrackInfo.SERIALIZER.deserialize(response) + + val expected = AnalyticsTrackInfo( + id = "id", + timestamp = 12345L, + component = "dropin", + type = "test", + target = "field", + isStoredPaymentMethod = true, + brand = "nike", + issuer = "ing", + validationErrorCode = "418", + validationErrorMessage = "I'm a teapot", + ) + + assertEquals(expected, actual) + } + + @Test + fun `when deserializing and a required field is missing, then an error is thrown`() { + val response = JSONObject() + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("target", "field") + .put("isStoredPaymentMethod", true) + .put("brand", "nike") + .put("issuer", "ing") + .put("validationErrorCode", "418") + .put("validationErrorMessage", "I'm a teapot") + + assertThrows { + AnalyticsTrackInfo.SERIALIZER.deserialize(response) + } + } +} From ba4b024e58b1835b2673707f4d369c59306e47ea Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 15 Mar 2024 10:47:32 +0100 Subject: [PATCH 217/272] Add tests for AnalyticsTrackLog COAND-844 --- .../data/model/AnalyticsTrackLogTest.kt | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt new file mode 100644 index 0000000000..8d902e92fc --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt @@ -0,0 +1,77 @@ +package com.adyen.checkout.components.core.internal.data.model + +import com.adyen.checkout.core.exception.ModelSerializationException +import org.json.JSONObject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +internal class AnalyticsTrackLogTest { + + @Test + fun `when serializing, then all fields should be serialized correctly`() { + val request = AnalyticsTrackLog( + id = "id", + timestamp = 12345L, + component = "dropin", + type = "test", + subType = "subtest", + target = "field", + message = "Hello", + ) + + val actual = AnalyticsTrackLog.SERIALIZER.serialize(request) + + val expected = JSONObject() + .put("id", "id") + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("subType", "subtest") + .put("target", "field") + .put("message", "Hello") + + assertEquals(expected.toString(), actual.toString()) + } + + @Test + fun `when deserializing, then all fields should be deserializing correctly`() { + val response = JSONObject() + .put("id", "id") + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("subType", "subtest") + .put("target", "field") + .put("message", "Hello") + + val actual = AnalyticsTrackLog.SERIALIZER.deserialize(response) + + val expected = AnalyticsTrackLog( + id = "id", + timestamp = 12345L, + component = "dropin", + type = "test", + subType = "subtest", + target = "field", + message = "Hello", + ) + + assertEquals(expected, actual) + } + + @Test + fun `when deserializing and a required field is missing, then an error is thrown`() { + val response = JSONObject() + .put("timestamp", 12345L) + .put("component", "dropin") + .put("type", "test") + .put("subType", "subtest") + .put("target", "field") + .put("message", "Hello") + + assertThrows { + AnalyticsTrackInfo.SERIALIZER.deserialize(response) + } + } +} From b7bc69e0d1bee4098bc3334b7fd01f44894ddfec Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 15 Mar 2024 11:33:32 +0100 Subject: [PATCH 218/272] Add tests for AnalyticsTrackRequestProvider COAND-844 --- .../AnalyticsTrackRequestProviderTest.kt | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt new file mode 100644 index 0000000000..506660799e --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt @@ -0,0 +1,83 @@ +package com.adyen.checkout.components.core.internal.analytics.data.remote + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.DirectAnalyticsEventCreation +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackInfo +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackLog +import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +@OptIn(DirectAnalyticsEventCreation::class) +internal class AnalyticsTrackRequestProviderTest { + + private lateinit var analyticsTrackRequestProvider: AnalyticsTrackRequestProvider + + @BeforeEach + fun setup() { + analyticsTrackRequestProvider = AnalyticsTrackRequestProvider() + } + + @Test + fun `when providing, then objects should be mapped correctly`() { + val infoList = listOf( + AnalyticsEvent.Info( + id = "id", + timestamp = 12345L, + component = "dropin", + type = AnalyticsEvent.Info.Type.INPUT, + target = "target", + isStoredPaymentMethod = true, + brand = "brand", + issuer = "issuer", + validationErrorCode = "418", + validationErrorMessage = "I'm a teapot", + ), + ) + val logList = listOf( + AnalyticsEvent.Log( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + subType = null, + target = null, + message = null, + ), + ) + + val result = analyticsTrackRequestProvider(infoList, logList) + + val expected = AnalyticsTrackRequest( + channel = "android", + platform = "android", + info = listOf( + AnalyticsTrackInfo( + id = "id", + timestamp = 12345L, + component = "dropin", + type = "input", + target = "target", + isStoredPaymentMethod = true, + brand = "brand", + issuer = "issuer", + validationErrorCode = "418", + validationErrorMessage = "I'm a teapot", + ), + ), + logs = listOf( + AnalyticsTrackLog( + id = "id", + timestamp = 12345L, + component = "dropin", + type = null, + subType = null, + target = null, + message = null, + ), + ), + ) + assertEquals(expected, result) + } +} From 99dc7dc887e6340f757f474d5d58f0dfcd71214c Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 18 Mar 2024 11:03:48 +0100 Subject: [PATCH 219/272] Add tests for DefaultAnalyticsManager COAND-844 --- .../analytics/DefaultAnalyticsManager.kt | 15 +- .../analytics/DefaultAnalyticsManagerTest.kt | 172 ++++++++++++++++++ 2 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index c679dfa43d..1f6b13fb83 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -8,6 +8,7 @@ package com.adyen.checkout.components.core.internal.analytics +import androidx.annotation.VisibleForTesting import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel @@ -24,7 +25,7 @@ import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicInteger import kotlin.time.Duration.Companion.seconds -internal class DefaultAnalyticsManager internal constructor( +internal class DefaultAnalyticsManager( private val analyticsRepository: AnalyticsRepository, private val analyticsParams: AnalyticsParams, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO, @@ -106,11 +107,6 @@ internal class DefaultAnalyticsManager internal constructor( } private suspend fun sendEvents() { - if (cannotSendEvents()) { - adyenLog(AdyenLogLevel.DEBUG) { "Not allowed to send events, ignoring." } - return - } - val checkoutAttemptId = checkoutAttemptId if (checkoutAttemptId == null) { adyenLog(AdyenLogLevel.WARN) { "checkoutAttemptId should not be null at this point." } @@ -151,7 +147,10 @@ internal class DefaultAnalyticsManager internal constructor( } companion object { - private const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" - private val DISPATCH_INTERVAL_MILLIS = 10.seconds.inWholeMilliseconds + @VisibleForTesting + internal const val CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS = "do-not-track" + + @VisibleForTesting + internal val DISPATCH_INTERVAL_MILLIS = 10.seconds.inWholeMilliseconds } } diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt new file mode 100644 index 0000000000..75a1c998fc --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt @@ -0,0 +1,172 @@ +package com.adyen.checkout.components.core.internal.analytics + +import com.adyen.checkout.components.core.internal.analytics.data.AnalyticsRepository +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParams +import com.adyen.checkout.components.core.internal.ui.model.AnalyticsParamsLevel +import com.adyen.checkout.test.LoggingExtension +import com.adyen.checkout.test.TestDispatcherExtension +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class, DirectAnalyticsEventCreation::class) +@ExtendWith(MockitoExtension::class, LoggingExtension::class, TestDispatcherExtension::class) +internal class DefaultAnalyticsManagerTest( + @Mock private val analyticsRepository: AnalyticsRepository, +) { + + + private lateinit var analyticsManager: DefaultAnalyticsManager + + @BeforeEach + fun setup() { + analyticsManager = createAnalyticsManager() + } + + @Nested + @DisplayName("when initializing and") + inner class InitializeTest { + + @Test + fun `sending events is disabled, then checkoutAttemptId is anonymous`() = runTest { + analyticsManager = createAnalyticsManager(AnalyticsParamsLevel.NONE) + + analyticsManager.initialize(this@InitializeTest, this) + + assertEquals( + DefaultAnalyticsManager.CHECKOUT_ATTEMPT_ID_FOR_DISABLED_ANALYTICS, + analyticsManager.getCheckoutAttemptId(), + ) + verify(analyticsRepository, never()).fetchCheckoutAttemptId() + } + + @Test + fun `fetching checkoutAttemptId succeeds, then it is set`() = runTest { + whenever(analyticsRepository.fetchCheckoutAttemptId()) doReturn "test value" + + analyticsManager.initialize(this@InitializeTest, this) + + assertEquals("test value", analyticsManager.getCheckoutAttemptId()) + analyticsManager.clear(this@InitializeTest) + } + + @Test + fun `fetching checkoutAttemptId fails, then checkoutAttemptId is null`() = runTest { + whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { throw RuntimeException() } + + analyticsManager.initialize(this@InitializeTest, this) + + assertNull(analyticsManager.getCheckoutAttemptId()) + } + + @Test + fun `initialize is called twice, then the second time is ignored`() = runTest { + whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { throw RuntimeException() } + + analyticsManager.initialize(this@InitializeTest, this) + analyticsManager.initialize(this@InitializeTest, this) + + verify(analyticsRepository, times(1)).fetchCheckoutAttemptId() + } + } + + @Nested + @DisplayName("when tracking events and") + inner class TrackEventTest { + + @Test + fun `sending events is disabled, then events should not be stored`() = runTest { + analyticsManager = createAnalyticsManager(AnalyticsParamsLevel.NONE) + analyticsManager.initialize(this@TrackEventTest, this) + + analyticsManager.trackEvent(GenericEvents.rendered("dropin", false)) + + verify(analyticsRepository, never()).storeEvent(any()) + } + + @Test + fun `sending events is enabled, then events should be stored`() = runTest { + analyticsManager.initialize(this@TrackEventTest, this) + val event = AnalyticsEvent.Info( + component = "test", + shouldForceSend = false, + ) + + analyticsManager.trackEvent(event) + + verify(analyticsRepository).storeEvent(event) + } + + @Test + fun `the event should force sending, then the event is sent right away`() = runTest { + whenever(analyticsRepository.fetchCheckoutAttemptId()) doReturn "test value" + analyticsManager.initialize(this@TrackEventTest, this) + val event = AnalyticsEvent.Info( + component = "test", + shouldForceSend = true, + ) + + analyticsManager.trackEvent(event) + + verify(analyticsRepository).sendEvents(any()) + analyticsManager.clear(this@TrackEventTest) + } + } + + @Test + fun `when timer ticks, then all stored events should be sent`() = runTest { + analyticsManager = createAnalyticsManager(coroutineDispatcher = StandardTestDispatcher(testScheduler)) + whenever(analyticsRepository.fetchCheckoutAttemptId()) doReturn "test value" + whenever(analyticsRepository.storeEvent(any())) doReturn Unit + whenever(analyticsRepository.sendEvents(any())) doReturn Unit + analyticsManager.initialize(this@DefaultAnalyticsManagerTest, this) + + analyticsManager.trackEvent(GenericEvents.rendered("dropin", false)) + testScheduler.advanceTimeBy(DefaultAnalyticsManager.DISPATCH_INTERVAL_MILLIS + 1) + + verify(analyticsRepository, times(1)).sendEvents(any()) + analyticsManager.clear(this@DefaultAnalyticsManagerTest) + } + + @Test + fun `when sending events and checkoutAttemptId is null, then events are not sent`() = runTest { + whenever(analyticsRepository.fetchCheckoutAttemptId()) doReturn null + analyticsManager.initialize(this@DefaultAnalyticsManagerTest, this) + val event = AnalyticsEvent.Info( + component = "test", + shouldForceSend = true, + ) + + analyticsManager.trackEvent(event) + + verify(analyticsRepository, never()).sendEvents(any()) + analyticsManager.clear(this@DefaultAnalyticsManagerTest) + } + + private fun createAnalyticsManager( + analyticsParamsLevel: AnalyticsParamsLevel = AnalyticsParamsLevel.ALL, + coroutineDispatcher: CoroutineDispatcher = UnconfinedTestDispatcher(), + ) = DefaultAnalyticsManager( + analyticsRepository = analyticsRepository, + analyticsParams = AnalyticsParams(analyticsParamsLevel, ""), + coroutineDispatcher = coroutineDispatcher, + ) +} From ba6e1e968e0838c39016a2a77bb385be4b89b727 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 18 Mar 2024 13:58:47 +0100 Subject: [PATCH 220/272] Add tests for DefaultAnalyticsRepository COAND-844 --- .../data/DefaultAnalyticsRepositoryTest.kt | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt new file mode 100644 index 0000000000..5899c48028 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt @@ -0,0 +1,117 @@ +package com.adyen.checkout.components.core.internal.analytics.data + +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.DirectAnalyticsEventCreation +import com.adyen.checkout.components.core.internal.analytics.data.local.AnalyticsLocalDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsRemoteDataStore +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider +import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockitoExtension::class) +internal class DefaultAnalyticsRepositoryTest( + @Mock private val localInfoDataStore: AnalyticsLocalDataStore, + @Mock private val localLogDataStore: AnalyticsLocalDataStore, + @Mock private val remoteDataStore: AnalyticsRemoteDataStore, + @Mock private val analyticsSetupProvider: AnalyticsSetupProvider, + @Mock private val analyticsTrackRequestProvider: AnalyticsTrackRequestProvider, +) { + + private lateinit var analyticsRepository: DefaultAnalyticsRepository + + @BeforeEach + fun setup() { + analyticsRepository = DefaultAnalyticsRepository( + localInfoDataStore = localInfoDataStore, + localLogDataStore = localLogDataStore, + remoteDataStore = remoteDataStore, + analyticsSetupProvider = analyticsSetupProvider, + analyticsTrackRequestProvider = analyticsTrackRequestProvider, + ) + } + + @Test + fun `when fetching attempt id, then it should be extracted from the response`() = runTest { + whenever(analyticsSetupProvider.provide()) doReturn mock() + val checkoutAttemptId = "some id" + whenever(remoteDataStore.fetchCheckoutAttemptId(any())) doReturn AnalyticsSetupResponse(checkoutAttemptId) + + val result = analyticsRepository.fetchCheckoutAttemptId() + + assertEquals(checkoutAttemptId, result) + } + + @OptIn(DirectAnalyticsEventCreation::class) + @Test + fun `when storing an event, then the correct data store should be used`() = runTest { + val infoEvent = AnalyticsEvent.Info(component = "test") + analyticsRepository.storeEvent(infoEvent) + + verify(localInfoDataStore).storeEvent(infoEvent) + + val logEvent = AnalyticsEvent.Log(component = "test") + analyticsRepository.storeEvent(logEvent) + + verify(localLogDataStore).storeEvent(logEvent) + } + + @Nested + @DisplayName("when sending events and") + inner class SendEventsTest { + + @Test + fun `there are no events stored, then sending is canceled`() = runTest { + whenever(localInfoDataStore.fetchEvents(any())) doReturn emptyList() + whenever(localLogDataStore.fetchEvents(any())) doReturn emptyList() + + analyticsRepository.sendEvents("test") + + verify(remoteDataStore, never()).sendEvents(any(), any()) + } + + @Test + fun `it is successful, then events are cleared from storage`() = runTest { + whenever(localInfoDataStore.fetchEvents(any())) doReturn listOf(mock()) + whenever(localLogDataStore.fetchEvents(any())) doReturn listOf(mock()) + whenever(analyticsTrackRequestProvider.invoke(any(), any())) doReturn mock() + + analyticsRepository.sendEvents("test") + + verify(localInfoDataStore).clear() + verify(localLogDataStore).clear() + } + + @Test + fun `it fails, then events are not cleared from storage`() = runTest { + whenever(localInfoDataStore.fetchEvents(any())) doReturn listOf(mock()) + whenever(localLogDataStore.fetchEvents(any())) doReturn listOf(mock()) + whenever(analyticsTrackRequestProvider.invoke(any(), any())) doReturn mock() + whenever(remoteDataStore.sendEvents(any(), any())) doAnswer { throw RuntimeException() } + + runCatching { + analyticsRepository.sendEvents("test") + } + + verify(localInfoDataStore, never()).clear() + verify(localLogDataStore, never()).clear() + } + } +} From 8f760f80986380bbeb4611e3bc24ce2b02b99399 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 18 Mar 2024 14:55:36 +0100 Subject: [PATCH 221/272] Add tests for DefaultAnalyticsSetupProvider COAND-844 --- .../DefaultAnalyticsSetupProviderTest.kt | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProviderTest.kt diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProviderTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProviderTest.kt new file mode 100644 index 0000000000..cbf9029ad7 --- /dev/null +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/DefaultAnalyticsSetupProviderTest.kt @@ -0,0 +1,132 @@ +package com.adyen.checkout.components.core.internal.analytics.data.remote + +import android.app.Application +import android.content.res.Resources +import android.os.Build +import android.util.DisplayMetrics +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.internal.analytics.AnalyticsPlatformParams +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource +import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupRequest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import java.util.Locale + +internal class DefaultAnalyticsSetupProviderTest { + + private lateinit var analyticsSetupProvider: DefaultAnalyticsSetupProvider + + @Test + fun `when providing AnalyticsSetupRequest, then it should be mapped correctly`() { + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = createMockApplication(), + shopperLocale = Locale.US, + isCreatedByDropIn = false, + amount = Amount("USD", 123), + source = AnalyticsSource.PaymentComponent("scheme"), + sessionId = "sessionId", + ) + + val result = analyticsSetupProvider.provide() + + val expected = AnalyticsSetupRequest( + version = AnalyticsPlatformParams.version, + channel = AnalyticsPlatformParams.channel, + platform = AnalyticsPlatformParams.platform, + locale = Locale.US.toString(), + component = "scheme", + flavor = "components", + deviceBrand = Build.BRAND, + deviceModel = Build.MODEL, + referrer = "com.adyen.checkout", + systemVersion = Build.VERSION.SDK_INT.toString(), + containerWidth = null, + screenWidth = 420, + paymentMethods = listOf("scheme"), + amount = Amount("USD", 123), + sessionId = "sessionId", + ) + assertEquals(expected, result) + } + + @Test + fun `when created by drop in, then flavor should be dropin`() { + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = createMockApplication(), + shopperLocale = Locale.US, + isCreatedByDropIn = true, + amount = Amount("USD", 123), + source = AnalyticsSource.PaymentComponent("scheme"), + sessionId = "sessionId", + ) + + val result = analyticsSetupProvider.provide() + + assertEquals("dropin", result.flavor) + } + + @Test + fun `when not created by drop in, then flavor should be components`() { + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = createMockApplication(), + shopperLocale = Locale.US, + isCreatedByDropIn = false, + amount = Amount("USD", 123), + source = AnalyticsSource.PaymentComponent("scheme"), + sessionId = "sessionId", + ) + + val result = analyticsSetupProvider.provide() + + assertEquals("components", result.flavor) + } + + @Test + fun `when source is drop in, then component should be dropin`() { + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = createMockApplication(), + shopperLocale = Locale.US, + isCreatedByDropIn = true, + amount = Amount("USD", 123), + source = AnalyticsSource.DropIn(listOf()), + sessionId = "sessionId", + ) + + val result = analyticsSetupProvider.provide() + + assertEquals("dropin", result.component) + } + + @Test + fun `when source is a component, then component should be equal to it's payment method`() { + analyticsSetupProvider = DefaultAnalyticsSetupProvider( + application = createMockApplication(), + shopperLocale = Locale.US, + isCreatedByDropIn = true, + amount = Amount("USD", 123), + source = AnalyticsSource.PaymentComponent("scheme"), + sessionId = "sessionId", + ) + + val result = analyticsSetupProvider.provide() + + assertEquals("scheme", result.component) + } + + private fun createMockApplication(): Application { + val application = mock() + val resources = mock() + val displayMetrics = DisplayMetrics().apply { + widthPixels = 420 + } + + whenever(application.packageName) doReturn "com.adyen.checkout" + whenever(application.resources) doReturn resources + whenever(resources.displayMetrics) doReturn displayMetrics + + return application + } +} From 4b2cb2fd5f5553b578d3f81a6db31a4acefb8221 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Mon, 18 Mar 2024 15:37:51 +0100 Subject: [PATCH 222/272] Fix detekt issues COAND-844 --- .../core/internal/analytics/AnalyticsPlatformParamsTest.kt | 6 ++++++ .../core/internal/analytics/DefaultAnalyticsManagerTest.kt | 5 ++--- .../analytics/data/DefaultAnalyticsRepositoryTest.kt | 2 +- .../data/remote/AnalyticsTrackRequestProviderTest.kt | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt index dfe44f1861..8c2f349961 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatformParamsTest.kt @@ -1,6 +1,7 @@ package com.adyen.checkout.components.core.internal.analytics import com.adyen.checkout.components.core.BuildConfig +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -12,6 +13,11 @@ internal class AnalyticsPlatformParamsTest { AnalyticsPlatformParams.resetToDefaults() } + @AfterEach + fun cleanup() { + AnalyticsPlatformParams.resetToDefaults() + } + @Test fun `when no overriding, then default are returned`() { assertEquals(AnalyticsPlatform.ANDROID.value, AnalyticsPlatformParams.platform) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt index 75a1c998fc..0401fc5e95 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManagerTest.kt @@ -33,7 +33,6 @@ internal class DefaultAnalyticsManagerTest( @Mock private val analyticsRepository: AnalyticsRepository, ) { - private lateinit var analyticsManager: DefaultAnalyticsManager @BeforeEach @@ -70,7 +69,7 @@ internal class DefaultAnalyticsManagerTest( @Test fun `fetching checkoutAttemptId fails, then checkoutAttemptId is null`() = runTest { - whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { throw RuntimeException() } + whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { error("test") } analyticsManager.initialize(this@InitializeTest, this) @@ -79,7 +78,7 @@ internal class DefaultAnalyticsManagerTest( @Test fun `initialize is called twice, then the second time is ignored`() = runTest { - whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { throw RuntimeException() } + whenever(analyticsRepository.fetchCheckoutAttemptId()) doAnswer { error("test") } analyticsManager.initialize(this@InitializeTest, this) analyticsManager.initialize(this@InitializeTest, this) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt index 5899c48028..d157a7f4c9 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt @@ -104,7 +104,7 @@ internal class DefaultAnalyticsRepositoryTest( whenever(localInfoDataStore.fetchEvents(any())) doReturn listOf(mock()) whenever(localLogDataStore.fetchEvents(any())) doReturn listOf(mock()) whenever(analyticsTrackRequestProvider.invoke(any(), any())) doReturn mock() - whenever(remoteDataStore.sendEvents(any(), any())) doAnswer { throw RuntimeException() } + whenever(remoteDataStore.sendEvents(any(), any())) doAnswer { error("test") } runCatching { analyticsRepository.sendEvents("test") diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt index 506660799e..f88f61e364 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt @@ -47,7 +47,7 @@ internal class AnalyticsTrackRequestProviderTest { ), ) - val result = analyticsTrackRequestProvider(infoList, logList) + val result = analyticsTrackRequestProvider.invoke(infoList, logList) val expected = AnalyticsTrackRequest( channel = "android", From d0a833effd0234bb8be8848df997408b52236c10 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 8 Mar 2024 15:14:19 +0100 Subject: [PATCH 223/272] Create test implementation of AnalyticsManager COAND-845 --- components-core/build.gradle | 7 ++ .../analytics/TestAnalyticsManager.java | 66 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java diff --git a/components-core/build.gradle b/components-core/build.gradle index dbbc3facd4..a7676e03f4 100644 --- a/components-core/build.gradle +++ b/components-core/build.gradle @@ -30,6 +30,10 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' consumerProguardFiles "consumer-rules.pro" } + + testFixtures { + enable = true + } } dependencies { @@ -53,4 +57,7 @@ dependencies { androidTestImplementation testLibraries.androidTest androidTestImplementation testLibraries.mockitoAndroid androidTestImplementation testLibraries.espresso + + testFixturesImplementation testLibraries.junit5 + testFixturesImplementation testLibraries.mockito } diff --git a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java new file mode 100644 index 0000000000..de066ddba9 --- /dev/null +++ b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java @@ -0,0 +1,66 @@ +/* + * 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 8/3/2024. + */ + +package com.adyen.checkout.components.core.internal.analytics; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.mockito.internal.matchers.apachecommons.ReflectionEquals; + +import kotlinx.coroutines.CoroutineScope; + +public class TestAnalyticsManager implements AnalyticsManager { + + private boolean isInitialized = false; + + private String checkoutAttemptId = null; + + private AnalyticsEvent lastEvent = null; + + @Override + public void initialize(@NonNull Object owner, @NonNull CoroutineScope coroutineScope) { + isInitialized = true; + } + + public void assertIsInitialized() { + assertTrue(isInitialized); + } + + @Override + public void trackEvent(@NonNull AnalyticsEvent event) { + lastEvent = event; + } + + public void assertLastEventEquals(AnalyticsEvent expected) { + ReflectionEquals re = new ReflectionEquals( + expected, + // Exclude these field as they are generated at runtime + "id", + "timestamp" + ); + assertTrue(re.matches(lastEvent)); + } + + @Nullable + @Override + public String getCheckoutAttemptId() { + return checkoutAttemptId; + } + + public void setCheckoutAttemptId(String checkoutAttemptId) { + this.checkoutAttemptId = checkoutAttemptId; + } + + @Override + public void clear(@NonNull Object owner) { + // noop + } +} From 2ce58c278eb0c76f9f101995159301de565c6817 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 6 Mar 2024 14:45:15 +0100 Subject: [PATCH 224/272] Implement MBWay analytics events COAND-845 --- .../analytics/TestAnalyticsManager.java | 8 +++- mbway/build.gradle | 1 + .../mbway/internal/ui/DefaultMBWayDelegate.kt | 10 +++++ .../internal/ui/DefaultMBWayDelegateTest.kt | 39 +++++++++++++------ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java index de066ddba9..50f3ee1e90 100644 --- a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java +++ b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java @@ -21,6 +21,8 @@ public class TestAnalyticsManager implements AnalyticsManager { private boolean isInitialized = false; + private boolean isCleared = false; + private String checkoutAttemptId = null; private AnalyticsEvent lastEvent = null; @@ -61,6 +63,10 @@ public void setCheckoutAttemptId(String checkoutAttemptId) { @Override public void clear(@NonNull Object owner) { - // noop + isCleared = true; + } + + public void assertIsCleared() { + assertTrue(isCleared); } } diff --git a/mbway/build.gradle b/mbway/build.gradle index dc51b3a268..08982a4d83 100644 --- a/mbway/build.gradle +++ b/mbway/build.gradle @@ -46,6 +46,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index acdd69f0c1..7b2049d52d 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -17,6 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.MBWayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -76,6 +77,12 @@ internal class DefaultMBWayDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered( + component = paymentMethod.type.orEmpty(), + isStoredPaymentMethod = false, + ) + analyticsManager.trackEvent(event) } override fun observe( @@ -165,6 +172,9 @@ internal class DefaultMBWayDelegate( override fun onSubmit() { val state = _componentStateFlow.value submitHandler.onSubmit(state) + + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun isConfirmationRequired(): Boolean = _viewFlow.value is ButtonComponentViewType diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 9dc49aa7ca..2554951ada 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -13,8 +13,10 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -41,24 +43,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultMBWayDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultMBWayDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createMBWayDelegate() } @@ -256,14 +255,32 @@ internal class DefaultMBWayDelegateTest( inner class AnalyticsTest { @Test - fun `when delegate is initialized then analytics manager is initialized`() { + fun `when delegate is initialized then analytics manager is initialized`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.MB_WAY, false) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() = runTest { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(PaymentMethodTypes.MB_WAY) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -280,7 +297,7 @@ internal class DefaultMBWayDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + analyticsManager.assertIsCleared() } } @@ -295,7 +312,7 @@ internal class DefaultMBWayDelegateTest( configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = PaymentMethodTypes.MB_WAY), order = TEST_ORDER, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = configuration, From 8ac73d7eb13918a4c6bdbc4e7426a356315f2af8 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 22 Mar 2024 10:42:56 +0100 Subject: [PATCH 225/272] Do optimisations on DefaultMBWayDelegate tracking COAND-845 --- .../core/internal/analytics/GenericEvents.kt | 3 +-- .../checkout/mbway/internal/ui/DefaultMBWayDelegate.kt | 5 +---- .../mbway/internal/ui/DefaultMBWayDelegateTest.kt | 10 ++++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index b38d154c8a..fa3f5cbeda 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -18,8 +18,7 @@ object GenericEvents { // Info events fun rendered( component: String, - // Check if this should be null or false by default - isStoredPaymentMethod: Boolean, + isStoredPaymentMethod: Boolean? = null, brand: String? = null, ) = AnalyticsEvent.Info( component = component, diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 7b2049d52d..8c0632c525 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -78,10 +78,7 @@ internal class DefaultMBWayDelegate( adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) - val event = GenericEvents.rendered( - component = paymentMethod.type.orEmpty(), - isStoredPaymentMethod = false, - ) + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) } diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 2554951ada..bd038995b3 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -255,21 +255,22 @@ internal class DefaultMBWayDelegateTest( inner class AnalyticsTest { @Test - fun `when delegate is initialized then analytics manager is initialized`() = runTest { + fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + analyticsManager.assertIsInitialized() } @Test - fun `when delegate is initialized, then render event is tracked`() = runTest { + fun `when delegate is initialized, then render event is tracked`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.MB_WAY, false) + val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.MB_WAY) analyticsManager.assertLastEventEquals(expectedEvent) } @Test - fun `when onSubmit is called, then submit event is tracked`() = runTest { + fun `when onSubmit is called, then submit event is tracked`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) delegate.onSubmit() @@ -297,6 +298,7 @@ internal class DefaultMBWayDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() + analyticsManager.assertIsCleared() } } From c02b7f711960ec6fa1664da11ab6905822e59e55 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 22 Mar 2024 10:50:08 +0100 Subject: [PATCH 226/272] Add rendered and submit events for DefaultCardDelegate and StoredCardDelegate COAND-845 --- card/build.gradle | 1 + .../card/internal/ui/DefaultCardDelegate.kt | 7 ++++ .../card/internal/ui/StoredCardDelegate.kt | 10 ++++++ .../internal/ui/DefaultCardDelegateTest.kt | 34 ++++++++++++++---- .../internal/ui/StoredCardDelegateTest.kt | 35 ++++++++++++++----- 5 files changed, 72 insertions(+), 15 deletions(-) diff --git a/card/build.gradle b/card/build.gradle index ae7ecbcb5d..68602532a1 100644 --- a/card/build.gradle +++ b/card/build.gradle @@ -56,6 +56,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index 87268d06a5..7f27d2deec 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -45,6 +45,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -174,6 +175,9 @@ class DefaultCardDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -476,6 +480,9 @@ class DefaultCardDelegate( override fun onSubmit() { val state = _componentStateFlow.value submitHandler.onSubmit(state = state) + + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun startAddressLookup() { diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt index 1391b880a1..31d3d2e1d7 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt @@ -33,6 +33,7 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -154,6 +155,12 @@ internal class StoredCardDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered( + component = storedPaymentMethod.type.orEmpty(), + isStoredPaymentMethod = true, + ) + analyticsManager.trackEvent(event) } override fun observe( @@ -304,6 +311,9 @@ internal class StoredCardDelegate( override fun onSubmit() { val state = _componentStateFlow.value submitHandler.onSubmit(state) + + val event = GenericEvents.submit(storedPaymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun startAddressLookup() = Unit diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index 8057210b3e..c5c9b30609 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -46,6 +46,8 @@ import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -90,9 +92,6 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -100,7 +99,6 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCardDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, @Mock private val addressLookupDelegate: AddressLookupDelegate, ) { @@ -110,6 +108,7 @@ internal class DefaultCardDelegateTest( private lateinit var publicKeyRepository: TestPublicKeyRepository private lateinit var addressRepository: TestAddressRepository private lateinit var detectCardTypeRepository: TestDetectCardTypeRepository + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultCardDelegate @BeforeEach @@ -119,6 +118,7 @@ internal class DefaultCardDelegateTest( publicKeyRepository = TestPublicKeyRepository() addressRepository = TestAddressRepository() detectCardTypeRepository = TestDetectCardTypeRepository() + analyticsManager = TestAnalyticsManager() whenever(addressLookupDelegate.addressLookupSubmitFlow).thenReturn(MutableStateFlow(AddressInputModel())) @@ -1039,12 +1039,31 @@ internal class DefaultCardDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.SCHEME) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(PaymentMethodTypes.SCHEME) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -1062,7 +1081,8 @@ internal class DefaultCardDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index bbbf769bec..839438fb8b 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -36,6 +36,8 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -70,28 +72,25 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class StoredCardDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler ) { private lateinit var cardEncryptor: TestCardEncryptor private lateinit var publicKeyRepository: TestPublicKeyRepository + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: StoredCardDelegate @BeforeEach fun before() { cardEncryptor = TestCardEncryptor() publicKeyRepository = TestPublicKeyRepository() + analyticsManager = TestAnalyticsManager() delegate = createCardDelegate() } @@ -428,12 +427,31 @@ internal class StoredCardDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.SCHEME, isStoredPaymentMethod = true) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(PaymentMethodTypes.SCHEME) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -449,7 +467,8 @@ internal class StoredCardDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } From 74cad3dc72a0901a60bdc028d15cccf17687386f Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 22 Mar 2024 16:07:43 +0100 Subject: [PATCH 227/272] Add rendered and submit events for DefaultIssuerListDelegate COAND-845 --- issuer-list/build.gradle | 1 + .../internal/ui/DefaultIssuerListDelegate.kt | 7 ++++ .../ui/view/IssuerListSpinnerAdapter.kt | 2 +- .../ui/DefaultIssuerListDelegateTest.kt | 39 ++++++++++++++----- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/issuer-list/build.gradle b/issuer-list/build.gradle index 926b06c5c9..caaae3da09 100644 --- a/issuer-list/build.gradle +++ b/issuer-list/build.gradle @@ -51,6 +51,7 @@ dependencies { testImplementation project(':test-core') testImplementation project(':twint') testImplementation project(':wechatpay') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index a4d9d46e6e..bc2e4a3c67 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -18,6 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.internal.util.adyenLog @@ -79,6 +80,9 @@ internal class DefaultIssuerListDelegate< private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -120,6 +124,9 @@ internal class DefaultIssuerListDelegate< override fun onSubmit() { val state = _componentStateFlow.value submitHandler.onSubmit(state) + + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } private fun onInputDataChanged() { diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/view/IssuerListSpinnerAdapter.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/view/IssuerListSpinnerAdapter.kt index c3bf016b3d..3509a3efb1 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/view/IssuerListSpinnerAdapter.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/view/IssuerListSpinnerAdapter.kt @@ -18,7 +18,7 @@ import com.adyen.checkout.issuerlist.internal.ui.model.IssuerModel import com.adyen.checkout.ui.core.databinding.SpinnerListWithImageBinding import com.adyen.checkout.ui.core.internal.ui.loadLogo -internal class IssuerListSpinnerAdapter constructor( +internal class IssuerListSpinnerAdapter( private val context: Context, private var issuerList: List, private val paymentMethod: String, diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index fc125f9ac2..31d13ccc19 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -13,8 +13,10 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.issuerlist.IssuerListViewType @@ -44,24 +46,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultIssuerListDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultIssuerListDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createIssuerListDelegate() } @@ -294,12 +293,31 @@ internal class DefaultIssuerListDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.IDEAL) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(PaymentMethodTypes.IDEAL) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -315,7 +333,8 @@ internal class DefaultIssuerListDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -331,7 +350,7 @@ internal class DefaultIssuerListDelegateTest( componentConfiguration = configuration.getConfiguration(TEST_CONFIGURATION_KEY), hideIssuerLogosDefaultValue = false, ), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = PaymentMethodTypes.IDEAL), order = TEST_ORDER, analyticsManager = analyticsManager, submitHandler = submitHandler, From 373601ce6ee75c5319068beedebf091b5c77dabe Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 22 Mar 2024 16:29:29 +0100 Subject: [PATCH 228/272] Add selected event for DefaultIssuerListDelegate COAND-845 --- .../internal/ui/DefaultIssuerListDelegate.kt | 12 ++++++++++++ .../internal/ui/DefaultIssuerListDelegateTest.kt | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index bc2e4a3c67..8fdec0895b 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -135,6 +135,13 @@ internal class DefaultIssuerListDelegate< _outputDataFlow.tryEmit(outputData) updateComponentState(outputData) + + val event = GenericEvents.selected( + component = paymentMethod.type.orEmpty(), + target = ANALYTICS_TARGET, + issuer = outputData.selectedIssuer?.name.orEmpty(), + ) + analyticsManager.trackEvent(event) } private fun createOutputData() = IssuerListOutputData(inputData.selectedIssuer) @@ -179,4 +186,9 @@ internal class DefaultIssuerListDelegate< removeObserver() analyticsManager.clear(this) } + + companion object { + @VisibleForTesting + internal const val ANALYTICS_TARGET = "list" + } } diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index 31d13ccc19..6b1bf68d9a 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -315,6 +315,20 @@ internal class DefaultIssuerListDelegateTest( analyticsManager.assertLastEventEquals(expectedEvent) } + @Test + fun `when updateInputData is called, then selected event is tracked`() { + delegate.updateInputData { + selectedIssuer = IssuerModel(id = "id", name = "test", environment = Environment.TEST) + } + + val expectedEvent = GenericEvents.selected( + component = PaymentMethodTypes.IDEAL, + target = DefaultIssuerListDelegate.ANALYTICS_TARGET, + issuer = "test", + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) From 9ceeb86e13c3f290fbdd72434214e009858cb804 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 22 Mar 2024 16:33:56 +0100 Subject: [PATCH 229/272] Clean up tests COAND-845 --- .../adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt | 2 -- .../adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt | 2 -- .../issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt | 2 -- .../checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt | 2 -- 4 files changed, 8 deletions(-) diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index c5c9b30609..3e9aaec759 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -1053,8 +1053,6 @@ internal class DefaultCardDelegateTest( @Test fun `when onSubmit is called, then submit event is tracked`() { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.onSubmit() val expectedEvent = GenericEvents.submit(PaymentMethodTypes.SCHEME) diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt index 839438fb8b..79097f0906 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/StoredCardDelegateTest.kt @@ -441,8 +441,6 @@ internal class StoredCardDelegateTest( @Test fun `when onSubmit is called, then submit event is tracked`() { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.onSubmit() val expectedEvent = GenericEvents.submit(PaymentMethodTypes.SCHEME) diff --git a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt index 6b1bf68d9a..ce91016bae 100644 --- a/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt +++ b/issuer-list/src/test/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegateTest.kt @@ -307,8 +307,6 @@ internal class DefaultIssuerListDelegateTest( @Test fun `when onSubmit is called, then submit event is tracked`() { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.onSubmit() val expectedEvent = GenericEvents.submit(PaymentMethodTypes.IDEAL) diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index bd038995b3..968d7a52fb 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -271,8 +271,6 @@ internal class DefaultMBWayDelegateTest( @Test fun `when onSubmit is called, then submit event is tracked`() { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.onSubmit() val expectedEvent = GenericEvents.submit(PaymentMethodTypes.MB_WAY) From 0e1a313c1e3845ec974607f1e0713a65de4e9098 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 26 Mar 2024 09:51:57 +0100 Subject: [PATCH 230/272] Track action event when action is handled COAND-845 --- action-core/build.gradle | 1 + .../GenericActionComponentProvider.kt | 11 ++++- .../ui/DefaultGenericActionDelegate.kt | 9 +++++ .../ui/DefaultGenericActionDelegateTest.kt | 40 ++++++++++++++++--- .../core/internal/analytics/GenericEvents.kt | 2 - 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/action-core/build.gradle b/action-core/build.gradle index 34d6d806e5..663577ac5b 100644 --- a/action-core/build.gradle +++ b/action-core/build.gradle @@ -50,6 +50,7 @@ dependencies { testImplementation project(':test-core') testImplementation project(':twint') testImplementation project(':wechatpay') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.mockito diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt index 3c7927cb05..9a7013abd7 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt @@ -34,6 +34,7 @@ import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -45,6 +46,7 @@ import com.adyen.checkout.core.internal.util.LocaleProvider class GenericActionComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -59,7 +61,11 @@ constructor( key: String? ): GenericActionComponent { val genericActionFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> - val genericActionDelegate = getDelegate(checkoutConfiguration, savedStateHandle, application) + val genericActionDelegate = getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) GenericActionComponent( genericActionDelegate = genericActionDelegate, actionComponentEventHandler = DefaultActionComponentEventHandler(), @@ -76,7 +82,7 @@ constructor( override fun getDelegate( checkoutConfiguration: CheckoutConfiguration, savedStateHandle: SavedStateHandle, - application: Application + application: Application, ): GenericActionDelegate { val componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = checkoutConfiguration, @@ -91,6 +97,7 @@ constructor( checkoutConfiguration = checkoutConfiguration, componentParams = componentParams, actionDelegateProvider = ActionDelegateProvider(dropInOverrideParams), + analyticsManager = analyticsManager, application = application, ) } diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 65c5e92a03..830627d17d 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -22,6 +22,8 @@ import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PermissionRequestData +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.ui.ActionDelegate @@ -55,6 +57,7 @@ internal class DefaultGenericActionDelegate( private val checkoutConfiguration: CheckoutConfiguration, override val componentParams: GenericComponentParams, private val actionDelegateProvider: ActionDelegateProvider, + private val analyticsManager: AnalyticsManager?, private val application: Application, ) : GenericActionDelegate, SavedStateHandleContainer { @@ -127,6 +130,12 @@ internal class DefaultGenericActionDelegate( createDelegateAndObserve(action) } + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + delegate.handleAction(action, activity) } diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt index 18ea8a1414..4148f98537 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt @@ -23,6 +23,8 @@ import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.internal.ActionObserverRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -38,6 +40,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock @@ -53,11 +56,14 @@ internal class DefaultGenericActionDelegateTest( @Mock private val activity: Activity, @Mock private val actionDelegateProvider: ActionDelegateProvider, ) { + + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var genericActionDelegate: DefaultGenericActionDelegate private lateinit var testDelegate: TestActionDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() genericActionDelegate = createDelegate() whenever(activity.application) doReturn Application() @@ -220,6 +226,27 @@ internal class DefaultGenericActionDelegateTest( assertTrue(adyen3DS2Delegate.handleActionCalled) } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = RedirectAction( + paymentMethodType = "redirect", + type = "redirect_type", + ) + + genericActionDelegate.handleAction(action, activity) + + val expectedEvent = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + @Test fun `when process died during handling action, then handleIntent should restore state and continue`() { val savedStateHandle = SavedStateHandle().apply { @@ -243,13 +270,14 @@ internal class DefaultGenericActionDelegateTest( ) return DefaultGenericActionDelegate( - ActionObserverRepository(), - savedStateHandle, - configuration, - GenericComponentParamsMapper(CommonComponentParamsMapper()) + observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, + checkoutConfiguration = configuration, + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), - actionDelegateProvider, - Application(), + actionDelegateProvider = actionDelegateProvider, + analyticsManager = analyticsManager, + application = Application(), ) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index fa3f5cbeda..39490486f1 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -119,11 +119,9 @@ object GenericEvents { fun action( component: String, subType: String, - message: String, ) = AnalyticsEvent.Log( component = component, type = AnalyticsEvent.Log.Type.ACTION, subType = subType, - message = message, ) } From ba60c92832ba21a755c0c382ee26c7f97cfc5a63 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 26 Mar 2024 09:52:59 +0100 Subject: [PATCH 231/272] Pass analytics manager to GenericActionComponentProvider from payment method component providers COAND-845 --- .../ACHDirectDebitComponentProvider.kt | 2 +- .../BacsDirectDebitComponentProvider.kt | 22 +++++----- .../provider/BcmcComponentProvider.kt | 22 +++++----- .../provider/BlikComponentProvider.kt | 44 ++++++++++--------- .../provider/BoletoComponentProvider.kt | 22 +++++----- .../provider/CardComponentProvider.kt | 44 ++++++++++--------- .../provider/CashAppPayComponentProvider.kt | 2 +- .../ui/ActionComponentDialogFragment.kt | 3 +- .../provider/EContextComponentProvider.kt | 22 +++++----- .../provider/GiftCardComponentProvider.kt | 22 +++++----- .../provider/GooglePayComponentProvider.kt | 22 +++++----- .../InstantPaymentComponentProvider.kt | 22 +++++----- .../provider/IssuerListComponentProvider.kt | 22 +++++----- .../provider/MBWayComponentProvider.kt | 22 +++++----- .../OnlineBankingComponentProvider.kt | 22 +++++----- .../provider/PayByBankComponentProvider.kt | 22 +++++----- .../provider/SepaComponentProvider.kt | 22 +++++----- .../internal/provider/UPIComponentProvider.kt | 22 +++++----- 18 files changed, 208 insertions(+), 173 deletions(-) diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt index 1d7c6c7c84..f4ea620536 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/provider/ACHDirectDebitComponentProvider.kt @@ -484,7 +484,7 @@ constructor( delegate: ACHDirectDebitDelegate, componentEventHandler: ComponentEventHandler, ): ACHDirectDebitComponent { - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( + val genericActionDelegate = GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( checkoutConfiguration = checkoutConfiguration, savedStateHandle = savedStateHandle, application = application, diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt index 6be4c43889..02ee7debd1 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/provider/BacsDirectDebitComponentProvider.kt @@ -109,11 +109,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) BacsDirectDebitComponent( bacsDelegate = bacsDelegate, @@ -196,11 +197,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt index f6ae0dbd0f..3834bf4b89 100644 --- a/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt +++ b/bcmc/src/main/java/com/adyen/checkout/bcmc/internal/provider/BcmcComponentProvider.kt @@ -140,11 +140,12 @@ constructor( ), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) BcmcComponent( cardDelegate = cardDelegate, @@ -244,11 +245,12 @@ constructor( ), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt index 75e047b0d2..562e2fc2a2 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/provider/BlikComponentProvider.kt @@ -126,11 +126,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) BlikComponent( blikDelegate = blikDelegate, @@ -210,11 +211,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) BlikComponent( blikDelegate = blikDelegate, @@ -297,11 +299,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, @@ -403,11 +406,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt index 51cb876156..e1d2261e2e 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/provider/BoletoComponentProvider.kt @@ -115,11 +115,12 @@ constructor( addressRepository = addressRepository, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) BoletoComponent( boletoDelegate = boletoDelegate, @@ -204,11 +205,12 @@ constructor( addressRepository = addressRepository, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt index e5d06843c7..fce090c372 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/provider/CardComponentProvider.kt @@ -160,11 +160,12 @@ constructor( ), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) CardComponent( cardDelegate = cardDelegate, @@ -269,11 +270,12 @@ constructor( ), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, @@ -381,11 +383,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) CardComponent( cardDelegate = cardDelegate, @@ -476,11 +479,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt index 21805c02fb..d076d24dce 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/provider/CashAppPayComponentProvider.kt @@ -440,7 +440,7 @@ constructor( delegate: CashAppPayDelegate, componentEventHandler: ComponentEventHandler, ): CashAppPayComponent { - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( + val genericActionDelegate = GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( checkoutConfiguration = checkoutConfiguration, savedStateHandle = savedStateHandle, application = application, diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt index 732f48dee9..c26d8c4af5 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/ActionComponentDialogFragment.kt @@ -94,8 +94,9 @@ internal class ActionComponentDialogFragment : binding.header.isVisible = false try { + val analyticsManager = dropInViewModel.analyticsManager val dropInOverrideParams = dropInViewModel.getDropInOverrideParams() - actionComponent = GenericActionComponentProvider(dropInOverrideParams).get( + actionComponent = GenericActionComponentProvider(analyticsManager, dropInOverrideParams).get( fragment = this, checkoutConfiguration = checkoutConfiguration, callback = this, diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt index 0aa660187b..cd384bb5d7 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/provider/EContextComponentProvider.kt @@ -116,11 +116,12 @@ constructor( componentStateFactory = ::createComponentState, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) createComponent( delegate = eContextDelegate, @@ -204,11 +205,12 @@ constructor( componentStateFactory = ::createComponentState, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt index 77dddd2a62..68df28ecdb 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/provider/GiftCardComponentProvider.kt @@ -116,11 +116,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) GiftCardComponent( giftCardDelegate = giftCardDelegate, @@ -206,11 +207,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt index bc915cb12e..be68f24472 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/provider/GooglePayComponentProvider.kt @@ -116,11 +116,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) GooglePayComponent( googlePayDelegate = googlePayDelegate, @@ -202,11 +203,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt index 3ed2f8f6d2..a62802107c 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/provider/InstantPaymentComponentProvider.kt @@ -107,11 +107,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) InstantPaymentComponent( instantPaymentDelegate = instantPaymentDelegate, @@ -193,11 +194,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt index ef8c497fb7..56e1ea09fe 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/provider/IssuerListComponentProvider.kt @@ -117,11 +117,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) createComponent( issuerListDelegate, @@ -203,11 +204,12 @@ constructor( analyticsManager = analyticsManager, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt index 8b4d812c87..74e0e524b9 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/provider/MBWayComponentProvider.kt @@ -109,11 +109,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) MBWayComponent( mbWayDelegate = mbWayDelegate, @@ -196,11 +197,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt index a35d80713a..5d4db5912f 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/provider/OnlineBankingComponentProvider.kt @@ -119,11 +119,12 @@ constructor( componentStateFactory = ::createComponentState, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) createComponent( delegate = onlineBankingDelegate, @@ -212,11 +213,12 @@ constructor( componentStateFactory = ::createComponentState, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt index f4cc1d56a7..30efc33ebe 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/provider/PayByBankComponentProvider.kt @@ -107,11 +107,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) PayByBankComponent( payByBankDelegate = payByBankDelegate, @@ -193,11 +194,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt index 6264a60df6..4c03c9b607 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/provider/SepaComponentProvider.kt @@ -110,11 +110,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) SepaComponent( sepaDelegate = sepaDelegate, @@ -197,11 +198,12 @@ constructor( submitHandler = SubmitHandler(savedStateHandle), ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt index 477e593bad..6f9acdc20b 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/provider/UPIComponentProvider.kt @@ -104,11 +104,12 @@ constructor( componentParams = componentParams, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) UPIComponent( upiDelegate = upiDelegate, @@ -191,11 +192,12 @@ constructor( componentParams = componentParams, ) - val genericActionDelegate = GenericActionComponentProvider(dropInOverrideParams).getDelegate( - checkoutConfiguration = checkoutConfiguration, - savedStateHandle = savedStateHandle, - application = application, - ) + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( savedStateHandle = savedStateHandle, From ac76eedc0a123a00cbd971abcd3ef1b2cdd6d06c Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 09:19:30 +0200 Subject: [PATCH 232/272] Add analyticsManager to ActionDelegateProvider COAND-845 --- .../internal/provider/GenericActionComponentProvider.kt | 2 +- .../action/core/internal/ui/ActionDelegateProvider.kt | 2 ++ .../core/internal/ui/ActionDelegateProviderTest.kt | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt index 9a7013abd7..3b4810976b 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt @@ -96,7 +96,7 @@ constructor( savedStateHandle = savedStateHandle, checkoutConfiguration = checkoutConfiguration, componentParams = componentParams, - actionDelegateProvider = ActionDelegateProvider(dropInOverrideParams), + actionDelegateProvider = ActionDelegateProvider(analyticsManager, dropInOverrideParams), analyticsManager = analyticsManager, application = application, ) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index b4710b77a7..aedc77b872 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.action.QrCodeAction import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.VoucherAction +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -33,6 +34,7 @@ import com.adyen.checkout.voucher.internal.provider.VoucherComponentProvider import com.adyen.checkout.wechatpay.internal.provider.WeChatPayActionComponentProvider internal class ActionDelegateProvider( + private val analyticsManager: AnalyticsManager?, private val dropInOverrideParams: DropInOverrideParams?, private val localeProvider: LocaleProvider = LocaleProvider(), ) { diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt index fbdfdf0124..de43aaea6b 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProviderTest.kt @@ -18,6 +18,7 @@ import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.action.WeChatPaySdkData +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.ActionDelegate import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.CheckoutException @@ -47,12 +48,18 @@ internal class ActionDelegateProviderTest( @Mock private val localeProvider: LocaleProvider ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var actionDelegateProvider: ActionDelegateProvider @BeforeEach fun setup() { whenever(localeProvider.getLocale(any())) doReturn Locale.US - actionDelegateProvider = ActionDelegateProvider(null, localeProvider) + analyticsManager = TestAnalyticsManager() + actionDelegateProvider = ActionDelegateProvider( + analyticsManager = analyticsManager, + dropInOverrideParams = null, + localeProvider = localeProvider, + ) } @ParameterizedTest From ea0380ec66cb5d476a5232d975d73cf66a848cbc Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 11:51:38 +0200 Subject: [PATCH 233/272] Track action event for DefaultAdyen3DS2Delegate COAND-845 --- 3ds2/build.gradle | 1 + .../provider/Adyen3DS2ComponentProvider.kt | 3 + .../internal/ui/DefaultAdyen3DS2Delegate.kt | 55 ++++++++++-- .../ui/DefaultAdyen3DS2DelegateTest.kt | 89 +++++++++++++++++++ .../internal/ui/ActionDelegateProvider.kt | 2 +- .../core/internal/analytics/GenericEvents.kt | 2 + 6 files changed, 142 insertions(+), 10 deletions(-) diff --git a/3ds2/build.gradle b/3ds2/build.gradle index a034923583..2eadbd63a4 100644 --- a/3ds2/build.gradle +++ b/3ds2/build.gradle @@ -45,6 +45,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.mockito diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt index 3e708b9b89..b8aed566e4 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/provider/Adyen3DS2ComponentProvider.kt @@ -33,6 +33,7 @@ import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -47,6 +48,7 @@ import kotlinx.coroutines.Dispatchers class Adyen3DS2ComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -105,6 +107,7 @@ constructor( threeDS2Service = ThreeDS2Service.INSTANCE, coroutineDispatcher = Dispatchers.Default, application = application, + analyticsManager = analyticsManager, ) } diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index 28135fc16b..b45173fb7b 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -34,6 +34,8 @@ import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -80,6 +82,7 @@ internal class DefaultAdyen3DS2Delegate( private val threeDS2Service: ThreeDS2Service, private val coroutineDispatcher: CoroutineDispatcher, private val application: Application, + private val analyticsManager: AnalyticsManager?, ) : Adyen3DS2Delegate, ChallengeStatusHandler, SavedStateHandleContainer { private val detailsChannel: Channel = bufferedChannel() @@ -146,6 +149,9 @@ internal class DefaultAdyen3DS2Delegate( emitError(ComponentException("Fingerprint token not found.")) return } + + trackFingerprintActionEvent(action) + identifyShopper( activity = activity, encodedFingerprintToken = action.token.orEmpty(), @@ -161,6 +167,9 @@ internal class DefaultAdyen3DS2Delegate( emitError(ComponentException("Challenge token not found.")) return } + + trackChallengeActionEvent(action) + challengeShopper(activity, action.token.orEmpty()) } @@ -177,22 +186,31 @@ internal class DefaultAdyen3DS2Delegate( return } val subtype = Threeds2Action.SubType.parse(action.subtype.orEmpty()) - handleActionSubtype(activity, subtype, action.token.orEmpty()) + handleThreeds2ActionSubtype(action, activity, subtype) } - private fun handleActionSubtype( + private fun handleThreeds2ActionSubtype( + action: Threeds2Action, activity: Activity, subtype: Threeds2Action.SubType, - token: String, ) { + val token = action.token.orEmpty() when (subtype) { - Threeds2Action.SubType.FINGERPRINT -> identifyShopper( - activity = activity, - encodedFingerprintToken = token, - submitFingerprintAutomatically = true, - ) + Threeds2Action.SubType.FINGERPRINT -> { + trackFingerprintActionEvent(action) + + identifyShopper( + activity = activity, + encodedFingerprintToken = token, + submitFingerprintAutomatically = true, + ) + } - Threeds2Action.SubType.CHALLENGE -> challengeShopper(activity, token) + Threeds2Action.SubType.CHALLENGE -> { + trackChallengeActionEvent(action) + + challengeShopper(activity, token) + } } } @@ -492,6 +510,19 @@ internal class DefaultAdyen3DS2Delegate( } } + private fun trackFingerprintActionEvent(action: Action) = trackActionEvent(action, ANALYTICS_MESSAGE_FINGERPRINT) + + private fun trackChallengeActionEvent(action: Action) = trackActionEvent(action, ANALYTICS_MESSAGE_CHALLENGE) + + private fun trackActionEvent(action: Action, message: String) { + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + message = message, + ) + analyticsManager?.trackEvent(event) + } + private fun closeTransaction() { currentTransaction?.close() currentTransaction = null @@ -558,6 +589,12 @@ internal class DefaultAdyen3DS2Delegate( } companion object { + @VisibleForTesting + internal const val ANALYTICS_MESSAGE_FINGERPRINT = "Fingerprint action was handled by the SDK" + + @VisibleForTesting + internal const val ANALYTICS_MESSAGE_CHALLENGE = "Challenge action was handled by the SDK" + private const val DEFAULT_CHALLENGE_TIME_OUT = 10 private const val PROTOCOL_VERSION_2_1_0 = "2.1.0" diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt index e1cf4e4747..d40bc8f158 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt @@ -29,6 +29,8 @@ import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.core.exception.ComponentException @@ -85,6 +87,7 @@ internal class DefaultAdyen3DS2DelegateTest( @Mock private val submitFingerprintRepository: SubmitFingerprintRepository, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var redirectHandler: TestRedirectHandler private lateinit var delegate: DefaultAdyen3DS2Delegate private lateinit var paymentDataRepository: PaymentDataRepository @@ -93,6 +96,7 @@ internal class DefaultAdyen3DS2DelegateTest( @BeforeEach fun setup() { + analyticsManager = TestAnalyticsManager() redirectHandler = TestRedirectHandler() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) delegate = createDelegate() @@ -117,6 +121,7 @@ internal class DefaultAdyen3DS2DelegateTest( threeDS2Service = threeDS2Service, coroutineDispatcher = UnconfinedTestDispatcher(), application = Application(), + analyticsManager = analyticsManager, ) } @@ -524,6 +529,88 @@ internal class DefaultAdyen3DS2DelegateTest( } } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called for Threeds2FingerprintAction, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = Threeds2FingerprintAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + token = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_FINGERPRINT, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when handleAction is called for Threeds2ChallengeAction, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = Threeds2ChallengeAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + token = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_CHALLENGE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when handleAction is called for Threeds2Action and subType is fingerprint, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = Threeds2Action( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + subtype = Threeds2Action.SubType.FINGERPRINT.value, + token = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_FINGERPRINT, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when handleAction is called for Threeds2Action and subType is challenge, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = Threeds2Action( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + subtype = Threeds2Action.SubType.CHALLENGE.value, + token = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_CHALLENGE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + @Test fun `when details are emitted, then state is cleared`() = runTest { val savedStateHandle = SavedStateHandle().apply { @@ -575,6 +662,8 @@ internal class DefaultAdyen3DS2DelegateTest( companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_ACTION_TYPE" private val TEST_FINGERPRINT_TOKEN = """ { diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index aedc77b872..d808f80f5b 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -49,7 +49,7 @@ internal class ActionDelegateProvider( is AwaitAction -> AwaitComponentProvider(dropInOverrideParams, localeProvider) is QrCodeAction -> QRCodeComponentProvider(dropInOverrideParams, localeProvider) is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) - is BaseThreeds2Action -> Adyen3DS2ComponentProvider(dropInOverrideParams, localeProvider) + is BaseThreeds2Action -> Adyen3DS2ComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) is SdkAction<*> -> getSdkActionComponentProvider(action) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index 39490486f1..83703c4161 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -119,9 +119,11 @@ object GenericEvents { fun action( component: String, subType: String, + message: String? = null ) = AnalyticsEvent.Log( component = component, type = AnalyticsEvent.Log.Type.ACTION, subType = subType, + message = message, ) } From f33110b818976db6647d0fb4991c3ae74babab2f Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 12:02:36 +0200 Subject: [PATCH 234/272] Track action event for DefaultAwaitDelegate COAND-845 --- .../internal/ui/ActionDelegateProvider.kt | 2 +- await/build.gradle | 1 + .../provider/AwaitComponentProvider.kt | 3 + .../await/internal/ui/DefaultAwaitDelegate.kt | 10 +++ .../internal/ui/DefaultAwaitDelegateTest.kt | 69 +++++++++++++++++-- 5 files changed, 77 insertions(+), 8 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index d808f80f5b..37f21a103c 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -46,7 +46,7 @@ internal class ActionDelegateProvider( application: Application, ): ActionDelegate { val provider = when (action) { - is AwaitAction -> AwaitComponentProvider(dropInOverrideParams, localeProvider) + is AwaitAction -> AwaitComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is QrCodeAction -> QRCodeComponentProvider(dropInOverrideParams, localeProvider) is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) diff --git a/await/build.gradle b/await/build.gradle index 6354a30f92..be411de9a4 100644 --- a/await/build.gradle +++ b/await/build.gradle @@ -41,6 +41,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.mockito diff --git a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt index 3cb471b4c8..5ac0c8f15b 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/provider/AwaitComponentProvider.kt @@ -28,6 +28,7 @@ import com.adyen.checkout.components.core.action.AwaitAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.DefaultStatusRepository import com.adyen.checkout.components.core.internal.data.api.StatusService import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider @@ -42,6 +43,7 @@ import com.adyen.checkout.core.internal.util.LocaleProvider class AwaitComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -94,6 +96,7 @@ constructor( componentParams = componentParams, statusRepository = statusRepository, paymentDataRepository = paymentDataRepository, + analyticsManager = analyticsManager, ) } diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index 8df3b643aa..2176c47cfd 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -53,6 +55,7 @@ internal class DefaultAwaitDelegate( override val componentParams: GenericComponentParams, private val statusRepository: StatusRepository, private val paymentDataRepository: PaymentDataRepository, + private val analyticsManager: AnalyticsManager?, ) : AwaitDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) @@ -133,6 +136,13 @@ internal class DefaultAwaitDelegate( return } createOutputData(null, action) + + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + startStatusPolling(paymentData, action) } diff --git a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt index 3385429260..44c66da26a 100644 --- a/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt +++ b/await/src/test/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegateTest.kt @@ -15,6 +15,8 @@ import com.adyen.checkout.components.core.action.AwaitAction import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.test.TestStatusRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -33,6 +35,7 @@ import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import java.io.IOException @@ -42,12 +45,14 @@ import java.util.Locale @ExtendWith(LoggingExtension::class) internal class DefaultAwaitDelegateTest { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var statusRepository: TestStatusRepository private lateinit var paymentDataRepository: PaymentDataRepository private lateinit var delegate: DefaultAwaitDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() statusRepository = TestStatusRepository() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) delegate = createDelegate() @@ -62,18 +67,24 @@ internal class DefaultAwaitDelegateTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val outputDataFlow = delegate.outputDataFlow.test(testScheduler) - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + AwaitAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) // We skip the first output data value as it's the initial value with(outputDataFlow.values[1]) { assertFalse(isValid) - assertEquals("test", paymentMethodType) + assertEquals(TEST_PAYMENT_METHOD_TYPE, paymentMethodType) } with(outputDataFlow.values[2]) { assertTrue(isValid) - assertEquals("test", paymentMethodType) + assertEquals(TEST_PAYMENT_METHOD_TYPE, paymentMethodType) } } @@ -85,7 +96,13 @@ internal class DefaultAwaitDelegateTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val detailsFlow = delegate.detailsFlow.test(testScheduler) - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + AwaitAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) val expectedDetails = JSONObject().apply { put(DefaultAwaitDelegate.PAYLOAD_DETAILS_KEY, "testpayload") @@ -93,7 +110,7 @@ internal class DefaultAwaitDelegateTest { with(detailsFlow.latestValue) { assertEquals(expectedDetails.toString(), details.toString()) - assertEquals("paymentData", paymentData) + assertEquals(TEST_PAYMENT_DATA, paymentData) } } @@ -104,7 +121,13 @@ internal class DefaultAwaitDelegateTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + AwaitAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) assertEquals(error, exceptionFlow.latestValue.cause) } @@ -117,7 +140,13 @@ internal class DefaultAwaitDelegateTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(AwaitAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + AwaitAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) assertTrue(exceptionFlow.latestValue is ComponentException) assertEquals("Payment was not completed. - finished", exceptionFlow.latestValue.message) @@ -186,6 +215,28 @@ internal class DefaultAwaitDelegateTest { assertNull(savedStateHandle[DefaultAwaitDelegate.ACTION_KEY]) } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = AwaitAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + paymentData = TEST_PAYMENT_DATA, + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + private fun createDelegate( savedStateHandle: SavedStateHandle = SavedStateHandle() ): DefaultAwaitDelegate { @@ -201,10 +252,14 @@ internal class DefaultAwaitDelegateTest { .mapToParams(configuration, Locale.US, null, null), statusRepository = statusRepository, paymentDataRepository = paymentDataRepository, + analyticsManager = analyticsManager, ) } companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" } } From 64e63239c72a737433f1bfb08bf3ae2b38f7660c Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 12:38:17 +0200 Subject: [PATCH 235/272] Track action event for DefaultQRCodeDelegate COAND-845 --- .../internal/ui/ActionDelegateProvider.kt | 2 +- qr-code/build.gradle | 1 + .../provider/QRCodeComponentProvider.kt | 3 + .../internal/ui/DefaultQRCodeDelegate.kt | 11 ++- .../internal/ui/DefaultQRCodeDelegateTest.kt | 72 +++++++++++++++---- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index 37f21a103c..165aa6fe50 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -47,7 +47,7 @@ internal class ActionDelegateProvider( ): ActionDelegate { val provider = when (action) { is AwaitAction -> AwaitComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) - is QrCodeAction -> QRCodeComponentProvider(dropInOverrideParams, localeProvider) + is QrCodeAction -> QRCodeComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) diff --git a/qr-code/build.gradle b/qr-code/build.gradle index a46d987823..952af14e57 100644 --- a/qr-code/build.gradle +++ b/qr-code/build.gradle @@ -48,6 +48,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt index 6823c811b5..be479c309b 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/provider/QRCodeComponentProvider.kt @@ -22,6 +22,7 @@ import com.adyen.checkout.components.core.action.QrCodeAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.DefaultStatusRepository import com.adyen.checkout.components.core.internal.data.api.StatusService import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider @@ -44,6 +45,7 @@ import com.adyen.checkout.ui.core.internal.util.ImageSaver class QRCodeComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -100,6 +102,7 @@ constructor( redirectHandler = redirectHandler, paymentDataRepository = paymentDataRepository, imageSaver = ImageSaver(), + analyticsManager = analyticsManager, ) } diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index ed02a1f9fa..6e61bd9d33 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -25,6 +25,8 @@ import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.PermissionRequestData import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -70,7 +72,8 @@ internal class DefaultQRCodeDelegate( private val statusCountDownTimer: QRCodeCountDownTimer, private val redirectHandler: RedirectHandler, private val paymentDataRepository: PaymentDataRepository, - private val imageSaver: ImageSaver + private val imageSaver: ImageSaver, + private val analyticsManager: AnalyticsManager?, ) : QRCodeDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) @@ -163,6 +166,12 @@ internal class DefaultQRCodeDelegate( this.action = action paymentDataRepository.paymentData = action.paymentData + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + launchAction(action, activity) initState(action) } diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 1208869157..1a0c2044ad 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.action.QrCodeAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.test.TestStatusRepository @@ -76,6 +78,7 @@ internal class DefaultQRCodeDelegateTest( @Mock private val imageSaver: ImageSaver ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var redirectHandler: TestRedirectHandler private lateinit var statusRepository: TestStatusRepository private lateinit var paymentDataRepository: PaymentDataRepository @@ -83,6 +86,7 @@ internal class DefaultQRCodeDelegateTest( @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() statusRepository = TestStatusRepository() redirectHandler = TestRedirectHandler() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) @@ -190,7 +194,7 @@ internal class DefaultQRCodeDelegateTest( QrCodeAction( paymentMethodType = PaymentMethodTypes.PIX, qrCodeData = "qrData", - paymentData = "paymentData", + paymentData = TEST_PAYMENT_DATA, ), Activity(), ) @@ -217,7 +221,7 @@ internal class DefaultQRCodeDelegateTest( val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -235,7 +239,7 @@ internal class DefaultQRCodeDelegateTest( val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -251,7 +255,7 @@ internal class DefaultQRCodeDelegateTest( val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -266,7 +270,7 @@ internal class DefaultQRCodeDelegateTest( assertNull(viewFlow.latestValue) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PIX, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -286,7 +290,7 @@ internal class DefaultQRCodeDelegateTest( QrCodeAction( paymentMethodType = PaymentMethodTypes.PAY_NOW, qrCodeData = "qrData", - paymentData = "paymentData", + paymentData = TEST_PAYMENT_DATA, ), Activity(), ) @@ -313,7 +317,7 @@ internal class DefaultQRCodeDelegateTest( val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -332,7 +336,7 @@ internal class DefaultQRCodeDelegateTest( val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -347,7 +351,7 @@ internal class DefaultQRCodeDelegateTest( val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -362,7 +366,7 @@ internal class DefaultQRCodeDelegateTest( assertNull(viewFlow.latestValue) delegate.handleAction( - QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = "paymentData"), + QrCodeAction(paymentMethodType = PaymentMethodTypes.PAY_NOW, paymentData = TEST_PAYMENT_DATA), Activity(), ) @@ -380,7 +384,13 @@ internal class DefaultQRCodeDelegateTest( redirectHandler.exception = error val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + QrCodeAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) assertEquals(error, exceptionFlow.latestValue) } @@ -389,7 +399,13 @@ internal class DefaultQRCodeDelegateTest( fun `handleAction is called with valid data, then no error is propagated`() = runTest { val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(QrCodeAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + QrCodeAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + paymentData = TEST_PAYMENT_DATA, + ), + Activity(), + ) assertTrue(exceptionFlow.values.isEmpty()) } @@ -408,12 +424,12 @@ internal class DefaultQRCodeDelegateTest( @Test fun `handleIntent is called with valid data, then the details are emitted`() = runTest { val detailsFlow = delegate.detailsFlow.test(testScheduler) - delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) + delegate.handleAction(QrCodeAction(paymentData = TEST_PAYMENT_DATA), Activity()) delegate.handleIntent(Intent()) with(detailsFlow.latestValue) { assertEquals(TestRedirectHandler.REDIRECT_RESULT, details) - assertEquals("paymentData", paymentData) + assertEquals(TEST_PAYMENT_DATA, paymentData) } } @@ -423,7 +439,7 @@ internal class DefaultQRCodeDelegateTest( assertNull(viewFlow.latestValue) - delegate.handleAction(QrCodeAction(paymentData = "paymentData"), Activity()) + delegate.handleAction(QrCodeAction(paymentData = TEST_PAYMENT_DATA), Activity()) assertEquals(QrCodeComponentViewType.REDIRECT, viewFlow.latestValue) } @@ -599,6 +615,28 @@ internal class DefaultQRCodeDelegateTest( verify(redirectHandler).removeOnRedirectListener() } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = QrCodeAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + paymentData = TEST_PAYMENT_DATA, + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + private fun createTestAction( type: String = "test", paymentData: String = "paymentData", @@ -629,9 +667,13 @@ internal class DefaultQRCodeDelegateTest( redirectHandler = redirectHandler, paymentDataRepository = paymentDataRepository, imageSaver = imageSaver, + analyticsManager = analyticsManager, ) companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" } } From a43f96dd19ec0df58a3b2510e8d3066af43b1e7c Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 12:43:51 +0200 Subject: [PATCH 236/272] Track action event for DefaultRedirectDelegate COAND-845 --- .../internal/ui/ActionDelegateProvider.kt | 2 +- redirect/build.gradle | 1 + .../provider/RedirectComponentProvider.kt | 3 ++ .../internal/ui/DefaultRedirectDelegate.kt | 9 ++++++ .../ui/DefaultRedirectDelegateTest.kt | 31 +++++++++++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index 165aa6fe50..7372f8d025 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -48,7 +48,7 @@ internal class ActionDelegateProvider( val provider = when (action) { is AwaitAction -> AwaitComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is QrCodeAction -> QRCodeComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) - is RedirectAction -> RedirectComponentProvider(dropInOverrideParams, localeProvider) + is RedirectAction -> RedirectComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) is SdkAction<*> -> getSdkActionComponentProvider(action) diff --git a/redirect/build.gradle b/redirect/build.gradle index 4345164b38..df0dbfc9c2 100644 --- a/redirect/build.gradle +++ b/redirect/build.gradle @@ -45,6 +45,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt index f0cac0d3af..8b3960c0ac 100644 --- a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt +++ b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt @@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -42,6 +43,7 @@ import com.adyen.checkout.ui.core.internal.DefaultRedirectHandler class RedirectComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -93,6 +95,7 @@ constructor( redirectHandler = redirectHandler, paymentDataRepository = paymentDataRepository, nativeRedirectService = nativeRedirectService, + analyticsManager = analyticsManager, ) } 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 1e7c554bab..ffff2f8ca8 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 @@ -18,6 +18,8 @@ import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel @@ -46,6 +48,7 @@ internal class DefaultRedirectDelegate( private val redirectHandler: RedirectHandler, private val paymentDataRepository: PaymentDataRepository, private val nativeRedirectService: NativeRedirectService, + private val analyticsManager: AnalyticsManager?, ) : RedirectDelegate { private val detailsChannel: Channel = bufferedChannel() @@ -88,6 +91,12 @@ internal class DefaultRedirectDelegate( return } + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + when (action.type) { ActionTypes.NATIVE_REDIRECT -> { paymentDataRepository.nativeRedirectData = action.nativeRedirectData diff --git a/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt b/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt index 212d6f1568..8ed15a008e 100644 --- a/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt +++ b/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt @@ -17,6 +17,8 @@ import com.adyen.checkout.components.core.action.ActionTypes import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -27,11 +29,14 @@ import com.adyen.checkout.redirect.internal.data.api.NativeRedirectService import com.adyen.checkout.redirect.internal.data.model.NativeRedirectResponse import com.adyen.checkout.redirect.redirect import com.adyen.checkout.ui.core.internal.test.TestRedirectHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest @@ -53,12 +58,14 @@ internal class DefaultRedirectDelegateTest( @Mock private val nativeRedirectService: NativeRedirectService, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var redirectHandler: TestRedirectHandler private lateinit var paymentDataRepository: PaymentDataRepository private lateinit var delegate: DefaultRedirectDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() redirectHandler = TestRedirectHandler() paymentDataRepository = PaymentDataRepository(SavedStateHandle()) redirectHandler = TestRedirectHandler() @@ -173,6 +180,27 @@ internal class DefaultRedirectDelegateTest( redirectHandler.assertRemoveOnRedirectListenerCalled() } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = RedirectAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + private fun createDelegate( observerRepository: ActionObserverRepository = ActionObserverRepository() ): DefaultRedirectDelegate { @@ -193,11 +221,14 @@ internal class DefaultRedirectDelegateTest( redirectHandler = redirectHandler, paymentDataRepository = paymentDataRepository, nativeRedirectService = nativeRedirectService, + analyticsManager = analyticsManager, ) } companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun errorSource() = listOf( From 3a74c8070f8ff6797f838bbab2a0d3b6e363ce8b Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 12:53:09 +0200 Subject: [PATCH 237/272] Add analyticsManager to ActionDelegateProvider COAND-845 --- .../internal/ui/ActionDelegateProvider.kt | 4 +-- voucher/build.gradle | 1 + .../provider/VoucherComponentProvider.kt | 3 ++ .../internal/ui/DefaultVoucherDelegate.kt | 9 ++++++ .../internal/ui/DefaultVoucherDelegateTest.kt | 28 +++++++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index 7372f8d025..baa80a02b8 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -50,9 +50,8 @@ internal class ActionDelegateProvider( is QrCodeAction -> QRCodeComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is RedirectAction -> RedirectComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is BaseThreeds2Action -> Adyen3DS2ComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) - is VoucherAction -> VoucherComponentProvider(dropInOverrideParams, localeProvider) + is VoucherAction -> VoucherComponentProvider(analyticsManager, dropInOverrideParams, localeProvider) is SdkAction<*> -> getSdkActionComponentProvider(action) - else -> throw CheckoutException("Can't find delegate for action: ${action.type}") } @@ -69,6 +68,7 @@ internal class ActionDelegateProvider( return when (action.paymentMethodType) { PaymentMethodTypes.TWINT -> TwintActionComponentProvider(dropInOverrideParams, localeProvider) PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPayActionComponentProvider( + analyticsManager, dropInOverrideParams, localeProvider, ) diff --git a/voucher/build.gradle b/voucher/build.gradle index f61bd7005d..d389d0960e 100644 --- a/voucher/build.gradle +++ b/voucher/build.gradle @@ -49,6 +49,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.mockito diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt index 0b8ff473f1..2ebc489af1 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/provider/VoucherComponentProvider.kt @@ -22,6 +22,7 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -40,6 +41,7 @@ import com.adyen.checkout.voucher.toCheckoutConfiguration class VoucherComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -86,6 +88,7 @@ constructor( componentParams = componentParams, pdfOpener = PdfOpener(), imageSaver = ImageSaver(), + analyticsManager = analyticsManager, ) } diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 616af0409c..6b31b69156 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PermissionRequestData import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.DateUtils import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -53,6 +55,7 @@ internal class DefaultVoucherDelegate( override val componentParams: GenericComponentParams, private val pdfOpener: PdfOpener, private val imageSaver: ImageSaver, + private val analyticsManager: AnalyticsManager?, ) : VoucherDelegate, SavedStateHandleContainer { private val _outputDataFlow = MutableStateFlow(createOutputData()) @@ -127,6 +130,12 @@ internal class DefaultVoucherDelegate( return } + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + _viewFlow.tryEmit(config.viewType) createOutputData(action, config) diff --git a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt index bd95833840..2c0e5de79f 100644 --- a/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt +++ b/voucher/src/test/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegateTest.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.VoucherAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -42,6 +44,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest @@ -67,10 +70,12 @@ internal class DefaultVoucherDelegateTest( @Mock private val imageSaver: ImageSaver, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultVoucherDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createDelegate() } @@ -331,6 +336,27 @@ internal class DefaultVoucherDelegateTest( verify(observerRepository).removeObservers() } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = VoucherAction( + paymentMethodType = PaymentMethodTypes.BACS, + type = TEST_ACTION_TYPE, + ) + + delegate.handleAction(action, activity) + + val expectedEvent = GenericEvents.action( + component = PaymentMethodTypes.BACS, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + private fun createDelegate( savedStateHandle: SavedStateHandle = SavedStateHandle() ): DefaultVoucherDelegate { @@ -345,6 +371,7 @@ internal class DefaultVoucherDelegateTest( .mapToParams(configuration, Locale.US, null, null), pdfOpener = pdfOpener, imageSaver = imageSaver, + analyticsManager = analyticsManager, ) } @@ -361,6 +388,7 @@ internal class DefaultVoucherDelegateTest( companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun viewTypeSource() = listOf( From b9caf543fdfa788f3fb930f38b7cd9743809b262 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 13:24:55 +0200 Subject: [PATCH 238/272] Track action event for DefaultWeChatDelegate COAND-845 --- wechatpay/build.gradle | 1 + .../WeChatPayActionComponentProvider.kt | 3 ++ .../internal/ui/DefaultWeChatDelegate.kt | 9 +++++ .../internal/ui/DefaultWeChatDelegateTest.kt | 38 ++++++++++++++++++- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/wechatpay/build.gradle b/wechatpay/build.gradle index a35ff51ca2..d58779c8f9 100644 --- a/wechatpay/build.gradle +++ b/wechatpay/build.gradle @@ -44,6 +44,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.mockito diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt index 301ea6af84..c53ccbdbb1 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt @@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams @@ -42,6 +43,7 @@ import com.tencent.mm.opensdk.openapi.WXAPIFactory class WeChatPayActionComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -92,6 +94,7 @@ constructor( iwxApi = iwxApi, payRequestGenerator = requestGenerator, paymentDataRepository = paymentDataRepository, + analyticsManager = analyticsManager, ) } diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt index 24877f7192..3180c4a5f0 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt @@ -19,6 +19,8 @@ import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel @@ -46,6 +48,7 @@ internal class DefaultWeChatDelegate( private val iwxApi: IWXAPI, private val payRequestGenerator: WeChatRequestGenerator<*>, private val paymentDataRepository: PaymentDataRepository, + private val analyticsManager: AnalyticsManager?, ) : WeChatDelegate { private val detailsChannel: Channel = bufferedChannel() @@ -134,6 +137,12 @@ internal class DefaultWeChatDelegate( return } + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + val isWeChatNotInitiated = !initiateWeChatPayRedirect(sdkData, activityName) if (isWeChatNotInitiated) { diff --git a/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt b/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt index 376b51cc79..08b1bd1ff0 100644 --- a/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt +++ b/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt @@ -17,6 +17,8 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -25,12 +27,15 @@ import com.adyen.checkout.wechatpay.internal.util.WeChatRequestGenerator import com.adyen.checkout.wechatpay.weChatPayAction import com.tencent.mm.opensdk.modelpay.PayResp import com.tencent.mm.opensdk.openapi.IWXAPI +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock @@ -49,11 +54,13 @@ internal class DefaultWeChatDelegateTest( @Mock private val weChatRequestGenerator: WeChatRequestGenerator<*> ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var paymentDataRepository: PaymentDataRepository private lateinit var delegate: DefaultWeChatDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() val configuration = CheckoutConfiguration( Environment.TEST, TEST_CLIENT_KEY, @@ -68,6 +75,7 @@ internal class DefaultWeChatDelegateTest( iwxApi = iwxApi, payRequestGenerator = weChatRequestGenerator, paymentDataRepository = paymentDataRepository, + analyticsManager = analyticsManager, ) } @@ -88,7 +96,7 @@ internal class DefaultWeChatDelegateTest( val action = SdkAction( sdkData = WeChatPaySdkData(), - paymentData = "paymentData", + paymentData = TEST_PAYMENT_DATA, ) delegate.detailsFlow.test { @@ -101,7 +109,7 @@ internal class DefaultWeChatDelegateTest( with(awaitItem()) { assertEquals(expected.toString(), details.toString()) - assertEquals("paymentData", paymentData) + assertEquals(TEST_PAYMENT_DATA, paymentData) } cancelAndIgnoreRemainingEvents() @@ -156,7 +164,33 @@ internal class DefaultWeChatDelegateTest( } } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = SdkAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + paymentData = TEST_PAYMENT_DATA, + sdkData = WeChatPaySdkData(), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" } } From e51e69f464654eb1eef9a71e0058c018917c4c88 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 4 Apr 2024 13:31:24 +0200 Subject: [PATCH 239/272] Remove action event for DefaultGenericActionDelegate COAND-845 --- .../GenericActionComponentProvider.kt | 1 - .../ui/DefaultGenericActionDelegate.kt | 9 ------- .../ui/DefaultGenericActionDelegateTest.kt | 27 ------------------- 3 files changed, 37 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt index 3b4810976b..3a30f829a3 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/provider/GenericActionComponentProvider.kt @@ -97,7 +97,6 @@ constructor( checkoutConfiguration = checkoutConfiguration, componentParams = componentParams, actionDelegateProvider = ActionDelegateProvider(analyticsManager, dropInOverrideParams), - analyticsManager = analyticsManager, application = application, ) } diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt index 830627d17d..65c5e92a03 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/DefaultGenericActionDelegate.kt @@ -22,8 +22,6 @@ import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PermissionRequestData -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager -import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.ui.ActionDelegate @@ -57,7 +55,6 @@ internal class DefaultGenericActionDelegate( private val checkoutConfiguration: CheckoutConfiguration, override val componentParams: GenericComponentParams, private val actionDelegateProvider: ActionDelegateProvider, - private val analyticsManager: AnalyticsManager?, private val application: Application, ) : GenericActionDelegate, SavedStateHandleContainer { @@ -130,12 +127,6 @@ internal class DefaultGenericActionDelegate( createDelegateAndObserve(action) } - val event = GenericEvents.action( - component = action.paymentMethodType.orEmpty(), - subType = action.type.orEmpty(), - ) - analyticsManager?.trackEvent(event) - delegate.handleAction(action, activity) } diff --git a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt index 4148f98537..a7c2a62f76 100644 --- a/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt +++ b/action-core/src/test/java/com/adyen/checkout/action/core/ui/DefaultGenericActionDelegateTest.kt @@ -23,8 +23,6 @@ import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.action.Threeds2ChallengeAction import com.adyen.checkout.components.core.action.Threeds2FingerprintAction import com.adyen.checkout.components.core.internal.ActionObserverRepository -import com.adyen.checkout.components.core.internal.analytics.GenericEvents -import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -40,7 +38,6 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock @@ -57,13 +54,11 @@ internal class DefaultGenericActionDelegateTest( @Mock private val actionDelegateProvider: ActionDelegateProvider, ) { - private lateinit var analyticsManager: TestAnalyticsManager private lateinit var genericActionDelegate: DefaultGenericActionDelegate private lateinit var testDelegate: TestActionDelegate @BeforeEach fun beforeEach() { - analyticsManager = TestAnalyticsManager() genericActionDelegate = createDelegate() whenever(activity.application) doReturn Application() @@ -226,27 +221,6 @@ internal class DefaultGenericActionDelegateTest( assertTrue(adyen3DS2Delegate.handleActionCalled) } - @Nested - inner class AnalyticsTest { - - @Test - fun `when handleAction is called, then action event is tracked`() { - genericActionDelegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - val action = RedirectAction( - paymentMethodType = "redirect", - type = "redirect_type", - ) - - genericActionDelegate.handleAction(action, activity) - - val expectedEvent = GenericEvents.action( - component = action.paymentMethodType.orEmpty(), - subType = action.type.orEmpty(), - ) - analyticsManager.assertLastEventEquals(expectedEvent) - } - } - @Test fun `when process died during handling action, then handleIntent should restore state and continue`() { val savedStateHandle = SavedStateHandle().apply { @@ -276,7 +250,6 @@ internal class DefaultGenericActionDelegateTest( componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), actionDelegateProvider = actionDelegateProvider, - analyticsManager = analyticsManager, application = Application(), ) } From 839feefe1e6029f0ca20f702ec262313157f858a Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 28 Mar 2024 15:52:59 +0100 Subject: [PATCH 240/272] Track download event when qr code is downloaded COAND-845 diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index d1efa7cbf..c58ab3a1b 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -310,6 +310,12 @@ internal class DefaultQRCodeDelegate( val timestamp = DateUtils.formatDateToString(Calendar.getInstance()) val imageName = String.format(IMAGE_NAME_FORMAT, paymentMethodType, timestamp) + val event = GenericEvents.download( + component = paymentMethodType, + target = "qr_download_button" + ) + analyticsManager?.trackEvent(event) + coroutineScope.launch { imageSaver.saveImageFromUrl( context = context, diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index ba3c19c97..aad9455b9 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -612,6 +612,27 @@ internal class DefaultQRCodeDelegateTest( ) analyticsManager.assertLastEventEquals(expectedEvent) } + + @Test + fun `when downloadQRImage is called, then download event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.handleAction( + QrCodeAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + qrCodeData = TEST_QR_CODE_DATA, + paymentData = TEST_PAYMENT_DATA, + ), + mock(), + ) + + delegate.downloadQRImage(context) + + val expectedEvent = GenericEvents.download( + component = TEST_PAYMENT_METHOD_TYPE, + target = "qr_download_button", + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } } private fun createTestAction( @@ -650,5 +671,6 @@ internal class DefaultQRCodeDelegateTest( private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" + private const val TEST_QR_CODE_DATA = "TEST_QR_CODE_DATA" } } --- .../internal/ui/DefaultQRCodeDelegate.kt | 9 +++++++ .../internal/ui/DefaultQRCodeDelegateTest.kt | 24 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt index 6e61bd9d33..b6069df77f 100644 --- a/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt +++ b/qr-code/src/main/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegate.kt @@ -335,6 +335,12 @@ internal class DefaultQRCodeDelegate( val timestamp = DateUtils.formatDateToString(Calendar.getInstance()) val imageName = String.format(IMAGE_NAME_FORMAT, paymentMethodType, timestamp) + val event = GenericEvents.download( + component = paymentMethodType, + target = ANALYTICS_TARGET_QR_BUTTON + ) + analyticsManager?.trackEvent(event) + coroutineScope.launch { imageSaver.saveImageFromUrl( context = context, @@ -405,6 +411,9 @@ internal class DefaultQRCodeDelegate( private val DEFAULT_MAX_POLLING_DURATION = 15.minutes.inWholeMilliseconds private const val HUNDRED = 100 + @VisibleForTesting + internal const val ANALYTICS_TARGET_QR_BUTTON = "qr_download_button" + private const val IMAGE_NAME_FORMAT = "%s-%s.png" private const val QR_IMAGE_BASE_PATH = "%sbarcode.shtml?barcodeType=qrCode&fileType=png&data=%s" diff --git a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt index 1a0c2044ad..65cccb483d 100644 --- a/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt +++ b/qr-code/src/test/java/com/adyen/checkout/qrcode/internal/ui/DefaultQRCodeDelegateTest.kt @@ -627,7 +627,7 @@ internal class DefaultQRCodeDelegateTest( paymentData = TEST_PAYMENT_DATA, ) - delegate.handleAction(action, Activity()) + delegate.handleAction(action, mock()) val expectedEvent = GenericEvents.action( component = TEST_PAYMENT_METHOD_TYPE, @@ -635,6 +635,27 @@ internal class DefaultQRCodeDelegateTest( ) analyticsManager.assertLastEventEquals(expectedEvent) } + + @Test + fun `when downloadQRImage is called, then download event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.handleAction( + QrCodeAction( + paymentMethodType = PaymentMethodTypes.PIX, + qrCodeData = TEST_QR_CODE_DATA, + paymentData = TEST_PAYMENT_DATA, + ), + mock(), + ) + + delegate.downloadQRImage(context) + + val expectedEvent = GenericEvents.download( + component = PaymentMethodTypes.PIX, + target = DefaultQRCodeDelegate.ANALYTICS_TARGET_QR_BUTTON, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } } private fun createTestAction( @@ -675,5 +696,6 @@ internal class DefaultQRCodeDelegateTest( private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" + private const val TEST_QR_CODE_DATA = "TEST_QR_CODE_DATA" } } From a80bcce7f41d63b70d460e767671630794f95f02 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:06:49 +0100 Subject: [PATCH 241/272] Add assertHasEventEquals method in TestAnalyticsManager to test payment methods, which track more than one event when initializing. COAND-845 --- .../analytics/TestAnalyticsManager.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java index 50f3ee1e90..23887f52c4 100644 --- a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java +++ b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java @@ -15,6 +15,9 @@ import org.mockito.internal.matchers.apachecommons.ReflectionEquals; +import java.util.ArrayList; +import java.util.List; + import kotlinx.coroutines.CoroutineScope; public class TestAnalyticsManager implements AnalyticsManager { @@ -25,6 +28,8 @@ public class TestAnalyticsManager implements AnalyticsManager { private String checkoutAttemptId = null; + private final List events = new ArrayList<>(); + private AnalyticsEvent lastEvent = null; @Override @@ -38,17 +43,28 @@ public void assertIsInitialized() { @Override public void trackEvent(@NonNull AnalyticsEvent event) { + events.add(event); lastEvent = event; } + public void assertHasEventEquals(AnalyticsEvent expected) { + assertTrue(events.stream().anyMatch(event -> + areEventsEqual(expected, event) + )); + } + public void assertLastEventEquals(AnalyticsEvent expected) { + assertTrue(areEventsEqual(expected, lastEvent)); + } + + private boolean areEventsEqual(AnalyticsEvent expected, AnalyticsEvent actual) { ReflectionEquals re = new ReflectionEquals( expected, // Exclude these field as they are generated at runtime "id", "timestamp" ); - assertTrue(re.matches(lastEvent)); + return re.matches(actual); } @Nullable From 6079ffd7da6ef9f28258bce7cd09bcceac477d64 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:15:51 +0100 Subject: [PATCH 242/272] Track events before calling onSubmit for delegates which already track submit event. COAND-845 --- .../adyen/checkout/card/internal/ui/DefaultCardDelegate.kt | 6 +++--- .../adyen/checkout/card/internal/ui/StoredCardDelegate.kt | 6 +++--- .../issuerlist/internal/ui/DefaultIssuerListDelegate.kt | 6 +++--- .../checkout/mbway/internal/ui/DefaultMBWayDelegate.kt | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index 7f27d2deec..9197e66969 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -478,11 +478,11 @@ class DefaultCardDelegate( } override fun onSubmit() { - val state = _componentStateFlow.value - submitHandler.onSubmit(state = state) - val event = GenericEvents.submit(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) + + val state = _componentStateFlow.value + submitHandler.onSubmit(state = state) } override fun startAddressLookup() { diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt index 31d3d2e1d7..1955f79e36 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/StoredCardDelegate.kt @@ -309,11 +309,11 @@ internal class StoredCardDelegate( } override fun onSubmit() { - val state = _componentStateFlow.value - submitHandler.onSubmit(state) - val event = GenericEvents.submit(storedPaymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) + + val state = _componentStateFlow.value + submitHandler.onSubmit(state) } override fun startAddressLookup() = Unit diff --git a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt index 8fdec0895b..c438256fd5 100644 --- a/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt +++ b/issuer-list/src/main/java/com/adyen/checkout/issuerlist/internal/ui/DefaultIssuerListDelegate.kt @@ -122,11 +122,11 @@ internal class DefaultIssuerListDelegate< } override fun onSubmit() { - val state = _componentStateFlow.value - submitHandler.onSubmit(state) - val event = GenericEvents.submit(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) + + val state = _componentStateFlow.value + submitHandler.onSubmit(state) } private fun onInputDataChanged() { diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 8c0632c525..344bbc1d18 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -167,11 +167,11 @@ internal class DefaultMBWayDelegate( } override fun onSubmit() { - val state = _componentStateFlow.value - submitHandler.onSubmit(state) - val event = GenericEvents.submit(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) + + val state = _componentStateFlow.value + submitHandler.onSubmit(state) } override fun isConfirmationRequired(): Boolean = _viewFlow.value is ButtonComponentViewType From ec3e8c378c99e6464f619433d31f4f354bc05931 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:16:57 +0100 Subject: [PATCH 243/272] Improve DefaultMBWayDelegateTest COAND-845 --- .../mbway/internal/ui/DefaultMBWayDelegateTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt index 968d7a52fb..2cc1edbdb4 100644 --- a/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt +++ b/mbway/src/test/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegateTest.kt @@ -13,7 +13,6 @@ import com.adyen.checkout.components.core.Amount import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod -import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager @@ -265,7 +264,7 @@ internal class DefaultMBWayDelegateTest( fun `when delegate is initialized, then render event is tracked`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - val expectedEvent = GenericEvents.rendered(PaymentMethodTypes.MB_WAY) + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) analyticsManager.assertLastEventEquals(expectedEvent) } @@ -273,7 +272,7 @@ internal class DefaultMBWayDelegateTest( fun `when onSubmit is called, then submit event is tracked`() { delegate.onSubmit() - val expectedEvent = GenericEvents.submit(PaymentMethodTypes.MB_WAY) + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) analyticsManager.assertLastEventEquals(expectedEvent) } @@ -312,7 +311,7 @@ internal class DefaultMBWayDelegateTest( configuration: CheckoutConfiguration = createCheckoutConfiguration(), ) = DefaultMBWayDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(type = PaymentMethodTypes.MB_WAY), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = TEST_ORDER, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = configuration, @@ -341,6 +340,7 @@ internal class DefaultMBWayDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 70b7795d760591660b4e35fb13f106ea019554af Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:17:48 +0100 Subject: [PATCH 244/272] Add generic events for StoredCashAppPayDelegate and DefaultCashAppPayDelegate COAND-845 --- cashapppay/build.gradle | 1 + .../internal/ui/DefaultCashAppPayDelegate.kt | 7 ++++ .../internal/ui/StoredCashAppPayDelegate.kt | 10 +++++ .../ui/DefaultCashAppPayDelegateTest.kt | 36 ++++++++++++++---- .../ui/StoredCashAppPayDelegateTest.kt | 38 ++++++++++++++----- 5 files changed, 75 insertions(+), 17 deletions(-) diff --git a/cashapppay/build.gradle b/cashapppay/build.gradle index b368bda3b7..4ce20369b3 100644 --- a/cashapppay/build.gradle +++ b/cashapppay/build.gradle @@ -52,6 +52,7 @@ dependencies { implementation libraries.material testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt index 5dbc29150c..82ff32edcd 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt @@ -34,6 +34,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -113,6 +114,9 @@ constructor( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -191,6 +195,9 @@ constructor( if (isConfirmationRequired()) { initiatePayment() } + + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } private fun initiatePayment() { diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt index 07a39c5715..e2e5c3a0ae 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegate.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -60,10 +61,19 @@ internal class StoredCashAppPayDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered( + component = paymentMethod.type.orEmpty(), + isStoredPaymentMethod = true + ) + analyticsManager.trackEvent(event) } private fun onState(componentState: CashAppPayComponentState) { if (componentState.isValid) { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitChannel.trySend(componentState) } } diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt index 0ba5870dd0..47ffd28410 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt @@ -35,7 +35,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -63,7 +64,6 @@ import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -74,15 +74,16 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultCashAppPayDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsManager: AnalyticsManager, @Mock private val cashAppPayFactory: CashAppPayFactory, @Mock private val cashAppPay: CashAppPay, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultCashAppPayDelegate @BeforeEach fun before() { + analyticsManager = TestAnalyticsManager() whenever(cashAppPayFactory.createSandbox(any())) doReturn cashAppPay whenever(cashAppPayFactory.create(any())) doReturn cashAppPay delegate = createDefaultCashAppPayDelegate() @@ -124,7 +125,7 @@ internal class DefaultCashAppPayDelegateTest( val expected = CashAppPayComponentState( data = PaymentComponentData( paymentMethod = CashAppPayPaymentMethod( - type = null, + type = TEST_PAYMENT_METHOD_TYPE, checkoutAttemptId = null, grantId = "grantId", onFileGrantId = "grantId", @@ -450,12 +451,21 @@ internal class DefaultCashAppPayDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) val testFlow = delegate.componentStateFlow.test(testScheduler) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -470,11 +480,21 @@ internal class DefaultCashAppPayDelegateTest( assertEquals(TEST_CHECKOUT_ATTEMPT_ID, testFlow.latestValue.data.paymentMethod?.checkoutAttemptId) } + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -518,6 +538,7 @@ internal class DefaultCashAppPayDelegateTest( clientId = "clientId", scopeId = TEST_SCOPE_ID, ), + type = TEST_PAYMENT_METHOD_TYPE, ) companion object { @@ -525,6 +546,7 @@ internal class DefaultCashAppPayDelegateTest( private const val TEST_RETURN_URL = "testReturnUrl" private const val TEST_SCOPE_ID = "testScopeId" private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt index 3efdb4d64f..c301b88629 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/StoredCashAppPayDelegateTest.kt @@ -18,7 +18,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.CashAppPayPaymentMethod import com.adyen.checkout.core.Environment @@ -37,23 +38,19 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource -import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.eq -import org.mockito.kotlin.verify import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) -internal class StoredCashAppPayDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, -) { +internal class StoredCashAppPayDelegateTest { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: StoredCashAppPayDelegate @BeforeEach fun before() { + analyticsManager = TestAnalyticsManager() delegate = createStoredCashAppPayDelegate() } @@ -121,13 +118,34 @@ internal class StoredCashAppPayDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered( + component = TEST_PAYMENT_METHOD_TYPE, + isStoredPaymentMethod = true, + ) + analyticsManager.assertHasEventEquals(expectedEvent) + } + + @Test + fun `when component is initialized with valid data, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } From e592d0912b46b001aaac33eb04b92bc7158de92d Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:32:03 +0100 Subject: [PATCH 245/272] Add generic events for StoredACHDirectDebitDelegate and DefaultACHDirectDebitDelegate COAND-845 --- ach/build.gradle | 1 + .../ui/DefaultACHDirectDebitDelegate.kt | 7 +++ .../ui/StoredACHDirectDebitDelegate.kt | 13 +++++ .../ui/DefaultACHDirectDebitDelegateTest.kt | 36 ++++++++++---- .../ui/StoredACHDirectDebitDelegateTest.kt | 48 +++++++++++-------- 5 files changed, 77 insertions(+), 28 deletions(-) diff --git a/ach/build.gradle b/ach/build.gradle index 953cb6ff44..7672767240 100644 --- a/ach/build.gradle +++ b/ach/build.gradle @@ -41,6 +41,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt index 200862a81a..5532b95b4e 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegate.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.util.bufferedChannel @@ -123,6 +124,9 @@ internal class DefaultACHDirectDebitDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun updateAddressInputData(update: AddressInputModel.() -> Unit) { @@ -356,6 +360,9 @@ internal class DefaultACHDirectDebitDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit( state = state, diff --git a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt index bcea14fb7b..0215d76fde 100644 --- a/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt +++ b/ach/src/main/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegate.kt @@ -20,6 +20,7 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -96,10 +97,19 @@ internal class StoredACHDirectDebitDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "setupAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered( + component = storedPaymentMethod.type.orEmpty(), + isStoredPaymentMethod = true + ) + analyticsManager.trackEvent(event) } private fun onState(achDirectDebitComponentState: ACHDirectDebitComponentState) { if (achDirectDebitComponentState.isValid) { + val event = GenericEvents.submit(storedPaymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitChannel.trySend(achDirectDebitComponentState) } } @@ -108,6 +118,9 @@ internal class StoredACHDirectDebitDelegate( adyenLog(AdyenLogLevel.ERROR) { "updateInputData should not be called in StoredACHDirectDebitDelegate" } } + @Suppress("ForbiddenComment") + // TODO: Here we only call this method on initialization. The checkoutAttemptId will only be available if it is + // passed by drop-in. This should be fixed as part of state refactoring. private fun createComponentState(): ACHDirectDebitComponentState { val paymentMethod = ACHDirectDebitPaymentMethod( type = ACHDirectDebitPaymentMethod.PAYMENT_METHOD_TYPE, diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt index 0605f3203a..67e4ccaa3b 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/DefaultACHDirectDebitDelegateTest.kt @@ -22,6 +22,8 @@ import com.adyen.checkout.components.core.PaymentComponentData import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel @@ -61,23 +63,19 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultACHDirectDebitDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler ) { private lateinit var publicKeyRepository: TestPublicKeyRepository private lateinit var addressRepository: TestAddressRepository private lateinit var genericEncryptor: TestGenericEncryptor + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultACHDirectDebitDelegate @BeforeEach @@ -85,6 +83,7 @@ internal class DefaultACHDirectDebitDelegateTest( publicKeyRepository = TestPublicKeyRepository() addressRepository = TestAddressRepository() genericEncryptor = TestGenericEncryptor() + analyticsManager = TestAnalyticsManager() delegate = createAchDelegate() } @@ -639,12 +638,29 @@ internal class DefaultACHDirectDebitDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -663,7 +679,8 @@ internal class DefaultACHDirectDebitDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -696,7 +713,7 @@ internal class DefaultACHDirectDebitDelegateTest( @Suppress("LongParameterList") private fun createAchDelegate( - paymentMethod: PaymentMethod = PaymentMethod(), + paymentMethod: PaymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), analyticsManager: AnalyticsManager = this.analyticsManager, publicKeyRepository: PublicKeyRepository = this.publicKeyRepository, addressRepository: AddressRepository = this.addressRepository, @@ -750,6 +767,7 @@ internal class DefaultACHDirectDebitDelegateTest( private val DEFAULT_SUPPORTED_COUNTRY_LIST = listOf("US", "PR") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" private val DEVICE_LOCALE = Locale("nl", "NL") + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun shouldStorePaymentMethodSource() = listOf( diff --git a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt index 0d4f063977..4e08d25023 100644 --- a/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt +++ b/ach/src/test/java/com/adyen/checkout/ach/internal/ui/StoredACHDirectDebitDelegateTest.kt @@ -18,6 +18,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.test.TestDispatcherExtension @@ -36,24 +38,19 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource -import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) -internal class StoredACHDirectDebitDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, -) { +internal class StoredACHDirectDebitDelegateTest { + + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: ACHDirectDebitDelegate @BeforeEach fun setUp() { + analyticsManager = TestAnalyticsManager() delegate = createAchDelegate() } @@ -98,29 +95,42 @@ internal class StoredACHDirectDebitDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() } @Test - fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate = createAchDelegate() + val expectedEvent = GenericEvents.rendered( + component = TEST_PAYMENT_METHOD_TYPE, + isStoredPaymentMethod = true, + ) + analyticsManager.assertHasEventEquals(expectedEvent) + } - delegate.componentStateFlow.test { - assertEquals(TEST_CHECKOUT_ATTEMPT_ID, expectMostRecentItem().data.paymentMethod?.checkoutAttemptId) - } + @Test + fun `when component is initialized with valid data, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } private fun createAchDelegate( - paymentMethod: StoredPaymentMethod = StoredPaymentMethod(id = STORED_ID), + paymentMethod: StoredPaymentMethod = StoredPaymentMethod( + id = STORED_ID, + type = TEST_PAYMENT_METHOD_TYPE, + ), analyticsManager: AnalyticsManager = this.analyticsManager, configuration: CheckoutConfiguration = createCheckoutConfiguration(), order: OrderRequest? = TEST_ORDER, @@ -149,8 +159,8 @@ internal class StoredACHDirectDebitDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val STORED_ID = "Stored_id" - private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" private val DEVICE_LOCALE = Locale("nl", "NL") + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From d902a7d825efcb92eba8681012e19ef159ecb5d5 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Fri, 29 Mar 2024 10:43:43 +0100 Subject: [PATCH 246/272] Add generic events for StoredBlikDelegate and DefaultBlikDelegate COAND-845 --- blik/build.gradle | 1 + .../blik/internal/ui/DefaultBlikDelegate.kt | 7 ++++ .../blik/internal/ui/StoredBlikDelegate.kt | 10 +++++ .../internal/ui/DefaultBlikDelegateTest.kt | 37 ++++++++++++----- .../internal/ui/StoredBlikDelegateTest.kt | 40 +++++++++++++++---- 5 files changed, 77 insertions(+), 18 deletions(-) diff --git a/blik/build.gradle b/blik/build.gradle index 27e5dee456..f540e3e0d0 100644 --- a/blik/build.gradle +++ b/blik/build.gradle @@ -47,6 +47,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt index b2f0bd08e0..910ebf01c0 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegate.kt @@ -20,6 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -75,6 +76,9 @@ internal class DefaultBlikDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -147,6 +151,9 @@ internal class DefaultBlikDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state) } diff --git a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt index a13b2dd723..1b76cadc1a 100644 --- a/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt +++ b/blik/src/main/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegate.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BlikPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -68,6 +69,12 @@ internal class StoredBlikDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered( + component = storedPaymentMethod.type.orEmpty(), + isStoredPaymentMethod = true, + ) + analyticsManager.trackEvent(event) } override fun observe( @@ -98,6 +105,9 @@ internal class StoredBlikDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(storedPaymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state) } diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt index 8f5b760f4d..5eb99626cc 100644 --- a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/DefaultBlikDelegateTest.kt @@ -19,7 +19,8 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -42,24 +43,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBlikDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultBlikDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createBlikDelegate() } @@ -253,12 +251,29 @@ internal class DefaultBlikDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -274,7 +289,8 @@ internal class DefaultBlikDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -289,7 +305,7 @@ internal class DefaultBlikDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getBlikConfiguration(), ), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = TEST_ORDER, analyticsManager = analyticsManager, submitHandler = submitHandler, @@ -311,6 +327,7 @@ internal class DefaultBlikDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( diff --git a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt index 29dbb66a2d..66c9ab06e5 100644 --- a/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt +++ b/blik/src/test/java/com/adyen/checkout/blik/internal/ui/StoredBlikDelegateTest.kt @@ -17,7 +17,8 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.StoredPaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -32,22 +33,20 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.eq -import org.mockito.kotlin.verify import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) class StoredBlikDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: StoredBlikDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createStoredBlikDelegate() } @@ -57,13 +56,34 @@ class StoredBlikDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered( + component = TEST_PAYMENT_METHOD_TYPE, + isStoredPaymentMethod = true, + ) + analyticsManager.assertHasEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -78,7 +98,10 @@ class StoredBlikDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getBlikConfiguration(), ), - storedPaymentMethod = StoredPaymentMethod(id = STORED_ID), + storedPaymentMethod = StoredPaymentMethod( + id = STORED_ID, + type = TEST_PAYMENT_METHOD_TYPE, + ), order = TEST_ORDER, analyticsManager = analyticsManager, submitHandler = submitHandler, @@ -100,5 +123,6 @@ class StoredBlikDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val STORED_ID = "Stored_id" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" } } From 94d02d90e69afb234097d819083c74b1bb256176 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 10:39:41 +0200 Subject: [PATCH 247/272] Add generic events for DefaultBacsDirectDebitDelegate COAND-845 --- bacs/build.gradle | 1 + .../ui/DefaultBacsDirectDebitDelegate.kt | 7 ++++ .../DefaultBacsDirectDebitDelegateTest.kt | 37 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/bacs/build.gradle b/bacs/build.gradle index 60fe8a0c86..01132e5fff 100644 --- a/bacs/build.gradle +++ b/bacs/build.gradle @@ -47,6 +47,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.mockito testImplementation testLibraries.kotlinCoroutines diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 3f7fe7cb04..6889ee3676 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -21,6 +21,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.BacsDirectDebitPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -69,6 +70,9 @@ internal class DefaultBacsDirectDebitDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -119,6 +123,9 @@ internal class DefaultBacsDirectDebitDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value when (inputData.mode) { BacsDirectDebitMode.INPUT -> { diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index 89b6b396ba..d56cdd6c85 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -23,7 +23,8 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -48,24 +49,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBacsDirectDebitDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultBacsDirectDebitDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createBacsDelegate() } @@ -555,12 +553,29 @@ internal class DefaultBacsDirectDebitDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -581,7 +596,8 @@ internal class DefaultBacsDirectDebitDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -597,7 +613,7 @@ internal class DefaultBacsDirectDebitDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getBacsDirectDebitConfiguration(), ), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, analyticsManager = analyticsManager, submitHandler = submitHandler, @@ -619,6 +635,7 @@ internal class DefaultBacsDirectDebitDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 5b7a6754c589b5b72d51456f58a932edd469f5c7 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 11:27:27 +0200 Subject: [PATCH 248/272] Add generic events for DefaultBoletoDelegate COAND-845 --- boleto/build.gradle | 1 + .../internal/ui/DefaultBoletoDelegate.kt | 7 ++++ .../internal/ui/DefaultBoletoDelegateTest.kt | 39 +++++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/boleto/build.gradle b/boleto/build.gradle index db3eb10a96..a1060b7a70 100644 --- a/boleto/build.gradle +++ b/boleto/build.gradle @@ -46,6 +46,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt index 4da71ec85b..2b596506f3 100644 --- a/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt +++ b/boleto/src/main/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegate.kt @@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.ShopperName import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -105,6 +106,9 @@ internal class DefaultBoletoDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } private fun subscribeToStatesList() { @@ -289,6 +293,9 @@ internal class DefaultBoletoDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitHandler.onSubmit(_componentStateFlow.value) } diff --git a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt index c14ca297c7..d10ab9e81a 100644 --- a/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt +++ b/boleto/src/test/java/com/adyen/checkout/boleto/internal/ui/DefaultBoletoDelegateTest.kt @@ -20,6 +20,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.AddressInputModel import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -45,27 +47,23 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultBoletoDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsManager: AnalyticsManager, ) { - private lateinit var delegate: DefaultBoletoDelegate - private lateinit var addressRepository: TestAddressRepository + private lateinit var analyticsManager: TestAnalyticsManager + private lateinit var delegate: DefaultBoletoDelegate @BeforeEach fun beforeEach() { addressRepository = TestAddressRepository() + analyticsManager = TestAnalyticsManager() delegate = createBoletoDelegate() } @@ -489,12 +487,29 @@ internal class DefaultBoletoDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -510,7 +525,8 @@ internal class DefaultBoletoDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -518,7 +534,7 @@ internal class DefaultBoletoDelegateTest( private fun createBoletoDelegate( submitHandler: SubmitHandler = this.submitHandler, analyticsManager: AnalyticsManager = this.analyticsManager, - paymentMethod: PaymentMethod = PaymentMethod(), + paymentMethod: PaymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), addressRepository: TestAddressRepository = this.addressRepository, order: Order? = TEST_ORDER, configuration: CheckoutConfiguration = createCheckoutConfiguration(), @@ -569,6 +585,7 @@ internal class DefaultBoletoDelegateTest( private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val BRAZIL_COUNTRY_CODE = "BR" private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 5af0572401eaf1ebe87a0c82bd0d0a3d4303580f Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 12:57:17 +0200 Subject: [PATCH 249/272] Add generic events for DefaultEContextDelegate COAND-845 --- econtext/build.gradle | 1 + .../internal/ui/DefaultEContextDelegate.kt | 7 ++++ .../ui/DefaultEContextDelegateTest.kt | 37 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/econtext/build.gradle b/econtext/build.gradle index a375c9c912..9a162bd6ec 100644 --- a/econtext/build.gradle +++ b/econtext/build.gradle @@ -50,6 +50,7 @@ dependencies { testImplementation project(':test-core') testImplementation project(':twint') testImplementation project(':wechatpay') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt index 80d7c6a61b..55ff652f7f 100644 --- a/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt +++ b/econtext/src/main/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegate.kt @@ -18,6 +18,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -84,6 +85,9 @@ internal class DefaultEContextDelegate< private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun updateInputData(update: EContextInputData.() -> Unit) { @@ -199,6 +203,9 @@ internal class DefaultEContextDelegate< } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state) } diff --git a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt index 83b7c543bb..ce2542bc71 100644 --- a/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt +++ b/econtext/src/test/java/com/adyen/checkout/econtext/internal/ui/DefaultEContextDelegateTest.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.FieldState @@ -45,23 +46,20 @@ import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultEContextDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultEContextDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createEContextDelegate() } @@ -258,12 +256,29 @@ internal class DefaultEContextDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -283,7 +298,8 @@ internal class DefaultEContextDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - org.mockito.kotlin.verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -306,7 +322,7 @@ internal class DefaultEContextDelegateTest( componentSessionParams = null, componentConfiguration = configuration.getConfiguration(TEST_CONFIGURATION_KEY), ), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, analyticsManager = analyticsManager, submitHandler = submitHandler, @@ -340,6 +356,7 @@ internal class DefaultEContextDelegateTest( private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" private const val TEST_CONFIGURATION_KEY = "TEST_CONFIGURATION_KEY" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 2b82e52bdb4e4e0075bc72e3f71427f9da69cdc2 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 12:58:03 +0200 Subject: [PATCH 250/272] Add generic events for DefaultGiftCardDelegate COAND-845 --- giftcard/build.gradle | 1 + .../internal/ui/DefaultGiftCardDelegate.kt | 7 ++++ .../ui/DefaultGiftCardDelegateTest.kt | 37 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/giftcard/build.gradle b/giftcard/build.gradle index 2182400f76..af43e658ca 100644 --- a/giftcard/build.gradle +++ b/giftcard/build.gradle @@ -51,6 +51,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt index 1b358bdb88..ec2463a5c7 100644 --- a/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt +++ b/giftcard/src/main/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegate.kt @@ -20,6 +20,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.PublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.FieldState import com.adyen.checkout.components.core.internal.ui.model.Validation @@ -102,6 +103,9 @@ internal class DefaultGiftCardDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } private fun fetchPublicKey(coroutineScope: CoroutineScope) { @@ -231,6 +235,9 @@ internal class DefaultGiftCardDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state) } diff --git a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt index 0230a63cf7..4f7c60116f 100644 --- a/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt +++ b/giftcard/src/test/java/com/adyen/checkout/giftcard/internal/ui/DefaultGiftCardDelegateTest.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.OrderResponse import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.test.TestPublicKeyRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -51,28 +52,25 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, TestDispatcherExtension::class) internal class DefaultGiftCardDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { private lateinit var cardEncryptor: TestCardEncryptor private lateinit var publicKeyRepository: TestPublicKeyRepository + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultGiftCardDelegate @BeforeEach fun before() { cardEncryptor = TestCardEncryptor() publicKeyRepository = TestPublicKeyRepository() + analyticsManager = TestAnalyticsManager() delegate = createGiftCardDelegate() } @@ -359,12 +357,29 @@ internal class DefaultGiftCardDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -381,7 +396,8 @@ internal class DefaultGiftCardDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -410,7 +426,7 @@ internal class DefaultGiftCardDelegateTest( order: OrderRequest? = TEST_ORDER ) = DefaultGiftCardDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, publicKeyRepository = publicKeyRepository, componentParams = GiftCardComponentParamsMapper(CommonComponentParamsMapper()) @@ -444,6 +460,7 @@ internal class DefaultGiftCardDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 03c5d5f0137c226da89d5be0cb1c74de9a5da9d5 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 13:30:25 +0200 Subject: [PATCH 251/272] Add generic events for DefaultGooglePayDelegate COAND-845 --- googlepay/build.gradle | 1 + .../internal/ui/DefaultGooglePayDelegate.kt | 7 +++ .../ui/DefaultGooglePayDelegateTest.kt | 44 +++++++++++++------ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/googlepay/build.gradle b/googlepay/build.gradle index 1d499b1379..46ea4de64d 100644 --- a/googlepay/build.gradle +++ b/googlepay/build.gradle @@ -44,6 +44,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt index 1ed3720e37..af2b87eb71 100644 --- a/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt +++ b/googlepay/src/main/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegate.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.core.AdyenLogLevel import com.adyen.checkout.core.exception.CheckoutException @@ -70,10 +71,16 @@ internal class DefaultGooglePayDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } private fun onState(state: GooglePayComponentState) { if (state.isValid) { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitChannel.trySend(state) } } diff --git a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt index e3666f0d4a..2d4b3c08ac 100644 --- a/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt +++ b/googlepay/src/test/java/com/adyen/checkout/googlepay/internal/ui/DefaultGooglePayDelegateTest.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.Configuration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.GooglePayPaymentMethod import com.adyen.checkout.core.Environment @@ -38,21 +39,14 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource -import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) -internal class DefaultGooglePayDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, -) { +internal class DefaultGooglePayDelegateTest { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultGooglePayDelegate private val paymentData: PaymentData @@ -60,6 +54,7 @@ internal class DefaultGooglePayDelegateTest( @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createGooglePayDelegate() } @@ -137,12 +132,31 @@ internal class DefaultGooglePayDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when component state updates amd the data is valid, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.updateComponentState(paymentData) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -156,7 +170,8 @@ internal class DefaultGooglePayDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -180,7 +195,7 @@ internal class DefaultGooglePayDelegateTest( ): DefaultGooglePayDelegate { return DefaultGooglePayDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = TEST_ORDER, componentParams = GooglePayComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null, paymentMethod), @@ -191,6 +206,7 @@ internal class DefaultGooglePayDelegateTest( companion object { private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 0689b728d5bc8f98d817e606e92646bf80e478b2 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 13:33:36 +0200 Subject: [PATCH 252/272] Add generic events for DefaultInstantPaymentDelegate COAND-845 --- instant/build.gradle | 1 + .../ui/DefaultInstantPaymentDelegate.kt | 7 +++ .../ui/DefaultInstantPaymentDelegateTest.kt | 45 ++++++++++++------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/instant/build.gradle b/instant/build.gradle index 3e641b5001..2861d446b7 100644 --- a/instant/build.gradle +++ b/instant/build.gradle @@ -37,6 +37,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.mockito testImplementation testLibraries.kotlinCoroutines diff --git a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt index a44d2ac784..55a1172cdb 100644 --- a/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt +++ b/instant/src/main/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegate.kt @@ -16,6 +16,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.GenericPaymentMethod import com.adyen.checkout.components.core.paymentmethod.PaymentMethodDetails @@ -83,6 +84,12 @@ internal class DefaultInstantPaymentDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val renderedEvent = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(renderedEvent) + + val submitEvent = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(submitEvent) } override fun observe( diff --git a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt index 8f51098b93..c92ebb4274 100644 --- a/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt +++ b/instant/src/test/java/com/adyen/checkout/instant/internal/ui/DefaultInstantPaymentDelegateTest.kt @@ -14,7 +14,8 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment import com.adyen.checkout.instant.internal.ui.model.InstantComponentParamsMapper @@ -32,25 +33,19 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource -import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) -class DefaultInstantPaymentDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, -) { +class DefaultInstantPaymentDelegateTest { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultInstantPaymentDelegate @BeforeEach fun before() { + analyticsManager = TestAnalyticsManager() delegate = createInstantPaymentDelegate() } @@ -58,7 +53,7 @@ class DefaultInstantPaymentDelegateTest( fun `when subscribed then component state flow should propagate a valid state`() = runTest { delegate.componentStateFlow.test { with(awaitItem()) { - assertEquals(TYPE, data.paymentMethod?.type) + assertEquals(TEST_PAYMENT_METHOD_TYPE, data.paymentMethod?.type) assertEquals(TEST_ORDER, data.order) assertTrue(isInputValid) assertTrue(isValid) @@ -90,12 +85,29 @@ class DefaultInstantPaymentDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertHasEventEquals(expectedEvent) + } + + @Test + fun `when delegate is initialized, then submit event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate = createInstantPaymentDelegate() @@ -107,7 +119,8 @@ class DefaultInstantPaymentDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -125,7 +138,7 @@ class DefaultInstantPaymentDelegateTest( ): DefaultInstantPaymentDelegate { return DefaultInstantPaymentDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(type = TYPE), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = TEST_ORDER, componentParams = InstantComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null, PaymentMethod(type = "paypal")), @@ -135,7 +148,7 @@ class DefaultInstantPaymentDelegateTest( companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" - private const val TYPE = "txVariant" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" From f7fc98e1a00f7dab03477f2a71f2a88ebc0b5557 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 2 Apr 2024 13:45:26 +0200 Subject: [PATCH 253/272] Add generic events for DefaultOnlineBankingDelegate COAND-845 --- online-banking-core/build.gradle | 1 + .../ui/DefaultOnlineBankingDelegate.kt | 7 ++++ .../ui/DefaultOnlineBankingDelegateTest.kt | 36 ++++++++++++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/online-banking-core/build.gradle b/online-banking-core/build.gradle index c3a05feca8..b3f1661e47 100644 --- a/online-banking-core/build.gradle +++ b/online-banking-core/build.gradle @@ -49,6 +49,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt index d2a64f6fd5..4ce120e348 100644 --- a/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt +++ b/online-banking-core/src/main/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegate.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.internal.util.bufferedChannel import com.adyen.checkout.components.core.paymentmethod.IssuerListPaymentMethod @@ -97,6 +98,9 @@ internal class DefaultOnlineBankingDelegate< private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -173,6 +177,9 @@ internal class DefaultOnlineBankingDelegate< } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state = state) } diff --git a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt index ff7c841c53..bd1eb44666 100644 --- a/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt +++ b/online-banking-core/src/test/java/com/adyen/checkout/onlinebankingcore/internal/ui/DefaultOnlineBankingDelegateTest.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.CheckoutConfiguration import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -45,10 +46,7 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import java.util.Locale @@ -56,12 +54,12 @@ import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultOnlineBankingDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val context: Context, @Mock private val pdfOpener: PdfOpener, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultOnlineBankingDelegate< TestOnlineBankingPaymentMethod, TestOnlineBankingComponentState, @@ -69,6 +67,7 @@ internal class DefaultOnlineBankingDelegateTest( @BeforeEach fun setup() { + analyticsManager = TestAnalyticsManager() delegate = createOnlineBankingDelegate() } @@ -238,12 +237,29 @@ internal class DefaultOnlineBankingDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -259,7 +275,8 @@ internal class DefaultOnlineBankingDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -269,7 +286,7 @@ internal class DefaultOnlineBankingDelegateTest( ) = DefaultOnlineBankingDelegate( observerRepository = PaymentObserverRepository(), pdfOpener = pdfOpener, - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, analyticsManager = analyticsManager, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()) @@ -313,6 +330,7 @@ internal class DefaultOnlineBankingDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 71438a4990fb5af9970cbe9db18e51f3c8378bff Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 3 Apr 2024 09:36:29 +0200 Subject: [PATCH 254/272] Add generic events for DefaultPayByBankDelegate COAND-845 --- paybybank/build.gradle | 1 + .../internal/ui/DefaultPayByBankDelegate.kt | 7 ++++ .../ui/DefaultPayByBankDelegateTest.kt | 36 ++++++++++++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/paybybank/build.gradle b/paybybank/build.gradle index c53dd0e344..75cadeb1de 100644 --- a/paybybank/build.gradle +++ b/paybybank/build.gradle @@ -40,6 +40,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.mockito testImplementation testLibraries.kotlinCoroutines diff --git a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt index 563bc61ba5..08d0646634 100644 --- a/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt +++ b/paybybank/src/main/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegate.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams import com.adyen.checkout.components.core.paymentmethod.PayByBankPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -81,6 +82,9 @@ internal class DefaultPayByBankDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -183,6 +187,9 @@ internal class DefaultPayByBankDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state = state) } diff --git a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt index 20a9dc37a3..3dc22356a7 100644 --- a/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt +++ b/paybybank/src/test/java/com/adyen/checkout/paybybank/internal/ui/DefaultPayByBankDelegateTest.kt @@ -16,7 +16,8 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParamsMapper import com.adyen.checkout.core.Environment @@ -44,24 +45,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultPayByBankDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultPayByBankDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createPayByBankDelegate( issuers = listOf( Issuer(id = "issuer-id", name = "issuer-name"), @@ -251,12 +249,29 @@ internal class DefaultPayByBankDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -272,7 +287,8 @@ internal class DefaultPayByBankDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -297,6 +313,7 @@ internal class DefaultPayByBankDelegateTest( componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) .mapToParams(configuration, Locale.US, null, null), paymentMethod = PaymentMethod( + type = TEST_PAYMENT_METHOD_TYPE, issuers = issuers, ), order = order, @@ -309,6 +326,7 @@ internal class DefaultPayByBankDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From a40fb39324b37653c90cb62b5bb7a9509492a113 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 3 Apr 2024 09:37:04 +0200 Subject: [PATCH 255/272] Add generic events for DefaultSepaDelegate COAND-845 --- sepa/build.gradle | 1 + .../sepa/internal/ui/DefaultSepaDelegate.kt | 7 ++++ .../internal/ui/DefaultSepaDelegateTest.kt | 37 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/sepa/build.gradle b/sepa/build.gradle index d1fa975eae..ad00323c42 100644 --- a/sepa/build.gradle +++ b/sepa/build.gradle @@ -51,6 +51,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt index 402a21bfc9..66d0be7204 100644 --- a/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt +++ b/sepa/src/main/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegate.kt @@ -17,6 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -68,6 +69,9 @@ internal class DefaultSepaDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -137,6 +141,9 @@ internal class DefaultSepaDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + val state = _componentStateFlow.value submitHandler.onSubmit(state = state) } diff --git a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt index 8db292c6d2..5f9a0ad68a 100644 --- a/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt +++ b/sepa/src/test/java/com/adyen/checkout/sepa/internal/ui/DefaultSepaDelegateTest.kt @@ -15,7 +15,8 @@ import com.adyen.checkout.components.core.Order import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.components.core.paymentmethod.SepaPaymentMethod @@ -43,24 +44,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultSepaDelegateTest( - @Mock private val analyticsManager: AnalyticsManager, @Mock private val submitHandler: SubmitHandler, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultSepaDelegate @BeforeEach fun before() { + analyticsManager = TestAnalyticsManager() delegate = createSepaDelegate() } @@ -185,12 +183,29 @@ internal class DefaultSepaDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -207,7 +222,8 @@ internal class DefaultSepaDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -216,7 +232,7 @@ internal class DefaultSepaDelegateTest( order: Order? = TEST_ORDER, ) = DefaultSepaDelegate( observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = configuration, @@ -245,6 +261,7 @@ internal class DefaultSepaDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From a4428b85113f5472a3d4701885ce8ea9ed916131 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 3 Apr 2024 09:37:55 +0200 Subject: [PATCH 256/272] Add generic events for DefaultUPIDelegate COAND-845 --- upi/build.gradle | 1 + .../upi/internal/ui/DefaultUPIDelegate.kt | 7 ++++ .../upi/internal/ui/DefaultUPIDelegateTest.kt | 37 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/upi/build.gradle b/upi/build.gradle index e579e62cb1..84c76085aa 100644 --- a/upi/build.gradle +++ b/upi/build.gradle @@ -46,6 +46,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines testImplementation testLibraries.mockito diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt index c3c2d674e5..1deb1b7ed7 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt @@ -17,6 +17,7 @@ import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentComponentEvent import com.adyen.checkout.components.core.internal.PaymentObserverRepository import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams import com.adyen.checkout.components.core.paymentmethod.UPIPaymentMethod import com.adyen.checkout.core.AdyenLogLevel @@ -71,6 +72,9 @@ internal class DefaultUPIDelegate( private fun initializeAnalytics(coroutineScope: CoroutineScope) { adyenLog(AdyenLogLevel.VERBOSE) { "initializeAnalytics" } analyticsManager.initialize(this, coroutineScope) + + val event = GenericEvents.rendered(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) } override fun observe( @@ -156,6 +160,9 @@ internal class DefaultUPIDelegate( } override fun onSubmit() { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitHandler.onSubmit(_componentStateFlow.value) } diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index a265c12502..a84448524b 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -16,7 +16,8 @@ import com.adyen.checkout.components.core.OrderRequest import com.adyen.checkout.components.core.PaymentMethod import com.adyen.checkout.components.core.PaymentMethodTypes import com.adyen.checkout.components.core.internal.PaymentObserverRepository -import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper import com.adyen.checkout.core.Environment @@ -47,24 +48,21 @@ import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import java.util.Locale @OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultUPIDelegateTest( @Mock private val submitHandler: SubmitHandler, - @Mock private val analyticsManager: AnalyticsManager, ) { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var delegate: DefaultUPIDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() delegate = createUPIDelegate() } @@ -215,12 +213,29 @@ internal class DefaultUPIDelegateTest( @Test fun `when delegate is initialized then analytics manager is initialized`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - verify(analyticsManager).initialize(eq(delegate), any()) + + analyticsManager.assertIsInitialized() + } + + @Test + fun `when delegate is initialized, then render event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.rendered(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when onSubmit is called, then submit event is tracked`() { + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) } @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { - whenever(analyticsManager.getCheckoutAttemptId()) doReturn TEST_CHECKOUT_ATTEMPT_ID + analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) @@ -236,7 +251,8 @@ internal class DefaultUPIDelegateTest( @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.onCleared() - verify(analyticsManager).clear(eq(delegate)) + + analyticsManager.assertIsCleared() } } @@ -258,7 +274,7 @@ internal class DefaultUPIDelegateTest( submitHandler = submitHandler, analyticsManager = analyticsManager, observerRepository = PaymentObserverRepository(), - paymentMethod = PaymentMethod(), + paymentMethod = PaymentMethod(type = TEST_PAYMENT_METHOD_TYPE), order = order, componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( checkoutConfiguration = configuration, @@ -273,6 +289,7 @@ internal class DefaultUPIDelegateTest( private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private val TEST_ORDER = OrderRequest("PSP", "ORDER_DATA") private const val TEST_CHECKOUT_ATTEMPT_ID = "TEST_CHECKOUT_ATTEMPT_ID" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" @JvmStatic fun amountSource() = listOf( From 825e21e914f93a9c7d6ace49e1d93168b6bde640 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 10 Apr 2024 16:41:40 +0200 Subject: [PATCH 257/272] Address CR comments COAND-845 --- .../ui/DefaultBacsDirectDebitDelegate.kt | 6 ++-- .../DefaultBacsDirectDebitDelegateTest.kt | 14 +++++++- .../internal/ui/DefaultCashAppPayDelegate.kt | 4 +-- .../ui/DefaultCashAppPayDelegateTest.kt | 36 +++++++++++++++++++ .../analytics/TestAnalyticsManager.java | 19 ++++++++-- 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt index 6889ee3676..dbadaf3077 100644 --- a/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt +++ b/bacs/src/main/java/com/adyen/checkout/bacs/internal/ui/DefaultBacsDirectDebitDelegate.kt @@ -123,9 +123,6 @@ internal class DefaultBacsDirectDebitDelegate( } override fun onSubmit() { - val event = GenericEvents.submit(paymentMethod.type.orEmpty()) - analyticsManager.trackEvent(event) - val state = _componentStateFlow.value when (inputData.mode) { BacsDirectDebitMode.INPUT -> { @@ -138,6 +135,9 @@ internal class DefaultBacsDirectDebitDelegate( } BacsDirectDebitMode.CONFIRMATION -> { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + submitHandler.onSubmit(state) } } diff --git a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt index d56cdd6c85..40d6987840 100644 --- a/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt +++ b/bacs/src/test/java/com/adyen/checkout/bacs/internal/DefaultBacsDirectDebitDelegateTest.kt @@ -566,13 +566,25 @@ internal class DefaultBacsDirectDebitDelegateTest( } @Test - fun `when onSubmit is called, then submit event is tracked`() { + fun `when onSubmit is called and component is in confirmation mode, then submit event is tracked`() { + delegate.updateInputData { mode = BacsDirectDebitMode.CONFIRMATION } + delegate.onSubmit() val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) analyticsManager.assertLastEventEquals(expectedEvent) } + @Test + fun `when onSubmit is called and component is not in confirmation mode, then submit event is not tracked`() { + delegate.updateInputData { mode = BacsDirectDebitMode.INPUT } + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventNotEquals(expectedEvent) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) diff --git a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt index 82ff32edcd..e5b178176b 100644 --- a/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt +++ b/cashapppay/src/main/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegate.kt @@ -195,12 +195,12 @@ constructor( if (isConfirmationRequired()) { initiatePayment() } + } + private fun initiatePayment() { val event = GenericEvents.submit(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) - } - private fun initiatePayment() { val actions = listOfNotNull( getOneTimeAction(), getOnFileAction(outputData), diff --git a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt index 47ffd28410..ce429124c9 100644 --- a/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt +++ b/cashapppay/src/test/java/com/adyen/checkout/cashapppay/internal/ui/DefaultCashAppPayDelegateTest.kt @@ -463,6 +463,28 @@ internal class DefaultCashAppPayDelegateTest( analyticsManager.assertLastEventEquals(expectedEvent) } + @Test + fun `when delegate is initialized, then submit event is not tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventNotEquals(expectedEvent) + } + + @Test + fun `when delegate is initialized and confirmation is not required, then submit event is tracked`() { + delegate = createDefaultCashAppPayDelegate( + createCheckoutConfiguration(Amount("USD", 10L)) { + setShowStorePaymentField(false) + }, + ) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventEquals(expectedEvent) + } + @Test fun `when component state is valid then PaymentMethodDetails should contain checkoutAttemptId`() = runTest { analyticsManager.setCheckoutAttemptId(TEST_CHECKOUT_ATTEMPT_ID) @@ -488,6 +510,20 @@ internal class DefaultCashAppPayDelegateTest( analyticsManager.assertLastEventEquals(expectedEvent) } + @Test + fun `when onSubmit is called and confirmation is not required, then submit event is not tracked`() { + delegate = createDefaultCashAppPayDelegate( + createCheckoutConfiguration(Amount("USD", 10L)) { + setShowStorePaymentField(false) + }, + ) + + delegate.onSubmit() + + val expectedEvent = GenericEvents.submit(TEST_PAYMENT_METHOD_TYPE) + analyticsManager.assertLastEventNotEquals(expectedEvent) + } + @Test fun `when delegate is cleared then analytics manager is cleared`() { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) diff --git a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java index 23887f52c4..3f6d13e9b1 100644 --- a/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java +++ b/components-core/src/testFixtures/java/com/adyen/checkout/components/core/internal/analytics/TestAnalyticsManager.java @@ -8,7 +8,9 @@ package com.adyen.checkout.components.core.internal.analytics; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -30,8 +32,6 @@ public class TestAnalyticsManager implements AnalyticsManager { private final List events = new ArrayList<>(); - private AnalyticsEvent lastEvent = null; - @Override public void initialize(@NonNull Object owner, @NonNull CoroutineScope coroutineScope) { isInitialized = true; @@ -44,7 +44,6 @@ public void assertIsInitialized() { @Override public void trackEvent(@NonNull AnalyticsEvent event) { events.add(event); - lastEvent = event; } public void assertHasEventEquals(AnalyticsEvent expected) { @@ -54,9 +53,23 @@ public void assertHasEventEquals(AnalyticsEvent expected) { } public void assertLastEventEquals(AnalyticsEvent expected) { + if (events.isEmpty()) { + fail("The events list is empty"); + } + + AnalyticsEvent lastEvent = events.get(events.size() - 1); assertTrue(areEventsEqual(expected, lastEvent)); } + public void assertLastEventNotEquals(AnalyticsEvent expected) { + if (events.isEmpty()) { + return; + } + + AnalyticsEvent lastEvent = events.get(events.size() - 1); + assertFalse(areEventsEqual(expected, lastEvent)); + } + private boolean areEventsEqual(AnalyticsEvent expected, AnalyticsEvent actual) { ReflectionEquals re = new ReflectionEquals( expected, From eaa68070e330faa3b66d06ecebd667181f6e8163 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 29 Apr 2024 09:58:05 +0200 Subject: [PATCH 258/272] Add threeDS2 event tracking functions COAND-845 --- .../internal/analytics/ThreeDS2Events.kt | 59 +++++++++++++++++++ .../core/internal/analytics/AnalyticsEvent.kt | 1 + .../core/internal/analytics/GenericEvents.kt | 9 --- 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/analytics/ThreeDS2Events.kt diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/analytics/ThreeDS2Events.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/analytics/ThreeDS2Events.kt new file mode 100644 index 0000000000..9b3a8a0f6b --- /dev/null +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/analytics/ThreeDS2Events.kt @@ -0,0 +1,59 @@ +/* + * 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 ararat on 16/4/2024. + */ + +package com.adyen.checkout.adyen3ds2.internal.analytics + +import androidx.annotation.RestrictTo +import com.adyen.checkout.components.core.internal.analytics.AnalyticsEvent +import com.adyen.checkout.components.core.internal.analytics.DirectAnalyticsEventCreation + +@OptIn(DirectAnalyticsEventCreation::class) +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +internal object ThreeDS2Events { + + fun threeDS2Fingerprint( + subType: SubType, + result: Result? = null, + message: String? = null, + ) = AnalyticsEvent.Log( + component = "threeDS2Fingerprint", + type = AnalyticsEvent.Log.Type.THREEDS2, + subType = subType.value, + result = result?.value, + message = message, + ) + + fun threeDS2Challenge( + subType: SubType, + result: Result? = null, + message: String? = null, + ) = AnalyticsEvent.Log( + component = "threeDS2Challenge", + type = AnalyticsEvent.Log.Type.THREEDS2, + subType = subType.value, + result = result?.value, + message = message, + ) + + enum class SubType(val value: String) { + FINGERPRINT_DATA_SENT("fingerprintDataSentMobile"), + FINGERPRINT_COMPLETED("fingerprintCompleted"), + CHALLENGE_DATA_SENT("challengeDataSentMobile"), + CHALLENGE_DISPLAYED("challengeDisplayed"), + CHALLENGE_COMPLETED("challengeCompleted"), + } + + enum class Result(val value: String) { + CANCELLED("cancelled"), + COMPLETED("completed"), + TIMEOUT("timeout"), + ERROR("error"), + REDIRECT("redirect"), + THREEDS2("threeDS2"), + } +} diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt index def1988672..0d239a3b92 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt @@ -52,6 +52,7 @@ sealed interface AnalyticsEvent { override val component: String, val type: Type? = null, val subType: String? = null, + val result: String? = null, val target: String? = null, val message: String? = null, ) : AnalyticsEvent { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index 83703c4161..95af050116 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -107,15 +107,6 @@ object GenericEvents { type = AnalyticsEvent.Log.Type.SUBMIT, ) - fun threeDS2( - component: String, - message: String, - ) = AnalyticsEvent.Log( - component = component, - type = AnalyticsEvent.Log.Type.THREEDS2, - message = message, - ) - fun action( component: String, subType: String, From f51faa93dcd7bfa656d9fceccc9c8d2cbc403b9a Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Mon, 29 Apr 2024 12:29:20 +0200 Subject: [PATCH 259/272] Track events for threeDS2 flow COAND-845 --- .../internal/ui/DefaultAdyen3DS2Delegate.kt | 58 +++++- .../ui/DefaultAdyen3DS2DelegateTest.kt | 191 ++++++++++++------ 2 files changed, 187 insertions(+), 62 deletions(-) diff --git a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt index b45173fb7b..bc1f82afa7 100644 --- a/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt +++ b/3ds2/src/main/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2Delegate.kt @@ -16,6 +16,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.adyen3ds2.Authentication3DS2Exception import com.adyen.checkout.adyen3ds2.Cancelled3DS2Exception +import com.adyen.checkout.adyen3ds2.internal.analytics.ThreeDS2Events import com.adyen.checkout.adyen3ds2.internal.data.api.SubmitFingerprintRepository import com.adyen.checkout.adyen3ds2.internal.data.model.Adyen3DS2Serializer import com.adyen.checkout.adyen3ds2.internal.data.model.ChallengeToken @@ -315,6 +316,11 @@ internal class DefaultAdyen3DS2Delegate( return null } + val event = ThreeDS2Events.threeDS2Fingerprint( + subType = ThreeDS2Events.SubType.FINGERPRINT_DATA_SENT, + ) + analyticsManager?.trackEvent(event) + return try { adyenLog(AdyenLogLevel.DEBUG) { "create transaction" } when (val result = threeDS2Service.createTransaction(null, fingerprintToken.threeDSMessageVersion)) { @@ -379,19 +385,30 @@ internal class DefaultAdyen3DS2Delegate( when (result) { is SubmitFingerprintResult.Completed -> { + trackFingerprintCompletedEvent(ThreeDS2Events.Result.COMPLETED) emitDetails(result.details) } is SubmitFingerprintResult.Redirect -> { + trackFingerprintCompletedEvent(ThreeDS2Events.Result.REDIRECT) makeRedirect(activity, result.action) } is SubmitFingerprintResult.Threeds2 -> { + trackFingerprintCompletedEvent(ThreeDS2Events.Result.THREEDS2) handleAction(result.action, activity) } } } + private fun trackFingerprintCompletedEvent(result: ThreeDS2Events.Result) { + val event = ThreeDS2Events.threeDS2Fingerprint( + subType = ThreeDS2Events.SubType.FINGERPRINT_COMPLETED, + result = result, + ) + analyticsManager?.trackEvent(event) + } + private fun makeRedirect(activity: Activity, action: RedirectAction) { val url = action.url try { @@ -422,6 +439,11 @@ internal class DefaultAdyen3DS2Delegate( return } + val challengeSentEvent = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_DATA_SENT, + ) + analyticsManager?.trackEvent(challengeSentEvent) + val challengeToken = ChallengeToken.SERIALIZER.deserialize(challengeTokenJson) val challengeParameters = createChallengeParameters(challengeToken) try { @@ -431,6 +453,11 @@ internal class DefaultAdyen3DS2Delegate( SharedChallengeStatusHandler, DEFAULT_CHALLENGE_TIME_OUT, ) + + val challengeDisplayedEvent = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_DISPLAYED, + ) + analyticsManager?.trackEvent(challengeDisplayedEvent) } catch (e: InvalidInputException) { emitError(CheckoutException("Error starting challenge", e)) } @@ -503,13 +530,36 @@ internal class DefaultAdyen3DS2Delegate( override fun onCompletion(result: ChallengeResult) { when (result) { - is ChallengeResult.Cancelled -> onCancelled() - is ChallengeResult.Completed -> onCompleted(result.transactionStatus) - is ChallengeResult.Error -> onError(result) - is ChallengeResult.Timeout -> onTimeout(result) + is ChallengeResult.Cancelled -> { + trackChallengeCompletedEvent(ThreeDS2Events.Result.CANCELLED) + onCancelled() + } + + is ChallengeResult.Completed -> { + trackChallengeCompletedEvent(ThreeDS2Events.Result.COMPLETED) + onCompleted(result.transactionStatus) + } + + is ChallengeResult.Error -> { + trackChallengeCompletedEvent(ThreeDS2Events.Result.ERROR) + onError(result) + } + + is ChallengeResult.Timeout -> { + trackChallengeCompletedEvent(ThreeDS2Events.Result.TIMEOUT) + onTimeout(result) + } } } + private fun trackChallengeCompletedEvent(result: ThreeDS2Events.Result) { + val event = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_COMPLETED, + result = result, + ) + analyticsManager?.trackEvent(event) + } + private fun trackFingerprintActionEvent(action: Action) = trackActionEvent(action, ANALYTICS_MESSAGE_FINGERPRINT) private fun trackChallengeActionEvent(action: Action) = trackActionEvent(action, ANALYTICS_MESSAGE_CHALLENGE) diff --git a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt index d40bc8f158..f6d3a906d8 100644 --- a/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt +++ b/3ds2/src/test/java/com/adyen/checkout/adyen3ds2/internal/ui/DefaultAdyen3DS2DelegateTest.kt @@ -17,6 +17,7 @@ import android.content.Intent import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.adyen3ds2.Authentication3DS2Exception import com.adyen.checkout.adyen3ds2.Cancelled3DS2Exception +import com.adyen.checkout.adyen3ds2.internal.analytics.ThreeDS2Events import com.adyen.checkout.adyen3ds2.internal.data.api.SubmitFingerprintRepository import com.adyen.checkout.adyen3ds2.internal.data.model.Adyen3DS2Serializer import com.adyen.checkout.adyen3ds2.internal.data.model.SubmitFingerprintResult @@ -68,6 +69,9 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any @@ -253,15 +257,8 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `fingerprint is submitted automatically and result is completed, then details are emitted`() = runTest { - val authReqParams = TestAuthenticationRequestParameters( - deviceData = "deviceData", - sdkTransactionID = "sdkTransactionID", - sdkAppID = "sdkAppID", - sdkReferenceNumber = "sdkReferenceNumber", - sdkEphemeralPublicKey = "{}", - messageVersion = "messageVersion", - ) - threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) + threeDS2Service.transactionResult = + TransactionResult.Success(TestTransaction(getAuthenticationRequestParams())) val submitFingerprintResult = SubmitFingerprintResult.Completed(JSONObject()) whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.success(submitFingerprintResult) @@ -282,15 +279,8 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `fingerprint is submitted automatically and result is redirect, then redirect should be handled`() = runTest { - val authReqParams = TestAuthenticationRequestParameters( - deviceData = "deviceData", - sdkTransactionID = "sdkTransactionID", - sdkAppID = "sdkAppID", - sdkReferenceNumber = "sdkReferenceNumber", - sdkEphemeralPublicKey = "{}", - messageVersion = "messageVersion", - ) - threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) + threeDS2Service.transactionResult = + TransactionResult.Success(TestTransaction(getAuthenticationRequestParams())) val submitFingerprintResult = SubmitFingerprintResult.Redirect(RedirectAction()) whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.success(submitFingerprintResult) @@ -305,15 +295,8 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `fingerprint is submitted automatically and it fails, then an exception is emitted`() = runTest { - val authReqParams = TestAuthenticationRequestParameters( - deviceData = "deviceData", - sdkTransactionID = "sdkTransactionID", - sdkAppID = "sdkAppID", - sdkReferenceNumber = "sdkReferenceNumber", - sdkEphemeralPublicKey = "{}", - messageVersion = "messageVersion", - ) - threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) + threeDS2Service.transactionResult = + TransactionResult.Success(TestTransaction(getAuthenticationRequestParams())) val error = IOException("test") whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn Result.failure(error) @@ -328,15 +311,8 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `fingerprint is not submitted automatically, then details are emitted`() = runTest { - val authReqParams = TestAuthenticationRequestParameters( - deviceData = "deviceData", - sdkTransactionID = "sdkTransactionID", - sdkAppID = "sdkAppID", - sdkReferenceNumber = "sdkReferenceNumber", - sdkEphemeralPublicKey = "{}", - messageVersion = "messageVersion", - ) - threeDS2Service.transactionResult = TransactionResult.Success(TestTransaction(authReqParams)) + threeDS2Service.transactionResult = + TransactionResult.Success(TestTransaction(getAuthenticationRequestParams())) val detailsFlow = delegate.detailsFlow.test(testScheduler) delegate.initialize(this) @@ -361,7 +337,7 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `token can't be decoded, then an exception is emitted`() = runTest { - initializeTransaction(this) + initializeChallengeTransaction(this) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) delegate.challengeShopper(Activity(), Base64.encode("token".toByteArray())) @@ -371,7 +347,7 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `everything is good, then challenge should be executed`() = runTest { - val transaction = initializeTransaction(this) + val transaction = initializeChallengeTransaction(this) // We need to set the messageVersion to workaround an error in the 3DS2 SDK delegate.challengeShopper(Activity(), Base64.encode("{\"messageVersion\":\"2.1.0\"}".toByteArray())) @@ -381,7 +357,7 @@ internal class DefaultAdyen3DS2DelegateTest( @Test fun `challenge fails, then an exception is emitted`() = runTest { - initializeTransaction(this).apply { + initializeChallengeTransaction(this).apply { shouldThrowError = true } @@ -392,26 +368,18 @@ internal class DefaultAdyen3DS2DelegateTest( assertTrue(exceptionFlow.latestValue.cause is InvalidInputException) } + } - private fun initializeTransaction(scope: CoroutineScope): TestTransaction { - val authReqParams = TestAuthenticationRequestParameters( - deviceData = "deviceData", - sdkTransactionID = "sdkTransactionID", - sdkAppID = "sdkAppID", - sdkReferenceNumber = "sdkReferenceNumber", - sdkEphemeralPublicKey = "{}", - messageVersion = "2.1.0", - ) - val transaction = TestTransaction(authReqParams) - threeDS2Service.transactionResult = TransactionResult.Success(transaction) + private fun initializeChallengeTransaction(scope: CoroutineScope): TestTransaction { + val transaction = TestTransaction(getAuthenticationRequestParams()) + threeDS2Service.transactionResult = TransactionResult.Success(transaction) - delegate.initialize(scope) + delegate.initialize(scope) - val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) - delegate.identifyShopper(Activity(), encodedJson, false) + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) + delegate.identifyShopper(Activity(), encodedJson, false) - return transaction - } + return transaction } @Nested @@ -548,7 +516,7 @@ internal class DefaultAdyen3DS2DelegateTest( subType = TEST_ACTION_TYPE, message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_FINGERPRINT, ) - analyticsManager.assertLastEventEquals(expectedEvent) + analyticsManager.assertHasEventEquals(expectedEvent) } @Test @@ -567,7 +535,7 @@ internal class DefaultAdyen3DS2DelegateTest( subType = TEST_ACTION_TYPE, message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_CHALLENGE, ) - analyticsManager.assertLastEventEquals(expectedEvent) + analyticsManager.assertHasEventEquals(expectedEvent) } @Test @@ -587,7 +555,7 @@ internal class DefaultAdyen3DS2DelegateTest( subType = TEST_ACTION_TYPE, message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_FINGERPRINT, ) - analyticsManager.assertLastEventEquals(expectedEvent) + analyticsManager.assertHasEventEquals(expectedEvent) } @Test @@ -607,6 +575,75 @@ internal class DefaultAdyen3DS2DelegateTest( subType = TEST_ACTION_TYPE, message = DefaultAdyen3DS2Delegate.ANALYTICS_MESSAGE_CHALLENGE, ) + analyticsManager.assertHasEventEquals(expectedEvent) + } + + @Test + fun `when identifyShopper is called, then event is tracked`() = runTest { + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) + delegate.initialize(this) + + delegate.identifyShopper(Activity(), encodedJson, true) + + val expectedEvent = ThreeDS2Events.threeDS2Fingerprint( + subType = ThreeDS2Events.SubType.FINGERPRINT_DATA_SENT, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @ParameterizedTest + @MethodSource("com.adyen.checkout.adyen3ds2.internal.ui.DefaultAdyen3DS2DelegateTest#fingerprintResult") + fun `when fingerprint result is returned, then event is tracked`( + fingerprintResult: SubmitFingerprintResult, + analyticsResult: ThreeDS2Events.Result + ) = runTest { + val encodedJson = Base64.encode(TEST_FINGERPRINT_TOKEN.toByteArray()) + threeDS2Service.transactionResult = + TransactionResult.Success(TestTransaction(getAuthenticationRequestParams())) + whenever(submitFingerprintRepository.submitFingerprint(any(), any(), anyOrNull())) doReturn + Result.success(fingerprintResult) + delegate.initialize(this) + + delegate.identifyShopper(Activity(), encodedJson, true) + + val expectedEvent = ThreeDS2Events.threeDS2Fingerprint( + subType = ThreeDS2Events.SubType.FINGERPRINT_COMPLETED, + result = analyticsResult, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + + @Test + fun `when challengeShopper is called, then event is tracked`() = runTest { + initializeChallengeTransaction(this) + // We need to set the messageVersion to workaround an error in the 3DS2 SDK + delegate.challengeShopper(Activity(), Base64.encode("{\"messageVersion\":\"2.1.0\"}".toByteArray())) + + val expectedDataSentEvent = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_DATA_SENT, + ) + analyticsManager.assertHasEventEquals(expectedDataSentEvent) + + val expectedDisplayedEvent = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_DISPLAYED, + ) + analyticsManager.assertLastEventEquals(expectedDisplayedEvent) + } + + @ParameterizedTest + @MethodSource("com.adyen.checkout.adyen3ds2.internal.ui.DefaultAdyen3DS2DelegateTest#challengeResult") + fun `when challenge result is returned, then event is tracked`( + challengeResult: ChallengeResult, + analyticsResult: ThreeDS2Events.Result + ) = runTest { + delegate.onCompletion( + result = challengeResult, + ) + + val expectedEvent = ThreeDS2Events.threeDS2Challenge( + subType = ThreeDS2Events.SubType.CHALLENGE_COMPLETED, + result = analyticsResult, + ) analyticsManager.assertLastEventEquals(expectedEvent) } } @@ -638,6 +675,15 @@ internal class DefaultAdyen3DS2DelegateTest( assertNull(savedStateHandle[DefaultAdyen3DS2Delegate.ACTION_KEY]) } + private fun getAuthenticationRequestParams() = TestAuthenticationRequestParameters( + deviceData = "deviceData", + sdkTransactionID = "sdkTransactionID", + sdkAppID = "sdkAppID", + sdkReferenceNumber = "sdkReferenceNumber", + sdkEphemeralPublicKey = "{}", + messageVersion = "messageVersion", + ) + private class TestAuthenticationRequestParameters( private val deviceData: String? = null, private val sdkTransactionID: String? = null, @@ -673,6 +719,35 @@ internal class DefaultAdyen3DS2DelegateTest( "threeDSMessageVersion":"2.1.0" } """.trimIndent() + + @JvmStatic + fun fingerprintResult() = listOf( + // fingerprintResult, analyticsResult + Arguments.arguments(SubmitFingerprintResult.Completed(JSONObject()), ThreeDS2Events.Result.COMPLETED), + Arguments.arguments(SubmitFingerprintResult.Redirect(RedirectAction()), ThreeDS2Events.Result.REDIRECT), + Arguments.arguments(SubmitFingerprintResult.Threeds2(Threeds2Action()), ThreeDS2Events.Result.THREEDS2), + ) + + @JvmStatic + fun challengeResult() = listOf( + // challengeResult, analyticsResult + Arguments.arguments( + ChallengeResult.Completed("transactionStatus"), + ThreeDS2Events.Result.COMPLETED, + ), + Arguments.arguments( + ChallengeResult.Cancelled("transactionStatus", "additionalDetails"), + ThreeDS2Events.Result.CANCELLED, + ), + Arguments.arguments( + ChallengeResult.Error("transactionStatus", "additionalDetails"), + ThreeDS2Events.Result.ERROR, + ), + Arguments.arguments( + ChallengeResult.Timeout("transactionStatus", "additionalDetails"), + ThreeDS2Events.Result.TIMEOUT, + ), + ) } } From a6f005b602f3ed9e87f8152b95288f76fb21c73a Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 16 May 2024 11:31:52 +0200 Subject: [PATCH 260/272] Clean up unused code and TODOs COAND-845 --- .../core/internal/analytics/AnalyticsEvent.kt | 6 ------ .../core/internal/analytics/AnalyticsPlatform.kt | 3 ++- .../core/internal/analytics/GenericEvents.kt | 10 +++++++--- .../analytics/data/DefaultAnalyticsRepositoryTest.kt | 2 -- .../core/internal/data/api/AnalyticsServiceTest.kt | 2 -- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt index 0d239a3b92..76cd8a775a 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsEvent.kt @@ -63,9 +63,3 @@ sealed interface AnalyticsEvent { } } } - -// TODO: Does it inherit the restrictTo from the Type? -@Suppress("ForbiddenComment") -typealias InfoEventType = AnalyticsEvent.Info.Type - -typealias LogEventType = AnalyticsEvent.Log.Type diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt index 547a944f4d..86c47a2e53 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/AnalyticsPlatform.kt @@ -10,10 +10,11 @@ package com.adyen.checkout.components.core.internal.analytics import androidx.annotation.RestrictTo -@Suppress("unused") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) enum class AnalyticsPlatform(val value: String) { ANDROID("android"), FLUTTER("flutter"), + + @Suppress("unused") REACT_NATIVE("react-native"), } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt index 95af050116..3bb610e21f 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/GenericEvents.kt @@ -56,9 +56,9 @@ object GenericEvents { target = target, ) - // TODO: This might move to the Input fields itself and not be bound to any component. But we should find a way to - // define targets. We could create an enum for target per module - @Suppress("ForbiddenComment") + // This might move to the Input fields itself and not be bound to any component. But we should find a way to + // define targets. We could create an enum for target per module + @Suppress("Unused") fun focus( component: String, target: String, @@ -68,6 +68,9 @@ object GenericEvents { target = target, ) + // This might move to the Input fields itself and not be bound to any component. But we should find a way to + // define targets. We could create an enum for target per module + @Suppress("Unused") fun unfocus( component: String, target: String, @@ -86,6 +89,7 @@ object GenericEvents { target = target, ) + @Suppress("Unused") fun invalidField( component: String, target: String, diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt index d157a7f4c9..8c36a5bea7 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt @@ -7,7 +7,6 @@ import com.adyen.checkout.components.core.internal.analytics.data.remote.Analyti import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider import com.adyen.checkout.components.core.internal.data.model.AnalyticsSetupResponse -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -25,7 +24,6 @@ import org.mockito.kotlin.never import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -@OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class) internal class DefaultAnalyticsRepositoryTest( @Mock private val localInfoDataStore: AnalyticsLocalDataStore, 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 2f322559ca..86e132c7f9 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 @@ -4,7 +4,6 @@ import com.adyen.checkout.components.core.internal.data.model.AnalyticsTrackRequ import com.adyen.checkout.core.internal.data.api.HttpClient import com.adyen.checkout.core.internal.data.model.EmptyResponse import com.adyen.checkout.test.LoggingExtension -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.BeforeEach @@ -17,7 +16,6 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.whenever -@OptIn(ExperimentalCoroutinesApi::class) @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class AnalyticsServiceTest( @Mock private val httpClient: HttpClient From ad9de18ca4d5a7bf4570cca03cf91eefa6c5e918 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 17 May 2024 15:18:26 +0200 Subject: [PATCH 261/272] Map result field for track events to response object COAND-845 --- .../analytics/data/remote/AnalyticsTrackRequestProvider.kt | 1 + .../components/core/internal/data/model/AnalyticsTrackLog.kt | 4 ++++ .../data/remote/AnalyticsTrackRequestProviderTest.kt | 1 + .../core/internal/data/model/AnalyticsTrackLogTest.kt | 4 ++++ .../core/internal/data/model/AnalyticsTrackRequestTest.kt | 2 ++ 5 files changed, 12 insertions(+) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt index 04d09ef941..c87cb24e2b 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProvider.kt @@ -49,5 +49,6 @@ internal class AnalyticsTrackRequestProvider { subType = subType, target = target, message = message, + result = result, ) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt index 7ce26e238a..55bfc4234e 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLog.kt @@ -23,6 +23,7 @@ internal data class AnalyticsTrackLog( val component: String?, val type: String?, val subType: String?, + val result: String?, val target: String?, val message: String?, ) : ModelObject() { @@ -33,6 +34,7 @@ internal data class AnalyticsTrackLog( private const val COMPONENT = "component" private const val TYPE = "type" private const val SUBTYPE = "subType" + private const val RESULT = "result" private const val TARGET = "target" private const val MESSAGE = "message" @@ -46,6 +48,7 @@ internal data class AnalyticsTrackLog( putOpt(COMPONENT, modelObject.component) putOpt(TYPE, modelObject.type) putOpt(SUBTYPE, modelObject.subType) + putOpt(RESULT, modelObject.result) putOpt(TARGET, modelObject.target) putOpt(MESSAGE, modelObject.message) } @@ -63,6 +66,7 @@ internal data class AnalyticsTrackLog( component = getStringOrNull(COMPONENT), type = getStringOrNull(TYPE), subType = getStringOrNull(SUBTYPE), + result = getStringOrNull(RESULT), target = getStringOrNull(TARGET), message = getStringOrNull(MESSAGE), ) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt index f88f61e364..cf8acdff31 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/remote/AnalyticsTrackRequestProviderTest.kt @@ -73,6 +73,7 @@ internal class AnalyticsTrackRequestProviderTest { component = "dropin", type = null, subType = null, + result = null, target = null, message = null, ), diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt index 8d902e92fc..0141eaf498 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackLogTest.kt @@ -16,6 +16,7 @@ internal class AnalyticsTrackLogTest { component = "dropin", type = "test", subType = "subtest", + result = "positive", target = "field", message = "Hello", ) @@ -28,6 +29,7 @@ internal class AnalyticsTrackLogTest { .put("component", "dropin") .put("type", "test") .put("subType", "subtest") + .put("result", "positive") .put("target", "field") .put("message", "Hello") @@ -42,6 +44,7 @@ internal class AnalyticsTrackLogTest { .put("component", "dropin") .put("type", "test") .put("subType", "subtest") + .put("result", "positive") .put("target", "field") .put("message", "Hello") @@ -53,6 +56,7 @@ internal class AnalyticsTrackLogTest { component = "dropin", type = "test", subType = "subtest", + result = "positive", target = "field", message = "Hello", ) diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt index b10ce86354..b67c29f61a 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/data/model/AnalyticsTrackRequestTest.kt @@ -30,6 +30,7 @@ internal class AnalyticsTrackRequestTest { component = "dropin", type = null, subType = null, + result = null, target = null, message = null, ), @@ -75,6 +76,7 @@ internal class AnalyticsTrackRequestTest { component = "dropin", type = null, subType = null, + result = null, target = null, message = null, ), From 8e4e040bfe2cfec8768dc9473bca84c8f848ff29 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 17 May 2024 15:19:29 +0200 Subject: [PATCH 262/272] Move log line for successfully sending events The line was logged even when no events were actually sent. Now it should only log when events are actually successfully sent. COAND-845 --- .../core/internal/analytics/DefaultAnalyticsManager.kt | 5 +---- .../internal/analytics/data/DefaultAnalyticsRepository.kt | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index 1f6b13fb83..9db6e43d27 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -116,10 +116,7 @@ internal class DefaultAnalyticsManager( runSuspendCatching { analyticsRepository.sendEvents(checkoutAttemptId) }.fold( - onSuccess = { - adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } - storedEventCount.set(0) - }, + onSuccess = { storedEventCount.set(0) }, onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, ) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index 6ece9c056e..c465e20966 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -13,6 +13,8 @@ import com.adyen.checkout.components.core.internal.analytics.data.local.Analytic import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsRemoteDataStore import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsSetupProvider import com.adyen.checkout.components.core.internal.analytics.data.remote.AnalyticsTrackRequestProvider +import com.adyen.checkout.core.AdyenLogLevel +import com.adyen.checkout.core.internal.util.adyenLog internal class DefaultAnalyticsRepository( private val localInfoDataStore: AnalyticsLocalDataStore, @@ -49,5 +51,7 @@ internal class DefaultAnalyticsRepository( remoteDataStore.sendEvents(request, checkoutAttemptId) localInfoDataStore.clear() localLogDataStore.clear() + + adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } } } From b96c99034680119663e4d8599c3fe4497fe2df68 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 May 2024 13:19:34 +0200 Subject: [PATCH 263/272] Only remove events that were successfully sent This will prevent an edge case where we would accidentally clear events that were not sent yet. COAND-845 --- .../analytics/data/DefaultAnalyticsRepository.kt | 5 +++-- .../data/local/AnalyticsLocalDataStore.kt | 2 +- .../data/local/InfoAnalyticsLocalDataStore.kt | 4 ++-- .../data/local/LogAnalyticsLocalDataStore.kt | 4 ++-- .../data/DefaultAnalyticsRepositoryTest.kt | 15 +++++++++------ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt index c465e20966..200a03df65 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepository.kt @@ -49,8 +49,9 @@ internal class DefaultAnalyticsRepository( logList = logEvents, ) remoteDataStore.sendEvents(request, checkoutAttemptId) - localInfoDataStore.clear() - localLogDataStore.clear() + + localInfoDataStore.removeEvents(infoEvents) + localLogDataStore.removeEvents(logEvents) adyenLog(AdyenLogLevel.DEBUG) { "Analytics events successfully sent" } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt index 9424ded07f..6f1d66e514 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/AnalyticsLocalDataStore.kt @@ -14,5 +14,5 @@ internal interface AnalyticsLocalDataStore { suspend fun fetchEvents(size: Int): List - suspend fun clear() + suspend fun removeEvents(events: List) } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt index e52f0308b3..c398392e42 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/InfoAnalyticsLocalDataStore.kt @@ -29,9 +29,9 @@ internal class InfoAnalyticsLocalDataStore : AnalyticsLocalDataStore) { mutex.withLock { - list.clear() + list.removeAll(events.toSet()) } } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt index 6ade5e8ba9..7db939a580 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/data/local/LogAnalyticsLocalDataStore.kt @@ -29,9 +29,9 @@ internal class LogAnalyticsLocalDataStore : AnalyticsLocalDataStore) { mutex.withLock { - list.clear() + list.removeAll(events.toSet()) } } } diff --git a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt index 8c36a5bea7..cb5378f592 100644 --- a/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt +++ b/components-core/src/test/java/com/adyen/checkout/components/core/internal/analytics/data/DefaultAnalyticsRepositoryTest.kt @@ -85,16 +85,19 @@ internal class DefaultAnalyticsRepositoryTest( verify(remoteDataStore, never()).sendEvents(any(), any()) } + @OptIn(DirectAnalyticsEventCreation::class) @Test fun `it is successful, then events are cleared from storage`() = runTest { - whenever(localInfoDataStore.fetchEvents(any())) doReturn listOf(mock()) - whenever(localLogDataStore.fetchEvents(any())) doReturn listOf(mock()) + val infoEvents = listOf(AnalyticsEvent.Info(component = "test info")) + val logEvents = listOf(AnalyticsEvent.Log(component = "test log")) + whenever(localInfoDataStore.fetchEvents(any())) doReturn infoEvents + whenever(localLogDataStore.fetchEvents(any())) doReturn logEvents whenever(analyticsTrackRequestProvider.invoke(any(), any())) doReturn mock() analyticsRepository.sendEvents("test") - verify(localInfoDataStore).clear() - verify(localLogDataStore).clear() + verify(localInfoDataStore).removeEvents(infoEvents) + verify(localLogDataStore).removeEvents(logEvents) } @Test @@ -108,8 +111,8 @@ internal class DefaultAnalyticsRepositoryTest( analyticsRepository.sendEvents("test") } - verify(localInfoDataStore, never()).clear() - verify(localLogDataStore, never()).clear() + verify(localInfoDataStore, never()).removeEvents(any()) + verify(localLogDataStore, never()).removeEvents(any()) } } } From f0bb1d731363f3124fcdc8c1c1878a0cf0c4a620 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 21 May 2024 17:01:22 +0200 Subject: [PATCH 264/272] Always initialize analytics when drop in is created COAND-845 --- .../adyen/checkout/dropin/internal/ui/DropInActivity.kt | 4 +--- .../adyen/checkout/dropin/internal/ui/DropInViewModel.kt | 7 +++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt index 0cfd2e0709..106bc6a05f 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInActivity.kt @@ -155,9 +155,7 @@ internal class DropInActivity : return } - if (noDialogPresent()) { - dropInViewModel.onCreated() - } + dropInViewModel.onCreated(noDialogPresent()) handleIntent(intent) diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt index f393096ac5..9a8aac5d07 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/ui/DropInViewModel.kt @@ -184,8 +184,11 @@ internal class DropInViewModel( ) } - fun onCreated() { - navigateToInitialDestination() + fun onCreated(noDialogPresent: Boolean) { + if (noDialogPresent) { + navigateToInitialDestination() + } + initializeAnalytics() } From 884b4298ac0d4187bd19eb7b1f693ab4299e4ed5 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 24 May 2024 13:30:58 +0200 Subject: [PATCH 265/272] Track action event for DefaultTwintDelegate COAND-845 --- .../internal/ui/ActionDelegateProvider.kt | 7 ++- twint/build.gradle | 1 + .../provider/TwintActionComponentProvider.kt | 3 + .../twint/internal/ui/DefaultTwintDelegate.kt | 9 +++ .../internal/ui/DefaultTwintDelegateTest.kt | 60 ++++++++++++++++--- 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt index baa80a02b8..a6b759dbd7 100644 --- a/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt +++ b/action-core/src/main/java/com/adyen/checkout/action/core/internal/ui/ActionDelegateProvider.kt @@ -66,7 +66,12 @@ internal class ActionDelegateProvider( action: Action, ): ActionComponentProvider<*, *, *> { return when (action.paymentMethodType) { - PaymentMethodTypes.TWINT -> TwintActionComponentProvider(dropInOverrideParams, localeProvider) + PaymentMethodTypes.TWINT -> TwintActionComponentProvider( + analyticsManager, + dropInOverrideParams, + localeProvider, + ) + PaymentMethodTypes.WECHAT_PAY_SDK -> WeChatPayActionComponentProvider( analyticsManager, dropInOverrideParams, diff --git a/twint/build.gradle b/twint/build.gradle index 8b4809aa89..89d994f27f 100644 --- a/twint/build.gradle +++ b/twint/build.gradle @@ -46,6 +46,7 @@ dependencies { //Tests testImplementation project(':test-core') + testImplementation testFixtures(project(':components-core')) testImplementation testLibraries.json testImplementation testLibraries.junit5 testImplementation testLibraries.kotlinCoroutines diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt index 53c33c979e..079ffcb7e5 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/provider/TwintActionComponentProvider.kt @@ -23,6 +23,7 @@ import com.adyen.checkout.components.core.action.SdkAction import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.DefaultActionComponentEventHandler import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.data.api.DefaultStatusRepository import com.adyen.checkout.components.core.internal.data.api.StatusService import com.adyen.checkout.components.core.internal.provider.ActionComponentProvider @@ -42,6 +43,7 @@ import com.adyen.checkout.twint.toCheckoutConfiguration class TwintActionComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor( + private val analyticsManager: AnalyticsManager? = null, private val dropInOverrideParams: DropInOverrideParams? = null, private val localeProvider: LocaleProvider = LocaleProvider(), ) : ActionComponentProvider { @@ -93,6 +95,7 @@ constructor( componentParams = componentParams, paymentDataRepository = PaymentDataRepository(savedStateHandle), statusRepository = statusRepository, + analyticsManager = analyticsManager, ) } diff --git a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt index e22065623a..961606637b 100644 --- a/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt +++ b/twint/src/main/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegate.kt @@ -22,6 +22,8 @@ import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository import com.adyen.checkout.components.core.internal.SavedStateHandleContainer import com.adyen.checkout.components.core.internal.SavedStateHandleProperty +import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager +import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.data.api.StatusRepository import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -52,6 +54,7 @@ internal class DefaultTwintDelegate( override val componentParams: GenericComponentParams, private val paymentDataRepository: PaymentDataRepository, private val statusRepository: StatusRepository, + private val analyticsManager: AnalyticsManager?, ) : TwintDelegate, SavedStateHandleContainer { private val detailsChannel: Channel = bufferedChannel() @@ -120,6 +123,12 @@ internal class DefaultTwintDelegate( @Suppress("UNCHECKED_CAST") this.action = action as SdkAction + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + initState(action) launchAction(sdkData) } diff --git a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt index 0b837b212b..2652e12bfc 100644 --- a/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt +++ b/twint/src/test/java/com/adyen/checkout/twint/internal/ui/DefaultTwintDelegateTest.kt @@ -21,6 +21,8 @@ import com.adyen.checkout.components.core.action.TwintSdkData import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.analytics.GenericEvents +import com.adyen.checkout.components.core.internal.analytics.TestAnalyticsManager import com.adyen.checkout.components.core.internal.data.model.StatusResponse import com.adyen.checkout.components.core.internal.test.TestStatusRepository import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper @@ -53,11 +55,13 @@ import java.util.Locale @ExtendWith(MockitoExtension::class, LoggingExtension::class) internal class DefaultTwintDelegateTest { + private lateinit var analyticsManager: TestAnalyticsManager private lateinit var statusRepository: TestStatusRepository private lateinit var delegate: DefaultTwintDelegate @BeforeEach fun beforeEach() { + analyticsManager = TestAnalyticsManager() statusRepository = TestStatusRepository() delegate = createDelegate() } @@ -65,7 +69,7 @@ internal class DefaultTwintDelegateTest { @Test fun `when handling action successfully, then a pay event should be emitted`() = runTest { val payEventFlow = delegate.payEventFlow.test(testScheduler) - val action = SdkAction(paymentData = "something", sdkData = TwintSdkData("token")) + val action = SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")) delegate.handleAction(action, Activity()) @@ -91,7 +95,7 @@ internal class DefaultTwintDelegateTest { ) val detailsFlow = delegate.detailsFlow.test(testScheduler) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + delegate.handleAction(SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")), Activity()) delegate.handleTwintResult(result) @@ -129,7 +133,10 @@ internal class DefaultTwintDelegateTest { statusRepository.pollingResults = listOf(Result.failure(IOException("Test"))) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + delegate.handleAction( + action = SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")), + activity = Activity(), + ) delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) @@ -144,7 +151,10 @@ internal class DefaultTwintDelegateTest { ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val exceptionFlow = delegate.exceptionFlow.test(testScheduler) - delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + delegate.handleAction( + action = SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")), + activity = Activity(), + ) delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) @@ -164,7 +174,10 @@ internal class DefaultTwintDelegateTest { ) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) val detailsFlow = delegate.detailsFlow.test(testScheduler) - delegate.handleAction(SdkAction(paymentData = "test", sdkData = TwintSdkData("token")), Activity()) + delegate.handleAction( + action = SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")), + activity = Activity(), + ) delegate.handleTwintResult(TwintPayResult.TW_B_SUCCESS) @@ -185,7 +198,10 @@ internal class DefaultTwintDelegateTest { Result.success(StatusResponse(resultCode = "finished", payload = "testpayload")), ) val savedStateHandle = SavedStateHandle().apply { - set(DefaultTwintDelegate.ACTION_KEY, SdkAction(paymentData = "test", sdkData = TwintSdkData("token"))) + set( + DefaultTwintDelegate.ACTION_KEY, + SdkAction(paymentData = TEST_PAYMENT_DATA, sdkData = TwintSdkData("token")), + ) set(DefaultTwintDelegate.IS_POLLING_KEY, true) } delegate = createDelegate(savedStateHandle) @@ -218,11 +234,37 @@ internal class DefaultTwintDelegateTest { delegate = createDelegate(savedStateHandle) delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + delegate.handleAction( + action = RedirectAction(paymentMethodType = TEST_PAYMENT_METHOD_TYPE, paymentData = TEST_PAYMENT_DATA), + activity = Activity(), + ) assertNull(savedStateHandle[DefaultTwintDelegate.ACTION_KEY]) } + @Nested + inner class AnalyticsTest { + + @Test + fun `when handleAction is called, then action event is tracked`() { + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + val action = SdkAction( + paymentMethodType = TEST_PAYMENT_METHOD_TYPE, + type = TEST_ACTION_TYPE, + paymentData = TEST_PAYMENT_DATA, + sdkData = TwintSdkData("token"), + ) + + delegate.handleAction(action, Activity()) + + val expectedEvent = GenericEvents.action( + component = TEST_PAYMENT_METHOD_TYPE, + subType = TEST_ACTION_TYPE, + ) + analyticsManager.assertLastEventEquals(expectedEvent) + } + } + private fun createDelegate( savedStateHandle: SavedStateHandle = SavedStateHandle() ): DefaultTwintDelegate { @@ -235,12 +277,16 @@ internal class DefaultTwintDelegateTest { .mapToParams(configuration, Locale.US, null, null), paymentDataRepository = PaymentDataRepository(SavedStateHandle()), statusRepository = statusRepository, + analyticsManager = analyticsManager, ) } companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private const val TEST_PAYLOAD = "TEST_PAYLOAD" + private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_ACTION_TYPE = "TEST_PAYMENT_METHOD_TYPE" + private const val TEST_PAYMENT_DATA = "TEST_PAYMENT_DATA" @JvmStatic fun handleActionSource() = listOf( From 6114555c616e7224138b7d96f784b3b7df408bf2 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Fri, 24 May 2024 13:31:45 +0200 Subject: [PATCH 266/272] Make sure action event is tracked when handleAction is called initState can be called from other places, so it's not the right place to send the event. COAND-845 --- .../await/internal/ui/DefaultAwaitDelegate.kt | 12 ++++++------ .../voucher/internal/ui/DefaultVoucherDelegate.kt | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index 2176c47cfd..c49d964260 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -124,6 +124,12 @@ internal class DefaultAwaitDelegate( this.action = action + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + initState(action) } @@ -137,12 +143,6 @@ internal class DefaultAwaitDelegate( } createOutputData(null, action) - val event = GenericEvents.action( - component = action.paymentMethodType.orEmpty(), - subType = action.type.orEmpty(), - ) - analyticsManager?.trackEvent(event) - startStatusPolling(paymentData, action) } diff --git a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt index 6b31b69156..656b86f2d0 100644 --- a/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt +++ b/voucher/src/main/java/com/adyen/checkout/voucher/internal/ui/DefaultVoucherDelegate.kt @@ -120,6 +120,12 @@ internal class DefaultVoucherDelegate( this.action = action + val event = GenericEvents.action( + component = action.paymentMethodType.orEmpty(), + subType = action.type.orEmpty(), + ) + analyticsManager?.trackEvent(event) + initState(action) } @@ -130,12 +136,6 @@ internal class DefaultVoucherDelegate( return } - val event = GenericEvents.action( - component = action.paymentMethodType.orEmpty(), - subType = action.type.orEmpty(), - ) - analyticsManager?.trackEvent(event) - _viewFlow.tryEmit(config.viewType) createOutputData(action, config) From c1d0f677ffd40409fe2a58f086c718ca24cba82f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 29 May 2024 11:47:06 +0200 Subject: [PATCH 267/272] Correctly restore state of redirect COAND-857 --- .../provider/RedirectComponentProvider.kt | 1 + .../internal/ui/DefaultRedirectDelegate.kt | 61 +++++++++++++++---- .../ui/DefaultRedirectDelegateTest.kt | 43 ++++++++++++- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt index 8b3960c0ac..59e000d2f5 100644 --- a/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt +++ b/redirect/src/main/java/com/adyen/checkout/redirect/internal/provider/RedirectComponentProvider.kt @@ -91,6 +91,7 @@ constructor( return DefaultRedirectDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, redirectHandler = redirectHandler, paymentDataRepository = paymentDataRepository, 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 ffff2f8ca8..375cd2a270 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 @@ -10,7 +10,9 @@ package com.adyen.checkout.redirect.internal.ui import android.app.Activity import android.content.Intent +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.ActionTypes @@ -18,6 +20,8 @@ import com.adyen.checkout.components.core.action.RedirectAction import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -42,14 +46,17 @@ import kotlinx.coroutines.launch import org.json.JSONObject @Suppress("TooManyFunctions") -internal class DefaultRedirectDelegate( +internal class DefaultRedirectDelegate +@Suppress("LongParameterList") +constructor( private val observerRepository: ActionObserverRepository, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val redirectHandler: RedirectHandler, private val paymentDataRepository: PaymentDataRepository, private val nativeRedirectService: NativeRedirectService, private val analyticsManager: AnalyticsManager?, -) : RedirectDelegate { +) : RedirectDelegate, SavedStateHandleContainer { private val detailsChannel: Channel = bufferedChannel() override val detailsFlow: Flow = detailsChannel.receiveAsFlow() @@ -62,8 +69,16 @@ internal class DefaultRedirectDelegate( private var _coroutineScope: CoroutineScope? = null private val coroutineScope: CoroutineScope get() = requireNotNull(_coroutineScope) + private var action: RedirectAction? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { _coroutineScope = coroutineScope + restoreState() + } + + private fun restoreState() { + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + action?.let { initState(it) } } override fun observe( @@ -87,16 +102,23 @@ internal class DefaultRedirectDelegate( override fun handleAction(action: Action, activity: Activity) { if (action !is RedirectAction) { - exceptionChannel.trySend(ComponentException("Unsupported action")) + emitError(ComponentException("Unsupported action")) return } + this.action = action + val event = GenericEvents.action( component = action.paymentMethodType.orEmpty(), subType = action.type.orEmpty(), ) analyticsManager?.trackEvent(event) + initState(action) + launchAction(activity, action.url) + } + + private fun initState(action: RedirectAction) { when (action.type) { ActionTypes.NATIVE_REDIRECT -> { paymentDataRepository.nativeRedirectData = action.nativeRedirectData @@ -106,8 +128,6 @@ internal class DefaultRedirectDelegate( paymentDataRepository.paymentData = action.paymentData } } - - launchAction(activity, action.url) } private fun launchAction(activity: Activity, url: String?) { @@ -118,7 +138,7 @@ internal class DefaultRedirectDelegate( // PaymentComponentState for actions. redirectHandler.launchUriRedirect(activity, url) } catch (ex: CheckoutException) { - exceptionChannel.trySend(ex) + emitError(ex) } } @@ -132,11 +152,11 @@ internal class DefaultRedirectDelegate( } else -> { - detailsChannel.trySend(createActionComponentData(details)) + emitDetails(details) } } } catch (ex: CheckoutException) { - exceptionChannel.trySend(ex) + emitError(ex) } } @@ -156,23 +176,37 @@ internal class DefaultRedirectDelegate( try { val response = nativeRedirectService.makeNativeRedirect(request, componentParams.clientKey) val detailsJson = NativeRedirectResponse.SERIALIZER.serialize(response) - detailsChannel.trySend(createActionComponentData(detailsJson)) + emitDetails(detailsJson) } catch (e: HttpException) { - onError(e) + emitError(e) } catch (e: ModelSerializationException) { - onError(e) + emitError(e) } } } override fun onError(e: CheckoutException) { - exceptionChannel.trySend(e) + emitError(e) } override fun setOnRedirectListener(listener: () -> Unit) { redirectHandler.setOnRedirectListener(listener) } + private fun emitError(e: CheckoutException) { + exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(details: JSONObject) { + detailsChannel.trySend(createActionComponentData(details)) + clearState() + } + + private fun clearState() { + action = null + } + override fun onCleared() { removeObserver() redirectHandler.removeOnRedirectListener() @@ -181,5 +215,8 @@ internal class DefaultRedirectDelegate( companion object { private const val RETURN_URL_QUERY_STRING_PARAMETER = "returnUrlQueryString" + + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt b/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt index 8ed15a008e..eeddf1fe54 100644 --- a/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt +++ b/redirect/src/test/java/com/adyen/checkout/redirect/internal/ui/DefaultRedirectDelegateTest.kt @@ -201,8 +201,48 @@ internal class DefaultRedirectDelegateTest( } } + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultRedirectDelegate.ACTION_KEY, + RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), + ) + } + delegate = createDelegate(savedStateHandle = savedStateHandle) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertEquals("paymentData", paymentDataRepository.paymentData) + } + + @Test + fun `when details are emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle = savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + delegate.handleIntent(Intent()) + + assertNull(savedStateHandle[DefaultRedirectDelegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle = savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + redirectHandler.exception = ComponentException("Test") + + delegate.handleAction(RedirectAction(paymentMethodType = "test", paymentData = "paymentData"), Activity()) + + assertNull(savedStateHandle[DefaultRedirectDelegate.ACTION_KEY]) + } + private fun createDelegate( - observerRepository: ActionObserverRepository = ActionObserverRepository() + observerRepository: ActionObserverRepository = ActionObserverRepository(), + savedStateHandle: SavedStateHandle = SavedStateHandle(), ): DefaultRedirectDelegate { val configuration = CheckoutConfiguration( Environment.TEST, @@ -212,6 +252,7 @@ internal class DefaultRedirectDelegateTest( } return DefaultRedirectDelegate( observerRepository = observerRepository, + savedStateHandle = savedStateHandle, componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( configuration, Locale.US, From 7a8a7168d5a7c17d03414fb37d21687615ea0b60 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 29 May 2024 13:44:21 +0200 Subject: [PATCH 268/272] Correctly restore state of WeChatPay COAND-857 --- .../WeChatPayActionComponentProvider.kt | 1 + .../internal/ui/DefaultWeChatDelegate.kt | 79 ++++++++++++----- .../internal/ui/DefaultWeChatDelegateTest.kt | 84 +++++++++++++++---- 3 files changed, 129 insertions(+), 35 deletions(-) diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt index c53ccbdbb1..249d034cad 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/provider/WeChatPayActionComponentProvider.kt @@ -90,6 +90,7 @@ constructor( val paymentDataRepository = PaymentDataRepository(savedStateHandle) return DefaultWeChatDelegate( observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, componentParams = componentParams, iwxApi = iwxApi, payRequestGenerator = requestGenerator, diff --git a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt index 3180c4a5f0..6353ab54da 100644 --- a/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt +++ b/wechatpay/src/main/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegate.kt @@ -12,6 +12,7 @@ import android.app.Activity import android.content.Intent import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.SavedStateHandle import com.adyen.checkout.components.core.ActionComponentData import com.adyen.checkout.components.core.action.Action import com.adyen.checkout.components.core.action.SdkAction @@ -19,6 +20,8 @@ import com.adyen.checkout.components.core.action.WeChatPaySdkData import com.adyen.checkout.components.core.internal.ActionComponentEvent import com.adyen.checkout.components.core.internal.ActionObserverRepository import com.adyen.checkout.components.core.internal.PaymentDataRepository +import com.adyen.checkout.components.core.internal.SavedStateHandleContainer +import com.adyen.checkout.components.core.internal.SavedStateHandleProperty import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager import com.adyen.checkout.components.core.internal.analytics.GenericEvents import com.adyen.checkout.components.core.internal.ui.model.GenericComponentParams @@ -42,14 +45,17 @@ import org.json.JSONException import org.json.JSONObject @Suppress("TooManyFunctions") -internal class DefaultWeChatDelegate( +internal class DefaultWeChatDelegate +@Suppress("LongParameterList") +constructor( private val observerRepository: ActionObserverRepository, + override val savedStateHandle: SavedStateHandle, override val componentParams: GenericComponentParams, private val iwxApi: IWXAPI, private val payRequestGenerator: WeChatRequestGenerator<*>, private val paymentDataRepository: PaymentDataRepository, private val analyticsManager: AnalyticsManager?, -) : WeChatDelegate { +) : WeChatDelegate, SavedStateHandleContainer { private val detailsChannel: Channel = bufferedChannel() override val detailsFlow: Flow = detailsChannel.receiveAsFlow() @@ -59,8 +65,15 @@ internal class DefaultWeChatDelegate( override val viewFlow: Flow = MutableStateFlow(WeChatComponentViewType) + private var action: SdkAction? by SavedStateHandleProperty(ACTION_KEY) + override fun initialize(coroutineScope: CoroutineScope) { - // no ops + restoreState() + } + + private fun restoreState() { + adyenLog(AdyenLogLevel.DEBUG) { "Restoring state" } + action?.let { initState(it) } } override fun observe( @@ -93,7 +106,7 @@ internal class DefaultWeChatDelegate( @VisibleForTesting internal fun onResponse(baseResponse: BaseResp) { parseResult(baseResponse)?.let { response -> - detailsChannel.trySend(createActionComponentData(response)) + emitDetails(response) } } @@ -102,7 +115,7 @@ internal class DefaultWeChatDelegate( try { result.put(RESULT_CODE, baseResp.errCode) } catch (e: JSONException) { - exceptionChannel.trySend(CheckoutException("Error parsing result.", e)) + emitError(CheckoutException("Error parsing result.", e)) return null } return result @@ -116,38 +129,47 @@ internal class DefaultWeChatDelegate( override fun handleAction(action: Action, activity: Activity) { val sdkAction = (action as? SdkAction<*>) if (sdkAction == null) { - exceptionChannel.trySend(ComponentException("Unsupported action")) - return - } - - val activityName = activity.javaClass.name - adyenLog(AdyenLogLevel.DEBUG) { "handleAction: activity - $activityName" } - - val paymentData = action.paymentData - paymentDataRepository.paymentData = paymentData - if (paymentData == null) { - adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } - exceptionChannel.trySend(ComponentException("Payment data is null")) + emitError(ComponentException("Unsupported action")) return } val sdkData = action.sdkData if (sdkData == null || sdkData !is WeChatPaySdkData) { - exceptionChannel.trySend(ComponentException("SDK Data is null")) + emitError(ComponentException("SDK Data is null")) return } + @Suppress("UNCHECKED_CAST") + this.action = action as SdkAction + val event = GenericEvents.action( component = action.paymentMethodType.orEmpty(), subType = action.type.orEmpty(), ) analyticsManager?.trackEvent(event) + initState(action) + launchAction(sdkData, activity) + } + + private fun initState(action: SdkAction) { + val paymentData = action.paymentData + paymentDataRepository.paymentData = paymentData + if (paymentData == null) { + adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } + emitError(ComponentException("Payment data is null")) + } + } + + private fun launchAction(sdkData: WeChatPaySdkData, activity: Activity) { + val activityName = activity.javaClass.name + + adyenLog(AdyenLogLevel.DEBUG) { "handleAction: activity - $activityName" } + val isWeChatNotInitiated = !initiateWeChatPayRedirect(sdkData, activityName) if (isWeChatNotInitiated) { - exceptionChannel.trySend(ComponentException("Failed to initialize WeChat app")) - return + emitError(ComponentException("Failed to initialize WeChat app")) } } @@ -166,7 +188,21 @@ internal class DefaultWeChatDelegate( } override fun onError(e: CheckoutException) { + emitError(e) + } + + private fun emitError(e: CheckoutException) { exceptionChannel.trySend(e) + clearState() + } + + private fun emitDetails(details: JSONObject) { + detailsChannel.trySend(createActionComponentData(details)) + clearState() + } + + private fun clearState() { + action = null } override fun onCleared() { @@ -175,5 +211,8 @@ internal class DefaultWeChatDelegate( companion object { private const val RESULT_CODE = "resultCode" + + @VisibleForTesting + internal const val ACTION_KEY = "ACTION_KEY" } } diff --git a/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt b/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt index 08b1bd1ff0..c717529aa5 100644 --- a/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt +++ b/wechatpay/src/test/java/com/adyen/checkout/wechatpay/internal/ui/DefaultWeChatDelegateTest.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.json.JSONObject import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -61,22 +62,8 @@ internal class DefaultWeChatDelegateTest( @BeforeEach fun beforeEach() { analyticsManager = TestAnalyticsManager() - val configuration = CheckoutConfiguration( - Environment.TEST, - TEST_CLIENT_KEY, - ) { - weChatPayAction() - } paymentDataRepository = PaymentDataRepository(SavedStateHandle()) - delegate = DefaultWeChatDelegate( - observerRepository = ActionObserverRepository(), - componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) - .mapToParams(configuration, Locale.US, null, null), - iwxApi = iwxApi, - payRequestGenerator = weChatRequestGenerator, - paymentDataRepository = paymentDataRepository, - analyticsManager = analyticsManager, - ) + delegate = createDelegate() } @Test @@ -187,6 +174,73 @@ internal class DefaultWeChatDelegateTest( } } + @Test + fun `when initializing and action is set, then state is restored`() = runTest { + val savedStateHandle = SavedStateHandle().apply { + set( + DefaultWeChatDelegate.ACTION_KEY, + SdkAction(paymentMethodType = "test", paymentData = "paymentData", sdkData = WeChatPaySdkData()), + ) + } + delegate = createDelegate(savedStateHandle) + + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + assertEquals("paymentData", paymentDataRepository.paymentData) + } + + @Test + fun `when details are emitted, then state is cleared`() = runTest { + whenever(iwxApi.sendReq(anyOrNull())) doReturn true + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.handleAction( + SdkAction(paymentMethodType = "test", paymentData = "paymentData", sdkData = WeChatPaySdkData()), + Activity(), + ) + + delegate.onResponse(PayResp().apply { errCode = 1 }) + + assertNull(savedStateHandle[DefaultWeChatDelegate.ACTION_KEY]) + } + + @Test + fun `when an error is emitted, then state is cleared`() = runTest { + val savedStateHandle = SavedStateHandle() + delegate = createDelegate(savedStateHandle) + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + + delegate.handleAction( + SdkAction(paymentMethodType = "test", paymentData = null, sdkData = WeChatPaySdkData()), + Activity(), + ) + + assertNull(savedStateHandle[DefaultWeChatDelegate.ACTION_KEY]) + } + + private fun createDelegate( + savedStateHandle: SavedStateHandle = SavedStateHandle(), + ): DefaultWeChatDelegate { + val configuration = CheckoutConfiguration( + Environment.TEST, + TEST_CLIENT_KEY, + ) { + weChatPayAction() + } + + return DefaultWeChatDelegate( + observerRepository = ActionObserverRepository(), + savedStateHandle = savedStateHandle, + componentParams = GenericComponentParamsMapper(CommonComponentParamsMapper()) + .mapToParams(configuration, Locale.US, null, null), + iwxApi = iwxApi, + payRequestGenerator = weChatRequestGenerator, + paymentDataRepository = paymentDataRepository, + analyticsManager = analyticsManager, + ) + } + companion object { private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" private const val TEST_PAYMENT_METHOD_TYPE = "TEST_PAYMENT_METHOD_TYPE" From fa75c597099b9afae7c16cd49df5bf0fcf39b8e1 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 29 May 2024 14:51:09 +0200 Subject: [PATCH 269/272] Remove storedEventCount counter, since it was not reliable due to concurrent operations COAND-845 --- .../core/internal/analytics/DefaultAnalyticsManager.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt index 9db6e43d27..89e0fcb27c 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/analytics/DefaultAnalyticsManager.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import java.util.concurrent.atomic.AtomicInteger import kotlin.time.Duration.Companion.seconds internal class DefaultAnalyticsManager( @@ -42,8 +41,6 @@ internal class DefaultAnalyticsManager( private var ownerReference: String? = null - private var storedEventCount: AtomicInteger = AtomicInteger(0) - override fun initialize(owner: Any, coroutineScope: CoroutineScope) { if (isInitialized) { adyenLog(AdyenLogLevel.DEBUG) { "Already initialized, ignoring." } @@ -86,7 +83,7 @@ internal class DefaultAnalyticsManager( startTimer() } }.fold( - onSuccess = { storedEventCount.incrementAndGet() }, + onSuccess = { /* Not necessary */ }, onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Storing event failed" } }, ) } @@ -116,7 +113,7 @@ internal class DefaultAnalyticsManager( runSuspendCatching { analyticsRepository.sendEvents(checkoutAttemptId) }.fold( - onSuccess = { storedEventCount.set(0) }, + onSuccess = { /* Not necessary */ }, onFailure = { throwable -> adyenLog(AdyenLogLevel.WARN, throwable) { "Failed sending analytics events" } }, ) } @@ -133,7 +130,7 @@ internal class DefaultAnalyticsManager( return } - adyenLog(AdyenLogLevel.DEBUG) { "Clear called while there are ${storedEventCount.get()} events stored." } + adyenLog(AdyenLogLevel.DEBUG) { "Clearing analytics manager" } _coroutineScope = null checkoutAttemptId = null From 3082a7b6da15bd3196189f959789bc194844759f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 28 May 2024 15:55:24 +0200 Subject: [PATCH 270/272] Bump version number to 5.4.0 --- README.md | 10 +++++----- dependencies.gradle | 2 +- example-app/build.gradle | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 875e809922..90edf1f1a1 100644 --- a/README.md +++ b/README.md @@ -31,23 +31,23 @@ Import the corresponding module in your `build.gradle` file. For Drop-in: ```groovy -implementation "com.adyen.checkout:drop-in-compose:5.3.1" +implementation "com.adyen.checkout:drop-in-compose:5.4.0" ``` For the Credit Card component: ```groovy -implementation "com.adyen.checkout:card:5.3.1" -implementation "com.adyen.checkout:components-compose:5.3.1" +implementation "com.adyen.checkout:card:5.4.0" +implementation "com.adyen.checkout:components-compose:5.4.0" ``` ### Without Jetpack Compose For Drop-in: ```groovy -implementation "com.adyen.checkout:drop-in:5.3.1" +implementation "com.adyen.checkout:drop-in:5.4.0" ``` For the Credit Card component: ```groovy -implementation "com.adyen.checkout:card:5.3.1" +implementation "com.adyen.checkout:card:5.4.0" ``` The library is available on [Maven Central][mavenRepo]. diff --git a/dependencies.gradle b/dependencies.gradle index 56962668d7..f4b2c862e2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -16,7 +16,7 @@ ext { // just for example app, don't need to increment version_code = 1 // The version_name format is "major.minor.patch(-(alpha|beta|rc)[0-9]{2}){0,1}" (e.g. 3.0.0, 3.1.1-alpha04 or 3.1.4-rc01 etc). - version_name = "5.3.1" + version_name = "5.4.0" // Build Script android_gradle_plugin_version = '8.3.2' diff --git a/example-app/build.gradle b/example-app/build.gradle index 07c00267b6..1390092971 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -71,8 +71,8 @@ dependencies { // Checkout implementation project(':drop-in') implementation project(':components-compose') -// implementation "com.adyen.checkout:drop-in:5.3.1" -// implementation "com.adyen.checkout:components-compose:5.3.1" +// implementation "com.adyen.checkout:drop-in:5.4.0" +// implementation "com.adyen.checkout:components-compose:5.4.0" // Dependencies implementation libraries.kotlinCoroutines From feb15cde71590af1c2fd06deedea64748069ec26 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 29 May 2024 10:05:51 +0200 Subject: [PATCH 271/272] Update release notes --- RELEASE_NOTES.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9c1cced4ca..e24dd0ac4a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,9 +9,8 @@ [//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object) ## New -- For external redirects launched in a Custom Tab, you can now [customize the toolbar and navigation bar colors](docs/UI_CUSTOMIZATION.md#styling-custom-tabs) of the Custom Tab. -- Payment method **twint** now supports a native flow, eliminating the need to redirect through the browser. -You can change this behaviour by using the following configuration: +- For external redirects, you can now [customize the colors of the toolbar and navigation bar](docs/UI_CUSTOMIZATION.md#styling-custom-tabs) displayed in [Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs). +- TWINT is now supported with a native flow, and you no longer need to redirect shoppers through the browser. To use the redirect flow, set the following configuration: ```kotlin CheckoutConfiguration( environment = environment, @@ -26,18 +25,18 @@ CheckoutConfiguration( ``` ## Fixed -- Fixed various memory leaks. -- Drop-in no longer overrides the log level in case of debug builds. -- Address Lookup not displaying validation error on Card Component when no address has been selected. +- Fixed some memory leaks. +- In case of a debug build, Drop-in no longer overrides the log level. +- For cards, when a shopper does not select an address, the address lookup function now displays a validation error. - Actions no longer crash when your app uses obfuscation. -- Drop-in no longer throws an error while handling a 3DS2 challenge on API 66 and below. -- When the app process dies during action handling, then the state will now be restored and the payment can be continued. -- Fixed ignoring `setEnableRemovingStoredPaymentMethods` flag set in Drop-in configuration for sessions. +- When handling a 3D Secure 2 challenge using Checkout API v66 or earlier, Drop-in no longer throws an error. +- If the app process unexpectedly terminates when handling actions, the state is now restored and you can proceed with the payment flow. +- For `/sessions`, fixed an issue where the `setEnableRemovingStoredPaymentMethods` flag in the [Drop-in configuration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Android&integration=Drop-in#3-optional-add-a-configuration-object) was ignored. ## Changed -- Flags are replaced by ISO codes in the phone number inputs (affected payment methods: MB Way, Pay Easy, Convenience Stores Japan, Online Banking Japan and Seven-Eleven). -- Strings containing "country" are changed to "country/region". +- The phone number input field in the payment form now shows ISO codes instead of flags. +- The UI elements that were previously labelled **Country** are now **Country/Region**. - Dependency versions: | Name | Version | |--------------------------------------------------------------------------------------------------------|-------------------------------| - | [Adyen 3DS2](https://github.com/Adyen/adyen-3ds2-android/releases/tag/2.2.18) | **2.2.18** | + | [Adyen 3DS2](https://github.com/Adyen/adyen-3ds2-android/releases/tag/2.2.18) | **2.2.18** | From 20e77ccb752ee386c437ec437153c933f34e828f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 29 May 2024 16:46:57 +0200 Subject: [PATCH 272/272] Add dependency updates to release notes --- RELEASE_NOTES.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e24dd0ac4a..fbb0bf6778 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -37,6 +37,16 @@ CheckoutConfiguration( - The phone number input field in the payment form now shows ISO codes instead of flags. - The UI elements that were previously labelled **Country** are now **Country/Region**. - Dependency versions: - | Name | Version | - |--------------------------------------------------------------------------------------------------------|-------------------------------| - | [Adyen 3DS2](https://github.com/Adyen/adyen-3ds2-android/releases/tag/2.2.18) | **2.2.18** | + | Name | Version | + |--------------------------------------------------------------------------------------------------------------|-------------------------------| + | [Adyen 3DS2](https://github.com/Adyen/adyen-3ds2-android/releases/tag/2.2.18) | **2.2.18** | + | [Android Gradle plugin](https://developer.android.com/build/releases/gradle-plugin) | **8.3.2** | + | [AndroidX Browser](https://developer.android.com/jetpack/androidx/releases/browser#1.8.0) | **1.8.0** | + | [AndroidX Compose Activity](https://developer.android.com/jetpack/androidx/releases/activity#1.9.0) | **1.9.0** | + | [AndroidX Compose BoM](https://developer.android.com/develop/ui/compose/bom/bom-mapping) | **2024.04.01** | + | [AndroidX Compose Compiler](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.12) | **1.5.12** | + | [AndroidX Lifecycle](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.7.0) | **2.7.0** | + | [Google Pay](https://developers.google.com/pay/api/android/support/release-notes#feb-24) | **19.3.0** | + | [Google Pay Compose Button](https://github.com/google-pay/compose-pay-button/releases/tag/v1.0.0) | **1.0.0** | + | [Kotlin](https://github.com/JetBrains/kotlin/releases/tag/v1.9.24) | **1.9.24** | + | [Kotlin coroutines](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/1.8.0) | **1.8.0** |