pub struct FraudCheckUpdateInternal {
    frm_status: Option<FraudCheckStatus>,
    frm_transaction_id: Option<String>,
    frm_reason: Option<serde_json::Value>,
    frm_score: Option<i32>,
    frm_error: Option<Option<String>>,
    metadata: Option<serde_json::Value>,
    last_step: FraudCheckLastStep,
    payment_capture_method: Option<storage_enums::CaptureMethod>,
}


pub struct HyperwalletPaymentsResponse {
    status: HyperwalletPaymentStatus,
    id: String,
}


File: crates/storage_impl/src/mock_db.rs
 Public functions: 5
 Public structs: 1

use std::sync::Arc;

use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState};
use diesel_models as store;
use error_stack::ResultExt;
use futures::lock::{Mutex, MutexGuard};
use hyperswitch_domain_models::{
    behaviour::{Conversion, ReverseConversion},
    merchant_key_store::MerchantKeyStore,
    payments::{payment_attempt::PaymentAttempt, PaymentIntent},
};
use redis_interface::RedisSettings;

use crate::{errors::StorageError, redis::RedisStore};

pub mod payment_attempt;
pub mod payment_intent;
#[cfg(feature = "payouts")]
pub mod payout_attempt;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod redis_conn;
#[cfg(not(feature = "payouts"))]
use hyperswitch_domain_models::{PayoutAttemptInterface, PayoutsInterface};

#[derive(Clone)]
pub struct MockDb {
    pub addresses: Arc<Mutex<Vec<store::Address>>>,
    pub configs: Arc<Mutex<Vec<store::Config>>>,
    pub merchant_accounts: Arc<Mutex<Vec<store::MerchantAccount>>>,
    pub merchant_connector_accounts: Arc<Mutex<Vec<store::MerchantConnectorAccount>>>,
    pub payment_attempts: Arc<Mutex<Vec<PaymentAttempt>>>,
    pub payment_intents: Arc<Mutex<Vec<PaymentIntent>>>,
    pub payment_methods: Arc<Mutex<Vec<store::PaymentMethod>>>,
    pub customers: Arc<Mutex<Vec<store::Customer>>>,
    pub refunds: Arc<Mutex<Vec<store::Refund>>>,
    pub processes: Arc<Mutex<Vec<store::ProcessTracker>>>,
    pub redis: Arc<RedisStore>,
    pub api_keys: Arc<Mutex<Vec<store::ApiKey>>>,
    pub ephemeral_keys: Arc<Mutex<Vec<store::EphemeralKey>>>,
    pub cards_info: Arc<Mutex<Vec<store::CardInfo>>>,
    pub events: Arc<Mutex<Vec<store::Event>>>,
    pub disputes: Arc<Mutex<Vec<store::Dispute>>>,
    pub lockers: Arc<Mutex<Vec<store::LockerMockUp>>>,
    pub mandates: Arc<Mutex<Vec<store::Mandate>>>,
    pub captures: Arc<Mutex<Vec<store::capture::Capture>>>,
    pub merchant_key_store: Arc<Mutex<Vec<store::merchant_key_store::MerchantKeyStore>>>,
    #[cfg(all(feature = "v2", feature = "tokenization_v2"))]
    pub tokenizations: Arc<Mutex<Vec<store::tokenization::Tokenization>>>,
    pub business_profiles: Arc<Mutex<Vec<store::business_profile::Profile>>>,
    pub reverse_lookups: Arc<Mutex<Vec<store::ReverseLookup>>>,
    pub payment_link: Arc<Mutex<Vec<store::payment_link::PaymentLink>>>,
    pub organizations: Arc<Mutex<Vec<store::organization::Organization>>>,
    pub users: Arc<Mutex<Vec<store::user::User>>>,
    pub user_roles: Arc<Mutex<Vec<store::user_role::UserRole>>>,
    pub authorizations: Arc<Mutex<Vec<store::authorization::Authorization>>>,
    pub dashboard_metadata: Arc<Mutex<Vec<store::user::dashboard_metadata::DashboardMetadata>>>,
    #[cfg(feature = "payouts")]
    pub payout_attempt: Arc<Mutex<Vec<store::payout_attempt::PayoutAttempt>>>,
    #[cfg(feature = "payouts")]
    pub payouts: Arc<Mutex<Vec<store::payouts::Payouts>>>,
    pub authentications: Arc<Mutex<Vec<store::authentication::Authentication>>>,
    pub roles: Arc<Mutex<Vec<store::role::Role>>>,
    pub user_key_store: Arc<Mutex<Vec<store::user_key_store::UserKeyStore>>>,
    pub user_authentication_methods:
        Arc<Mutex<Vec<store::user_authentication_method::UserAuthenticationMethod>>>,
    pub themes: Arc<Mutex<Vec<store::user::theme::Theme>>>,
}

impl MockDb {
    pub async fn new(redis: &RedisSettings) -> error_stack::Result<Self, StorageError> {
        Ok(Self {
            addresses: Default::default(),
            configs: Default::default(),
            merchant_accounts: Default::default(),
            merchant_connector_accounts: Default::default(),
            payment_attempts: Default::default(),
            payment_intents: Default::default(),
            payment_methods: Default::default(),
            customers: Default::default(),
            refunds: Default::default(),
            processes: Default::default(),
            redis: Arc::new(
                RedisStore::new(redis)
                    .await
                    .change_context(StorageError::InitializationError)?,
            ),
            api_keys: Default::default(),
            ephemeral_keys: Default::default(),
            cards_info: Default::default(),
            events: Default::default(),
            disputes: Default::default(),
            lockers: Default::default(),
            mandates: Default::default(),
            captures: Default::default(),
            merchant_key_store: Default::default(),
            #[cfg(all(feature = "v2", feature = "tokenization_v2"))]
            tokenizations: Default::default(),
            business_profiles: Default::default(),
            reverse_lookups: Default::default(),
            payment_link: Default::default(),
            organizations: Default::default(),
            users: Default::default(),
            user_roles: Default::default(),
            authorizations: Default::default(),
            dashboard_metadata: Default::default(),
            #[cfg(feature = "payouts")]
            payout_attempt: Default::default(),
            #[cfg(feature = "payouts")]
            payouts: Default::default(),
            authentications: Default::default(),
            roles: Default::default(),
            user_key_store: Default::default(),
            user_authentication_methods: Default::default(),
            themes: Default::default(),
        })
    }

    /// Returns an option of the resource if it exists
    pub async fn find_resource<D, R>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        resources: MutexGuard<'_, Vec<D>>,
        filter_fn: impl Fn(&&D) -> bool,
    ) -> CustomResult<Option<R>, StorageError>
    where
        D: Sync + ReverseConversion<R> + Clone,
        R: Conversion,
    {
        let resource = resources.iter().find(filter_fn).cloned();
        match resource {
            Some(res) => Ok(Some(
                res.convert(
                    state,
                    key_store.key.get_inner(),
                    key_store.merchant_id.clone().into(),
                )
                .await
                .change_context(StorageError::DecryptionError)?,
            )),
            None => Ok(None),
        }
    }

    /// Throws errors when the requested resource is not found
    pub async fn get_resource<D, R>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        resources: MutexGuard<'_, Vec<D>>,
        filter_fn: impl Fn(&&D) -> bool,
        error_message: String,
    ) -> CustomResult<R, StorageError>
    where
        D: Sync + ReverseConversion<R> + Clone,
        R: Conversion,
    {
        match self
            .find_resource(state, key_store, resources, filter_fn)
            .await?
        {
            Some(res) => Ok(res),
            None => Err(StorageError::ValueNotFound(error_message).into()),
        }
    }

    pub async fn get_resources<D, R>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        resources: MutexGuard<'_, Vec<D>>,
        filter_fn: impl Fn(&&D) -> bool,
        error_message: String,
    ) -> CustomResult<Vec<R>, StorageError>
    where
        D: Sync + ReverseConversion<R> + Clone,
        R: Conversion,
    {
        let resources: Vec<_> = resources.iter().filter(filter_fn).cloned().collect();
        if resources.is_empty() {
            Err(StorageError::ValueNotFound(error_message).into())
        } else {
            let pm_futures = resources
                .into_iter()
                .map(|pm| async {
                    pm.convert(
                        state,
                        key_store.key.get_inner(),
                        key_store.merchant_id.clone().into(),
                    )
                    .await
                    .change_context(StorageError::DecryptionError)
                })
                .collect::<Vec<_>>();

            let domain_resources = futures::future::try_join_all(pm_futures).await?;

            Ok(domain_resources)
        }
    }

    pub async fn update_resource<D, R>(
        &self,
        state: &KeyManagerState,
        key_store: &MerchantKeyStore,
        mut resources: MutexGuard<'_, Vec<D>>,
        resource_updated: D,
        filter_fn: impl Fn(&&mut D) -> bool,
        error_message: String,
    ) -> CustomResult<R, StorageError>
    where
        D: Sync + ReverseConversion<R> + Clone,
        R: Conversion,
    {
        if let Some(pm) = resources.iter_mut().find(filter_fn) {
            *pm = resource_updated.clone();
            let result = resource_updated
                .convert(
                    state,
                    key_store.key.get_inner(),
                    key_store.merchant_id.clone().into(),
                )
                .await
                .change_context(StorageError::DecryptionError)?;
            Ok(result)
        } else {
            Err(StorageError::ValueNotFound(error_message).into())
        }
    }
}

#[cfg(not(feature = "payouts"))]
impl PayoutsInterface for MockDb {}

#[cfg(not(feature = "payouts"))]
impl PayoutAttemptInterface for MockDb {}


pub struct MpgsAuthType {
    pub(super) api_key: Secret<String>,
}


pub struct NetworkTokenDetailsResponse {
    /// Network token generated against the Card Number
    #[schema(value_type = String)]
    pub network_token: cards::NetworkToken,

    /// Expiry month of the network token
    #[schema(value_type = String)]
    pub network_token_exp_month: masking::Secret<String>,

    /// Expiry year of the network token
    #[schema(value_type = String)]
    pub network_token_exp_year: masking::Secret<String>,

    /// Cryptogram generated by the Network
    #[schema(value_type = Option<String>)]
    pub cryptogram: Option<masking::Secret<String>>,

    /// Issuer of the card
    pub card_issuer: Option<String>,

    /// Card network of the token
    #[schema(value_type = Option<CardNetwork>)]
    pub card_network: Option<common_enums::CardNetwork>,

    /// Card type of the token
    pub card_type: Option<CardType>,

    /// Issuing country of the card
    #[schema(value_type = Option<CountryAlpha2>)]
    pub card_issuing_country: Option<common_enums::CountryAlpha2>,

    /// Bank code of the card
    pub bank_code: Option<String>,

    /// Name of the card holder
    #[schema(value_type = Option<String>)]
    pub card_holder_name: Option<masking::Secret<String>>,

    /// Nick name of the card holder
    #[schema(value_type = Option<String>)]
    pub nick_name: Option<masking::Secret<String>>,

    /// ECI indicator of the card
    pub eci: Option<String>,
}


impl MerchantAccount {
    pub fn get_compatible_connector(&self) -> Option<api_models::enums::Connector> {
        let metadata: Option<api_models::admin::MerchantAccountMetadata> =
            self.metadata.as_ref().and_then(|meta| {
                meta.clone()
                    .parse_value("MerchantAccountMetadata")
                    .map_err(|err| logger::error!("Failed to deserialize {:?}", err))
                    .ok()
            });
        metadata.and_then(|a| a.compatible_connector)
    }
}


pub struct AuthipayRouterData<T> {
    pub amount: FloatMajorUnit, // Amount in major units (e.g., dollars instead of cents)
    pub router_data: T,
}


pub struct FieldMeta {
    _meta_type: Ident,
    pub value: Ident,
}


impl api::PaymentSync for Deutschebank {}

impl api::PaymentSync for for Deutschebank


pub struct BluesnapAuthErrorResponse {
    pub error_code: String,
    pub error_description: String,
    pub error_name: Option<String>,
}


pub struct PaymentCaptureData<F>
where
    F: Clone,
{
    pub flow: PhantomData<F>,
    pub payment_intent: PaymentIntent,
    pub payment_attempt: PaymentAttempt,
}


/// fn is_connector_timeout
    pub fn is_connector_timeout(&self) -> bool {
        self == &Self::RequestTimeoutReceived
    }


impl api::PaymentVoid for Paypal {}


impl CardInfo {
    pub async fn find_by_iin(conn: &PgPooledConn, card_iin: &str) -> StorageResult<Option<Self>> {
        generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
            conn,
            card_iin.to_owned(),
        )
        .await
    }
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Self> {
        generics::generic_insert(conn, self).await
    }
    pub async fn update(
        conn: &PgPooledConn,
        card_iin: String,
        data: UpdateCardInfo,
    ) -> StorageResult<Self> {
        generics::generic_update_with_results::<<Self as HasTable>::Table, UpdateCardInfo, _, _>(
            conn,
            dsl::card_iin.eq(card_iin),
            data,
        )
        .await?
        .first()
        .cloned()
        .ok_or_else(|| {
            report!(errors::DatabaseError::NotFound)
                .attach_printable("Error while updating card_info entry")
        })
    }
}


            let transaction_data = core_routing::PaymentsDslInput::new(
                payment_data.get_setup_mandate(),
                payment_data.get_payment_attempt(),
                payment_data.get_payment_intent(),
                payment_data.get_payment_method_data(),
                payment_data.get_address(),
                payment_data.get_recurring_details(),
                payment_data.get_currency(),
            );

            connectors = routing::perform_eligibility_analysis_with_fallback(
                &state.clone(),
                merchant_context.get_merchant_key_store(),
                connectors,
                &TransactionData::Payment(transaction_data),
                eligible_connectors,
                business_profile,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("failed eligibility analysis and fallback")?;
        }

        let connector_data = connectors
            .into_iter()
            .map(|conn| {
                api::ConnectorData::get_connector_by_name(
                    &state.conf.connectors,
                    &conn.connector.to_string(),
                    api::GetToken::Connector,
                    conn.merchant_connector_id.clone(),
                )
                .map(|connector_data| connector_data.into())
            })
            .collect::<CustomResult<Vec<_>, _>>()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Invalid connector name received")?;

        logger::debug!("euclid_routing: straight through connector present");
        return decide_multiplex_connector_for_normal_or_recurring_payment(
            &state,
            payment_data,
            routing_data,
            connector_data,
            mandate_type,
            business_profile.is_connector_agnostic_mit_enabled,
            business_profile.is_network_tokenization_enabled,
        )
        .await;
    }

    if let Some(ref routing_algorithm) = routing_data.routing_info.algorithm {
        let (mut connectors, check_eligibility) = routing::perform_straight_through_routing(
            routing_algorithm,
            payment_data.get_creds_identifier(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed execution of straight through routing")?;

        if check_eligibility {
            let transaction_data = core_routing::PaymentsDslInput::new(
                payment_data.get_setup_mandate(),
                payment_data.get_payment_attempt(),
                payment_data.get_payment_intent(),
                payment_data.get_payment_method_data(),
                payment_data.get_address(),
                payment_data.get_recurring_details(),
                payment_data.get_currency(),
            );

            connectors = routing::perform_eligibility_analysis_with_fallback(
                &state,
                merchant_context.get_merchant_key_store(),
                connectors,
                &TransactionData::Payment(transaction_data),
                eligible_connectors,
                business_profile,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("failed eligibility analysis and fallback")?;
        }

        logger::debug!("euclid_routing: single connector present in algorithm data");
        let connector_data = connectors
            .into_iter()
            .map(|conn| {
                api::ConnectorData::get_connector_by_name(
                    &state.conf.connectors,
                    &conn.connector.to_string(),
                    api::GetToken::Connector,
                    conn.merchant_connector_id,
                )
                .map(|connector_data| connector_data.into())
            })
            .collect::<CustomResult<Vec<_>, _>>()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Invalid connector name received")?;

        return decide_multiplex_connector_for_normal_or_recurring_payment(
            &state,
            payment_data,
            routing_data,
            connector_data,
            mandate_type,
            business_profile.is_connector_agnostic_mit_enabled,
            business_profile.is_network_tokenization_enabled,
        )
        .await;
    }

    let new_pd = payment_data.clone();
    let transaction_data = core_routing::PaymentsDslInput::new(
        new_pd.get_setup_mandate(),
        new_pd.get_payment_attempt(),
        new_pd.get_payment_intent(),
        new_pd.get_payment_method_data(),
        new_pd.get_address(),
        new_pd.get_recurring_details(),
        new_pd.get_currency(),
    );

    route_connector_v1_for_payments(
        &state,
        merchant_context,
        business_profile,
        payment_data,
        transaction_data,
        routing_data,
        eligible_connectors,
        mandate_type,
    )
    .await
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn decide_multiplex_connector_for_normal_or_recurring_payment<F: Clone, D>(
    state: &SessionState,
    payment_data: &mut D,
    routing_data: &mut storage::RoutingData,
    connectors: Vec<api::ConnectorRoutingData>,
    mandate_type: Option<api::MandateTransactionType>,
    is_connector_agnostic_mit_enabled: Option<bool>,
    is_network_tokenization_enabled: bool,
) -> RouterResult<ConnectorCallType>
where
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    match (
        payment_data.get_payment_intent().setup_future_usage,
        payment_data.get_token_data().as_ref(),
        payment_data.get_recurring_details().as_ref(),
        payment_data.get_payment_intent().off_session,
        mandate_type,
    ) {
        (
            Some(storage_enums::FutureUsage::OffSession),
            Some(_),
            None,
            None,
            Some(api::MandateTransactionType::RecurringMandateTransaction),
        )
        | (
            None,
            None,
            Some(RecurringDetails::PaymentMethodId(_)),
            Some(true),
            Some(api::MandateTransactionType::RecurringMandateTransaction),
        )
        | (None, Some(_), None, Some(true), _) => {
            logger::debug!("euclid_routing: performing routing for token-based MIT flow");

            let payment_method_info = payment_data
                .get_payment_method_info()
                .get_required_value("payment_method_info")?
                .clone();

            //fetch connectors that support ntid flow
            let ntid_supported_connectors = &state
                .conf
                .network_transaction_id_supported_connectors
                .connector_list;
            //filered connectors list with ntid_supported_connectors
            let filtered_ntid_supported_connectors =
                filter_ntid_supported_connectors(connectors.clone(), ntid_supported_connectors);

            //fetch connectors that support network tokenization flow
            let network_tokenization_supported_connectors = &state
                .conf
                .network_tokenization_supported_connectors
                .connector_list;
            //filered connectors list with ntid_supported_connectors and network_tokenization_supported_connectors
            let filtered_nt_supported_connectors = filter_network_tokenization_supported_connectors(
                filtered_ntid_supported_connectors,
                network_tokenization_supported_connectors,
            );

            let action_type = decide_action_type(
                state,
                is_connector_agnostic_mit_enabled,
                is_network_tokenization_enabled,
                &payment_method_info,
                filtered_nt_supported_connectors.clone(),
            )
            .await;

            match action_type {
                Some(ActionType::NetworkTokenWithNetworkTransactionId(nt_data)) => {
                    logger::info!(
                        "using network_tokenization with network_transaction_id for MIT flow"
                    );

                    let mandate_reference_id =
                        Some(payments_api::MandateReferenceId::NetworkTokenWithNTI(
                            payments_api::NetworkTokenWithNTIRef {
                                network_transaction_id: nt_data.network_transaction_id.to_string(),
                                token_exp_month: nt_data.token_exp_month,
                                token_exp_year: nt_data.token_exp_year,
                            },
                        ));
                    let chosen_connector_data = filtered_nt_supported_connectors
                        .first()
                        .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                        .attach_printable(
                            "no eligible connector found for token-based MIT payment",
                        )?;

                    routing_data.routed_through = Some(
                        chosen_connector_data
                            .connector_data
                            .connector_name
                            .to_string(),
                    );

                    routing_data
                        .merchant_connector_id
                        .clone_from(&chosen_connector_data.connector_data.merchant_connector_id);

                    payment_data.set_mandate_id(payments_api::MandateIds {
                        mandate_id: None,
                        mandate_reference_id,
                    });

                    Ok(ConnectorCallType::PreDetermined(
                        chosen_connector_data.clone(),
                    ))
                }
                None => {
                    decide_connector_for_normal_or_recurring_payment(
                        state,
                        payment_data,
                        routing_data,
                        connectors,
                        is_connector_agnostic_mit_enabled,
                        &payment_method_info,
                    )
                    .await
                }
            }
        }
        (
            None,
            None,
            Some(RecurringDetails::ProcessorPaymentToken(_token)),
            Some(true),
            Some(api::MandateTransactionType::RecurringMandateTransaction),
        ) => {
            if let Some(connector) = connectors.first() {
                let connector = &connector.connector_data;
                routing_data.routed_through = Some(connector.connector_name.clone().to_string());
                routing_data
                    .merchant_connector_id
                    .clone_from(&connector.merchant_connector_id);
                Ok(ConnectorCallType::PreDetermined(
                    api::ConnectorData {
                        connector: connector.connector.clone(),
                        connector_name: connector.connector_name,
                        get_token: connector.get_token.clone(),
                        merchant_connector_id: connector.merchant_connector_id.clone(),
                    }
                    .into(),
                ))
            } else {
                logger::error!(
                    "euclid_routing: no eligible connector found for the ppt_mandate payment"
                );
                Err(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration.into())
            }
        }
        _ => {
            helpers::override_setup_future_usage_to_on_session(&*state.store, payment_data).await?;

            let first_choice = connectors
                .first()
                .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                .attach_printable("no eligible connector found for payment")?
                .clone();

            routing_data.routed_through =
                Some(first_choice.connector_data.connector_name.to_string());

            routing_data.merchant_connector_id = first_choice.connector_data.merchant_connector_id;

            Ok(ConnectorCallType::Retryable(connectors))
        }
    }
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
    state: &SessionState,
    payment_data: &mut D,
    routing_data: &mut storage::RoutingData,
    connectors: Vec<api::ConnectorRoutingData>,
    is_connector_agnostic_mit_enabled: Option<bool>,
    payment_method_info: &domain::PaymentMethod,
) -> RouterResult<ConnectorCallType>
where
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let connector_common_mandate_details = payment_method_info
        .get_common_mandate_reference()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get the common mandate reference")?;

    let connector_mandate_details = connector_common_mandate_details.payments.clone();

    let mut connector_choice = None;

    for connector_info in connectors {
        let connector_data = connector_info.connector_data;
        let merchant_connector_id = connector_data
            .merchant_connector_id
            .as_ref()
            .ok_or(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to find the merchant connector id")?;
        if connector_mandate_details
            .clone()
            .map(|connector_mandate_details| {
                connector_mandate_details.contains_key(merchant_connector_id)
            })
            .unwrap_or(false)
        {
            logger::info!("euclid_routing: using connector_mandate_id for MIT flow");
            if let Some(merchant_connector_id) = connector_data.merchant_connector_id.as_ref() {
                if let Some(mandate_reference_record) = connector_mandate_details.clone()
                        .get_required_value("connector_mandate_details")
                            .change_context(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                            .attach_printable("no eligible connector found for token-based MIT flow since there were no connector mandate details")?
                            .get(merchant_connector_id)
                        {
                            common_utils::fp_utils::when(
                                mandate_reference_record
                                    .original_payment_authorized_currency
                                    .map(|mandate_currency| mandate_currency != payment_data.get_currency())
                                    .unwrap_or(false),
                                || {
                                    Err(report!(errors::ApiErrorResponse::MandateValidationFailed {
                                        reason: "cross currency mandates not supported".into()
                                    }))
                                },
                            )?;
                            let mandate_reference_id = Some(payments_api::MandateReferenceId::ConnectorMandateId(
                                api_models::payments::ConnectorMandateReferenceId::new(
                                    Some(mandate_reference_record.connector_mandate_id.clone()),
                                    Some(payment_method_info.get_id().clone()),
                                    // update_history
                                    None,
                                    mandate_reference_record.mandate_metadata.clone(),
                                    mandate_reference_record.connector_mandate_request_reference_id.clone(),
                                )
                            ));
                            payment_data.set_recurring_mandate_payment_data(
                                hyperswitch_domain_models::router_data::RecurringMandatePaymentData {
                                    payment_method_type: mandate_reference_record
                                        .payment_method_type,
                                    original_payment_authorized_amount: mandate_reference_record
                                        .original_payment_authorized_amount,
                                    original_payment_authorized_currency: mandate_reference_record
                                        .original_payment_authorized_currency,
                                    mandate_metadata: mandate_reference_record
                                        .mandate_metadata.clone()
                                });
                            connector_choice = Some((connector_data, mandate_reference_id.clone()));
                            break;
                        }
            }
        } else if is_network_transaction_id_flow(
            state,
            is_connector_agnostic_mit_enabled,
            connector_data.connector_name,
            payment_method_info,
        ) {
            logger::info!("using network_transaction_id for MIT flow");
            let network_transaction_id = payment_method_info
                .network_transaction_id
                .as_ref()
                .ok_or(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to fetch the network transaction id")?;

            let mandate_reference_id = Some(payments_api::MandateReferenceId::NetworkMandateId(
                network_transaction_id.to_string(),
            ));

            connector_choice = Some((connector_data, mandate_reference_id.clone()));
            break;
        } else {
            continue;
        }
    }

    let (chosen_connector_data, mandate_reference_id) = connector_choice
        .get_required_value("connector_choice")
        .change_context(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
        .attach_printable("no eligible connector found for token-based MIT payment")?;

    routing_data.routed_through = Some(chosen_connector_data.connector_name.to_string());

    routing_data
        .merchant_connector_id
        .clone_from(&chosen_connector_data.merchant_connector_id);

    payment_data.set_mandate_id(payments_api::MandateIds {
        mandate_id: None,
        mandate_reference_id,
    });

    Ok(ConnectorCallType::PreDetermined(
        chosen_connector_data.into(),
    ))
}

pub fn filter_ntid_supported_connectors(
    connectors: Vec<api::ConnectorRoutingData>,
    ntid_supported_connectors: &HashSet<enums::Connector>,
) -> Vec<api::ConnectorRoutingData> {
    connectors
        .into_iter()
        .filter(|data| ntid_supported_connectors.contains(&data.connector_data.connector_name))
        .collect()
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub struct NetworkTokenExpiry {
    pub token_exp_month: Option<Secret<String>>,
    pub token_exp_year: Option<Secret<String>>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub struct NTWithNTIRef {
    pub network_transaction_id: String,
    pub token_exp_month: Option<Secret<String>>,
    pub token_exp_year: Option<Secret<String>>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub enum ActionType {
    NetworkTokenWithNetworkTransactionId(NTWithNTIRef),
}

pub fn filter_network_tokenization_supported_connectors(
    connectors: Vec<api::ConnectorRoutingData>,
    network_tokenization_supported_connectors: &HashSet<enums::Connector>,
) -> Vec<api::ConnectorRoutingData> {
    connectors
        .into_iter()
        .filter(|data| {
            network_tokenization_supported_connectors.contains(&data.connector_data.connector_name)
        })
        .collect()
}

#[cfg(feature = "v1")]
pub async fn decide_action_type(
    state: &SessionState,
    is_connector_agnostic_mit_enabled: Option<bool>,
    is_network_tokenization_enabled: bool,
    payment_method_info: &domain::PaymentMethod,
    filtered_nt_supported_connectors: Vec<api::ConnectorRoutingData>, //network tokenization supported connectors
) -> Option<ActionType> {
    match (
        is_network_token_with_network_transaction_id_flow(
            is_connector_agnostic_mit_enabled,
            is_network_tokenization_enabled,
            payment_method_info,
        ),
        !filtered_nt_supported_connectors.is_empty(),
    ) {
        (IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id), true) => {
            if let Ok((token_exp_month, token_exp_year)) =
                network_tokenization::do_status_check_for_network_token(state, payment_method_info)
                    .await
            {
                Some(ActionType::NetworkTokenWithNetworkTransactionId(
                    NTWithNTIRef {
                        token_exp_month,
                        token_exp_year,
                        network_transaction_id,
                    },
                ))
            } else {
                None
            }
        }
        (IsNtWithNtiFlow::NtWithNtiSupported(_), false)
        | (IsNtWithNtiFlow::NTWithNTINotSupported, _) => None,
    }
}

pub fn is_network_transaction_id_flow(
    state: &SessionState,
    is_connector_agnostic_mit_enabled: Option<bool>,
    connector: enums::Connector,
    payment_method_info: &domain::PaymentMethod,
) -> bool {
    let ntid_supported_connectors = &state
        .conf
        .network_transaction_id_supported_connectors
        .connector_list;

    is_connector_agnostic_mit_enabled == Some(true)
        && payment_method_info.get_payment_method_type() == Some(storage_enums::PaymentMethod::Card)
        && ntid_supported_connectors.contains(&connector)
        && payment_method_info.network_transaction_id.is_some()
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
pub enum IsNtWithNtiFlow {
    NtWithNtiSupported(String), //Network token with Network transaction id supported flow
    NTWithNTINotSupported,      //Network token with Network transaction id not supported
}

pub fn is_network_token_with_network_transaction_id_flow(
    is_connector_agnostic_mit_enabled: Option<bool>,
    is_network_tokenization_enabled: bool,
    payment_method_info: &domain::PaymentMethod,
) -> IsNtWithNtiFlow {
    match (
        is_connector_agnostic_mit_enabled,
        is_network_tokenization_enabled,
        payment_method_info.get_payment_method_type(),
        payment_method_info.network_transaction_id.clone(),
        payment_method_info.network_token_locker_id.is_some(),
        payment_method_info
            .network_token_requestor_reference_id
            .is_some(),
    ) {
        (
            Some(true),
            true,
            Some(storage_enums::PaymentMethod::Card),
            Some(network_transaction_id),
            true,
            true,
        ) => IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id),
        _ => IsNtWithNtiFlow::NTWithNTINotSupported,
    }
}

pub fn should_add_task_to_process_tracker<F: Clone, D: OperationSessionGetters<F>>(
    payment_data: &D,
) -> bool {
    let connector = payment_data.get_payment_attempt().connector.as_deref();

    !matches!(
        (
            payment_data.get_payment_attempt().get_payment_method(),
            connector
        ),
        (
            Some(storage_enums::PaymentMethod::BankTransfer),
            Some("stripe")
        )
    )
}

#[cfg(feature = "v1")]
pub async fn perform_session_token_routing<F, D>(
    state: SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    connectors: api::SessionConnectorDatas,
) -> RouterResult<api::SessionConnectorDatas>
where
    F: Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F>,
{
    let chosen = connectors.apply_filter_for_session_routing();
    let sfr = SessionFlowRoutingInput {
        state: &state,
        country: payment_data
            .get_address()
            .get_payment_method_billing()
            .and_then(|address| address.address.as_ref())
            .and_then(|details| details.country),
        key_store: merchant_context.get_merchant_key_store(),
        merchant_account: merchant_context.get_merchant_account(),
        payment_attempt: payment_data.get_payment_attempt(),
        payment_intent: payment_data.get_payment_intent(),
        chosen,
    };
    let (result, routing_approach) = self_routing::perform_session_flow_routing(
        sfr,
        business_profile,
        &enums::TransactionType::Payment,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("error performing session flow routing")?;

    payment_data.set_routing_approach_in_attempt(routing_approach);

    let final_list = connectors.filter_and_validate_for_session_flow(&result)?;

    Ok(final_list)
}

pub struct SessionTokenRoutingResult {
    pub final_result: api::SessionConnectorDatas,
    pub routing_result:
        FxHashMap<common_enums::PaymentMethodType, Vec<api::routing::SessionRoutingChoice>>,
}
#[cfg(feature = "v2")]
pub async fn perform_session_token_routing<F, D>(
    state: SessionState,
    business_profile: &domain::Profile,
    merchant_context: domain::MerchantContext,
    payment_data: &D,
    connectors: api::SessionConnectorDatas,
) -> RouterResult<SessionTokenRoutingResult>
where
    F: Clone,
    D: OperationSessionGetters<F>,
{
    let chosen = connectors.apply_filter_for_session_routing();
    let sfr = SessionFlowRoutingInput {
        country: payment_data
            .get_payment_intent()
            .billing_address
            .as_ref()
            .and_then(|address| address.get_inner().address.as_ref())
            .and_then(|details| details.country),
        payment_intent: payment_data.get_payment_intent(),

        chosen,
    };
    let result = self_routing::perform_session_flow_routing(
        &state,
        merchant_context.get_merchant_key_store(),
        sfr,
        business_profile,
        &enums::TransactionType::Payment,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("error performing session flow routing")?;

    let final_list = connectors.filter_and_validate_for_session_flow(&result)?;
    Ok(SessionTokenRoutingResult {
        final_result: final_list,
        routing_result: result,
    })
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn route_connector_v2_for_payments(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    transaction_data: core_routing::PaymentsDslInput<'_>,
    routing_data: &mut storage::RoutingData,
    _mandate_type: Option<api::MandateTransactionType>,
) -> RouterResult<ConnectorCallType> {
    let routing_algorithm_id = routing_data
        .algorithm_requested
        .as_ref()
        .or(business_profile.routing_algorithm_id.as_ref());

    let (connectors, _) = routing::perform_static_routing_v1(
        state,
        merchant_context.get_merchant_account().get_id(),
        routing_algorithm_id,
        business_profile,
        &TransactionData::Payment(transaction_data.clone()),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)?;

    let connectors = routing::perform_eligibility_analysis_with_fallback(
        &state.clone(),
        merchant_context.get_merchant_key_store(),
        connectors,
        &TransactionData::Payment(transaction_data),
        None,
        business_profile,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("failed eligibility analysis and fallback")?;

    connectors
        .first()
        .map(|conn| {
            routing_data.routed_through = Some(conn.connector.to_string());
            routing_data.merchant_connector_id = conn.merchant_connector_id.clone();
            api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &conn.connector.to_string(),
                api::GetToken::Connector,
                conn.merchant_connector_id.clone(),
            )
        })
        .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?
        .map(|connector_data| ConnectorCallType::PreDetermined(connector_data.into()))
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn route_connector_v1_for_payments<F, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    payment_data: &mut D,
    transaction_data: core_routing::PaymentsDslInput<'_>,
    routing_data: &mut storage::RoutingData,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
    mandate_type: Option<api::MandateTransactionType>,
) -> RouterResult<ConnectorCallType>
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let routing_algorithm_id = {
        let routing_algorithm = business_profile.routing_algorithm.clone();

        let algorithm_ref = routing_algorithm
            .map(|ra| ra.parse_value::<api::routing::RoutingAlgorithmRef>("RoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Could not decode merchant routing algorithm ref")?
            .unwrap_or_default();
        algorithm_ref.algorithm_id
    };

    let (connectors, routing_approach) = routing::perform_static_routing_v1(
        state,
        merchant_context.get_merchant_account().get_id(),
        routing_algorithm_id.as_ref(),
        business_profile,
        &TransactionData::Payment(transaction_data.clone()),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)?;

    payment_data.set_routing_approach_in_attempt(routing_approach);

    #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
    let payment_attempt = transaction_data.payment_attempt.clone();

    let connectors = routing::perform_eligibility_analysis_with_fallback(
        &state.clone(),
        merchant_context.get_merchant_key_store(),
        connectors,
        &TransactionData::Payment(transaction_data),
        eligible_connectors,
        business_profile,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("failed eligibility analysis and fallback")?;

    // dynamic success based connector selection
    #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
    let connectors = if let Some(algo) = business_profile.dynamic_routing_algorithm.clone() {
        let dynamic_routing_config: api_models::routing::DynamicRoutingAlgorithmRef = algo
            .parse_value("DynamicRoutingAlgorithmRef")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?;
        let dynamic_split = api_models::routing::RoutingVolumeSplit {
            routing_type: api_models::routing::RoutingType::Dynamic,
            split: dynamic_routing_config
                .dynamic_routing_volume_split
                .unwrap_or_default(),
        };
        let static_split: api_models::routing::RoutingVolumeSplit =
            api_models::routing::RoutingVolumeSplit {
                routing_type: api_models::routing::RoutingType::Static,
                split: consts::DYNAMIC_ROUTING_MAX_VOLUME
                    - dynamic_routing_config
                        .dynamic_routing_volume_split
                        .unwrap_or_default(),
            };
        let volume_split_vec = vec![dynamic_split, static_split];
        let routing_choice = routing::perform_dynamic_routing_volume_split(volume_split_vec, None)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("failed to perform volume split on routing type")?;

        if routing_choice.routing_type.is_dynamic_routing() {
            if state.conf.open_router.dynamic_routing_enabled {
                routing::perform_dynamic_routing_with_open_router(
                    state,
                    connectors.clone(),
                    business_profile,
                    payment_attempt,
                    payment_data,
                )
                .await
                .map_err(|e| logger::error!(open_routing_error=?e))
                .unwrap_or(connectors)
            } else {
                let dynamic_routing_config_params_interpolator =
                    routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
                        payment_data.get_payment_attempt().payment_method,
                        payment_data.get_payment_attempt().payment_method_type,
                        payment_data.get_payment_attempt().authentication_type,
                        payment_data.get_payment_attempt().currency,
                        payment_data
                            .get_billing_address()
                            .and_then(|address| address.address)
                            .and_then(|address| address.country),
                        payment_data
                            .get_payment_attempt()
                            .payment_method_data
                            .as_ref()
                            .and_then(|data| data.as_object())
                            .and_then(|card| card.get("card"))
                            .and_then(|data| data.as_object())
                            .and_then(|card| card.get("card_network"))
                            .and_then(|network| network.as_str())
                            .map(|network| network.to_string()),
                        payment_data
                            .get_payment_attempt()
                            .payment_method_data
                            .as_ref()
                            .and_then(|data| data.as_object())
                            .and_then(|card| card.get("card"))
                            .and_then(|data| data.as_object())
                            .and_then(|card| card.get("card_isin"))
                            .and_then(|card_isin| card_isin.as_str())
                            .map(|card_isin| card_isin.to_string()),
                    );

                routing::perform_dynamic_routing_with_intelligent_router(
                    state,
                    connectors.clone(),
                    business_profile,
                    dynamic_routing_config_params_interpolator,
                    payment_data,
                )
                .await
                .map_err(|e| logger::error!(dynamic_routing_error=?e))
                .unwrap_or(connectors)
            }
        } else {
            connectors
        }
    } else {
        connectors
    };

    let connector_data = connectors
        .into_iter()
        .map(|conn| {
            api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &conn.connector.to_string(),
                api::GetToken::Connector,
                conn.merchant_connector_id,
            )
            .map(|connector_data| connector_data.into())
        })
        .collect::<CustomResult<Vec<_>, _>>()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Invalid connector name received")?;

    decide_multiplex_connector_for_normal_or_recurring_payment(
        state,
        payment_data,
        routing_data,
        connector_data,
        mandate_type,
        business_profile.is_connector_agnostic_mit_enabled,
        business_profile.is_network_tokenization_enabled,
    )
    .await
}

#[cfg(feature = "payouts")]
#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
pub async fn route_connector_v1_for_payouts(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    transaction_data: &payouts::PayoutData,
    routing_data: &mut storage::RoutingData,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
) -> RouterResult<ConnectorCallType> {
    todo!()
}

#[cfg(feature = "payouts")]
#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
pub async fn route_connector_v1_for_payouts(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    business_profile: &domain::Profile,
    transaction_data: &payouts::PayoutData,
    routing_data: &mut storage::RoutingData,
    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
) -> RouterResult<ConnectorCallType> {
    let routing_algorithm_id = {
        let routing_algorithm = business_profile.payout_routing_algorithm.clone();

        let algorithm_ref = routing_algorithm
            .map(|ra| ra.parse_value::<api::routing::RoutingAlgorithmRef>("RoutingAlgorithmRef"))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Could not decode merchant routing algorithm ref")?
            .unwrap_or_default();
        algorithm_ref.algorithm_id
    };

    let (connectors, _) = routing::perform_static_routing_v1(
        state,
        merchant_context.get_merchant_account().get_id(),
        routing_algorithm_id.as_ref(),
        business_profile,
        &TransactionData::Payout(transaction_data),
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)?;
    let connectors = routing::perform_eligibility_analysis_with_fallback(
        &state.clone(),
        merchant_context.get_merchant_key_store(),
        connectors,
        &TransactionData::Payout(transaction_data),
        eligible_connectors,
        business_profile,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("failed eligibility analysis and fallback")?;
    let first_connector_choice = connectors
        .first()
        .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
        .attach_printable("Empty connector list returned")?
        .clone();

    let connector_data = connectors
        .into_iter()
        .map(|conn| {
            api::ConnectorData::get_connector_by_name(
                &state.conf.connectors,
                &conn.connector.to_string(),
                api::GetToken::Connector,
                conn.merchant_connector_id,
            )
            .map(|connector_data| connector_data.into())
        })
        .collect::<CustomResult<Vec<_>, _>>()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Invalid connector name received")?;

    routing_data.routed_through = Some(first_connector_choice.connector.to_string());

    routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id;

    Ok(ConnectorCallType::Retryable(connector_data))
}

#[cfg(feature = "v2")]
pub async fn payment_external_authentication(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
    _req: api_models::payments::PaymentsExternalAuthenticationRequest,
) -> RouterResponse<api_models::payments::PaymentsExternalAuthenticationResponse> {
    todo!()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn payment_external_authentication<F: Clone + Sync>(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: api_models::payments::PaymentsExternalAuthenticationRequest,
) -> RouterResponse<api_models::payments::PaymentsExternalAuthenticationResponse> {
    use super::unified_authentication_service::types::ExternalAuthentication;
    use crate::core::unified_authentication_service::{
        types::UnifiedAuthenticationService, utils::external_authentication_update_trackers,
    };

    let db = &*state.store;
    let key_manager_state = &(&state).into();

    let merchant_id = merchant_context.get_merchant_account().get_id();
    let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
    let payment_id = req.payment_id;
    let payment_intent = db
        .find_payment_intent_by_payment_id_merchant_id(
            key_manager_state,
            &payment_id,
            merchant_id,
            merchant_context.get_merchant_key_store(),
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
    let attempt_id = payment_intent.active_attempt.get_id().clone();
    let payment_attempt = db
        .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
            &payment_intent.payment_id,
            merchant_id,
            &attempt_id.clone(),
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
    if payment_attempt.external_three_ds_authentication_attempted != Some(true) {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message:
                "You cannot authenticate this payment because payment_attempt.external_three_ds_authentication_attempted is false".to_owned(),
        })?
    }
    helpers::validate_payment_status_against_allowed_statuses(
        payment_intent.status,
        &[storage_enums::IntentStatus::RequiresCustomerAction],
        "authenticate",
    )?;

    let optional_customer = match &payment_intent.customer_id {
        Some(customer_id) => Some(
            state
                .store
                .find_customer_by_customer_id_merchant_id(
                    key_manager_state,
                    customer_id,
                    merchant_context.get_merchant_account().get_id(),
                    merchant_context.get_merchant_key_store(),
                    storage_scheme,
                )
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable_lazy(|| {
                    format!("error while finding customer with customer_id {customer_id:?}")
                })?,
        ),
        None => None,
    };

    let profile_id = payment_intent
        .profile_id
        .as_ref()
        .get_required_value("profile_id")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("'profile_id' not set in payment intent")?;
    let currency = payment_attempt.currency.get_required_value("currency")?;
    let amount = payment_attempt.get_total_amount();
    let shipping_address = helpers::create_or_find_address_for_payment_by_request(
        &state,
        None,
        payment_intent.shipping_address_id.as_deref(),
        merchant_id,
        payment_intent.customer_id.as_ref(),
        merchant_context.get_merchant_key_store(),
        &payment_intent.payment_id,
        storage_scheme,
    )
    .await?;
    let billing_address = helpers::create_or_find_address_for_payment_by_request(
        &state,
        None,
        payment_attempt
            .payment_method_billing_address_id
            .as_deref()
            .or(payment_intent.billing_address_id.as_deref()),
        merchant_id,
        payment_intent.customer_id.as_ref(),
        merchant_context.get_merchant_key_store(),
        &payment_intent.payment_id,
        storage_scheme,
    )
    .await?;


impl HashiCorpVaultConfig {
    /// Verifies that the [`HashiCorpVault`] configuration is usable.
    pub fn validate(&self) -> Result<(), &'static str> {
        when(self.url.is_default_or_empty(), || {
            Err("HashiCorp vault url must not be empty")
        })?;

        when(self.token.is_default_or_empty(), || {
            Err("HashiCorp vault token must not be empty")
        })
    }
}


pub fn get_connector_name_as_string(&self) -> String {
        self.connector_name.clone().to_string()
    }


pub async fn diesel_make_pg_pool(
    database: &Database,
    schema: &str,
    test_transaction: bool,
) -> StorageResult<PgPool> {
    let database_url = database.get_database_url(schema);
    let manager = async_bb8_diesel::ConnectionManager::<PgConnection>::new(database_url);
    let mut pool = bb8::Pool::builder()
        .max_size(database.pool_size)
        .min_idle(database.min_idle)
        .queue_strategy(database.queue_strategy.into())
        .connection_timeout(std::time::Duration::from_secs(database.connection_timeout))
        .max_lifetime(database.max_lifetime.map(std::time::Duration::from_secs));

    if test_transaction {
        pool = pool.connection_customizer(Box::new(TestTransaction));
    }

    pool.build(manager)
        .await
        .change_context(StorageError::InitializationError)
        .attach_printable("Failed to create PostgreSQL connection pool")
}


pub struct RefundFiltersResponse {
    pub query_data: Vec<RefundFilterValue>,
}


impl api::ConnectorAccessToken for Square {}

impl api::ConnectorAccessToken for for Square


pub struct TransactionFee {
    /// Monetary amount
    pub amount: InstructedAmount,
    pub description: Option<String>,
    pub excluded_from_total_fee: Option<bool>,
    pub percentage: Option<bool>,
    #[serde(rename = "type")]
    pub fees_type: Option<FeesType>,
}


pub fn get_capture_by_capture_id(&self, capture_id: String) -> Option<&storage::Capture> {
        self.all_captures.get(&capture_id)
    }


File: crates/router/src/core/three_ds_decision_rule/utils.rs
 Public functions: 1

use api_models::three_ds_decision_rule as api_threedsecure;
use common_types::three_ds_decision_rule_engine::ThreeDSDecision;
use euclid::backend::inputs as dsl_inputs;

use crate::{consts::PSD2_COUNTRIES, types::transformers::ForeignFrom};

// function to apply PSD2 validations to the decision
pub fn apply_psd2_validations_during_execute(
    decision: ThreeDSDecision,
    request: &api_models::three_ds_decision_rule::ThreeDsDecisionRuleExecuteRequest,
) -> ThreeDSDecision {
    let issuer_in_psd2 = request
        .issuer
        .as_ref()
        .and_then(|issuer| issuer.country)
        .map(|country| PSD2_COUNTRIES.contains(&country))
        .unwrap_or(false);
    let acquirer_in_psd2 = request
        .acquirer
        .as_ref()
        .and_then(|acquirer| acquirer.country)
        .map(|country| PSD2_COUNTRIES.contains(&country))
        .unwrap_or(false);
    if issuer_in_psd2 && acquirer_in_psd2 {
        // If both issuer and acquirer are in PSD2 region
        match decision {
            // If the decision is to enforce no 3DS, override it to enforce 3DS
            ThreeDSDecision::NoThreeDs => ThreeDSDecision::ChallengeRequested,
            _ => decision,
        }
    } else {
        // If PSD2 doesn't apply, exemptions cannot be applied
        match decision {
            ThreeDSDecision::NoThreeDs => ThreeDSDecision::NoThreeDs,
            // For all other decisions (including exemptions), enforce challenge as exemptions are only valid in PSD2 regions
            _ => ThreeDSDecision::ChallengeRequested,
        }
    }
}

impl ForeignFrom<api_threedsecure::PaymentData> for dsl_inputs::PaymentInput {
    fn foreign_from(request_payment_data: api_threedsecure::PaymentData) -> Self {
        Self {
            amount: request_payment_data.amount,
            currency: request_payment_data.currency,
            authentication_type: None,
            capture_method: None,
            business_country: None,
            billing_country: None,
            business_label: None,
            setup_future_usage: None,
            card_bin: None,
        }
    }
}

impl ForeignFrom<Option<api_threedsecure::PaymentMethodMetaData>>
    for dsl_inputs::PaymentMethodInput
{
    fn foreign_from(
        request_payment_method_metadata: Option<api_threedsecure::PaymentMethodMetaData>,
    ) -> Self {
        Self {
            payment_method: None,
            payment_method_type: None,
            card_network: request_payment_method_metadata.and_then(|pm| pm.card_network),
        }
    }
}

impl ForeignFrom<api_threedsecure::CustomerDeviceData> for dsl_inputs::CustomerDeviceDataInput {
    fn foreign_from(request_customer_device_data: api_threedsecure::CustomerDeviceData) -> Self {
        Self {
            platform: request_customer_device_data.platform,
            device_type: request_customer_device_data.device_type,
            display_size: request_customer_device_data.display_size,
        }
    }
}

impl ForeignFrom<api_threedsecure::IssuerData> for dsl_inputs::IssuerDataInput {
    fn foreign_from(request_issuer_data: api_threedsecure::IssuerData) -> Self {
        Self {
            name: request_issuer_data.name,
            country: request_issuer_data.country,
        }
    }
}

impl ForeignFrom<api_threedsecure::AcquirerData> for dsl_inputs::AcquirerDataInput {
    fn foreign_from(request_acquirer_data: api_threedsecure::AcquirerData) -> Self {
        Self {
            country: request_acquirer_data.country,
            fraud_rate: request_acquirer_data.fraud_rate,
        }
    }
}

impl ForeignFrom<api_threedsecure::ThreeDsDecisionRuleExecuteRequest> for dsl_inputs::BackendInput {
    fn foreign_from(request: api_threedsecure::ThreeDsDecisionRuleExecuteRequest) -> Self {
        Self {
            metadata: None,
            payment: dsl_inputs::PaymentInput::foreign_from(request.payment),
            payment_method: dsl_inputs::PaymentMethodInput::foreign_from(request.payment_method),
            mandate: dsl_inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: request.acquirer.map(ForeignFrom::foreign_from),
            customer_device_data: request.customer_device.map(ForeignFrom::foreign_from),
            issuer_data: request.issuer.map(ForeignFrom::foreign_from),
        }
    }
}


impl Refund for Riskified {}


OpenAPI Block Path: components.schemas.ValueType
{
  "oneOf": [
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "number"
          ]
        },
        "value": {
          "type": "integer",
          "format": "int64",
          "description": "Represents a number literal",
          "minimum": 0
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "enum_variant"
          ]
        },
        "value": {
          "type": "string",
          "description": "Represents an enum variant"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "metadata_variant"
          ]
        },
        "value": {
          "$ref": "#/components/schemas/MetadataValue"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "str_value"
          ]
        },
        "value": {
          "type": "string",
          "description": "Represents a arbitrary String value"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "global_ref"
          ]
        },
        "value": {
          "type": "string",
          "description": "Represents a global reference, which is a reference to a global variable"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "number_array"
          ]
        },
        "value": {
          "type": "array",
          "items": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "description": "Represents an array of numbers. This is basically used for\n\"one of the given numbers\" operations\neg: payment.method.amount = (1, 2, 3)"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "enum_variant_array"
          ]
        },
        "value": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Similar to NumberArray but for enum variants\neg: payment.method.cardtype = (debit, credit)"
        }
      }
    },
    {
      "type": "object",
      "required": [
        "type",
        "value"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "number_comparison_array"
          ]
        },
        "value": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/NumberComparison"
          },
          "description": "Like a number array but can include comparisons. Useful for\nconditions like \"500 < amount < 1000\"\neg: payment.amount = (> 500, < 1000)"
        }
      }
    }
  ],
  "description": "Represents a value in the DSL",
  "discriminator": {
    "propertyName": "type"
  }
}


impl RequiredFieldsInput {
    fn new(
        required_fields_config: settings::RequiredFields,
        setup_future_usage: common_enums::FutureUsage,
    ) -> Self {
        Self {
            required_fields_config,
            setup_future_usage,
        }
    }
}


impl api::Refund for Coinbase {}


pub struct ThreedsecureioRouterData<T> {
    pub amount: String,
    pub router_data: T,
}


impl api::PayoutSync for Nomupay {}


impl api::Refund for Bitpay {}


pub async fn get_metrics(
    pool: &AnalyticsProvider,
    auth: &AuthInfo,
    req: GetDisputeMetricRequest,
) -> AnalyticsResult<DisputesMetricsResponse<DisputeMetricsBucketResponse>> {
    let mut metrics_accumulator: HashMap<
        DisputeMetricsBucketIdentifier,
        DisputeMetricsAccumulator,
    > = HashMap::new();
    let mut set = tokio::task::JoinSet::new();
    for metric_type in req.metrics.iter().cloned() {
        let req = req.clone();
        let pool = pool.clone();
        let task_span = tracing::debug_span!(
            "analytics_dispute_query",
            refund_metric = metric_type.as_ref()
        );
        // Currently JoinSet works with only static lifetime references even if the task pool does not outlive the given reference
        // We can optimize away this clone once that is fixed
        let auth_scoped = auth.to_owned();
        set.spawn(
            async move {
                let data = pool
                    .get_dispute_metrics(
                        &metric_type,
                        &req.group_by_names.clone(),
                        &auth_scoped,
                        &req.filters,
                        req.time_series.map(|t| t.granularity),
                        &req.time_range,
                    )
                    .await
                    .change_context(AnalyticsError::UnknownError);
                (metric_type, data)
            }
            .instrument(task_span),
        );
    }

    while let Some((metric, data)) = set
        .join_next()
        .await
        .transpose()
        .change_context(AnalyticsError::UnknownError)?
    {
        let data = data?;
        let attributes = router_env::metric_attributes!(
            ("metric_type", metric.to_string()),
            ("source", pool.to_string()),
        );

        let value = u64::try_from(data.len());
        if let Ok(val) = value {
            metrics::BUCKETS_FETCHED.record(val, attributes);
            logger::debug!("Attributes: {:?}, Buckets fetched: {}", attributes, val);
        }

        for (id, value) in data {
            logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
            let metrics_builder = metrics_accumulator.entry(id).or_default();
            match metric {
                DisputeMetrics::DisputeStatusMetric
                | DisputeMetrics::SessionizedDisputeStatusMetric => metrics_builder
                    .disputes_status_rate
                    .add_metrics_bucket(&value),
                DisputeMetrics::TotalAmountDisputed
                | DisputeMetrics::SessionizedTotalAmountDisputed => {
                    metrics_builder.disputed_amount.add_metrics_bucket(&value)
                }
                DisputeMetrics::TotalDisputeLostAmount
                | DisputeMetrics::SessionizedTotalDisputeLostAmount => metrics_builder
                    .dispute_lost_amount
                    .add_metrics_bucket(&value),
            }
        }

        logger::debug!(
            "Analytics Accumulated Results: metric: {}, results: {:#?}",
            metric,
            metrics_accumulator
        );
    }
    let mut total_disputed_amount = 0;
    let mut total_dispute_lost_amount = 0;
    let query_data: Vec<DisputeMetricsBucketResponse> = metrics_accumulator
        .into_iter()
        .map(|(id, val)| {
            let collected_values = val.collect();
            if let Some(amount) = collected_values.disputed_amount {
                total_disputed_amount += amount;
            }
            if let Some(amount) = collected_values.dispute_lost_amount {
                total_dispute_lost_amount += amount;
            }

            DisputeMetricsBucketResponse {
                values: collected_values,
                dimensions: id,
            }
        })
        .collect();

    Ok(DisputesMetricsResponse {
        query_data,
        meta_data: [DisputesAnalyticsMetadata {
            total_disputed_amount: Some(total_disputed_amount),
            total_dispute_lost_amount: Some(total_dispute_lost_amount),
        }],
    })
}


File: crates/router/src/core/pm_auth/helpers.rs
 Public functions: 1

use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use pm_auth::types::{self as pm_auth_types, api::BoxedPaymentAuthConnector};

use crate::{
    core::errors::{self, ApiErrorResponse},
    types::{self, domain, transformers::ForeignTryFrom},
};

pub trait PaymentAuthConnectorDataExt {
    fn get_connector_by_name(name: &str) -> errors::CustomResult<Self, ApiErrorResponse>
    where
        Self: Sized;
    fn convert_connector(
        connector_name: pm_auth_types::PaymentMethodAuthConnectors,
    ) -> errors::CustomResult<BoxedPaymentAuthConnector, ApiErrorResponse>;
}

pub fn get_connector_auth_type(
    merchant_connector_account: domain::MerchantConnectorAccount,
) -> errors::CustomResult<pm_auth_types::ConnectorAuthType, ApiErrorResponse> {
    let auth_type: types::ConnectorAuthType = merchant_connector_account
        .connector_account_details
        .parse_value("ConnectorAuthType")
        .change_context(ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: "ConnectorAuthType".to_string(),
        })?;

    pm_auth_types::ConnectorAuthType::foreign_try_from(auth_type)
        .change_context(ApiErrorResponse::InternalServerError)
        .attach_printable("Failed while converting ConnectorAuthType")
}


pub struct CardNetworkTokenizeResponse {
    /// Response for payment method entry in DB
    pub payment_method_response: Option<PaymentMethodResponse>,

    /// Customer details
    #[schema(value_type = CustomerDetails)]
    pub customer: Option<payments::CustomerDetails>,

    /// Card network tokenization status
    pub card_tokenized: bool,

    /// Error code
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_code: Option<String>,

    /// Error message
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,

    /// Details that were sent for tokenization
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tokenization_data: Option<TokenizeDataRequest>,
}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &StringMinorUnitForConnector,
        }
    }


pub struct DynamicDescriptor {
    pub merchant_name: String,
    pub merchant_city: String,
}


OpenAPI Block Path: components.schemas.CardAdditionalData
{
  "type": "object",
  "description": "Masked payout method details for card payout method",
  "required": [
    "card_exp_month",
    "card_exp_year",
    "card_holder_name"
  ],
  "properties": {
    "card_issuer": {
      "type": "string",
      "description": "Issuer of the card",
      "nullable": true
    },
    "card_network": {
      "allOf": [
        {
          "$ref": "#/components/schemas/CardNetwork"
        }
      ],
      "nullable": true
    },
    "card_type": {
      "type": "string",
      "description": "Card type, can be either `credit` or `debit`",
      "nullable": true
    },
    "card_issuing_country": {
      "type": "string",
      "description": "Card issuing country",
      "nullable": true
    },
    "bank_code": {
      "type": "string",
      "description": "Code for Card issuing bank",
      "nullable": true
    },
    "last4": {
      "type": "string",
      "description": "Last 4 digits of the card number",
      "nullable": true
    },
    "card_isin": {
      "type": "string",
      "description": "The ISIN of the card",
      "nullable": true
    },
    "card_extended_bin": {
      "type": "string",
      "description": "Extended bin of card, contains the first 8 digits of card number",
      "nullable": true
    },
    "card_exp_month": {
      "type": "string",
      "description": "Card expiry month",
      "example": "01"
    },
    "card_exp_year": {
      "type": "string",
      "description": "Card expiry year",
      "example": "2026"
    },
    "card_holder_name": {
      "type": "string",
      "description": "Card holder name",
      "example": "John Doe"
    }
  }
}


pub fn to_merchant_connector_info(&self, connector_label: &String) -> MerchantConnectorInfo {
        MerchantConnectorInfo {
            connector_label: connector_label.to_string(),
            merchant_connector_id: self.id.clone(),
        }
    }


pub struct Card {
    number: cards::CardNumber,
    expiration_month: Secret<String>,
    expiration_year: Secret<String>,
    security_code: Secret<String>,
    #[serde(rename = "type")]
    card_type: Option<String>,
    type_selection_indicator: Option<String>,
}


pub async fn list_customer_payment_method_api() {}


impl api::PaymentAuthorize for Datatrans {}

impl api::PaymentAuthorize for for Datatrans


pub struct Redirect {
    pub redirect_url: Option<Url>,
}


pub async fn payments_session_operation_core<F, Req, Op, FData, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    operation: Op,
    req: Req,
    payment_id: id_type::GlobalPaymentId,
    _call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
) -> RouterResult<(D, Req, Option<domain::Customer>, Option<u16>, Option<u128>)>
where
    F: Send + Clone + Sync,
    Req: Send + Sync,
    Op: Operation<F, Req, Data = D> + Send + Sync,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,

    // To create connector flow specific interface data
    D: ConstructFlowSpecificData<F, FData, router_types::PaymentsResponseData>,
    RouterData<F, FData, router_types::PaymentsResponseData>: Feature<F, FData>,

    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, FData, router_types::PaymentsResponseData>,
    FData: Send + Sync + Clone,
{
    let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation);

    let _validate_result = operation
        .to_validate_request()?
        .validate_request(&req, &merchant_context)?;

    let operations::GetTrackerResponse { mut payment_data } = operation
        .to_get_tracker()?
        .get_trackers(
            state,
            &payment_id,
            &req,
            &merchant_context,
            &profile,
            &header_payload,
        )
        .await?;

    let (_operation, customer) = operation
        .to_domain()?
        .get_customer_details(
            state,
            &mut payment_data,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)
        .attach_printable("Failed while fetching/creating customer")?;

    vault_session::populate_vault_session_details(
        state,
        req_state.clone(),
        &customer,
        &merchant_context,
        &operation,
        &profile,
        &mut payment_data,
        header_payload.clone(),
    )
    .await?;

    let connector = operation
        .to_domain()?
        .perform_routing(
            &merchant_context,
            &profile,
            &state.clone(),
            &mut payment_data,
        )
        .await?;

    let payment_data = match connector {
        api::ConnectorCallType::PreDetermined(_connector) => {
            todo!()
        }
        api::ConnectorCallType::Retryable(_connectors) => todo!(),
        api::ConnectorCallType::Skip => todo!(),
        api::ConnectorCallType::SessionMultiple(connectors) => {
            operation
                .to_update_tracker()?
                .update_trackers(
                    state,
                    req_state,
                    payment_data.clone(),
                    customer.clone(),
                    merchant_context.get_merchant_account().storage_scheme,
                    None,
                    merchant_context.get_merchant_key_store(),
                    None,
                    header_payload.clone(),
                )
                .await?;
            // todo: call surcharge manager for session token call.
            Box::pin(call_multiple_connectors_service(
                state,
                &merchant_context,
                connectors,
                &operation,
                payment_data,
                &customer,
                None,
                &profile,
                header_payload.clone(),
                None,
            ))
            .await?
        }
    };

    Ok((payment_data, req, customer, None, None))
}


pub struct RefundResponse {
    payment: RefundResponseData,
}


pub struct RefundCount;


pub fn new() -> Self {
        Self {
            object: "list",
            data: vec![],
            has_more: false,
            total_count: 0,
            url: "http://placeholder".to_string(),
        }
    }


pub struct NoonConfiguration {
    tokenize_c_c: Option<bool>,
    payment_action: NoonPaymentActions,
    return_url: Option<String>,
}


impl SecretsManagementConfig {
    /// Verifies that the client configuration is usable
    pub fn validate(&self) -> Result<(), &'static str> {
        match self {
            #[cfg(feature = "aws_kms")]
            Self::AwsKms { aws_kms } => aws_kms.validate(),
            #[cfg(feature = "hashicorp-vault")]
            Self::HashiCorpVault { hc_vault } => hc_vault.validate(),
            Self::NoEncryption => Ok(()),
        }
    }

    /// Retrieves the appropriate secret management client based on the configuration.
    pub async fn get_secret_management_client(
        &self,
    ) -> CustomResult<Box<dyn SecretManagementInterface>, SecretsManagementError> {
        match self {
            #[cfg(feature = "aws_kms")]
            Self::AwsKms { aws_kms } => {
                Ok(Box::new(aws_kms::core::AwsKmsClient::new(aws_kms).await))
            }
            #[cfg(feature = "hashicorp-vault")]
            Self::HashiCorpVault { hc_vault } => {
                hashicorp_vault::core::HashiCorpVault::new(hc_vault)
                    .change_context(SecretsManagementError::ClientCreationFailed)
                    .map(|inner| -> Box<dyn SecretManagementInterface> { Box::new(inner) })
            }
            Self::NoEncryption => Ok(Box::new(NoEncryption)),
        }
    }
}


OpenAPI Block Path: components.schemas.AdyenTestingData
{
  "type": "object",
  "required": [
    "holder_name"
  ],
  "properties": {
    "holder_name": {
      "type": "string",
      "description": "Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions."
    }
  }
}


File: crates/hyperswitch_connectors/src/connectors/coingate/transformers.rs
 Public structs: 10

use std::collections::HashMap;

use common_enums::{enums, Currency};
use common_utils::{ext_traits::OptionExt, pii, request::Method, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, PaymentsSyncRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::{ExposeInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{PaymentsSyncResponseRouterData, RefundsResponseRouterData, ResponseRouterData},
    utils::{self, PaymentsAuthorizeRequestData},
};

pub struct CoingateRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for CoingateRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CoingateConnectorMetadataObject {
    pub currency_id: i32,
    pub platform_id: i32,
    pub ledger_account_id: Secret<String>,
}

impl TryFrom<&Option<pii::SecretSerdeValue>> for CoingateConnectorMetadataObject {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(meta_data: &Option<pii::SecretSerdeValue>) -> Result<Self, Self::Error> {
        let metadata: Self = utils::to_connector_meta_from_secret::<Self>(meta_data.clone())
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "metadata",
            })?;
        Ok(metadata)
    }
}

#[derive(Default, Debug, Serialize, PartialEq)]
pub struct CoingatePaymentsRequest {
    price_amount: StringMajorUnit,
    price_currency: Currency,
    receive_currency: String,
    callback_url: String,
    success_url: Option<String>,
    cancel_url: Option<String>,
    title: String,
    token: Secret<String>,
}

impl TryFrom<&CoingateRouterData<&PaymentsAuthorizeRouterData>> for CoingatePaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &CoingateRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        let auth = CoingateAuthType::try_from(&item.router_data.connector_auth_type)?;
        Ok(match item.router_data.request.payment_method_data {
            PaymentMethodData::Crypto(_) => Ok(Self {
                price_amount: item.amount.clone(),
                price_currency: item.router_data.request.currency,
                receive_currency: "DO_NOT_CONVERT".to_string(),
                callback_url: item.router_data.request.get_webhook_url()?,
                success_url: item.router_data.request.router_return_url.clone(),
                cancel_url: item.router_data.request.router_return_url.clone(),
                title: item.router_data.connector_request_reference_id.clone(),
                token: auth.merchant_token,
            }),
            _ => Err(errors::ConnectorError::NotImplemented(
                utils::get_unimplemented_payment_method_error_message("Coingate"),
            )),
        }?)
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CoingateSyncResponse {
    status: CoingatePaymentStatus,
    id: i64,
}
impl TryFrom<PaymentsSyncResponseRouterData<CoingateSyncResponse>> for PaymentsSyncRouterData {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: PaymentsSyncResponseRouterData<CoingateSyncResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status.clone()),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.to_string()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}
pub struct CoingateAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) merchant_token: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for CoingateAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                api_key: api_key.to_owned(),
                merchant_token: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum CoingatePaymentStatus {
    New,
    Pending,
    Confirming,
    Paid,
    Invalid,
    Expired,
    Canceled,
}

impl From<CoingatePaymentStatus> for common_enums::AttemptStatus {
    fn from(item: CoingatePaymentStatus) -> Self {
        match item {
            CoingatePaymentStatus::Paid => Self::Charged,
            CoingatePaymentStatus::Canceled
            | CoingatePaymentStatus::Expired
            | CoingatePaymentStatus::Invalid => Self::Failure,
            CoingatePaymentStatus::Confirming | CoingatePaymentStatus::New => {
                Self::AuthenticationPending
            }
            CoingatePaymentStatus::Pending => Self::Pending,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CoingatePaymentsResponse {
    status: CoingatePaymentStatus,
    id: i64,
    payment_url: Option<String>,
    order_id: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, CoingatePaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, CoingatePaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status.clone()),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id.to_string()),
                redirection_data: Box::new(Some(RedirectForm::Form {
                    endpoint: item.response.payment_url.clone().ok_or(
                        errors::ConnectorError::MissingRequiredField {
                            field_name: "payment_url",
                        },
                    )?,
                    method: Method::Get,
                    form_fields: HashMap::new(),
                })),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: item.response.order_id.clone(),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize)]
pub struct CoingateRefundRequest {
    pub amount: StringMajorUnit,
    pub address: Secret<String>,
    pub currency_id: i32,
    pub platform_id: i32,
    pub reason: String,
    pub email: pii::Email,
    pub ledger_account_id: Secret<String>,
}

impl<F> TryFrom<&CoingateRouterData<&RefundsRouterData<F>>> for CoingateRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &CoingateRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let metadata: CoingateConnectorMetadataObject =
            utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone())
                .change_context(errors::ConnectorError::InvalidConnectorConfig {
                    config: "merchant_connector_account.metadata",
                })?;

        let refund_metadata = item
            .router_data
            .request
            .refund_connector_metadata
            .as_ref()
            .get_required_value("refund_connector_metadata")
            .change_context(errors::ConnectorError::MissingRequiredField {
                field_name: "refund_connector_metadata",
            })?
            .clone()
            .expose();

        let address: Secret<String> = serde_json::from_value::<Secret<String>>(
            refund_metadata.get("address").cloned().ok_or_else(|| {
                errors::ConnectorError::MissingRequiredField {
                    field_name: "address",
                }
            })?,
        )
        .change_context(errors::ConnectorError::MissingRequiredField {
            field_name: "address",
        })?;

        let email: pii::Email = serde_json::from_value::<pii::Email>(
            refund_metadata.get("email").cloned().ok_or_else(|| {
                errors::ConnectorError::MissingRequiredField {
                    field_name: "email",
                }
            })?,
        )
        .change_context(errors::ConnectorError::MissingRequiredField {
            field_name: "email",
        })?;

        Ok(Self {
            amount: item.amount.clone(),
            address,
            currency_id: metadata.currency_id,
            platform_id: metadata.platform_id,
            reason: item.router_data.request.reason.clone().ok_or(
                errors::ConnectorError::MissingRequiredField {
                    field_name: "refund.reason",
                },
            )?,
            email,
            ledger_account_id: metadata.ledger_account_id,
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CoingateRefundResponse {
    pub status: CoingateRefundStatus,
    pub id: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum CoingateRefundStatus {
    Pending,
    Completed,
    Rejected,
    Processing,
}

impl TryFrom<RefundsResponseRouterData<Execute, CoingateRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, CoingateRefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, CoingateRefundResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, CoingateRefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl From<CoingateRefundStatus> for common_enums::RefundStatus {
    fn from(item: CoingateRefundStatus) -> Self {
        match item {
            CoingateRefundStatus::Pending => Self::Pending,
            CoingateRefundStatus::Completed => Self::Success,
            CoingateRefundStatus::Rejected => Self::Failure,
            CoingateRefundStatus::Processing => Self::Pending,
        }
    }
}

#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct CoingateErrorResponse {
    pub message: String,
    pub reason: String,
    pub errors: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CoingateWebhookBody {
    pub token: Secret<String>,
    pub status: CoingatePaymentStatus,
    pub id: i64,
}


File: crates/currency_conversion/src/lib.rs

pub mod conversion;
pub mod error;
pub mod types;


impl ConnectorSpecifications for ConnectorEnum {
    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        match self {
            Self::Old(connector) => connector.get_supported_payment_methods(),
            Self::New(connector) => connector.get_supported_payment_methods(),
        }
    }

    /// Supported webhooks flows
    fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> {
        match self {
            Self::Old(connector) => connector.get_supported_webhook_flows(),
            Self::New(connector) => connector.get_supported_webhook_flows(),
        }
    }

    /// Details related to connector
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        match self {
            Self::Old(connector) => connector.get_connector_about(),
            Self::New(connector) => connector.get_connector_about(),
        }
    }

    /// Check if connector supports authentication token
    fn authentication_token_for_token_creation(&self) -> bool {
        match self {
            Self::Old(connector) => connector.authentication_token_for_token_creation(),
            Self::New(connector) => connector.authentication_token_for_token_creation(),
        }
    }

    #[cfg(feature = "v1")]
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
        is_config_enabled_to_send_payment_id_as_connector_request_id: bool,
    ) -> String {
        match self {
            Self::Old(connector) => connector.generate_connector_request_reference_id(
                payment_intent,
                payment_attempt,
                is_config_enabled_to_send_payment_id_as_connector_request_id,
            ),
            Self::New(connector) => connector.generate_connector_request_reference_id(
                payment_intent,
                payment_attempt,
                is_config_enabled_to_send_payment_id_as_connector_request_id,
            ),
        }
    }

    #[cfg(feature = "v2")]
    /// Generate connector request reference ID
    fn generate_connector_request_reference_id(
        &self,
        payment_intent: &hyperswitch_domain_models::payments::PaymentIntent,
        payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> String {
        match self {
            Self::Old(connector) => {
                connector.generate_connector_request_reference_id(payment_intent, payment_attempt)
            }
            Self::New(connector) => {
                connector.generate_connector_request_reference_id(payment_intent, payment_attempt)
            }
        }
    }
}

impl ConnectorSpecifications for for ConnectorEnum


impl api::Payment for Opayo {}


pub struct SearchData {
    transactions: TransactionData,
}


/// Generate a new GlobalTokenId from a cell id
    pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
        let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Token);
        Self(global_id)
    }


pub struct DeutschebankAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) merchant_id: Secret<String>,
    pub(super) client_key: Secret<String>,
}


pub struct UserFromStorage(pub storage_user::User);