Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Prototype] events framework #9980

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.stripe.android.paymentsheet.example.playground
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
Expand All @@ -29,6 +30,8 @@ import com.stripe.android.customersheet.CustomerSheet
import com.stripe.android.customersheet.CustomerSheetResult
import com.stripe.android.customersheet.rememberCustomerSheet
import com.stripe.android.model.PaymentMethod
import com.stripe.android.paymentelement.AnalyticEvent
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi
import com.stripe.android.paymentsheet.ExperimentalCustomerSessionApi
import com.stripe.android.paymentsheet.ExternalPaymentMethodConfirmHandler
import com.stripe.android.paymentsheet.PaymentSheet
Expand Down Expand Up @@ -77,7 +80,7 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
viewModel.onEmbeddedResult(success)
}

@OptIn(ExperimentalCustomerSessionApi::class)
@OptIn(ExperimentalCustomerSessionApi::class, ExperimentalAnalyticEventCallbackApi::class)
@Suppress("LongMethod")
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
Expand All @@ -90,6 +93,16 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
val paymentSheet = PaymentSheet.Builder(viewModel::onPaymentSheetResult)
.externalPaymentMethodConfirmHandler(this)
.createIntentCallback(viewModel::createIntentCallback)
.analyticEventCallback({ event ->
when (event) {
is AnalyticEvent.PresentedSheet -> {
Log.d("AnalyticEvent", "Event: $event")
}
is AnalyticEvent.DisplayedPaymentMethodForm -> {
Log.d("AnalyticEvent", "Event: $event, PM: ${event.paymentMethodType}")
}
}
})
.build()
val flowController = PaymentSheet.FlowController.Builder(
viewModel::onPaymentSheetResult,
Expand Down
71 changes: 71 additions & 0 deletions paymentsheet/api/paymentsheet.api
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,75 @@ public final class com/stripe/android/lpmfoundations/paymentmethod/link/LinkInli
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public abstract class com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$CompletedPaymentMethodForm : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$DisplayedPaymentMethodForm : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$PresentedSheet : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
}

public final class com/stripe/android/paymentelement/AnalyticEvent$RemovedSavedPaymentMethod : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$SelectedPaymentMethodType : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$SelectedSavedPaymentMethod : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$StartedInteractionWithPaymentMethodForm : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/stripe/android/paymentelement/AnalyticEvent$TappedConfirmButton : com/stripe/android/paymentelement/AnalyticEvent {
public static final field $stable I
public fun equals (Ljava/lang/Object;)Z
public final fun getPaymentMethodType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class com/stripe/android/paymentelement/AnalyticEventCallback {
public abstract fun onEvent (Lcom/stripe/android/paymentelement/AnalyticEvent;)V
}

public final class com/stripe/android/paymentelement/EmbeddedPaymentElement$Configuration$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/paymentelement/EmbeddedPaymentElement$Configuration;
Expand Down Expand Up @@ -1176,6 +1245,7 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$BillingDetailsCo
public final class com/stripe/android/paymentsheet/PaymentSheet$Builder {
public static final field $stable I
public fun <init> (Lcom/stripe/android/paymentsheet/PaymentSheetResultCallback;)V
public final fun analyticEventCallback (Lcom/stripe/android/paymentelement/AnalyticEventCallback;)Lcom/stripe/android/paymentsheet/PaymentSheet$Builder;
public final fun build (Landroidx/activity/ComponentActivity;)Lcom/stripe/android/paymentsheet/PaymentSheet;
public final fun build (Landroidx/compose/runtime/Composer;I)Lcom/stripe/android/paymentsheet/PaymentSheet;
public final fun build (Landroidx/fragment/app/Fragment;)Lcom/stripe/android/paymentsheet/PaymentSheet;
Expand Down Expand Up @@ -1441,6 +1511,7 @@ public abstract interface class com/stripe/android/paymentsheet/PaymentSheet$Flo
public final class com/stripe/android/paymentsheet/PaymentSheet$FlowController$Builder {
public static final field $stable I
public fun <init> (Lcom/stripe/android/paymentsheet/PaymentSheetResultCallback;Lcom/stripe/android/paymentsheet/PaymentOptionCallback;)V
public final fun analyticEventCallback (Lcom/stripe/android/paymentelement/AnalyticEventCallback;)Lcom/stripe/android/paymentsheet/PaymentSheet$FlowController$Builder;
public final fun build (Landroidx/activity/ComponentActivity;)Lcom/stripe/android/paymentsheet/PaymentSheet$FlowController;
public final fun build (Landroidx/compose/runtime/Composer;I)Lcom/stripe/android/paymentsheet/PaymentSheet$FlowController;
public final fun build (Landroidx/fragment/app/Fragment;)Lcom/stripe/android/paymentsheet/PaymentSheet$FlowController;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ import com.stripe.android.link.repositories.LinkApiRepository
import com.stripe.android.link.repositories.LinkRepository
import com.stripe.android.networking.StripeApiRepository
import com.stripe.android.networking.StripeRepository
import com.stripe.android.paymentelement.AnalyticEventCallback
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi
import com.stripe.android.paymentelement.confirmation.ALLOWS_MANUAL_CONFIRMATION
import com.stripe.android.paymentelement.confirmation.ConfirmationDefinition
import com.stripe.android.paymentelement.confirmation.link.LinkPassthroughConfirmationDefinition
import com.stripe.android.payments.core.analytics.ErrorReporter
import com.stripe.android.payments.core.analytics.RealErrorReporter
import com.stripe.android.payments.core.injection.PRODUCT_USAGE
import com.stripe.android.paymentsheet.AnalyticEventInterceptor
import com.stripe.android.paymentsheet.analytics.DefaultEventReporter
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.repository.ConsumersApiService
Expand Down Expand Up @@ -218,5 +221,9 @@ internal interface NativeLinkModule {
linkAccountManager = linkAccountManager,
)
}

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
@Provides
fun provideAnalyticEventCallback(): AnalyticEventCallback? = AnalyticEventInterceptor.analyticEventCallback
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.stripe.android.paymentelement

import dev.drewhamilton.poko.Poko

/**
* Called when an analytic event is emitted.
*
* These events are intended to be used for analytics purposes ONLY.
*
* @param event the [AnalyticEvent] that was emitted
*/
@ExperimentalAnalyticEventCallbackApi
fun interface AnalyticEventCallback {
fun onEvent(event: AnalyticEvent)
}

@ExperimentalAnalyticEventCallbackApi
abstract class AnalyticEvent internal constructor() {

@Override
override fun toString(): String {
return this::class.java.simpleName
}

// Sheet is presented
class PresentedSheet internal constructor() : AnalyticEvent()

// Selected a different payment method type
@Poko
class SelectedPaymentMethodType internal constructor(val paymentMethodType: String) : AnalyticEvent()

// Payment method form for was displayed
@Poko
class DisplayedPaymentMethodForm internal constructor(val paymentMethodType: String) : AnalyticEvent()

// User interacted with a payment method form
@Poko
class StartedInteractionWithPaymentMethodForm internal constructor(val paymentMethodType: String) : AnalyticEvent()

// All mandatory fields for the payment method form have been completed
@Poko
class CompletedPaymentMethodForm internal constructor(val paymentMethodType: String) : AnalyticEvent()

// User tapped on the confirm button
@Poko
class TappedConfirmButton internal constructor(val paymentMethodType: String) : AnalyticEvent()

// User selected a saved payment method
@Poko
class SelectedSavedPaymentMethod internal constructor(val paymentMethodType: String) : AnalyticEvent()

// User removed a saved payment method
@Poko
class RemovedSavedPaymentMethod internal constructor(val paymentMethodType: String) : AnalyticEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,24 @@ class EmbeddedPaymentElement @Inject internal constructor(
internal var externalPaymentMethodConfirmHandler: ExternalPaymentMethodConfirmHandler? = null
private set

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
internal var analyticEventCallback: AnalyticEventCallback? = null
private set

/**
* Called when a user confirms payment for an external payment method.
*/
fun externalPaymentMethodConfirmHandler(handler: ExternalPaymentMethodConfirmHandler) = apply {
this.externalPaymentMethodConfirmHandler = handler
}

/**
* Called when an analytic event is emitted.
*/
@ExperimentalAnalyticEventCallbackApi
fun analyticEventCallback(callback: AnalyticEventCallback) = apply {
analyticEventCallback = callback
}
}

/** Configuration for [EmbeddedPaymentElement] **/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.stripe.android.paymentelement

import androidx.annotation.RestrictTo

@RequiresOptIn(
level = RequiresOptIn.Level.ERROR,
message = "This API is under construction. It can be changed or removed at any time (use at your own risk)."
)
@Retention(AnnotationRetention.BINARY)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
annotation class ExperimentalAnalyticEventCallbackApi
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ import com.stripe.android.core.networking.NetworkTypeDetector
import com.stripe.android.core.utils.ContextUtils.packageInfo
import com.stripe.android.core.utils.DefaultDurationProvider
import com.stripe.android.core.utils.DurationProvider
import com.stripe.android.paymentelement.AnalyticEventCallback
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi
import com.stripe.android.paymentelement.confirmation.ALLOWS_MANUAL_CONFIRMATION
import com.stripe.android.payments.core.analytics.ErrorReporter
import com.stripe.android.payments.core.analytics.RealErrorReporter
import com.stripe.android.payments.core.injection.PRODUCT_USAGE
import com.stripe.android.payments.core.injection.StripeRepositoryModule
import com.stripe.android.paymentsheet.AnalyticEventInterceptor
import com.stripe.android.paymentsheet.BuildConfig
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.analytics.DefaultEventReporter
Expand Down Expand Up @@ -131,5 +134,9 @@ internal interface EmbeddedCommonModule {
@Singleton
@UIContext
fun provideUiContext(): CoroutineContext = Dispatchers.Main

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
@Provides
fun provideAnalyticEventCallback(): AnalyticEventCallback? = AnalyticEventInterceptor.analyticEventCallback
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.stripe.android.paymentsheet

import com.stripe.android.paymentelement.AnalyticEventCallback
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
internal object AnalyticEventInterceptor {
var analyticEventCallback: AnalyticEventCallback? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi
import com.stripe.android.paymentelement.confirmation.intent.IntentConfirmationInterceptor
import com.stripe.android.paymentsheet.state.PaymentElementLoader
import com.stripe.android.uicore.utils.AnimationConstants
Expand All @@ -18,6 +19,7 @@ import org.jetbrains.annotations.TestOnly
* This is used internally for integrations that don't use Jetpack Compose and are
* able to pass in an activity.
*/
@OptIn(ExperimentalAnalyticEventCallbackApi::class)
internal class DefaultPaymentSheetLauncher(
private val activityResultLauncher: ActivityResultLauncher<PaymentSheetContractV2.Args>,
private val activity: Activity,
Expand All @@ -32,6 +34,7 @@ internal class DefaultPaymentSheetLauncher(
override fun onDestroy(owner: LifecycleOwner) {
IntentConfirmationInterceptor.createIntentCallback = null
ExternalPaymentMethodInterceptor.externalPaymentMethodConfirmHandler = null
AnalyticEventInterceptor.analyticEventCallback = null
super.onDestroy(owner)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import com.stripe.android.link.account.LinkStore
import com.stripe.android.model.CardBrand
import com.stripe.android.model.PaymentIntent
import com.stripe.android.model.SetupIntent
import com.stripe.android.paymentelement.AnalyticEventCallback
import com.stripe.android.paymentelement.ExperimentalAnalyticEventCallbackApi
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.confirmation.intent.IntentConfirmationInterceptor
import com.stripe.android.paymentsheet.addresselement.AddressDetails
Expand Down Expand Up @@ -195,6 +197,10 @@ class PaymentSheet internal constructor(
internal var createIntentCallback: CreateIntentCallback? = null
private set

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
internal var analyticEventCallback: AnalyticEventCallback? = null
private set

/**
* @param handler Called when a user confirms payment for an external payment method. Use with
* [Configuration.Builder.externalPaymentMethods] to specify external payment methods.
Expand All @@ -211,6 +217,14 @@ class PaymentSheet internal constructor(
createIntentCallback = callback
}

/**
* @param callback Called when an analytic event occurs.
*/
@ExperimentalAnalyticEventCallbackApi
fun analyticEventCallback(callback: AnalyticEventCallback) = apply {
analyticEventCallback = callback
}

/**
* Returns a [PaymentSheet].
*
Expand Down Expand Up @@ -240,13 +254,17 @@ class PaymentSheet internal constructor(
return rememberPaymentSheet(resultCallback)
}

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
private fun initializeCallbacks() {
createIntentCallback?.let {
IntentConfirmationInterceptor.createIntentCallback = it
}
externalPaymentMethodConfirmHandler?.let {
ExternalPaymentMethodInterceptor.externalPaymentMethodConfirmHandler = it
}
analyticEventCallback?.let {
AnalyticEventInterceptor.analyticEventCallback = it
}
}
}

Expand Down Expand Up @@ -2106,6 +2124,10 @@ class PaymentSheet internal constructor(
internal var createIntentCallback: CreateIntentCallback? = null
private set

@OptIn(ExperimentalAnalyticEventCallbackApi::class)
internal var analyticEventCallback: AnalyticEventCallback? = null
private set

/**
* @param handler Called when a user confirms payment for an external payment method.
*/
Expand All @@ -2120,6 +2142,14 @@ class PaymentSheet internal constructor(
createIntentCallback = callback
}

/**
* @param callback If specified, called when an analytic event occurs.
*/
@ExperimentalAnalyticEventCallbackApi
fun analyticEventCallback(callback: AnalyticEventCallback) = apply {
analyticEventCallback = callback
}

/**
* Returns a [PaymentSheet.FlowController].
*
Expand Down
Loading
Loading