Skip to content

Commit

Permalink
Merge pull request #1887 from Adyen/feature/analytics_track_third_par…
Browse files Browse the repository at this point in the history
…ty_error_events

Analytics - Track third party error events
  • Loading branch information
araratthehero authored Nov 28, 2024
2 parents 9a85e78 + a9e34b5 commit 81c2842
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.ErrorEvent
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
Expand Down Expand Up @@ -284,6 +285,7 @@ constructor(
}

is CashAppPayState.CashAppPayExceptionState -> {
trackThirdPartyErrorEvent()
exceptionChannel.trySend(
ComponentException("Cash App Pay has encountered an error", newState.exception),
)
Expand Down Expand Up @@ -311,6 +313,14 @@ constructor(
)
}

private fun trackThirdPartyErrorEvent() {
val event = GenericEvents.error(
component = getPaymentMethodType(),
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager.trackEvent(event)
}

override fun isConfirmationRequired(): Boolean =
_viewFlow.value is ButtonComponentViewType &&
componentParams.showStorePaymentField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +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.analytics.ErrorEvent
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
Expand Down Expand Up @@ -532,6 +533,20 @@ internal class DefaultCashAppPayDelegateTest(
analyticsManager.assertLastEventNotEquals(expectedEvent)
}

@Test
fun `when state is exception, then an error is tracked`() = runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val exception = RuntimeException("Stub!")

delegate.cashAppPayStateDidChange(CashAppPayState.CashAppPayExceptionState(exception))

val expectedEvent = GenericEvents.error(
component = TEST_PAYMENT_METHOD_TYPE,
event = ErrorEvent.THIRD_PARTY
)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@Test
fun `when delegate is cleared then analytics manager is cleared`() {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.ErrorEvent
import com.adyen.checkout.components.core.internal.analytics.GenericEvents
import com.adyen.checkout.components.core.internal.util.bufferedChannel
import com.adyen.checkout.core.AdyenLogLevel
Expand Down Expand Up @@ -149,6 +150,7 @@ internal class DefaultGooglePayDelegate(
when (resultCode) {
Activity.RESULT_OK -> {
if (data == null) {
trackThirdPartyErrorEvent()
exceptionChannel.trySend(ComponentException("Result data is null"))
return
}
Expand All @@ -161,6 +163,8 @@ internal class DefaultGooglePayDelegate(
}

AutoResolveHelper.RESULT_ERROR -> {
trackThirdPartyErrorEvent()

val status = AutoResolveHelper.getStatusFromIntent(data)
val statusMessage: String = status?.let { ": ${it.statusMessage}" }.orEmpty()
exceptionChannel.trySend(ComponentException("GooglePay returned an error$statusMessage"))
Expand All @@ -170,6 +174,14 @@ internal class DefaultGooglePayDelegate(
}
}

private fun trackThirdPartyErrorEvent() {
val event = GenericEvents.error(
component = getPaymentMethodType(),
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager.trackEvent(event)
}

override fun getGooglePayButtonParameters(): GooglePayButtonParameters {
val allowedPaymentMethodsList = GooglePayUtils.getAllowedPaymentMethods(componentParams)
val allowedPaymentMethods = ModelUtils.serializeOptList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

package com.adyen.checkout.googlepay.internal.ui

import android.app.Activity
import app.cash.turbine.test
import com.adyen.checkout.components.core.Amount
import com.adyen.checkout.components.core.CheckoutConfiguration
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.ErrorEvent
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
Expand Down Expand Up @@ -185,6 +187,17 @@ internal class DefaultGooglePayDelegateTest {

analyticsManager.assertIsCleared()
}

@Test
fun `when activity result is OK and data is null, then error event is tracked`() {
delegate.handleActivityResult(Activity.RESULT_OK, data = null)

val expectedEvent = GenericEvents.error(
component = TEST_PAYMENT_METHOD_TYPE,
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager.assertLastEventEquals(expectedEvent)
}
}

private fun createCheckoutConfiguration(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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.ErrorEvent
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
Expand Down Expand Up @@ -163,10 +164,12 @@ internal class DefaultTwintActionDelegate(
}

TwintPayResult.TW_B_ERROR -> {
trackThirdPartyErrorEvent()
onError(ComponentException("Twint encountered an error."))
}

TwintPayResult.TW_B_APP_NOT_INSTALLED -> {
trackThirdPartyErrorEvent()
onError(ComponentException("Twint app not installed."))
}
}
Expand Down Expand Up @@ -220,6 +223,14 @@ internal class DefaultTwintActionDelegate(
)
}

private fun trackThirdPartyErrorEvent() {
val event = GenericEvents.error(
component = action?.paymentMethodType.orEmpty(),
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager?.trackEvent(event)
}

override fun onError(e: CheckoutException) {
emitError(e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ 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.ErrorEvent
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.TestStatusRepository
Expand Down Expand Up @@ -287,6 +288,32 @@ internal class DefaultTwintActionDelegateTest {
)
analyticsManager.assertLastEventEquals(expectedEvent)
}

@ParameterizedTest
@MethodSource(
"com.adyen.checkout.twint.action.internal.ui.DefaultTwintActionDelegateTest#handleTwintResultErrorEvents",
)
fun `when handling twint result, then event is tracked`(
result: TwintPayResult,
errorEvent: ErrorEvent
) = runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
val action = SdkAction(
paymentMethodType = TEST_PAYMENT_METHOD_TYPE,
type = TEST_ACTION_TYPE,
paymentData = TEST_PAYMENT_DATA,
sdkData = TwintSdkData("token", false),
)
delegate.handleAction(action, Activity())

delegate.handleTwintResult(result)

val expectedEvent = GenericEvents.error(
component = TEST_PAYMENT_METHOD_TYPE,
event = errorEvent,
)
analyticsManager.assertLastEventEquals(expectedEvent)
}
}

private fun createDelegate(
Expand Down Expand Up @@ -347,6 +374,19 @@ internal class DefaultTwintActionDelegateTest {
TwintTestResult.Error("Twint app not installed."),
),
)

@JvmStatic
fun handleTwintResultErrorEvents() = listOf(
// result, errorEvent
arguments(
TwintPayResult.TW_B_ERROR,
ErrorEvent.THIRD_PARTY,
),
arguments(
TwintPayResult.TW_B_APP_NOT_INSTALLED,
ErrorEvent.THIRD_PARTY,
),
)
}

sealed class TwintTestResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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.ErrorEvent
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
Expand Down Expand Up @@ -169,6 +170,7 @@ constructor(
val isWeChatNotInitiated = !initiateWeChatPayRedirect(sdkData, activityName)

if (isWeChatNotInitiated) {
trackThirdPartyErrorEvent()
emitError(ComponentException("Failed to initialize WeChat app"))
}
}
Expand All @@ -187,6 +189,14 @@ constructor(
)
}

private fun trackThirdPartyErrorEvent() {
val event = GenericEvents.error(
component = action?.paymentMethodType.orEmpty(),
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager?.trackEvent(event)
}

override fun onError(e: CheckoutException) {
emitError(e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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.ErrorEvent
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
Expand Down Expand Up @@ -170,6 +171,25 @@ internal class DefaultWeChatDelegateTest(
component = TEST_PAYMENT_METHOD_TYPE,
subType = TEST_ACTION_TYPE,
)
analyticsManager.assertHasEventEquals(expectedEvent)
}

@Test
fun `when handleAction is called and sending a request to WeChat fails, then an error is tracked`() {
whenever(iwxApi.sendReq(any())) doReturn false
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.error(
component = TEST_PAYMENT_METHOD_TYPE,
event = ErrorEvent.THIRD_PARTY,
)
analyticsManager.assertLastEventEquals(expectedEvent)
}
}
Expand Down

0 comments on commit 81c2842

Please sign in to comment.