Skip to content

Commit

Permalink
Merge pull request #1936 from Adyen/feature/analytics_track_api_errors
Browse files Browse the repository at this point in the history
Analytics - Track api errors
  • Loading branch information
araratthehero authored Jan 3, 2025
2 parents f478fb4 + e91f5fe commit e377d9a
Show file tree
Hide file tree
Showing 43 changed files with 172 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ internal class DefaultAdyen3DS2Delegate(
val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
adyenLog(AdyenLogLevel.ERROR, throwable) { "Unexpected uncaught 3DS2 Exception" }
trackFingerprintErrorEvent(
errorEvent = ErrorEvent.THREEDS2_FINGERPRINT_CREATION,
message = "Fingerprint creation failed because of uncaught exception",
errorEvent = ErrorEvent.THREEDS2_FINGERPRINT_HANDLING,
message = "Fingerprint handling failed because of uncaught exception",
)
emitError(CheckoutException("Unexpected 3DS2 exception.", throwable))
}
Expand Down Expand Up @@ -436,7 +436,7 @@ internal class DefaultAdyen3DS2Delegate(
.fold(
onSuccess = { result -> onSubmitFingerprintResult(result, activity) },
onFailure = { e ->
trackFingerprintErrorEvent(ErrorEvent.THREEDS2_FINGERPRINT_HANDLING)
trackFingerprintErrorEvent(ErrorEvent.API_THREEDS2)
emitError(ComponentException("Unable to submit fingerprint", e))
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ internal class DefaultAdyen3DS2DelegateTest(
delegate.identifyShopper(Activity(), encodedJson, false)

val expectedEvent = ThreeDS2Events.threeDS2FingerprintError(
event = ErrorEvent.THREEDS2_FINGERPRINT_CREATION,
event = ErrorEvent.THREEDS2_FINGERPRINT_HANDLING,
)
analyticsManager.assertLastEventEquals(expectedEvent)
}
Expand Down Expand Up @@ -837,7 +837,7 @@ internal class DefaultAdyen3DS2DelegateTest(
delegate.identifyShopper(Activity(), encodedJson, true)

val expectedEvent = ThreeDS2Events.threeDS2FingerprintError(
event = ErrorEvent.THREEDS2_FINGERPRINT_HANDLING,
event = ErrorEvent.API_THREEDS2,
)
analyticsManager.assertLastEventEquals(expectedEvent)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

return SessionComponentEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ internal class DefaultACHDirectDebitDelegate(
},
onFailure = { e ->
adyenLog(AdyenLogLevel.ERROR) { "Unable to fetch public key" }

val event = GenericEvents.error(paymentMethod.type.orEmpty(), ErrorEvent.API_PUBLIC_KEY)
analyticsManager.trackEvent(event)

exceptionChannel.trySend(ComponentException("Unable to fetch publicKey.", e))
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,14 @@ internal class DefaultACHDirectDebitDelegateTest(
assertEquals(TEST_CHECKOUT_ATTEMPT_ID, componentState.data.paymentMethod?.checkoutAttemptId)
}

@Test
fun `when fetching the public key fails, then an error event is tracked`() = runTest {
publicKeyRepository.shouldReturnError = true
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val expectedEvent = GenericEvents.error(TEST_PAYMENT_METHOD_TYPE, ErrorEvent.API_PUBLIC_KEY)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.onCleared()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<BacsDirectDebitComponentState>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler = SessionComponentEventHandler<BcmcComponentState>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler = SessionComponentEventHandler<BlikComponentState>(
Expand Down Expand Up @@ -425,6 +426,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<CardComponentState>(
sessionInteractor = sessionInteractor,
Expand Down Expand Up @@ -504,6 +505,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<CardComponentState>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ class DefaultCardDelegate(
},
onFailure = { e ->
adyenLog(AdyenLogLevel.ERROR) { "Unable to fetch public key" }

val event = GenericEvents.error(paymentMethod.type.orEmpty(), ErrorEvent.API_PUBLIC_KEY)
analyticsManager.trackEvent(event)

exceptionChannel.trySend(ComponentException("Unable to fetch publicKey.", e))
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ internal class StoredCardDelegate(
updateComponentState(outputData)
},
onFailure = { e ->
val event = GenericEvents.error(storedPaymentMethod.type.orEmpty(), ErrorEvent.API_PUBLIC_KEY)
analyticsManager.trackEvent(event)

exceptionChannel.trySend(ComponentException("Unable to fetch publicKey.", e))
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,14 @@ internal class DefaultCardDelegateTest(
}
}

@Test
fun `when fetching the public key fails, then an error event is tracked`() = runTest {
publicKeyRepository.shouldReturnError = true
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val expectedEvent = GenericEvents.error(PaymentMethodTypes.SCHEME, ErrorEvent.API_PUBLIC_KEY)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.onCleared()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,14 @@ internal class StoredCardDelegateTest(
}
}

@Test
fun `when fetching the public key fails, then an error event is tracked`() = runTest {
publicKeyRepository.shouldReturnError = true
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val expectedEvent = GenericEvents.error(CardPaymentMethod.PAYMENT_METHOD_TYPE, ErrorEvent.API_PUBLIC_KEY)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.onCleared()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

return SessionComponentEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ enum class ErrorEvent(val errorType: Type, val errorCode: String) {

// API
API_PAYMENTS(Type.API_ERROR, "620"),
API_PAYMENTS_DETAILS(Type.API_ERROR, "621"),
API_THREEDS2(Type.API_ERROR, "622"),
API_ORDER(Type.API_ERROR, "623"),
API_PUBLIC_KEY(Type.API_ERROR, "624"),
API_NATIVE_REDIRECT(Type.API_ERROR, "625"),

Expand Down
2 changes: 1 addition & 1 deletion drop-in/api/drop-in.api
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public final class com/adyen/checkout/dropin/SessionDropInResultContract : andro

public class com/adyen/checkout/dropin/SessionDropInService : com/adyen/checkout/dropin/internal/service/BaseDropInService, com/adyen/checkout/dropin/SessionDropInServiceContract, com/adyen/checkout/dropin/internal/service/SessionDropInServiceInterface {
public fun <init> ()V
public final fun initialize (Lcom/adyen/checkout/sessions/core/SessionModel;Ljava/lang/String;Lcom/adyen/checkout/core/Environment;Z)V
public final fun initialize (Lcom/adyen/checkout/sessions/core/SessionModel;Ljava/lang/String;Lcom/adyen/checkout/core/Environment;ZLcom/adyen/checkout/components/core/internal/analytics/AnalyticsManager;)V
public final fun isFlowTakenOver ()Z
public fun onAdditionalDetails (Lcom/adyen/checkout/components/core/ActionComponentData;)Z
public fun onBalanceCheck (Lcom/adyen/checkout/components/core/PaymentComponentState;)Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.adyen.checkout.components.core.OrderRequest
import com.adyen.checkout.components.core.OrderResponse
import com.adyen.checkout.components.core.PaymentComponentState
import com.adyen.checkout.components.core.StoredPaymentMethod
import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.Environment
import com.adyen.checkout.core.internal.data.api.HttpClientFactory
Expand Down Expand Up @@ -50,7 +51,8 @@ open class SessionDropInService : BaseDropInService(), SessionDropInServiceInter
sessionModel: SessionModel,
clientKey: String,
environment: Environment,
isFlowTakenOver: Boolean
isFlowTakenOver: Boolean,
analyticsManager: AnalyticsManager,
) {
val httpClient = HttpClientFactory.getHttpClient(environment)
val sessionService = SessionService(httpClient)
Expand All @@ -61,6 +63,7 @@ open class SessionDropInService : BaseDropInService(), SessionDropInServiceInter
),
sessionModel = sessionModel,
isFlowTakenOver = isFlowTakenOver,
analyticsManager = analyticsManager,
)
this.isFlowTakenOver = isFlowTakenOver

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.adyen.checkout.components.core.LookupAddress
import com.adyen.checkout.components.core.OrderRequest
import com.adyen.checkout.components.core.PaymentComponentState
import com.adyen.checkout.components.core.StoredPaymentMethod
import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager
import com.adyen.checkout.core.Environment
import com.adyen.checkout.dropin.BaseDropInServiceResult
import com.adyen.checkout.sessions.core.SessionModel
Expand All @@ -39,6 +40,7 @@ internal interface SessionDropInServiceInterface : BaseDropInServiceInterface {
sessionModel: SessionModel,
clientKey: String,
environment: Environment,
isFlowTakenOver: Boolean
isFlowTakenOver: Boolean,
analyticsManager: AnalyticsManager,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ internal class DropInActivity :
clientKey = event.clientKey,
environment = event.environment,
isFlowTakenOver = event.isFlowTakenOver,
analyticsManager = event.analyticsManager,
)
}

Expand Down Expand Up @@ -728,8 +729,6 @@ internal class DropInActivity :
private const val LOADING_FRAGMENT_TAG = "LOADING_DIALOG_FRAGMENT"
private const val GIFT_CARD_PAYMENT_CONFIRMATION_FRAGMENT_TAG = "GIFT_CARD_PAYMENT_CONFIRMATION_FRAGMENT"

internal const val GOOGLE_PAY_REQUEST_CODE = 1

fun createIntent(
context: Context,
checkoutConfiguration: CheckoutConfiguration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ internal class DropInViewModel(
clientKey = dropInParams.clientKey,
environment = dropInParams.environment,
isFlowTakenOver = isSessionsFlowTakenOver,
analyticsManager = analyticsManager,
)
sendEvent(event)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package com.adyen.checkout.dropin.internal.ui.model
import com.adyen.checkout.components.core.OrderRequest
import com.adyen.checkout.components.core.PaymentComponentState
import com.adyen.checkout.components.core.PaymentMethod
import com.adyen.checkout.components.core.internal.analytics.AnalyticsManager
import com.adyen.checkout.core.Environment
import com.adyen.checkout.sessions.core.SessionModel

Expand All @@ -25,6 +26,7 @@ internal sealed class DropInActivityEvent {
val clientKey: String,
val environment: Environment,
val isFlowTakenOver: Boolean,
val analyticsManager: AnalyticsManager,
) : DropInActivityEvent()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<ComponentStateT>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionsGiftCardComponentEventHandler = SessionsGiftCardComponentEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ class DefaultGiftCardDelegate(
},
onFailure = { e ->
adyenLog(AdyenLogLevel.ERROR) { "Unable to fetch public key" }

val event = GenericEvents.error(paymentMethod.type.orEmpty(), ErrorEvent.API_PUBLIC_KEY)
analyticsManager.trackEvent(event)

exceptionChannel.trySend(ComponentException("Unable to fetch publicKey.", e))
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,14 @@ internal class DefaultGiftCardDelegateTest(
}
}

@Test
fun `when fetching the public key fails, then an error event is tracked`() = runTest {
publicKeyRepository.shouldReturnError = true
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val expectedEvent = GenericEvents.error(TEST_PAYMENT_METHOD_TYPE, ErrorEvent.API_PUBLIC_KEY)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.onCleared()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler = SessionComponentEventHandler<GooglePayComponentState>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<IdealComponentState>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<InstantComponentState>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<ComponentStateT>(
sessionInteractor = sessionInteractor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionComponentEventHandler = SessionComponentEventHandler<MBWayComponentState>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)

val sessionsGiftCardComponentEventHandler = SessionsGiftCardComponentEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ constructor(
),
sessionModel = sessionSavedStateHandleContainer.getSessionModel(),
isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false,
analyticsManager = analyticsManager,
)
val sessionComponentEventHandler = SessionComponentEventHandler<ComponentStateT>(
sessionInteractor = sessionInteractor,
Expand Down
Loading

0 comments on commit e377d9a

Please sign in to comment.