    // with the connector have to be made using the same connector account.
    if let Some(ref connector_name) = payment_data.get_payment_attempt().connector {
        // Connector was already decided previously, use the same connector
        let connector_data = api::ConnectorData::get_connector_by_name(
            &state.conf.connectors,
            connector_name,
            api::GetToken::Connector,
            payment_data
                .get_payment_attempt()
                .merchant_connector_id
                .clone(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Invalid connector name received in 'routed_through'")?;

        routing_data.routed_through = Some(connector_name.clone());
        logger::debug!("euclid_routing: predetermined connector present in attempt");
        return Ok(ConnectorCallType::PreDetermined(connector_data.into()));
    }

    if let Some(mandate_connector_details) = payment_data.get_mandate_connector().as_ref() {
        let connector_data = api::ConnectorData::get_connector_by_name(
            &state.conf.connectors,
            &mandate_connector_details.connector,
            api::GetToken::Connector,
            mandate_connector_details.merchant_connector_id.clone(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Invalid connector name received in 'routed_through'")?;

        routing_data.routed_through = Some(mandate_connector_details.connector.clone());

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

        logger::debug!("euclid_routing: predetermined mandate connector");
        return Ok(ConnectorCallType::PreDetermined(connector_data.into()));
    }

    if let Some((pre_routing_results, storage_pm_type)) =
        routing_data.routing_info.pre_routing_results.as_ref().zip(
            payment_data
                .get_payment_attempt()
                .payment_method_type
                .as_ref(),
        )
    {
        if let (Some(routable_connector_choice), None) = (
            pre_routing_results.get(storage_pm_type),
            &payment_data.get_token_data(),
        ) {
            let routable_connector_list = match routable_connector_choice {
                storage::PreRoutingConnectorChoice::Single(routable_connector) => {
                    vec![routable_connector.clone()]
                }
                storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => {
                    routable_connector_list.clone()
                }
            };

            let mut pre_routing_connector_data_list = vec![];

            let first_routable_connector = routable_connector_list
                .first()
                .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?;

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

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

            for connector_choice in routable_connector_list.clone() {
                let connector_data = api::ConnectorData::get_connector_by_name(
                    &state.conf.connectors,
                    &connector_choice.connector.to_string(),
                    api::GetToken::Connector,
                    connector_choice.merchant_connector_id.clone(),
                )
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Invalid connector name received")?
                .into();

                pre_routing_connector_data_list.push(connector_data);
            }

            #[cfg(feature = "retry")]
            let should_do_retry = retry::config_should_call_gsm(
                &*state.store,
                merchant_context.get_merchant_account().get_id(),
                business_profile,
            )
            .await;

            #[cfg(feature = "retry")]
            if payment_data.get_payment_attempt().payment_method_type
                == Some(storage_enums::PaymentMethodType::ApplePay)
                && should_do_retry
            {
                let retryable_connector_data = helpers::get_apple_pay_retryable_connectors(
                    &state,
                    merchant_context,
                    payment_data,
                    &pre_routing_connector_data_list,
                    first_routable_connector
                        .merchant_connector_id
                        .clone()
                        .as_ref(),
                    business_profile.clone(),
                )
                .await?;

                if let Some(connector_data_list) = retryable_connector_data {
                    if connector_data_list.len() > 1 {
                        logger::info!("Constructed apple pay retryable connector list");
                        return Ok(ConnectorCallType::Retryable(connector_data_list));
                    }
                }
            }

            logger::debug!("euclid_routing: pre-routing connector present");

            let first_pre_routing_connector_data_list = pre_routing_connector_data_list
                .first()
                .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?;

            helpers::override_setup_future_usage_to_on_session(&*state.store, payment_data).await?;

            return Ok(ConnectorCallType::PreDetermined(
                first_pre_routing_connector_data_list.clone(),
            ));
        }
    }

    if let Some(routing_algorithm) = request_straight_through {
        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")?;

        payment_data.set_routing_approach_in_attempt(Some(
            common_enums::RoutingApproach::StraightThroughRouting,
        ));

        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.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();

            let retryable_connectors =
                join_all(connectors.into_iter().map(|connector_routing_data| {
                    let payment_method = payment_method_info.clone();
                    async move {
                        let action_types = get_all_action_types(
                            state,
                            is_connector_agnostic_mit_enabled,
                            is_network_tokenization_enabled,
                            &payment_method.clone(),
                            connector_routing_data.connector_data.clone(),
                        )
                        .await;

                        action_types
                            .into_iter()
                            .map(|action_type| api::ConnectorRoutingData {
                                connector_data: connector_routing_data.connector_data.clone(),
                                action_type: Some(action_type),
                                network: connector_routing_data.network.clone(),
                            })
                            .collect::<Vec<_>>()
                    }
                }))
                .await
                .into_iter()
                .flatten()
                .collect::<Vec<_>>();

            let chosen_connector_routing_data = retryable_connectors
                .first()
                .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                .attach_printable("no eligible connector found for token-based MIT payment")?;

            let mandate_reference_id = get_mandate_reference_id(
                chosen_connector_routing_data.action_type.clone(),
                chosen_connector_routing_data.clone(),
                payment_data,
                &payment_method_info,
            )?;

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

            routing_data.merchant_connector_id.clone_from(
                &chosen_connector_routing_data
                    .connector_data
                    .merchant_connector_id,
            );

            payment_data.set_mandate_id(payments_api::MandateIds {
                mandate_id: None,
                mandate_reference_id,
            });
            Ok(ConnectorCallType::Retryable(retryable_connectors))
        }
        (
            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")]
pub fn get_mandate_reference_id<F: Clone, D>(
    action_type: Option<ActionType>,
    connector_routing_data: api::ConnectorRoutingData,
    payment_data: &mut D,
    payment_method_info: &domain::PaymentMethod,
) -> RouterResult<Option<api_models::payments::MandateReferenceId>>
where
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
{
    let mandate_reference_id = match action_type {
        Some(ActionType::NetworkTokenWithNetworkTransactionId(network_token_data)) => {
            logger::info!("using network token with network_transaction_id for MIT flow");

            Some(payments_api::MandateReferenceId::NetworkTokenWithNTI(
                network_token_data.into(),
            ))
        }
        Some(ActionType::CardWithNetworkTransactionId(network_transaction_id)) => {
            logger::info!("using card with network_transaction_id for MIT flow");

            Some(payments_api::MandateReferenceId::NetworkMandateId(
                network_transaction_id,
            ))
        }
        Some(ActionType::ConnectorMandate(connector_mandate_details)) => {
            logger::info!("using connector_mandate_id for MIT flow");
            let merchant_connector_id = connector_routing_data
                .connector_data
                .merchant_connector_id
                .as_ref()
                .ok_or_else(|| {
                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                        .attach_printable("No eligible connector found for token-based MIT flow: no connector mandate details")
                })?;

            let mandate_reference_record = connector_mandate_details
                .get(merchant_connector_id)
                .ok_or_else(|| {
                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
                        .attach_printable("No mandate record found for merchant connector ID")
                })?;

            if let Some(mandate_currency) =
                mandate_reference_record.original_payment_authorized_currency
            {
                if mandate_currency != payment_data.get_currency() {
                    return Err(report!(errors::ApiErrorResponse::MandateValidationFailed {
                        reason: "Cross currency mandates not supported".into(),
                    }));
                }
            }

            payment_data.set_recurring_mandate_payment_data(mandate_reference_record.into());

            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()),
                    None,
                    mandate_reference_record.mandate_metadata.clone(),
                    mandate_reference_record
                        .connector_mandate_request_reference_id
                        .clone(),
                ),
            ))
        }
        None => None,
    };
    Ok(mandate_reference_id)
}

#[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(
                                mandate_reference_record.into(),
                            );
                            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>>,
}

impl From<NTWithNTIRef> for payments_api::NetworkTokenWithNTIRef {
    fn from(network_token_data: NTWithNTIRef) -> Self {
        Self {
            network_transaction_id: network_token_data.network_transaction_id,
            token_exp_month: network_token_data.token_exp_month,
            token_exp_year: network_token_data.token_exp_year,
        }
    }
}

// This represents the recurring details of a connector which will be used for retries
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub enum ActionType {
    NetworkTokenWithNetworkTransactionId(NTWithNTIRef),
    CardWithNetworkTransactionId(String), // Network Transaction Id
    #[cfg(feature = "v1")]
    ConnectorMandate(hyperswitch_domain_models::mandates::PaymentsMandateReference),
}

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")]
#[derive(Default)]
pub struct ActionTypesBuilder {
    action_types: Vec<ActionType>,
}

#[cfg(feature = "v1")]
impl ActionTypesBuilder {
    pub fn new() -> Self {
        Self {
            action_types: Vec::new(),
        }
    }

    pub fn with_mandate_flow(
        mut self,
        is_mandate_flow: bool,
        connector_mandate_details: Option<
            hyperswitch_domain_models::mandates::PaymentsMandateReference,
        >,
    ) -> Self {
        if is_mandate_flow {
            self.action_types.extend(
                connector_mandate_details
                    .map(|details| ActionType::ConnectorMandate(details.to_owned())),
            );
        }
        self
    }

    pub async fn with_network_tokenization(
        mut self,
        state: &SessionState,
        is_network_token_with_ntid_flow: IsNtWithNtiFlow,
        is_nt_with_ntid_supported_connector: bool,
        payment_method_info: &domain::PaymentMethod,
    ) -> Self {
        match is_network_token_with_ntid_flow {
            IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id)
                if is_nt_with_ntid_supported_connector =>
            {
                self.action_types.extend(
                    network_tokenization::do_status_check_for_network_token(
                        state,
                        payment_method_info,
                    )
                    .await
                    .inspect_err(|e| {
                        logger::error!("Status check for network token failed: {:?}", e)
                    })
                    .ok()
                    .map(|(token_exp_month, token_exp_year)| {
                        ActionType::NetworkTokenWithNetworkTransactionId(NTWithNTIRef {
                            token_exp_month,
                            token_exp_year,
                            network_transaction_id,
                        })
                    }),
                );
            }
            _ => (),
        }
        self
    }

    pub fn with_card_network_transaction_id(
        mut self,
        is_card_with_ntid_flow: bool,
        payment_method_info: &domain::PaymentMethod,
    ) -> Self {
        if is_card_with_ntid_flow {
            self.action_types.extend(
                payment_method_info
                    .network_transaction_id
                    .as_ref()
                    .map(|ntid| ActionType::CardWithNetworkTransactionId(ntid.clone())),
            );
        }
        self
    }

    pub fn build(self) -> Vec<ActionType> {
        self.action_types
    }
}

#[cfg(feature = "v1")]
pub async fn get_all_action_types(
    state: &SessionState,
    is_connector_agnostic_mit_enabled: Option<bool>,
    is_network_tokenization_enabled: bool,
    payment_method_info: &domain::PaymentMethod,
    connector: api::ConnectorData,
) -> Vec<ActionType> {
    let merchant_connector_id = connector.merchant_connector_id.as_ref();

    //fetch connectors that support ntid flow
    let ntid_supported_connectors = &state
        .conf
        .network_transaction_id_supported_connectors
        .connector_list;

    //fetch connectors that support network tokenization flow
    let network_tokenization_supported_connectors = &state
        .conf
        .network_tokenization_supported_connectors
        .connector_list;

    let is_network_token_with_ntid_flow = is_network_token_with_network_transaction_id_flow(
        is_connector_agnostic_mit_enabled,
        is_network_tokenization_enabled,
        payment_method_info,
    );
    let is_card_with_ntid_flow = is_network_transaction_id_flow(
        state,
        is_connector_agnostic_mit_enabled,
        connector.connector_name,
        payment_method_info,
    );
    let payments_mandate_reference = payment_method_info
        .get_common_mandate_reference()
        .map_err(|err| {
            logger::warn!("Error getting connector mandate details: {:?}", err);
            err
        })
        .ok()
        .and_then(|details| details.payments);

    let is_mandate_flow = payments_mandate_reference
        .clone()
        .zip(merchant_connector_id)
        .map(|(details, merchant_connector_id)| details.contains_key(merchant_connector_id))
        .unwrap_or(false);

    let is_nt_with_ntid_supported_connector = ntid_supported_connectors
        .contains(&connector.connector_name)
        && network_tokenization_supported_connectors.contains(&connector.connector_name);

    ActionTypesBuilder::new()
        .with_mandate_flow(is_mandate_flow, payments_mandate_reference)
        .with_network_tokenization(
            state,
            is_network_token_with_ntid_flow,
            is_nt_with_ntid_supported_connector,
            payment_method_info,
        )
        .await
        .with_card_network_transaction_id(is_card_with_ntid_flow, payment_method_info)
        .build()
}

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")?;


// File: crates/router_derive/src/macros/generate_schema.rs
// Module: router_derive
// Public functions: 1
// Public structs: 1

use std::collections::{HashMap, HashSet};

use indexmap::IndexMap;
use syn::{self, parse::Parse, parse_quote, punctuated::Punctuated, Token};

use crate::macros::helpers;

/// Parse schemas from attribute
/// Example
///
/// #[mandatory_in(PaymentsCreateRequest, PaymentsUpdateRequest)]
/// would return
///
/// [PaymentsCreateRequest, PaymentsUpdateRequest]
fn get_inner_path_ident(attribute: &syn::Attribute) -> syn::Result<Vec<syn::Ident>> {
    Ok(attribute
        .parse_args_with(Punctuated::<syn::Ident, Token![,]>::parse_terminated)?
        .into_iter()
        .collect::<Vec<_>>())
}

#[allow(dead_code)]
/// Get the type of field
fn get_field_type(field_type: syn::Type) -> syn::Result<syn::Ident> {
    if let syn::Type::Path(path) = field_type {
        path.path
            .segments
            .last()
            .map(|last_path_segment| last_path_segment.ident.to_owned())
            .ok_or(syn::Error::new(
                proc_macro2::Span::call_site(),
                "Atleast one ident must be specified",
            ))
    } else {
        Err(syn::Error::new(
            proc_macro2::Span::call_site(),
            "Only path fields are supported",
        ))
    }
}

#[allow(dead_code)]
/// Get the inner type of option
fn get_inner_option_type(field: &syn::Type) -> syn::Result<syn::Ident> {
    if let syn::Type::Path(ref path) = &field {
        if let Some(segment) = path.path.segments.last() {
            if let syn::PathArguments::AngleBracketed(ref args) = &segment.arguments {
                if let Some(syn::GenericArgument::Type(ty)) = args.args.first() {
                    return get_field_type(ty.clone());
                }
            }
        }
    }

    Err(syn::Error::new(
        proc_macro2::Span::call_site(),
        "Only path fields are supported",
    ))
}

mod schema_keyword {
    use syn::custom_keyword;

    custom_keyword!(schema);
}

#[derive(Debug, Clone)]
pub struct SchemaMeta {
    struct_name: syn::Ident,
    type_ident: syn::Ident,
}

/// parse #[mandatory_in(PaymentsCreateRequest = u64)]
impl Parse for SchemaMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let struct_name = input.parse::<syn::Ident>()?;
        input.parse::<syn::Token![=]>()?;
        let type_ident = input.parse::<syn::Ident>()?;
        Ok(Self {
            struct_name,
            type_ident,
        })
    }
}

impl quote::ToTokens for SchemaMeta {
    fn to_tokens(&self, _: &mut proc_macro2::TokenStream) {}
}

pub fn polymorphic_macro_derive_inner(
    input: syn::DeriveInput,
) -> syn::Result<proc_macro2::TokenStream> {
    let schemas_to_create =
        helpers::get_metadata_inner::<syn::Ident>("generate_schemas", &input.attrs)?;

    let fields = helpers::get_struct_fields(input.data)
        .map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?;

    // Go through all the fields and create a mapping of required fields for a schema
    // PaymentsCreate -> ["amount","currency"]
    // This will be stored in the hashmap with key as
    // required_fields -> ((amount, PaymentsCreate), (currency, PaymentsCreate))
    // and values as the type
    //
    // (amount, PaymentsCreate) -> Amount
    let mut required_fields = HashMap::<(syn::Ident, syn::Ident), syn::Ident>::new();

    // These fields will be removed in the schema
    // PaymentsUpdate -> ["client_secret"]
    // This will be stored in a hashset
    // hide_fields -> ((client_secret, PaymentsUpdate))
    let mut hide_fields = HashSet::<(syn::Ident, syn::Ident)>::new();
    let mut all_fields = IndexMap::<syn::Field, Vec<syn::Attribute>>::new();

    for field in fields {
        // Partition the attributes of a field into two vectors
        // One with #[mandatory_in] attributes present
        // Rest of the attributes ( include only the schema attribute, serde is not required)
        let (mandatory_attribute, other_attributes) = field
            .attrs
            .iter()
            .partition::<Vec<_>, _>(|attribute| attribute.path().is_ident("mandatory_in"));

        let hidden_fields = field
            .attrs
            .iter()
            .filter(|attribute| attribute.path().is_ident("remove_in"))
            .collect::<Vec<_>>();

        // Other attributes ( schema ) are to be printed as is
        other_attributes
            .iter()
            .filter(|attribute| {
                attribute.path().is_ident("schema") || attribute.path().is_ident("doc")
            })
            .for_each(|attribute| {
                // Since attributes will be modified, the field should not contain any attributes
                // So create a field, with previous attributes removed
                let mut field_without_attributes = field.clone();
                field_without_attributes.attrs.clear();

                all_fields
                    .entry(field_without_attributes.to_owned())
                    .or_default()
                    .push(attribute.to_owned().to_owned());
            });

        // Mandatory attributes are to be inserted into hashset
        // The hashset will store it in this format
        // ("amount", PaymentsCreateRequest)
        // ("currency", PaymentsConfirmRequest)
        //
        // For these attributes, we need to later add #[schema(required = true)] attribute
        let field_ident = field.ident.ok_or(syn::Error::new(
            proc_macro2::Span::call_site(),
            "Cannot use `mandatory_in` on unnamed fields",
        ))?;

        // Parse the  #[mandatory_in(PaymentsCreateRequest = u64)] and insert into hashmap
        // key -> ("amount", PaymentsCreateRequest)
        // value -> u64
        if let Some(mandatory_in_attribute) =
            helpers::get_metadata_inner::<SchemaMeta>("mandatory_in", mandatory_attribute)?.first()
        {
            let key = (
                field_ident.clone(),
                mandatory_in_attribute.struct_name.clone(),
            );
            let value = mandatory_in_attribute.type_ident.clone();
            required_fields.insert(key, value);
        }

        // Hidden fields are to be inserted in the Hashset
        // The hashset will store it in this format
        // ("client_secret", PaymentsUpdate)
        //
        // These fields will not be added to the struct
        _ = hidden_fields
            .iter()
            // Filter only #[mandatory_in] attributes
            .map(|&attribute| get_inner_path_ident(attribute))
            .try_for_each(|schemas| {
                let res = schemas
                    .map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?
                    .iter()
                    .map(|schema| (field_ident.clone(), schema.to_owned()))
                    .collect::<HashSet<_>>();

                hide_fields.extend(res);
                Ok::<_, syn::Error>(())
            });
    }

    // iterate over the schemas and build them with their fields
    let schemas = schemas_to_create
        .iter()
        .map(|schema| {
            let fields = all_fields
                .iter()
                .filter_map(|(field, attributes)| {
                    let mut final_attributes = attributes.clone();

                    if let Some(field_ident) = field.ident.to_owned() {
                        // If the field is required for this schema, then add
                        // #[schema(value_type = type)] for this field
                        if let Some(required_field_type) =
                            required_fields.get(&(field_ident.clone(), schema.to_owned()))
                        {
                            // This is a required field in the Schema
                            // Add the value type and remove original value type ( if present )
                            let attribute_without_schema_type = attributes
                                .iter()
                                .filter(|attribute| !attribute.path().is_ident("schema"))
                                .map(Clone::clone)
                                .collect::<Vec<_>>();

                            final_attributes = attribute_without_schema_type;

                            let value_type_attribute: syn::Attribute =
                                parse_quote!(#[schema(value_type = #required_field_type)]);
                            final_attributes.push(value_type_attribute);
                        }
                    }

                    // If the field is to be not shown then
                    let is_hidden_field = field
                        .ident
                        .clone()
                        .map(|field_ident| hide_fields.contains(&(field_ident, schema.to_owned())))
                        .unwrap_or(false);

                    if is_hidden_field {
                        None
                    } else {
                        Some(quote::quote! {
                            #(#final_attributes)*
                            #field,
                        })
                    }
                })
                .collect::<Vec<_>>();
            quote::quote! {
                #[derive(utoipa::ToSchema)]
                pub struct #schema {
                    #(#fields)*
                }
            }
        })
        .collect::<Vec<_>>();

    Ok(quote::quote! {
        #(#schemas)*
    })
}


// Implementation: impl Subscription
// File: crates/diesel_models/src/query/subscription.rs
// Module: diesel_models
// Methods: 2 total (0 public)

impl Subscription


// Function: ucs_logging_wrapper
// File: crates/router/src/core/unified_connector_service.rs
// Module: router

pub fn ucs_logging_wrapper<T, F, Fut, Req, Resp, GrpcReq, GrpcResp>(
    router_data: RouterData<T, Req, Resp>,
    state: &SessionState,
    grpc_request: GrpcReq,
    grpc_header_builder: external_services::grpc_client::GrpcHeadersUcsBuilderFinal,
    handler: F,
) -> RouterResult<RouterData<T, Req, Resp>>
where
    T: std::fmt::Debug + Clone + Send + 'static,
    Req: std::fmt::Debug + Clone + Send + Sync + 'static,
    Resp: std::fmt::Debug + Clone + Send + Sync + 'static,
    GrpcReq: serde::Serialize,
    GrpcResp: serde::Serialize,
    F: FnOnce(
            RouterData<T, Req, Resp>,
            GrpcReq,
            external_services::grpc_client::GrpcHeadersUcs,
        ) -> Fut
        + Send,
    Fut: std::future::Future<Output = RouterResult<(RouterData<T, Req, Resp>, GrpcResp)>> + Send,


// Struct: ThreeDsAuthDetails
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreeDsAuthDetails


// Function: enable_decision_engine_dynamic_routing_setup
// File: crates/router/src/core/routing/helpers.rs
// Module: router

pub fn enable_decision_engine_dynamic_routing_setup(
    state: &SessionState,
    profile_id: &id_type::ProfileId,
    dynamic_routing_type: routing_types::DynamicRoutingType,
    dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
    payload: Option<routing_types::DynamicRoutingPayload>,
) -> RouterResult<()>


// Implementation: impl Responder
// File: crates/router/src/routes/payments.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Struct: NoonPaymentsTransactionResponse
// File: crates/hyperswitch_connectors/src/connectors/noon/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NoonPaymentsTransactionResponse


// Function: update_user_scoped_metadata
// File: crates/router/src/utils/user/dashboard_metadata.rs
// Module: router

pub fn update_user_scoped_metadata(
    state: &SessionState,
    user_id: String,
    merchant_id: id_type::MerchantId,
    org_id: id_type::OrganizationId,
    metadata_key: DBEnum,
    metadata_value: impl serde::Serialize,
) -> UserResult<DashboardMetadata>


// Struct: ExternalVaultInsertFlow
// File: crates/hyperswitch_domain_models/src/router_flow_types/vault.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct ExternalVaultInsertFlow


// Function: find_by_payment_id_merchant_id_attempt_id
// File: crates/diesel_models/src/query/connector_response.rs
// Module: diesel_models

pub fn find_by_payment_id_merchant_id_attempt_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self>


// Function: generic_user_roles_list_for_org_and_extra
// File: crates/diesel_models/src/query/user_role.rs
// Module: diesel_models

pub fn generic_user_roles_list_for_org_and_extra(
        conn: &PgPooledConn,
        user_id: Option<String>,
        tenant_id: id_type::TenantId,
        org_id: id_type::OrganizationId,
        merchant_id: Option<id_type::MerchantId>,
        profile_id: Option<id_type::ProfileId>,
        version: Option<UserRoleVersion>,
        limit: Option<u32>,
    ) -> StorageResult<Vec<Self>>


// Implementation: impl api::ConnectorAccessToken for for Globalpay
// File: crates/hyperswitch_connectors/src/connectors/globalpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Globalpay


// Struct: NexinetsOrder
// File: crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexinetsOrder


// Struct: PaymentMethodListContext
// File: crates/router/src/types/storage/payment_method.rs
// Module: router
// Implementations: 1

pub struct PaymentMethodListContext


// File: crates/router/src/routes/mandates.rs
// Module: router
// Public functions: 3

use actix_web::{web, HttpRequest, HttpResponse};
use router_env::{instrument, tracing, Flow};

use super::app::AppState;
use crate::{
    core::{api_locking, mandate},
    services::{api, authentication as auth, authorization::permissions::Permission},
    types::{api::mandates, domain},
};

/// Mandates - Retrieve Mandate
///
/// Retrieves a mandate created using the Payments/Create API
#[instrument(skip_all, fields(flow = ?Flow::MandatesRetrieve))]
// #[get("/{id}")]
pub async fn get_mandate(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
) -> HttpResponse {
    let flow = Flow::MandatesRetrieve;
    let mandate_id = mandates::MandateId {
        mandate_id: path.into_inner(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        mandate_id,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            mandate::get_mandate(state, merchant_context, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::MandatesRevoke))]
pub async fn revoke_mandate(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<String>,
) -> HttpResponse {
    let flow = Flow::MandatesRevoke;
    let mandate_id = mandates::MandateId {
        mandate_id: path.into_inner(),
    };
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        mandate_id,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            mandate::revoke_mandate(state, merchant_context, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
/// Mandates - List Mandates
#[instrument(skip_all, fields(flow = ?Flow::MandatesList))]
pub async fn retrieve_mandates_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    payload: web::Query<api_models::mandates::MandateListConstraints>,
) -> HttpResponse {
    let flow = Flow::MandatesList;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            mandate::retrieve_mandates_list(state, merchant_context, req)
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            }),
            &auth::JWTAuth {
                permission: Permission::MerchantMandateRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


// Function: create_start_redirection_url
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models

// Documentation: This is the url to which the customer will be redirected to, to complete the redirection flow

pub fn create_start_redirection_url(
        &self,
        base_url: &str,
        publishable_key: String,
    ) -> CustomResult<url::Url, errors::api_error_response::ApiErrorResponse>


// Function: network_token_incoming_webhooks_wrapper
// File: crates/router/src/core/webhooks/incoming.rs
// Module: router

pub fn network_token_incoming_webhooks_wrapper<W: types::OutgoingWebhookType>(
    flow: &impl router_env::types::FlowMetric,
    state: SessionState,
    req: &actix_web::HttpRequest,
    body: actix_web::web::Bytes,
) -> RouterResponse<serde_json::Value>