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

Link Confirmation Handler #9865

Merged
merged 5 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -112,6 +112,7 @@ internal class LinkActivityViewModel @Inject constructor(
.configuration(args.configuration)
.publishableKeyProvider { args.publishableKey }
.stripeAccountIdProvider { args.stripeAccountId }
.savedStateHandle(handle)
.context(app)
.build()
.viewModel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.stripe.android.link.confirmation

import com.stripe.android.core.Logger
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.link.LinkConfiguration
import com.stripe.android.link.model.LinkAccount
import com.stripe.android.model.ConsumerPaymentDetails
import com.stripe.android.model.PaymentMethodCreateParams
import com.stripe.android.paymentelement.confirmation.ConfirmationHandler
import com.stripe.android.paymentelement.confirmation.PaymentMethodConfirmationOption
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.R
import com.stripe.android.paymentsheet.state.PaymentElementLoader
import javax.inject.Inject

internal class DefaultLinkConfirmationHandler @Inject constructor(
private val configuration: LinkConfiguration,
private val logger: Logger,
private val confirmationHandler: ConfirmationHandler
) : LinkConfirmationHandler {
override suspend fun confirm(
paymentDetails: ConsumerPaymentDetails.PaymentDetails,
linkAccount: LinkAccount
): Result {
return kotlin.runCatching {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return kotlin.runCatching {
return runCatching {

Is it possible to write this this way? Or why do we need to specify kotlin here?

val args = confirmationArgs(paymentDetails, linkAccount)
confirmationHandler.start(args)
val result = confirmationHandler.awaitResult()
transformResult(result)
}.getOrElse { error ->
logger.error(
msg = "DefaultLinkConfirmationHandler: Failed to confirm payment",
t = error
)
Result.Failed(R.string.stripe_something_went_wrong.resolvableString)
}
}

private fun transformResult(result: ConfirmationHandler.Result?): Result {
return when (result) {
is ConfirmationHandler.Result.Canceled -> Result.Canceled
is ConfirmationHandler.Result.Failed -> {
logger.error(
msg = "DefaultLinkConfirmationHandler: Failed to confirm payment",
t = result.cause
)
Result.Failed(result.message)
}
is ConfirmationHandler.Result.Succeeded -> Result.Succeeded
null -> {
logger.error("DefaultLinkConfirmationHandler: Payment confirmation returned null")
Result.Failed(R.string.stripe_something_went_wrong.resolvableString)
}
}
}

private fun confirmationArgs(
paymentDetails: ConsumerPaymentDetails.PaymentDetails,
linkAccount: LinkAccount
): ConfirmationHandler.Args {
return ConfirmationHandler.Args(
intent = configuration.stripeIntent,
confirmationOption = PaymentMethodConfirmationOption.New(
createParams = createPaymentMethodCreateParams(
selectedPaymentDetails = paymentDetails,
linkAccount = linkAccount
),
optionsParams = null,
shouldSave = false
),
appearance = PaymentSheet.Appearance(),
initializationMode = PaymentElementLoader.InitializationMode.PaymentIntent(
clientSecret = configuration.stripeIntent.clientSecret
?: throw NO_CLIENT_SECRET_FOUND
),
shippingDetails = configuration.shippingDetails
)
}

private fun createPaymentMethodCreateParams(
selectedPaymentDetails: ConsumerPaymentDetails.PaymentDetails,
linkAccount: LinkAccount,
): PaymentMethodCreateParams {
return PaymentMethodCreateParams.createLink(
paymentDetailsId = selectedPaymentDetails.id,
consumerSessionClientSecret = linkAccount.clientSecret,
extraParams = emptyMap(),
)
}

class Factory @Inject constructor(
private val configuration: LinkConfiguration,
private val logger: Logger,
) : LinkConfirmationHandler.Factory {
override fun create(confirmationHandler: ConfirmationHandler): LinkConfirmationHandler {
return DefaultLinkConfirmationHandler(
confirmationHandler = confirmationHandler,
logger = logger,
configuration = configuration
)
}
}

companion object {
val NO_CLIENT_SECRET_FOUND = IllegalStateException("no client secret found")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val NO_CLIENT_SECRET_FOUND = IllegalStateException("no client secret found")
val NO_CLIENT_SECRET_FOUND = IllegalStateException("No client secret found.")

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.stripe.android.link.confirmation

import com.stripe.android.core.strings.ResolvableString
import com.stripe.android.link.model.LinkAccount
import com.stripe.android.model.ConsumerPaymentDetails
import com.stripe.android.paymentelement.confirmation.ConfirmationHandler

internal interface LinkConfirmationHandler {
suspend fun confirm(
paymentDetails: ConsumerPaymentDetails.PaymentDetails,
linkAccount: LinkAccount
): Result

fun interface Factory {
fun create(confirmationHandler: ConfirmationHandler): LinkConfirmationHandler
}
}

internal sealed interface Result {
data object Succeeded : Result
data object Canceled : Result
data class Failed(val message: ResolvableString) : Result
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.stripe.android.link.injection

import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.stripe.android.core.Logger
import com.stripe.android.core.injection.PUBLISHABLE_KEY
import com.stripe.android.core.injection.STRIPE_ACCOUNT_ID
import com.stripe.android.link.LinkActivityViewModel
import com.stripe.android.link.LinkConfiguration
import com.stripe.android.link.account.LinkAccountManager
import com.stripe.android.link.analytics.LinkEventsReporter
import com.stripe.android.link.confirmation.LinkConfirmationHandler
import com.stripe.android.paymentelement.confirmation.injection.DefaultConfirmationModule
import com.stripe.android.payments.core.injection.STATUS_BAR_COLOR
import dagger.BindsInstance
import dagger.Component
import javax.inject.Named
Expand All @@ -21,13 +25,15 @@ internal annotation class NativeLinkScope
@Component(
modules = [
NativeLinkModule::class,
DefaultConfirmationModule::class,
]
)
internal interface NativeLinkComponent {
val linkAccountManager: LinkAccountManager
val configuration: LinkConfiguration
val linkEventsReporter: LinkEventsReporter
val logger: Logger
val linkConfirmationHandlerFactory: LinkConfirmationHandler.Factory
val viewModel: LinkActivityViewModel

@Component.Builder
Expand All @@ -44,6 +50,12 @@ internal interface NativeLinkComponent {
@BindsInstance
fun context(context: Context): Builder

@BindsInstance
fun savedStateHandle(savedStateHandle: SavedStateHandle): Builder

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

fun build(): NativeLinkComponent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.stripe.android.link.injection
import android.content.Context
import androidx.core.os.LocaleListCompat
import com.stripe.android.BuildConfig
import com.stripe.android.PaymentConfiguration
import com.stripe.android.Stripe
import com.stripe.android.core.Logger
import com.stripe.android.core.injection.ENABLE_LOGGING
Expand All @@ -21,10 +22,13 @@ import com.stripe.android.link.account.DefaultLinkAccountManager
import com.stripe.android.link.account.LinkAccountManager
import com.stripe.android.link.analytics.DefaultLinkEventsReporter
import com.stripe.android.link.analytics.LinkEventsReporter
import com.stripe.android.link.confirmation.DefaultLinkConfirmationHandler
import com.stripe.android.link.confirmation.LinkConfirmationHandler
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.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
Expand Down Expand Up @@ -59,6 +63,7 @@ internal interface NativeLinkModule {
@NativeLinkScope
fun stripeRepository(stripeRepository: StripeApiRepository): StripeRepository

@SuppressWarnings("TooManyFunctions")
companion object {
@Provides
@NativeLinkScope
Expand Down Expand Up @@ -116,13 +121,30 @@ internal interface NativeLinkModule {

@Provides
@NativeLinkScope
internal fun providesAnalyticsRequestExecutor(
fun providesAnalyticsRequestExecutor(
executor: DefaultAnalyticsRequestExecutor
): AnalyticsRequestExecutor = executor

@Provides
@Named(ENABLE_LOGGING)
@NativeLinkScope
fun providesEnableLogging(): Boolean = BuildConfig.DEBUG

@Provides
@NativeLinkScope
fun providePaymentConfiguration(appContext: Context): PaymentConfiguration {
return PaymentConfiguration.getInstance(appContext)
}

@Provides
@NativeLinkScope
@Named(ALLOWS_MANUAL_CONFIRMATION)
fun provideAllowsManualConfirmation() = true

@Provides
@NativeLinkScope
fun provideLinkConfirmationHandlerFactory(
factory: DefaultLinkConfirmationHandler.Factory
): LinkConfirmationHandler.Factory = factory
}
}
Loading
Loading