Skip to content
Closed
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 @@ -118,10 +118,12 @@ internal interface ConfirmationHandler {
/**
* Defines the result types that can be returned after completing a confirmation process.
*/
sealed interface Result {
@Parcelize
sealed interface Result : Parcelable {
/**
* Indicates that the confirmation process was canceled by the customer.
*/
@Parcelize
data class Canceled(
val action: Action,
) : Result {
Expand Down Expand Up @@ -152,6 +154,7 @@ internal interface ConfirmationHandler {
* Indicates that the confirmation process has been successfully completed. A [StripeIntent] with an updated
* state is returned as part of the result as well.
*/
@Parcelize
data class Succeeded(
val intent: StripeIntent,
val deferredIntentConfirmationType: DeferredIntentConfirmationType?,
Expand All @@ -161,6 +164,7 @@ internal interface ConfirmationHandler {
* Indicates that the confirmation process has failed. A cause and potentially a resolvable message are
* returned as part of the result.
*/
@Parcelize
data class Failed(
val cause: Throwable,
val message: ResolvableString,
Expand All @@ -169,35 +173,42 @@ internal interface ConfirmationHandler {
/**
* Types of errors that can occur when confirming a payment.
*/
sealed interface ErrorType {
@Parcelize
sealed interface ErrorType : Parcelable {
/**
* Fatal confirmation error that occurred while confirming a payment. This should never happen.
*/
@Parcelize
data object Fatal : ErrorType

/**
* Indicates an error when processing a payment during the confirmation process.
*/
@Parcelize
data object Payment : ErrorType

/**
* Indicates an internal process error occurred during the confirmation process.
*/
@Parcelize
data object Internal : ErrorType

/**
* Indicates a merchant integration error occurred during the confirmation process.
*/
@Parcelize
data object MerchantIntegration : ErrorType

/**
* Indicates an error occurred when confirming with external payment methods
*/
@Parcelize
data object ExternalPaymentMethod : ErrorType

/**
* Indicates an error occurred when confirming with Google Pay
*/
@Parcelize
data class GooglePay(val errorCode: Int) : ErrorType
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import androidx.activity.result.ActivityResultCaller
import androidx.activity.result.ActivityResultLauncher
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.stripe.android.common.model.asCommonConfiguration
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.embedded.EmbeddedSelectionHolder
import com.stripe.android.paymentelement.embedded.form.FormContract
import com.stripe.android.paymentelement.embedded.form.FormResult
import com.stripe.android.paymentelement.embedded.manage.ManageContract
import com.stripe.android.paymentelement.embedded.manage.ManageResult
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.state.CustomerState
import javax.inject.Inject
Expand All @@ -19,7 +23,7 @@ internal interface EmbeddedSheetLauncher {
fun launchForm(
code: String,
paymentMethodMetadata: PaymentMethodMetadata,
hasSavedPaymentMethods: Boolean
hasSavedPaymentMethods: Boolean,
)

fun launchManage(
Expand All @@ -30,12 +34,15 @@ internal interface EmbeddedSheetLauncher {
}

@EmbeddedPaymentElementScope
@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
internal class DefaultEmbeddedSheetLauncher @Inject constructor(
activityResultCaller: ActivityResultCaller,
lifecycleOwner: LifecycleOwner,
private val selectionHolder: EmbeddedSelectionHolder,
private val customerStateHolder: CustomerStateHolder,
private val sheetStateHolder: SheetStateHolder,
private val resultCallback: EmbeddedPaymentElement.ResultCallback,
private val confirmationStateSupplier: () -> EmbeddedConfirmationStateHolder.State?
) : EmbeddedSheetLauncher {

init {
Expand All @@ -55,6 +62,7 @@ internal class DefaultEmbeddedSheetLauncher @Inject constructor(
sheetStateHolder.sheetIsOpen = false
if (result is FormResult.Complete) {
selectionHolder.set(result.selection)
resultCallback.onResult(EmbeddedPaymentElement.Result.Completed())
}
}

Expand All @@ -77,13 +85,16 @@ internal class DefaultEmbeddedSheetLauncher @Inject constructor(
override fun launchForm(
code: String,
paymentMethodMetadata: PaymentMethodMetadata,
hasSavedPaymentMethods: Boolean
hasSavedPaymentMethods: Boolean,
) {
sheetStateHolder.sheetIsOpen = true
val confirmationState = confirmationStateSupplier() ?: return
val args = FormContract.Args(
selectedPaymentMethodCode = code,
paymentMethodMetadata = paymentMethodMetadata,
hasSavedPaymentMethods = hasSavedPaymentMethods
hasSavedPaymentMethods = hasSavedPaymentMethods,
initializationMode = confirmationState.initializationMode,
configuration = confirmationState.configuration
)
formActivityLauncher.launch(args)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.embedded.EmbeddedFormHelperFactory
import com.stripe.android.paymentelement.embedded.EmbeddedSelectionHolder
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.PaymentSheet.Appearance.Embedded
import com.stripe.android.paymentsheet.SavedPaymentMethodMutator
import com.stripe.android.paymentsheet.analytics.EventReporter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.getValue
import com.stripe.android.common.ui.ElementsBottomSheetLayout
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.verticalmode.DefaultVerticalModeFormInteractor
import com.stripe.android.uicore.StripeTheme
import com.stripe.android.uicore.elements.bottomsheet.rememberStripeBottomSheetState
import com.stripe.android.uicore.utils.collectAsState
import com.stripe.android.uicore.utils.fadeOut
import javax.inject.Inject

Expand All @@ -31,29 +33,52 @@ internal class FormActivity : AppCompatActivity() {
@Inject
lateinit var eventReporter: EventReporter

@Inject
lateinit var formStateHolder: FormStateHolder

@Inject
lateinit var formActivityConfirmationHandler: FormActivityConfirmationHandler

@OptIn(ExperimentalMaterialApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (args == null) {
setFormResult(FormResult.Cancelled)
finish()
setCancelAndFinish()
return
}

viewModel.component.inject(this)

// todo figure out how to inject these
formActivityConfirmationHandler.register(this, this)

setContent {
StripeTheme {
val bottomSheetState = rememberStripeBottomSheetState()
val state by formStateHolder.formState.collectAsState()
val primaryButtonState = formActivityConfirmationHandler.primaryButtonProcessingState.collectAsState()
ElementsBottomSheetLayout(
state = bottomSheetState,
onDismissed = ::setCancelAndFinish
) {
FormActivityUI(
interactor = formInteractor,
eventReporter = eventReporter,
onDismissed = ::setCancelAndFinish
onDismissed = ::setCancelAndFinish,
primaryButtonProcessingState = primaryButtonState.value,
state = state,
onFormFieldValuesChanged = formStateHolder::formValuesChanged,
onConfirm = formActivityConfirmationHandler::confirm,
onProcessingCompleted = {
setFormResult(
FormResult.Complete(
selection = formStateHolder.formState.value.paymentSelection,
confirmationResult = null
)
)
finish()
}
)
}
}
Expand All @@ -76,4 +101,4 @@ internal class FormActivity : AppCompatActivity() {
FormResult.toIntent(intent, result)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,48 @@ import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.stripe.android.cards.CardAccountRangeRepository
import com.stripe.android.cards.DefaultCardAccountRangeRepositoryFactory
import com.stripe.android.common.model.CommonConfiguration
import com.stripe.android.core.injection.IOContext
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.googlepaylauncher.injection.GooglePayLauncherModule
import com.stripe.android.link.LinkConfigurationCoordinator
import com.stripe.android.link.RealLinkConfigurationCoordinator
import com.stripe.android.link.gate.DefaultLinkGate
import com.stripe.android.link.gate.LinkGate
import com.stripe.android.link.injection.LinkAnalyticsComponent
import com.stripe.android.link.injection.LinkComponent
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.model.PaymentMethodCode
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.confirmation.injection.ExtendedPaymentElementConfirmationModule
import com.stripe.android.paymentelement.embedded.EmbeddedCommonModule
import com.stripe.android.payments.core.injection.STATUS_BAR_COLOR
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.state.PaymentElementLoader
import com.stripe.android.paymentsheet.verticalmode.DefaultVerticalModeFormInteractor
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.CoroutineScope
import javax.inject.Named
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext

@Component(
modules = [
EmbeddedCommonModule::class,
FormActivityModule::class
FormActivityModule::class,
ExtendedPaymentElementConfirmationModule::class,
GooglePayLauncherModule::class
]
)
@Singleton
@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
internal interface FormActivityComponent {

val viewModel: FormActivityViewModel
Expand All @@ -50,6 +66,15 @@ internal interface FormActivityComponent {
@BindsInstance
fun context(context: Context): Builder

@BindsInstance
fun statusBarColor(@Named(STATUS_BAR_COLOR) statusBarColor: Int?): Builder

@BindsInstance
fun initializationMode(initializationMode: PaymentElementLoader.InitializationMode): Builder

@BindsInstance
fun configuration(configuration: EmbeddedPaymentElement.Configuration): Builder

@BindsInstance
fun savedStateHandle(savedStateHandle: SavedStateHandle): Builder

Expand All @@ -72,6 +97,12 @@ internal interface FormActivityModule {
@Binds
fun bindsLinkConfigurationCoordinator(impl: RealLinkConfigurationCoordinator): LinkConfigurationCoordinator

@Binds
fun bindsUserFacingLogger(impl: RealUserFacingLogger): UserFacingLogger

@Binds
fun bindLinkGateFactory(linkGateFactory: DefaultLinkGate.Factory): LinkGate.Factory

companion object {
@Provides
@Singleton
Expand Down
Loading