Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,51 +49,48 @@ internal class CheckoutPaymentElementAnalyticsTest {
private val formPage = EmbeddedFormPage(testRules.compose)

@Test
fun testSheetAnalyticsUsesCheckoutProductUsage() = runCheckoutPaymentElementScenario(
fun testSheetAnalyticsUsesPaymentElementProductUsageAndCheckoutSessionId() = runCheckoutPaymentElementScenario(
networkRule = networkRule,
setup = { controller ->
networkRule.validateAnalyticsRequest(
eventName = "mc_load_started",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_load_succeeded",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_initial_displayed_payment_methods",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
controller.configure(DEFAULT_CLIENT_SECRET).getOrThrow()
},
) {
networkRule.validateAnalyticsRequest(
eventName = "mc_carousel_payment_method_tapped",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
networkRule.validateAnalyticsRequest(
eventName = "stripe_android.card_metadata_pk_available",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_form_shown",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
networkRule.validateAnalyticsRequest(
eventName = "stripe_android.card_metadata_pk_available",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_cardscan_api_check_failed",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)

contentPage.clickOnLpm("card")
formPage.waitUntilVisible()

networkRule.validateAnalyticsRequest(
eventName = "mc_dismiss",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
Espresso.pressBack()
formPage.waitUntilMissing()
Expand All @@ -111,17 +108,10 @@ internal class CheckoutPaymentElementAnalyticsTest {
},
checkoutInitResponse = ::billingTaxCheckoutInitResponse,
setup = { controller ->
networkRule.validateAnalyticsRequest(
eventName = "mc_load_started",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_load_succeeded",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_initial_displayed_payment_methods",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
controller.configure(DEFAULT_CLIENT_SECRET).getOrThrow()
},
Expand Down Expand Up @@ -164,15 +154,6 @@ internal class CheckoutPaymentElementAnalyticsTest {
analyticsPayloadField("error_code", "checkout_session_total_changed"),
query("error_message", null),
)
// The Checkout Session is refreshed after confirmation fails.
networkRule.validateAnalyticsRequest(
eventName = "mc_load_started",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_load_succeeded",
productUsage = setOf("Checkout"),
)

contentPage.clickOnLpm("google_pay")
confirm()
Expand All @@ -197,17 +178,10 @@ internal class CheckoutPaymentElementAnalyticsTest {
}
},
setup = { controller ->
networkRule.validateAnalyticsRequest(
eventName = "mc_load_started",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_load_succeeded",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_initial_displayed_payment_methods",
productUsage = setOf("Checkout"),
productUsage = setOf("PaymentElement"),
analyticsPayloadField("checkout_session_id", DEFAULT_CHECKOUT_SESSION_ID),
)
controller.configure(DEFAULT_CLIENT_SECRET).getOrThrow()
},
Expand All @@ -217,15 +191,6 @@ internal class CheckoutPaymentElementAnalyticsTest {
eventName = "unexpected_error.checkout.saved_payment_method.missing_billing_address",
productUsage = setOf("Checkout"),
)
// Changing the selection reloads the payment element.
networkRule.validateAnalyticsRequest(
eventName = "mc_load_started",
productUsage = setOf("Checkout"),
)
networkRule.validateAnalyticsRequest(
eventName = "mc_load_succeeded",
productUsage = setOf("Checkout"),
)

// Billing-tax filtering keeps addressless saved payment methods out of the UI, so
// select one directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,6 @@ internal class ExpressCheckoutElementAnalyticsTest {
requestId = "req_checkout_confirm",
)

// We re-load after confirm succeeds, which triggers another two loads: one for ECE, one for PE.
repeat(2) {
validateLoadingAnalyticsRequests()
}

page.clickGooglePayButton()
}

Expand All @@ -88,8 +83,7 @@ internal class ExpressCheckoutElementAnalyticsTest {
validateAnalyticsRequest(eventName = "link.popup.success")
validateConfirmationAnalyticsRequests(selectedLpm = "link", linkContext = "wallet")

// We re-load after confirm succeeds, which triggers another two loads: one for ECE, one for PE.
// We also re-load Link state since the confirmation was for a Link payment method.
// Confirmation reloads Link state for both PE and ECE.
enqueueLoadingRequests(linkEnabled = true)

page.clickLinkButton()
Expand All @@ -112,7 +106,7 @@ internal class ExpressCheckoutElementAnalyticsTest {
errorCode = "1",
)

// We re-load after confirmation fails, which triggers another two loads: one for ECE, one for PE.
// Confirmation reloads Link state for both PE and ECE.
enqueueLoadingRequests(linkEnabled = true)

page.clickGooglePayButton()
Expand All @@ -139,11 +133,9 @@ internal class ExpressCheckoutElementAnalyticsTest {
errorType = "java.lang.IllegalStateException",
)

// We re-load after confirmation fails, which triggers another two loads: one for ECE, one for PE.
// We also re-load Link state since the confirmation was for a Link payment method.
// Confirmation reloads Link state for both PE and ECE.
repeat(2) {
networkRule.enqueueLinkAccountLookup()
validateLoadingAnalyticsRequests()
validateLinkAccountLookupAnalyticsRequest()
}

Expand Down Expand Up @@ -199,11 +191,6 @@ internal class ExpressCheckoutElementAnalyticsTest {
}
validateConfirmationAnalyticsRequests(selectedLpm = "google_pay")

// We re-load after confirm succeeds, which triggers another two loads: one for ECE, one for PE.
repeat(2) {
validateLoadingAnalyticsRequests()
}

page.clickGooglePayButton()
}

Expand Down Expand Up @@ -244,11 +231,6 @@ internal class ExpressCheckoutElementAnalyticsTest {
}
validateConfirmationAnalyticsRequests(selectedLpm = "google_pay")

// We re-load after confirm succeeds, which triggers another two loads: one for ECE, one for PE.
repeat(2) {
validateLoadingAnalyticsRequests()
}

page.clickGooglePayButton()
}

Expand Down Expand Up @@ -282,11 +264,6 @@ internal class ExpressCheckoutElementAnalyticsTest {
errorCode = "checkout_session_total_changed",
)

// We re-load after confirmation fails, which triggers another two loads: one for ECE, one for PE.
repeat(2) {
validateLoadingAnalyticsRequests()
}

page.clickGooglePayButton()
}

Expand All @@ -313,21 +290,15 @@ internal class ExpressCheckoutElementAnalyticsTest {
}

private fun enqueueLoadingRequests(linkEnabled: Boolean) {
// We load twice, once for PE and once for ECE. So all these requests are made twice.
repeat(2) {
if (linkEnabled) {
if (linkEnabled) {
// PE and ECE each load Link state.
repeat(2) {
networkRule.enqueueLinkAccountLookup()
validateLinkAccountLookupAnalyticsRequest()
}
validateLoadingAnalyticsRequests()
}
}

private fun validateLoadingAnalyticsRequests() {
validateAnalyticsRequest(eventName = "mc_load_started")
validateAnalyticsRequest(eventName = "mc_load_succeeded")
}

private fun validateConfirmationAnalyticsRequests(
selectedLpm: String,
linkContext: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,35 @@ package com.stripe.android.checkout.injection
import android.app.Application
import android.content.Context
import com.stripe.android.common.di.DisplayDensity
import com.stripe.android.core.ApiConfiguration
import com.stripe.android.core.injection.ENABLE_LOGGING
import com.stripe.android.core.injection.StripeNetworkClientModule
import com.stripe.android.networking.PaymentAnalyticsRequestFactory
import com.stripe.android.payments.core.injection.PRODUCT_USAGE
import com.stripe.android.paymentsheet.BuildConfig
import com.stripe.android.paymentsheet.analytics.PaymentElementAnalytics
import com.stripe.android.uicore.image.DefaultStripeImageLoader
import com.stripe.android.uicore.image.StripeImageLoader
import dagger.Module
import dagger.Provides
import javax.inject.Named
import javax.inject.Provider

@Module(includes = [StripeNetworkClientModule::class])
internal object CheckoutModule {
@Provides
@PaymentElementAnalytics
fun provideEventReporterAnalyticsRequestFactory(
context: Context,
apiConfigurationProvider: Provider<ApiConfiguration.State>,
): PaymentAnalyticsRequestFactory {
return PaymentAnalyticsRequestFactory(
context = context,
defaultProductUsageTokens = setOf("PaymentElement"),
apiConfigurationProvider = apiConfigurationProvider,
)
}

@Provides
fun provideContext(application: Application): Context = application.applicationContext

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import com.stripe.android.paymentsheet.DefaultPrefsRepository
import com.stripe.android.paymentsheet.PrefsRepository
import com.stripe.android.paymentsheet.analytics.DefaultEventReporter
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.analytics.PaymentElementAnalytics
import com.stripe.android.paymentsheet.injection.ApiConfigurationModule
import com.stripe.android.paymentsheet.repositories.CustomerApiRepository
import com.stripe.android.paymentsheet.repositories.CustomerRepository
Expand Down Expand Up @@ -207,6 +208,12 @@ internal interface TapToAddViewModelModule {
): TapToAddConfirmationInteractor.Factory

companion object {
@Provides
@PaymentElementAnalytics
fun provideEventReporterAnalyticsRequestFactory(
factory: PaymentAnalyticsRequestFactory,
): PaymentAnalyticsRequestFactory = factory

@Provides
@Named(ENABLE_LOGGING)
fun provideEnabledLogging(): Boolean = BuildConfig.DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import com.stripe.android.paymentsheet.addresselement.AutocompleteLauncher
import com.stripe.android.paymentsheet.addresselement.DefaultAutocompleteLauncher
import com.stripe.android.paymentsheet.analytics.DefaultEventReporter
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.analytics.PaymentElementAnalytics
import com.stripe.android.repository.ConsumersApiService
import com.stripe.android.repository.ConsumersApiServiceImpl
import com.stripe.android.uicore.image.DefaultStripeImageLoader
Expand Down Expand Up @@ -152,6 +153,12 @@ internal interface NativeLinkModule {

@SuppressWarnings("TooManyFunctions")
companion object {
@Provides
@PaymentElementAnalytics
fun provideEventReporterAnalyticsRequestFactory(
factory: PaymentAnalyticsRequestFactory,
): PaymentAnalyticsRequestFactory = factory

@Provides
@NativeLinkScope
fun providesLinkAccountHolder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ import com.stripe.android.common.spms.SavedPaymentMethodLinkFormHelper
import com.stripe.android.common.taptoadd.DefaultTapToAddHelper
import com.stripe.android.common.taptoadd.TapToAddHelper
import com.stripe.android.common.taptoadd.TapToAddMode
import com.stripe.android.core.ApiConfiguration
import com.stripe.android.core.injection.ViewModelScope
import com.stripe.android.core.utils.RealUserFacingLogger
import com.stripe.android.core.utils.UserFacingLogger
import com.stripe.android.link.account.LinkAccountHolder
import com.stripe.android.lpmfoundations.paymentmethod.IntegrationMetadata
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.PaymentMethodMessagePromotion
import com.stripe.android.networking.PaymentAnalyticsRequestFactory
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.confirmation.ConfirmationHandler
import com.stripe.android.paymentelement.embedded.form.OnClickDelegateOverrideImpl
Expand All @@ -44,6 +47,7 @@ import com.stripe.android.paymentelement.embedded.sheet.SheetActivityConfirmatio
import com.stripe.android.paymentelement.embedded.sheet.SheetActivityContinueCoordinator
import com.stripe.android.paymentelement.embedded.sheet.SheetActivityRegistrar
import com.stripe.android.paymentelement.embedded.sheet.SheetActivityStateHolder
import com.stripe.android.payments.core.injection.PRODUCT_USAGE
import com.stripe.android.payments.core.injection.STATUS_BAR_COLOR
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.DefaultPrefsRepository
Expand All @@ -55,6 +59,7 @@ import com.stripe.android.paymentsheet.addresselement.StripeAutocompleteReposito
import com.stripe.android.paymentsheet.addresselement.analytics.AddressLauncherEventReporter
import com.stripe.android.paymentsheet.addresselement.analytics.DefaultAddressLauncherEventReporter
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.analytics.PaymentElementAnalytics
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.repositories.CheckoutSessionResponse
import com.stripe.android.paymentsheet.repositories.PaymentMethodMessagePromotionsHelper
Expand Down Expand Up @@ -143,6 +148,25 @@ internal interface EmbeddedActivityModule {

@Suppress("TooManyFunctions")
companion object {
@Provides
@PaymentElementAnalytics
fun provideEventReporterAnalyticsRequestFactory(
context: Context,
paymentMethodMetadata: PaymentMethodMetadata,
@Named(PRODUCT_USAGE) productUsage: Set<String>,
apiConfigurationProvider: Provider<ApiConfiguration.State>,
): PaymentAnalyticsRequestFactory {
val eventProductUsage = when (paymentMethodMetadata.integrationMetadata) {
is IntegrationMetadata.CheckoutSession -> setOf("PaymentElement")
else -> productUsage
}
return PaymentAnalyticsRequestFactory(
context = context,
defaultProductUsageTokens = eventProductUsage,
apiConfigurationProvider = apiConfigurationProvider,
)
}

@Provides
fun providesContext(application: Application): Context {
return application
Expand Down
Loading
Loading