diff --git a/drop-in/api/drop-in.api b/drop-in/api/drop-in.api index ff04806cfd..b4025b2d42 100644 --- a/drop-in/api/drop-in.api +++ b/drop-in/api/drop-in.api @@ -135,6 +135,7 @@ public final class com/adyen/checkout/dropin/DropInConfiguration$Builder : com/a public final fun addOpenBankingConfiguration (Lcom/adyen/checkout/openbanking/OpenBankingConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; public final fun addPayByBankUSConfiguration (Lcom/adyen/checkout/paybybankus/PayByBankUSConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; public final fun addPayEasyConfiguration (Lcom/adyen/checkout/payeasy/PayEasyConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; + public final fun addPayToConfiguration (Lcom/adyen/checkout/payto/PayToConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; public final fun addSepaConfiguration (Lcom/adyen/checkout/sepa/SepaConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; public final fun addSevenElevenConfiguration (Lcom/adyen/checkout/seveneleven/SevenElevenConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; public final fun addTwintConfiguration (Lcom/adyen/checkout/twint/TwintConfiguration;)Lcom/adyen/checkout/dropin/DropInConfiguration$Builder; diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt index 0109bb412d..534e7f6d5a 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/DropInConfiguration.kt @@ -47,6 +47,7 @@ import com.adyen.checkout.onlinebankingsk.OnlineBankingSKConfiguration import com.adyen.checkout.openbanking.OpenBankingConfiguration import com.adyen.checkout.paybybankus.PayByBankUSConfiguration import com.adyen.checkout.payeasy.PayEasyConfiguration +import com.adyen.checkout.payto.PayToConfiguration import com.adyen.checkout.sepa.SepaConfiguration import com.adyen.checkout.seveneleven.SevenElevenConfiguration import com.adyen.checkout.twint.TwintConfiguration @@ -468,6 +469,14 @@ class DropInConfiguration private constructor( return this } + /** + * Add configuration for PAY TO payment method. + */ + fun addPayToConfiguration(payToConfiguration: PayToConfiguration): Builder { + availablePaymentConfigs[PaymentMethodTypes.PAY_TO] = payToConfiguration + return this + } + /** * Provide a custom name to be shown in Drop-in for payment methods with a type matching [paymentMethodType]. * For [paymentMethodType] you can pass [PaymentMethodTypes] or any other custom value. diff --git a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt index 3a60975b69..ac62fc1aaf 100644 --- a/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt +++ b/drop-in/src/main/java/com/adyen/checkout/dropin/internal/provider/ComponentProvider.kt @@ -97,6 +97,9 @@ import com.adyen.checkout.paybybankus.internal.provider.PayByBankUSComponentProv import com.adyen.checkout.payeasy.PayEasyComponent import com.adyen.checkout.payeasy.PayEasyComponentState import com.adyen.checkout.payeasy.internal.provider.PayEasyComponentProvider +import com.adyen.checkout.payto.PayToComponent +import com.adyen.checkout.payto.PayToComponentState +import com.adyen.checkout.payto.internal.provider.PayToComponentProvider import com.adyen.checkout.sepa.SepaComponent import com.adyen.checkout.sepa.SepaComponentState import com.adyen.checkout.sepa.internal.provider.SepaComponentProvider @@ -439,7 +442,14 @@ internal fun getComponentFor( ) } - // TODO Add PayTo here when component provider is created + checkCompileOnly { PayToComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { + PayToComponentProvider(dropInOverrideParams, analyticsManager).get( + fragment = fragment, + paymentMethod = paymentMethod, + checkoutConfiguration = checkoutConfiguration, + callback = componentCallback as ComponentCallback, + ) + } checkCompileOnly { SepaComponent.PROVIDER.isPaymentMethodSupported(paymentMethod) } -> { SepaComponentProvider(dropInOverrideParams, analyticsManager).get( diff --git a/payto/api/payto.api b/payto/api/payto.api index 018ce247a7..99b084e813 100644 --- a/payto/api/payto.api +++ b/payto/api/payto.api @@ -40,6 +40,70 @@ public final class com/adyen/checkout/payto/PayToComponentState : com/adyen/chec public fun toString ()Ljava/lang/String; } -public final class com/adyen/checkout/payto/internal/provider/PayToComponentProvider { +public final class com/adyen/checkout/payto/PayToConfiguration : com/adyen/checkout/components/core/internal/ButtonConfiguration, com/adyen/checkout/components/core/internal/Configuration { + public static final field CREATOR Landroid/os/Parcelable$Creator; + public fun (Ljava/util/Locale;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;Lcom/adyen/checkout/components/core/AnalyticsConfiguration;Lcom/adyen/checkout/components/core/Amount;Ljava/lang/Boolean;Lcom/adyen/checkout/action/core/GenericActionConfiguration;)V + public fun describeContents ()I + public fun getAmount ()Lcom/adyen/checkout/components/core/Amount; + public fun getAnalyticsConfiguration ()Lcom/adyen/checkout/components/core/AnalyticsConfiguration; + public fun getClientKey ()Ljava/lang/String; + public fun getEnvironment ()Lcom/adyen/checkout/core/Environment; + public fun getShopperLocale ()Ljava/util/Locale; + public fun isSubmitButtonVisible ()Ljava/lang/Boolean; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/adyen/checkout/payto/PayToConfiguration$Builder : com/adyen/checkout/action/core/internal/ActionHandlingPaymentMethodConfigurationBuilder, com/adyen/checkout/components/core/internal/ButtonConfigurationBuilder { + public fun (Landroid/content/Context;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V + public fun (Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V + public fun (Ljava/util/Locale;Lcom/adyen/checkout/core/Environment;Ljava/lang/String;)V + public synthetic fun buildInternal ()Lcom/adyen/checkout/components/core/internal/Configuration; + public synthetic fun setSubmitButtonVisible (Z)Lcom/adyen/checkout/components/core/internal/ButtonConfigurationBuilder; + public fun setSubmitButtonVisible (Z)Lcom/adyen/checkout/payto/PayToConfiguration$Builder; +} + +public final class com/adyen/checkout/payto/PayToConfiguration$Creator : android/os/Parcelable$Creator { + public fun ()V + public final fun createFromParcel (Landroid/os/Parcel;)Lcom/adyen/checkout/payto/PayToConfiguration; + public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; + public final fun newArray (I)[Lcom/adyen/checkout/payto/PayToConfiguration; + public synthetic fun newArray (I)[Ljava/lang/Object; +} + +public final class com/adyen/checkout/payto/PayToConfigurationKt { + public static final fun payTo (Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lkotlin/jvm/functions/Function1;)Lcom/adyen/checkout/components/core/CheckoutConfiguration; + public static synthetic fun payTo$default (Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/CheckoutConfiguration; +} + +public final class com/adyen/checkout/payto/internal/provider/PayToComponentProvider : com/adyen/checkout/components/core/internal/provider/PaymentComponentProvider, com/adyen/checkout/sessions/core/internal/provider/SessionPaymentComponentProvider { + public synthetic fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/activity/ComponentActivity;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/fragment/app/Fragment;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Landroid/app/Application;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Landroid/app/Application;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Landroid/app/Application;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Landroid/app/Application;Lcom/adyen/checkout/components/core/ComponentCallback;Lcom/adyen/checkout/components/core/OrderRequest;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Landroid/app/Application;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/CheckoutConfiguration;Landroid/app/Application;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public synthetic fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/components/core/internal/Configuration;Landroid/app/Application;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/components/core/internal/PaymentComponent; + public fun get (Landroidx/savedstate/SavedStateRegistryOwner;Landroidx/lifecycle/ViewModelStoreOwner;Landroidx/lifecycle/LifecycleOwner;Lcom/adyen/checkout/sessions/core/CheckoutSession;Lcom/adyen/checkout/components/core/PaymentMethod;Lcom/adyen/checkout/payto/PayToConfiguration;Landroid/app/Application;Lcom/adyen/checkout/sessions/core/SessionComponentCallback;Ljava/lang/String;)Lcom/adyen/checkout/payto/PayToComponent; + public fun isPaymentMethodSupported (Lcom/adyen/checkout/components/core/PaymentMethod;)Z } diff --git a/payto/src/main/java/com/adyen/checkout/payto/PayToConfiguration.kt b/payto/src/main/java/com/adyen/checkout/payto/PayToConfiguration.kt new file mode 100644 index 0000000000..ca48036b5e --- /dev/null +++ b/payto/src/main/java/com/adyen/checkout/payto/PayToConfiguration.kt @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by ararat on 3/2/2025. + */ + +package com.adyen.checkout.payto + +import android.content.Context +import com.adyen.checkout.action.core.GenericActionConfiguration +import com.adyen.checkout.action.core.internal.ActionHandlingPaymentMethodConfigurationBuilder +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.AnalyticsConfiguration +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.PaymentMethodTypes +import com.adyen.checkout.components.core.internal.ButtonConfiguration +import com.adyen.checkout.components.core.internal.ButtonConfigurationBuilder +import com.adyen.checkout.components.core.internal.Configuration +import com.adyen.checkout.components.core.internal.util.CheckoutConfigurationMarker +import com.adyen.checkout.core.Environment +import kotlinx.parcelize.Parcelize +import java.util.Locale + +/** + * Configuration class for the [PayToComponent]. + */ +@Parcelize +@Suppress("LongParameterList") +class PayToConfiguration( + override val shopperLocale: Locale?, + override val environment: Environment, + override val clientKey: String, + override val analyticsConfiguration: AnalyticsConfiguration?, + override val amount: Amount?, + override val isSubmitButtonVisible: Boolean?, + internal val genericActionConfiguration: GenericActionConfiguration, +) : Configuration, ButtonConfiguration { + + /** + * Builder to create an [PayToConfiguration]. + */ + class Builder : + ActionHandlingPaymentMethodConfigurationBuilder, + ButtonConfigurationBuilder { + + private var isSubmitButtonVisible: Boolean? = null + + /** + * Initialize a configuration builder with the required fields. + * + * The shopper locale will match the value passed to the API with the sessions flow, or the primary user locale + * on the device otherwise. Check out the + * [Sessions API documentation](https://docs.adyen.com/api-explorer/Checkout/latest/post/sessions) on how to set + * this value. + * + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + constructor(environment: Environment, clientKey: String) : super( + environment, + clientKey, + ) + + /** + * Alternative constructor that uses the [context] to fetch the user locale and use it as a shopper locale. + * + * @param context A context + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + @Deprecated("You can omit the context parameter") + constructor(context: Context, environment: Environment, clientKey: String) : super( + context, + environment, + clientKey, + ) + + /** + * Initialize a configuration builder with the required fields and a shopper locale. + * + * @param shopperLocale The [Locale] of the shopper. + * @param environment The [Environment] to be used for internal network calls from the SDK to Adyen. + * @param clientKey Your Client Key used for internal network calls from the SDK to Adyen. + */ + constructor(shopperLocale: Locale, environment: Environment, clientKey: String) : super( + shopperLocale, + environment, + clientKey, + ) + + /** + * Sets if submit button will be visible or not. + * + * Default is True. + * + * @param isSubmitButtonVisible Is submit button should be visible or not. + */ + override fun setSubmitButtonVisible(isSubmitButtonVisible: Boolean): Builder { + this.isSubmitButtonVisible = isSubmitButtonVisible + return this + } + + override fun buildInternal(): PayToConfiguration { + return PayToConfiguration( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + analyticsConfiguration = analyticsConfiguration, + amount = amount, + isSubmitButtonVisible = isSubmitButtonVisible, + genericActionConfiguration = genericActionConfigurationBuilder.build(), + ) + } + } +} + +fun CheckoutConfiguration.payTo( + configuration: @CheckoutConfigurationMarker PayToConfiguration.Builder.() -> Unit = {} +): CheckoutConfiguration { + val config = PayToConfiguration.Builder(environment, clientKey) + .apply { + shopperLocale?.let { setShopperLocale(it) } + amount?.let { setAmount(it) } + analyticsConfiguration?.let { setAnalyticsConfiguration(it) } + } + .apply(configuration) + .build() + addConfiguration(PaymentMethodTypes.PAY_TO, config) + return this +} + +internal fun CheckoutConfiguration.getPayToConfiguration(): PayToConfiguration? { + return getConfiguration(PaymentMethodTypes.PAY_TO) +} + +internal fun PayToConfiguration.toCheckoutConfiguration(): CheckoutConfiguration { + return CheckoutConfiguration( + shopperLocale = shopperLocale, + environment = environment, + clientKey = clientKey, + amount = amount, + analyticsConfiguration = analyticsConfiguration, + ) { + addConfiguration(PaymentMethodTypes.PAY_TO, this@toCheckoutConfiguration) + + genericActionConfiguration.getAllConfigurations().forEach { + addActionConfiguration(it) + } + } +} diff --git a/payto/src/main/java/com/adyen/checkout/payto/internal/provider/PayToComponentProvider.kt b/payto/src/main/java/com/adyen/checkout/payto/internal/provider/PayToComponentProvider.kt index b07d178a0a..189cfa3136 100644 --- a/payto/src/main/java/com/adyen/checkout/payto/internal/provider/PayToComponentProvider.kt +++ b/payto/src/main/java/com/adyen/checkout/payto/internal/provider/PayToComponentProvider.kt @@ -8,9 +8,269 @@ package com.adyen.checkout.payto.internal.provider +import android.app.Application import androidx.annotation.RestrictTo +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner +import androidx.savedstate.SavedStateRegistryOwner +import com.adyen.checkout.action.core.internal.DefaultActionHandlingComponent +import com.adyen.checkout.action.core.internal.provider.GenericActionComponentProvider +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.components.core.ComponentCallback +import com.adyen.checkout.components.core.Order +import com.adyen.checkout.components.core.PaymentMethod +import com.adyen.checkout.components.core.internal.DefaultComponentEventHandler +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.AnalyticsManagerFactory +import com.adyen.checkout.components.core.internal.analytics.AnalyticsSource +import com.adyen.checkout.components.core.internal.provider.PaymentComponentProvider +import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.CommonComponentParamsMapper +import com.adyen.checkout.components.core.internal.ui.model.DropInOverrideParams +import com.adyen.checkout.components.core.internal.util.get +import com.adyen.checkout.components.core.internal.util.viewModelFactory +import com.adyen.checkout.core.exception.ComponentException +import com.adyen.checkout.core.internal.data.api.HttpClientFactory +import com.adyen.checkout.core.internal.util.LocaleProvider +import com.adyen.checkout.payto.PayToComponent +import com.adyen.checkout.payto.PayToComponentState +import com.adyen.checkout.payto.PayToConfiguration +import com.adyen.checkout.payto.getPayToConfiguration +import com.adyen.checkout.payto.internal.ui.DefaultPayToDelegate +import com.adyen.checkout.payto.toCheckoutConfiguration +import com.adyen.checkout.sessions.core.CheckoutSession +import com.adyen.checkout.sessions.core.SessionComponentCallback +import com.adyen.checkout.sessions.core.internal.SessionComponentEventHandler +import com.adyen.checkout.sessions.core.internal.SessionInteractor +import com.adyen.checkout.sessions.core.internal.SessionSavedStateHandleContainer +import com.adyen.checkout.sessions.core.internal.data.api.SessionRepository +import com.adyen.checkout.sessions.core.internal.data.api.SessionService +import com.adyen.checkout.sessions.core.internal.provider.SessionPaymentComponentProvider +import com.adyen.checkout.sessions.core.internal.ui.model.SessionParamsFactory +import com.adyen.checkout.ui.core.internal.ui.SubmitHandler -// TODO To be implemented class PayToComponentProvider @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -constructor() +constructor( + private val dropInOverrideParams: DropInOverrideParams? = null, + private val analyticsManager: AnalyticsManager? = null, + private val localeProvider: LocaleProvider = LocaleProvider(), +) : + PaymentComponentProvider< + PayToComponent, + PayToConfiguration, + PayToComponentState, + ComponentCallback, + >, + SessionPaymentComponentProvider< + PayToComponent, + PayToConfiguration, + PayToComponentState, + SessionComponentCallback, + > { + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + paymentMethod: PaymentMethod, + checkoutConfiguration: CheckoutConfiguration, + application: Application, + componentCallback: ComponentCallback, + order: Order?, + key: String?, + ): PayToComponent { + assertSupported(paymentMethod) + + val genericFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> + val componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = checkoutConfiguration, + deviceLocale = localeProvider.getLocale(application), + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = null, + componentConfiguration = checkoutConfiguration.getPayToConfiguration(), + ) + + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = null, + ) + + val payToDelegate = DefaultPayToDelegate( + observerRepository = PaymentObserverRepository(), + paymentMethod = paymentMethod, + order = order, + componentParams = componentParams, + analyticsManager = analyticsManager, + submitHandler = SubmitHandler(savedStateHandle), + ) + + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) + + PayToComponent( + payToDelegate = payToDelegate, + genericActionDelegate = genericActionDelegate, + actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, payToDelegate), + componentEventHandler = DefaultComponentEventHandler(), + ) + } + + return ViewModelProvider(viewModelStoreOwner, genericFactory)[key, PayToComponent::class.java] + .also { component -> + component.observe(lifecycleOwner) { + component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) + } + } + } + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + paymentMethod: PaymentMethod, + configuration: PayToConfiguration, + application: Application, + componentCallback: ComponentCallback, + order: Order?, + key: String?, + ): PayToComponent { + return get( + savedStateRegistryOwner = savedStateRegistryOwner, + viewModelStoreOwner = viewModelStoreOwner, + lifecycleOwner = lifecycleOwner, + paymentMethod = paymentMethod, + checkoutConfiguration = configuration.toCheckoutConfiguration(), + application = application, + componentCallback = componentCallback, + order = order, + key = key, + ) + } + + @Suppress("LongMethod") + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + checkoutSession: CheckoutSession, + paymentMethod: PaymentMethod, + checkoutConfiguration: CheckoutConfiguration, + application: Application, + componentCallback: SessionComponentCallback, + key: String? + ): PayToComponent { + assertSupported(paymentMethod) + + val genericFactory = viewModelFactory(savedStateRegistryOwner, null) { savedStateHandle -> + val componentParams = ButtonComponentParamsMapper(CommonComponentParamsMapper()).mapToParams( + checkoutConfiguration = checkoutConfiguration, + deviceLocale = localeProvider.getLocale(application), + dropInOverrideParams = dropInOverrideParams, + componentSessionParams = SessionParamsFactory.create(checkoutSession), + componentConfiguration = checkoutConfiguration.getPayToConfiguration(), + ) + + val httpClient = HttpClientFactory.getHttpClient(componentParams.environment) + + val analyticsManager = analyticsManager ?: AnalyticsManagerFactory().provide( + componentParams = componentParams, + application = application, + source = AnalyticsSource.PaymentComponent(paymentMethod.type.orEmpty()), + sessionId = checkoutSession.sessionSetupResponse.id, + ) + + val payToDelegate = DefaultPayToDelegate( + observerRepository = PaymentObserverRepository(), + paymentMethod = paymentMethod, + order = checkoutSession.order, + componentParams = componentParams, + analyticsManager = analyticsManager, + submitHandler = SubmitHandler(savedStateHandle), + ) + + val genericActionDelegate = + GenericActionComponentProvider(analyticsManager, dropInOverrideParams).getDelegate( + checkoutConfiguration = checkoutConfiguration, + savedStateHandle = savedStateHandle, + application = application, + ) + + val sessionSavedStateHandleContainer = SessionSavedStateHandleContainer( + savedStateHandle = savedStateHandle, + checkoutSession = checkoutSession, + ) + + val sessionInteractor = SessionInteractor( + sessionRepository = SessionRepository( + sessionService = SessionService(httpClient), + clientKey = componentParams.clientKey, + ), + sessionModel = sessionSavedStateHandleContainer.getSessionModel(), + isFlowTakenOver = sessionSavedStateHandleContainer.isFlowTakenOver ?: false, + analyticsManager = analyticsManager, + ) + + val sessionComponentEventHandler = SessionComponentEventHandler( + sessionInteractor = sessionInteractor, + sessionSavedStateHandleContainer = sessionSavedStateHandleContainer, + ) + + PayToComponent( + payToDelegate = payToDelegate, + genericActionDelegate = genericActionDelegate, + actionHandlingComponent = DefaultActionHandlingComponent(genericActionDelegate, payToDelegate), + componentEventHandler = sessionComponentEventHandler, + ) + } + + return ViewModelProvider(viewModelStoreOwner, genericFactory)[key, PayToComponent::class.java] + .also { component -> + component.observe(lifecycleOwner) { + component.componentEventHandler.onPaymentComponentEvent(it, componentCallback) + } + } + } + + override fun get( + savedStateRegistryOwner: SavedStateRegistryOwner, + viewModelStoreOwner: ViewModelStoreOwner, + lifecycleOwner: LifecycleOwner, + checkoutSession: CheckoutSession, + paymentMethod: PaymentMethod, + configuration: PayToConfiguration, + application: Application, + componentCallback: SessionComponentCallback, + key: String? + ): PayToComponent { + return get( + savedStateRegistryOwner = savedStateRegistryOwner, + viewModelStoreOwner = viewModelStoreOwner, + lifecycleOwner = lifecycleOwner, + checkoutSession = checkoutSession, + paymentMethod = paymentMethod, + checkoutConfiguration = configuration.toCheckoutConfiguration(), + application = application, + componentCallback = componentCallback, + key = key, + ) + } + + private fun assertSupported(paymentMethod: PaymentMethod) { + if (!isPaymentMethodSupported(paymentMethod)) { + throw ComponentException("Unsupported payment method ${paymentMethod.type}") + } + } + + override fun isPaymentMethodSupported(paymentMethod: PaymentMethod): Boolean { + return PayToComponent.PAYMENT_METHOD_TYPES.contains(paymentMethod.type) + } +} diff --git a/payto/src/main/java/com/adyen/checkout/payto/internal/ui/DefaultPayToDelegate.kt b/payto/src/main/java/com/adyen/checkout/payto/internal/ui/DefaultPayToDelegate.kt new file mode 100644 index 0000000000..6349b5f32f --- /dev/null +++ b/payto/src/main/java/com/adyen/checkout/payto/internal/ui/DefaultPayToDelegate.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by ararat on 3/2/2025. + */ + +package com.adyen.checkout.payto.internal.ui + +import androidx.lifecycle.LifecycleOwner +import com.adyen.checkout.components.core.OrderRequest +import com.adyen.checkout.components.core.PaymentMethod +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.GenericEvents +import com.adyen.checkout.components.core.internal.ui.model.ButtonComponentParams +import com.adyen.checkout.payto.PayToComponentState +import com.adyen.checkout.ui.core.internal.ui.ComponentViewType +import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIEvent +import com.adyen.checkout.ui.core.internal.ui.PaymentComponentUIState +import com.adyen.checkout.ui.core.internal.ui.SubmitHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.onEach + +// TODO Remove the unnecessary annotations +@Suppress("TooManyFunctions") +internal class DefaultPayToDelegate( + @Suppress("UnusedPrivateProperty") + private val observerRepository: PaymentObserverRepository, + private val paymentMethod: PaymentMethod, + @Suppress("UnusedPrivateProperty") + private val order: OrderRequest?, + override val componentParams: ButtonComponentParams, + private val analyticsManager: AnalyticsManager, + private val submitHandler: SubmitHandler, +) : PayToDelegate { + + private val _viewFlow: MutableStateFlow = MutableStateFlow(PayToComponentViewType) + override val viewFlow: Flow = _viewFlow + + override val uiStateFlow: Flow = submitHandler.uiStateFlow + + override val uiEventFlow: Flow = submitHandler.uiEventFlow + + private fun getTrackedSubmitFlow() = submitHandler.submitFlow.onEach { + val event = GenericEvents.submit(paymentMethod.type.orEmpty()) + analyticsManager.trackEvent(event) + } + + override fun onSubmit() { + TODO("Not yet implemented") + } + + override fun isConfirmationRequired(): Boolean { + TODO("Not yet implemented") + } + + override fun shouldShowSubmitButton(): Boolean { + TODO("Not yet implemented") + } + + override fun setInteractionBlocked(isInteractionBlocked: Boolean) { + TODO("Not yet implemented") + } + + override val submitFlow: Flow = getTrackedSubmitFlow() + override fun getPaymentMethodType(): String { + TODO("Not yet implemented") + } + + override fun observe( + lifecycleOwner: LifecycleOwner, + coroutineScope: CoroutineScope, + callback: (PaymentComponentEvent) -> Unit + ) { + TODO("Not yet implemented") + } + + override fun removeObserver() { + TODO("Not yet implemented") + } + + override fun initialize(coroutineScope: CoroutineScope) { + TODO("Not yet implemented") + } + + override fun onCleared() { + TODO("Not yet implemented") + } +} diff --git a/payto/src/test/java/com/adyen/checkout/payto/PayToConfigurationTest.kt b/payto/src/test/java/com/adyen/checkout/payto/PayToConfigurationTest.kt new file mode 100644 index 0000000000..648ca7ff20 --- /dev/null +++ b/payto/src/test/java/com/adyen/checkout/payto/PayToConfigurationTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Adyen N.V. + * + * This file is open source and available under the MIT license. See the LICENSE file for more info. + * + * Created by ararat on 3/2/2025. + */ + +package com.adyen.checkout.payto + +import com.adyen.checkout.components.core.Amount +import com.adyen.checkout.components.core.AnalyticsConfiguration +import com.adyen.checkout.components.core.AnalyticsLevel +import com.adyen.checkout.components.core.CheckoutConfiguration +import com.adyen.checkout.core.Environment +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.util.Locale + +internal class PayToConfigurationTest { + + @Test + fun `when creating the configuration through CheckoutConfiguration, then it should be the same as when the builder is used`() { + val checkoutConfiguration = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + amount = Amount("EUR", 123L), + analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), + ) { + payTo { + setSubmitButtonVisible(false) + } + } + + val actual = checkoutConfiguration.getPayToConfiguration() + + val expected = PayToConfiguration.Builder( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + ) + .setAmount(Amount("EUR", 123L)) + .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .setSubmitButtonVisible(false) + .build() + + assertEquals(expected.shopperLocale, actual?.shopperLocale) + assertEquals(expected.environment, actual?.environment) + assertEquals(expected.clientKey, actual?.clientKey) + assertEquals(expected.amount, actual?.amount) + assertEquals(expected.analyticsConfiguration, actual?.analyticsConfiguration) + assertEquals(expected.isSubmitButtonVisible, actual?.isSubmitButtonVisible) + } + + @Test + fun `when the configuration is mapped to CheckoutConfiguration, then CheckoutConfiguration is created correctly`() { + val config = PayToConfiguration.Builder( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + ) + .setAmount(Amount("EUR", 123L)) + .setAnalyticsConfiguration(AnalyticsConfiguration(AnalyticsLevel.ALL)) + .setSubmitButtonVisible(false) + .build() + + val actual = config.toCheckoutConfiguration() + + val expected = CheckoutConfiguration( + shopperLocale = Locale.US, + environment = Environment.TEST, + clientKey = TEST_CLIENT_KEY, + amount = Amount("EUR", 123L), + analyticsConfiguration = AnalyticsConfiguration(AnalyticsLevel.ALL), + ) + + assertEquals(expected.shopperLocale, actual.shopperLocale) + assertEquals(expected.environment, actual.environment) + assertEquals(expected.clientKey, actual.clientKey) + assertEquals(expected.amount, actual.amount) + assertEquals(expected.analyticsConfiguration, actual.analyticsConfiguration) + + val actualPayToConfig = actual.getPayToConfiguration() + assertEquals(config.shopperLocale, actualPayToConfig?.shopperLocale) + assertEquals(config.environment, actualPayToConfig?.environment) + assertEquals(config.clientKey, actualPayToConfig?.clientKey) + assertEquals(config.amount, actualPayToConfig?.amount) + assertEquals(config.analyticsConfiguration, actualPayToConfig?.analyticsConfiguration) + assertEquals(config.isSubmitButtonVisible, actualPayToConfig?.isSubmitButtonVisible) + } + + companion object { + private const val TEST_CLIENT_KEY = "test_qwertyuiopasdfghjklzxcvbnmqwerty" + } +}