File: crates/euclid/src/dssa/analyzer.rs
 Public functions: 3

//! Static Analysis for the Euclid Rule DSL
//!
//! Exposes certain functions that can be used to perform static analysis over programs
//! in the Euclid Rule DSL. These include standard control flow analyses like testing
//! conflicting assertions, to Domain Specific Analyses making use of the
//! [`Knowledge Graph Framework`](crate::dssa::graph).
use hyperswitch_constraint_graph::{ConstraintGraph, Memoization};
use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    dssa::{
        graph::CgraphExt,
        state_machine, truth,
        types::{self, EuclidAnalysable},
    },
    frontend::{
        ast,
        dir::{self, EuclidDirFilter},
        vir,
    },
    types::{DataType, Metadata},
};

/// Analyses conflicting assertions on the same key in a conjunctive context.
///
/// For example,
/// ```notrust
/// payment_method = card && ... && payment_method = bank_debit
/// ```notrust
/// This is a condition that will never evaluate to `true` given a single
/// payment method and needs to be caught in analysis.
pub fn analyze_conflicting_assertions(
    keywise_assertions: &FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>>,
    assertion_metadata: &FxHashMap<&dir::DirValue, &Metadata>,
) -> Result<(), types::AnalysisError> {
    for (key, value_set) in keywise_assertions {
        if value_set.len() > 1 {
            let err_type = types::AnalysisErrorType::ConflictingAssertions {
                key: key.clone(),
                values: value_set
                    .iter()
                    .map(|val| types::ValueData {
                        value: (*val).clone(),
                        metadata: assertion_metadata
                            .get(val)
                            .map(|meta| (*meta).clone())
                            .unwrap_or_default(),
                    })
                    .collect(),
            };

            Err(types::AnalysisError {
                error_type: err_type,
                metadata: Default::default(),
            })?;
        }
    }
    Ok(())
}

/// Analyses exhaustive negations on the same key in a conjunctive context.
///
/// For example,
/// ```notrust
/// authentication_type /= three_ds && ... && authentication_type /= no_three_ds
/// ```notrust
/// This is a condition that will never evaluate to `true` given any authentication_type
/// since all the possible values authentication_type can take have been negated.
pub fn analyze_exhaustive_negations(
    keywise_negations: &FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>>,
    keywise_negation_metadata: &FxHashMap<dir::DirKey, Vec<&Metadata>>,
) -> Result<(), types::AnalysisError> {
    for (key, negation_set) in keywise_negations {
        let mut value_set = if let Some(set) = key.kind.get_value_set() {
            set
        } else {
            continue;
        };

        value_set.retain(|val| !negation_set.contains(val));

        if value_set.is_empty() {
            let error_type = types::AnalysisErrorType::ExhaustiveNegation {
                key: key.clone(),
                metadata: keywise_negation_metadata
                    .get(key)
                    .cloned()
                    .unwrap_or_default()
                    .iter()
                    .copied()
                    .cloned()
                    .collect(),
            };

            Err(types::AnalysisError {
                error_type,
                metadata: Default::default(),
            })?;
        }
    }
    Ok(())
}

fn analyze_negated_assertions(
    keywise_assertions: &FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>>,
    assertion_metadata: &FxHashMap<&dir::DirValue, &Metadata>,
    keywise_negations: &FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>>,
    negation_metadata: &FxHashMap<&dir::DirValue, &Metadata>,
) -> Result<(), types::AnalysisError> {
    for (key, negation_set) in keywise_negations {
        let assertion_set = if let Some(set) = keywise_assertions.get(key) {
            set
        } else {
            continue;
        };

        let intersection = negation_set & assertion_set;

        intersection.iter().next().map_or(Ok(()), |val| {
            let error_type = types::AnalysisErrorType::NegatedAssertion {
                value: (*val).clone(),
                assertion_metadata: assertion_metadata
                    .get(*val)
                    .copied()
                    .cloned()
                    .unwrap_or_default(),
                negation_metadata: negation_metadata
                    .get(*val)
                    .copied()
                    .cloned()
                    .unwrap_or_default(),
            };

            Err(types::AnalysisError {
                error_type,
                metadata: Default::default(),
            })
        })?;
    }
    Ok(())
}

fn perform_condition_analyses(
    context: &types::ConjunctiveContext<'_>,
) -> Result<(), types::AnalysisError> {
    let mut assertion_metadata: FxHashMap<&dir::DirValue, &Metadata> = FxHashMap::default();
    let mut keywise_assertions: FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>> =
        FxHashMap::default();
    let mut negation_metadata: FxHashMap<&dir::DirValue, &Metadata> = FxHashMap::default();
    let mut keywise_negation_metadata: FxHashMap<dir::DirKey, Vec<&Metadata>> =
        FxHashMap::default();
    let mut keywise_negations: FxHashMap<dir::DirKey, FxHashSet<&dir::DirValue>> =
        FxHashMap::default();

    for ctx_val in context {
        let key = if let Some(k) = ctx_val.value.get_key() {
            k
        } else {
            continue;
        };

        if let dir::DirKeyKind::Connector = key.kind {
            continue;
        }

        if !matches!(key.kind.get_type(), DataType::EnumVariant) {
            continue;
        }

        match ctx_val.value {
            types::CtxValueKind::Assertion(val) => {
                keywise_assertions
                    .entry(key.clone())
                    .or_default()
                    .insert(val);

                assertion_metadata.insert(val, ctx_val.metadata);
            }

            types::CtxValueKind::Negation(vals) => {
                let negation_set = keywise_negations.entry(key.clone()).or_default();

                for val in vals {
                    negation_set.insert(val);
                    negation_metadata.insert(val, ctx_val.metadata);
                }

                keywise_negation_metadata
                    .entry(key.clone())
                    .or_default()
                    .push(ctx_val.metadata);
            }
        }
    }

    analyze_conflicting_assertions(&keywise_assertions, &assertion_metadata)?;
    analyze_exhaustive_negations(&keywise_negations, &keywise_negation_metadata)?;
    analyze_negated_assertions(
        &keywise_assertions,
        &assertion_metadata,
        &keywise_negations,
        &negation_metadata,
    )?;

    Ok(())
}

fn perform_context_analyses(
    context: &types::ConjunctiveContext<'_>,
    knowledge_graph: &ConstraintGraph<dir::DirValue>,
) -> Result<(), types::AnalysisError> {
    perform_condition_analyses(context)?;
    let mut memo = Memoization::new();
    knowledge_graph
        .perform_context_analysis(context, &mut memo, None)
        .map_err(|err| types::AnalysisError {
            error_type: types::AnalysisErrorType::GraphAnalysis(err, memo),
            metadata: Default::default(),
        })?;
    Ok(())
}

pub fn analyze<O: EuclidAnalysable + EuclidDirFilter>(
    program: ast::Program<O>,
    knowledge_graph: Option<&ConstraintGraph<dir::DirValue>>,
) -> Result<vir::ValuedProgram<O>, types::AnalysisError> {
    let dir_program = ast::lowering::lower_program(program)?;

    let selection_data = state_machine::make_connector_selection_data(&dir_program);
    let mut ctx_manager = state_machine::AnalysisContextManager::new(&dir_program, &selection_data);
    while let Some(ctx) = ctx_manager.advance().map_err(|err| types::AnalysisError {
        metadata: Default::default(),
        error_type: types::AnalysisErrorType::StateMachine(err),
    })? {
        perform_context_analyses(ctx, knowledge_graph.unwrap_or(&truth::ANALYSIS_GRAPH))?;
    }

    dir::lowering::lower_program(dir_program)
}

#[cfg(all(test, feature = "ast_parser"))]
mod tests {
    #![allow(clippy::panic, clippy::expect_used)]

    use std::{ops::Deref, sync::Weak};

    use euclid_macros::knowledge;
    use hyperswitch_constraint_graph as cgraph;

    use super::*;
    use crate::{
        dirval,
        dssa::graph::{self, euclid_graph_prelude},
        types::DummyOutput,
    };

    #[test]
    fn test_conflicting_assertion_detection() {
        let program_str = r#"
            default: ["stripe", "adyen"]

            stripe_first: ["stripe", "adyen"]
            {
                payment_method = wallet {
                    amount > 500 & capture_method = automatic
                    amount < 500 & payment_method = card
                }
            }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let analysis_result = analyze(program, None);

        if let Err(types::AnalysisError {
            error_type: types::AnalysisErrorType::ConflictingAssertions { key, values },
            ..
        }) = analysis_result
        {
            assert!(
                matches!(key.kind, dir::DirKeyKind::PaymentMethod),
                "Key should be payment_method"
            );
            let values: Vec<dir::DirValue> = values.into_iter().map(|v| v.value).collect();
            assert_eq!(values.len(), 2, "There should be 2 conflicting conditions");
            assert!(
                values.contains(&dirval!(PaymentMethod = Wallet)),
                "Condition should include payment_method = wallet"
            );
            assert!(
                values.contains(&dirval!(PaymentMethod = Card)),
                "Condition should include payment_method = card"
            );
        } else {
            panic!("Did not receive conflicting assertions error");
        }
    }

    #[test]
    fn test_exhaustive_negation_detection() {
        let program_str = r#"
            default: ["stripe"]

            rule_1: ["adyen"]
            {
                payment_method /= wallet {
                    capture_method = manual & payment_method /= card {
                        authentication_type = three_ds & payment_method /= pay_later {
                            amount > 1000 & payment_method /= bank_redirect {
                                payment_method /= crypto
                                    & payment_method /= bank_debit
                                    & payment_method /= bank_transfer
                                    & payment_method /= upi
                                    & payment_method /= reward
                                    & payment_method /= voucher
                                    & payment_method /= gift_card
                                    & payment_method /= card_redirect
                                    & payment_method /= real_time_payment
                                    & payment_method /= open_banking
                                    & payment_method /= mobile_payment
                            }
                        }
                    }
                }
            }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let analysis_result = analyze(program, None);

        if let Err(types::AnalysisError {
            error_type: types::AnalysisErrorType::ExhaustiveNegation { key, .. },
            ..
        }) = analysis_result
        {
            assert!(
                matches!(key.kind, dir::DirKeyKind::PaymentMethod),
                "Expected key to be payment_method"
            );
        } else {
            panic!("Expected exhaustive negation error");
        }
    }

    #[test]
    fn test_negated_assertions_detection() {
        let program_str = r#"
            default: ["stripe"]

            rule_1: ["adyen"]
            {
                payment_method = wallet {
                    amount > 500 {
                        capture_method = automatic
                    }

                    amount < 501 {
                        payment_method /= wallet
                    }
                }
            }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let analysis_result = analyze(program, None);

        if let Err(types::AnalysisError {
            error_type: types::AnalysisErrorType::NegatedAssertion { value, .. },
            ..
        }) = analysis_result
        {
            assert_eq!(
                value,
                dirval!(PaymentMethod = Wallet),
                "Expected to catch payment_method = wallet as conflict"
            );
        } else {
            panic!("Expected negated assertion error");
        }
    }

    #[test]
    fn test_negation_graph_analysis() {
        let graph = knowledge! {
            CaptureMethod(Automatic) ->> PaymentMethod(Card);
        };

        let program_str = r#"
            default: ["stripe"]

            rule_1: ["adyen"]
            {
                amount > 500 {
                    payment_method = pay_later
                }

                amount < 500 {
                    payment_method /= wallet & payment_method /= pay_later
                }
            }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Graph");
        let analysis_result = analyze(program, Some(&graph));

        let error_type = match analysis_result {
            Err(types::AnalysisError { error_type, .. }) => error_type,
            _ => panic!("Error_type not found"),
        };

        let a_err = match error_type {
            types::AnalysisErrorType::GraphAnalysis(trace, memo) => (trace, memo),
            _ => panic!("Graph Analysis not found"),
        };

        let (trace, metadata) = match a_err.0 {
            graph::AnalysisError::NegationTrace { trace, metadata } => (trace, metadata),
            _ => panic!("Negation Trace not found"),
        };

        let predecessor = match Weak::upgrade(&trace)
            .expect("Expected Arc not found")
            .deref()
            .clone()
        {
            cgraph::AnalysisTrace::Value { predecessors, .. } => {
                let _value = cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(
                    dir::enums::PaymentMethod::Card,
                ));
                let _relation = cgraph::Relation::Positive;
                predecessors
            }
            _ => panic!("Expected Negation Trace for payment method = card"),
        };

        let pred = match predecessor {
            Some(cgraph::error::ValueTracePredecessor::Mandatory(predecessor)) => predecessor,
            _ => panic!("No predecessor found"),
        };
        assert_eq!(
            metadata.len(),
            2,
            "Expected two metadats for wallet and pay_later"
        );
        assert!(matches!(
            *Weak::upgrade(&pred)
                .expect("Expected Arc not found")
                .deref(),
            cgraph::AnalysisTrace::Value {
                value: cgraph::NodeValue::Value(dir::DirValue::CaptureMethod(
                    dir::enums::CaptureMethod::Automatic
                )),
                relation: cgraph::Relation::Positive,
                info: None,
                metadata: None,
                predecessors: None,
            }
        ));
    }
}


pub async fn add_publishable_key(
    state: &SessionState,
    api_key: Secret<String>,
    merchant_id: common_utils::id_type::MerchantId,
    expiry: Option<u64>,
) -> CustomResult<(), ApiClientError> {
    let decision_config = if let Some(config) = &state.conf.decision {
        config
    } else {
        return Ok(());
    };

    let rule = RuleRequest {
        tag: state.tenant.schema.clone(),
        expiry,
        variant: AuthRuleType::ApiKey {
            api_key,
            identifiers: Identifiers::PublishableKey {
                merchant_id: merchant_id.get_string_repr().to_owned(),
            },
        },
    };

    call_decision_service(state, decision_config, rule, RULE_ADD_METHOD).await
}


File: crates/analytics/src/refunds/metrics/sessionized_metrics/refund_error_message.rs

use std::collections::HashSet;

use api_models::analytics::{
    refunds::{RefundDimensions, RefundFilters, RefundMetricsBucketIdentifier},
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::RefundMetricRow;
use crate::{
    enums::AuthInfo,
    query::{
        Aggregate, FilterTypes, GroupByClause, Order, QueryBuilder, QueryFilter, SeriesBucket,
        ToSql, Window,
    },
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(crate) struct RefundErrorMessage;

#[async_trait::async_trait]
impl<T> super::RefundMetric<T> for RefundErrorMessage
where
    T: AnalyticsDataSource + super::RefundMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[RefundDimensions],
        auth: &AuthInfo,
        filters: &RefundFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(RefundMetricsBucketIdentifier, RefundMetricRow)>> {
        let mut inner_query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::RefundSessionized);
        inner_query_builder
            .add_select_column("sum(sign_flag)")
            .switch()?;

        inner_query_builder
            .add_custom_filter_clause(
                RefundDimensions::RefundErrorMessage,
                "NULL",
                FilterTypes::IsNotNull,
            )
            .switch()?;

        time_range
            .set_filter_clause(&mut inner_query_builder)
            .attach_printable("Error filtering time range for inner query")
            .switch()?;

        let inner_query_string = inner_query_builder
            .build_query()
            .attach_printable("Error building inner query")
            .change_context(MetricsError::QueryBuildingError)?;

        let mut outer_query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::RefundSessionized);

        for dim in dimensions.iter() {
            outer_query_builder.add_select_column(dim).switch()?;
        }

        outer_query_builder
            .add_select_column("sum(sign_flag) AS count")
            .switch()?;

        outer_query_builder
            .add_select_column(format!("({inner_query_string}) AS total"))
            .switch()?;

        outer_query_builder
            .add_select_column(Aggregate::Min {
                field: "created_at",
                alias: Some("start_bucket"),
            })
            .switch()?;

        outer_query_builder
            .add_select_column(Aggregate::Max {
                field: "created_at",
                alias: Some("end_bucket"),
            })
            .switch()?;

        filters
            .set_filter_clause(&mut outer_query_builder)
            .switch()?;

        auth.set_filter_clause(&mut outer_query_builder).switch()?;

        time_range
            .set_filter_clause(&mut outer_query_builder)
            .attach_printable("Error filtering time range for outer query")
            .switch()?;

        outer_query_builder
            .add_filter_clause(
                RefundDimensions::RefundStatus,
                storage_enums::RefundStatus::Failure,
            )
            .switch()?;

        outer_query_builder
            .add_custom_filter_clause(
                RefundDimensions::RefundErrorMessage,
                "NULL",
                FilterTypes::IsNotNull,
            )
            .switch()?;

        for dim in dimensions.iter() {
            outer_query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(granularity) = granularity {
            granularity
                .set_group_by_clause(&mut outer_query_builder)
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        outer_query_builder
            .add_order_by_clause("count", Order::Descending)
            .attach_printable("Error adding order by clause")
            .switch()?;

        let filtered_dimensions: Vec<&RefundDimensions> = dimensions
            .iter()
            .filter(|&&dim| dim != RefundDimensions::RefundErrorMessage)
            .collect();

        for dim in &filtered_dimensions {
            outer_query_builder
                .add_order_by_clause(*dim, Order::Ascending)
                .attach_printable("Error adding order by clause")
                .switch()?;
        }

        outer_query_builder
            .execute_query::<RefundMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    RefundMetricsBucketIdentifier::new(
                        i.currency.as_ref().map(|i| i.0),
                        None,
                        i.connector.clone(),
                        i.refund_type.as_ref().map(|i| i.0.to_string()),
                        i.profile_id.clone(),
                        i.refund_reason.clone(),
                        i.refund_error_message.clone(),
                        TimeRange {
                            start_time: match (granularity, i.start_bucket) {
                                (Some(g), Some(st)) => g.clip_to_start(st)?,
                                _ => time_range.start_time,
                            },
                            end_time: granularity.as_ref().map_or_else(
                                || Ok(time_range.end_time),
                                |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
                            )?,
                        },
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(RefundMetricsBucketIdentifier, RefundMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


impl ConnectorCommon for Gpayments {
    fn id(&self) -> &'static str {
        "gpayments"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
        //    TODO! Check connector documentation, on which unit they are processing the currency.
        //    If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor,
        //    if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.gpayments.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        _auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(vec![])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: gpayments_types::TDS2ApiError = res
            .response
            .parse_struct("gpayments_types TDS2ApiError")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error_code,
            message: response.error_description,
            reason: response.error_detail,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


pub struct FacilitapayCustomerResponse {
    pub data: FacilitapaySubject,
}


pub async fn migrate_customers(
    state: SessionState,
    customers_migration: Vec<payment_methods_domain::PaymentMethodCustomerMigrate>,
    merchant_context: domain::MerchantContext,
) -> errors::CustomerResponse<()> {
    for customer_migration in customers_migration {
        match create_customer(
            state.clone(),
            merchant_context.clone(),
            customer_migration.customer,
            customer_migration.connector_customer_details,
        )
        .await
        {
            Ok(_) => (),
            Err(e) => match e.current_context() {
                errors::CustomersErrorResponse::CustomerAlreadyExists => (),
                _ => return Err(e),
            },
        }
    }
    Ok(services::ApplicationResponse::Json(()))
}


impl SerializableSecret for u16 {}


impl api::PaymentVoid for Bankofamerica {}

impl api::PaymentVoid for for Bankofamerica


pub fn get_payment_method(&self) -> Option<storage_enums::PaymentMethod> {
        // TODO: check if we can fix this
        Some(self.payment_method_type)
    }


impl ValidateStatusForOperation for PaymentProxyIntent {
    /// Validate if the current operation can be performed on the current status of the payment intent
    fn validate_status_for_operation(
        &self,
        intent_status: common_enums::IntentStatus,
    ) -> Result<(), errors::ApiErrorResponse> {
        match intent_status {
            //Failed state is included here so that in PCR, retries can be done for failed payments, otherwise for a failed attempt it was asking for new payment_intent
            common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::Failed
            | common_enums::IntentStatus::Processing => Ok(()),
            //Failed state is included here so that in PCR, retries can be done for failed payments, otherwise for a failed attempt it was asking for new payment_intent
            common_enums::IntentStatus::RequiresPaymentMethod
            | common_enums::IntentStatus::Failed => Ok(()),
            common_enums::IntentStatus::Conflicted
            | common_enums::IntentStatus::Succeeded
            | common_enums::IntentStatus::Cancelled
            | common_enums::IntentStatus::CancelledPostCapture
            | common_enums::IntentStatus::RequiresCustomerAction
            | common_enums::IntentStatus::RequiresMerchantAction
            | common_enums::IntentStatus::RequiresCapture
            | common_enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
            | common_enums::IntentStatus::PartiallyCaptured
            | common_enums::IntentStatus::RequiresConfirmation
            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
            | common_enums::IntentStatus::Expired => {
                Err(errors::ApiErrorResponse::PaymentUnexpectedState {
                    current_flow: format!("{self:?}"),
                    field_name: "status".to_string(),
                    current_value: intent_status.to_string(),
                    states: ["requires_payment_method".to_string()].join(", "),
                })
            }
        }
    }
}

impl ValidateStatusForOperation for for PaymentProxyIntent


impl ConnectorCommon for Jpmorgan {
    fn id(&self) -> &'static str {
        "jpmorgan"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.jpmorgan.base_url.as_ref()
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: JpmorganErrorResponse = res
            .response
            .parse_struct("JpmorganErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        router_env::logger::info!(connector_response=?response);
        event_builder.map(|i| i.set_response_body(&response));

        let response_message = response
            .response_message
            .as_ref()
            .map_or_else(|| consts::NO_ERROR_MESSAGE.to_string(), ToString::to_string);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.response_code,
            message: response_message.clone(),
            reason: Some(response_message),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}


impl api::RefundSync for Fiserv {}


pub struct FiservemeaExpiryDate {
    month: Secret<String>,
    year: Secret<String>,
}


pub struct NexiCardDetails {
    #[serde(flatten)]
    card_data: CardDataDetails,
    cof_contract: Option<CofContract>,
}


///Get payment_method_subtype
    pub fn get_payment_method_type(&self) -> Option<common_enums::PaymentMethodType> {
        Some(self.payment_method_subtype)
    }


pub struct PayuRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}


pub async fn create_customer(&self) -> RouterResult<api::CustomerDetails> {
        let db = &*self.state.store;
        let customer_id = self
            .customer
            .customer_id
            .as_ref()
            .get_required_value("customer_id")
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "customer_id",
            })?;
        let key_manager_state: &KeyManagerState = &self.state.into();

        let encrypted_data = crypto_operation(
            key_manager_state,
            type_name!(domain::Customer),
            CryptoOperation::BatchEncrypt(domain::FromRequestEncryptableCustomer::to_encryptable(
                domain::FromRequestEncryptableCustomer {
                    name: self.customer.name.clone(),
                    email: self
                        .customer
                        .email
                        .clone()
                        .map(|email| email.expose().switch_strategy()),
                    phone: self.customer.phone.clone(),
                    tax_registration_id: self.customer.tax_registration_id.clone(),
                },
            )),
            Identifier::Merchant(self.merchant_account.get_id().clone()),
            self.key_store.key.get_inner().peek(),
        )
        .await
        .inspect_err(|err| logger::info!("Error encrypting customer: {:?}", err))
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to encrypt customer")?;

        let encryptable_customer =
            domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Failed to form EncryptableCustomer")?;

        let new_customer_id = generate_customer_id_of_default_length();
        let domain_customer = domain::Customer {
            customer_id: new_customer_id.clone(),
            merchant_id: self.merchant_account.get_id().clone(),
            name: encryptable_customer.name,
            email: encryptable_customer.email.map(|email| {
                utils::Encryptable::new(
                    email.clone().into_inner().switch_strategy(),
                    email.into_encrypted(),
                )
            }),
            phone: encryptable_customer.phone,
            description: None,
            phone_country_code: self.customer.phone_country_code.to_owned(),
            metadata: None,
            connector_customer: None,
            created_at: common_utils::date_time::now(),
            modified_at: common_utils::date_time::now(),
            address_id: None,
            default_payment_method_id: None,
            updated_by: None,
            version: common_types::consts::API_VERSION,
            tax_registration_id: encryptable_customer.tax_registration_id,
        };

        db.insert_customer(
            domain_customer,
            key_manager_state,
            self.key_store,
            self.merchant_account.storage_scheme,
        )
        .await
        .inspect_err(|err| logger::info!("Error creating a customer: {:?}", err))
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed to insert customer [id - {:?}] for merchant [id - {:?}]",
                customer_id,
                self.merchant_account.get_id()
            )
        })?;

        Ok(api::CustomerDetails {
            id: new_customer_id,
            name: self.customer.name.clone(),
            email: self.customer.email.clone(),
            phone: self.customer.phone.clone(),
            phone_country_code: self.customer.phone_country_code.clone(),
            tax_registration_id: self.customer.tax_registration_id.clone(),
        })
    }


OpenAPI Block Path: components.schemas.LinkedRoutingConfigRetrieveResponse
{
  "oneOf": [
    {
      "$ref": "#/components/schemas/RoutingRetrieveResponse"
    },
    {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/RoutingDictionaryRecord"
      }
    }
  ]
}


impl api::PaymentAuthorize for Forte {}

impl api::PaymentAuthorize for for Forte


/// value is NULL. The default value is provided by the `Default` trait implementation of `T`.
    pub struct RequiredFromNullableWithDefault<T>(T);


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


pub struct PayuPaymentsSyncResponse {
    orders: Vec<PayuOrderResponseData>,
    status: PayuPaymentStatusData,
    properties: Option<Vec<PayuOrderResponseProperty>>,
}


File: crates/api_models/src/feature_matrix.rs
 Public structs: 5

use std::collections::HashSet;

use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Default, Debug, Deserialize, Serialize, Clone, ToSchema)]
pub struct FeatureMatrixRequest {
    // List of connectors for which the feature matrix is requested
    #[schema(value_type = Option<Vec<Connector>>)]
    pub connectors: Option<Vec<common_enums::connector_enums::Connector>>,
}

#[derive(Debug, Clone, ToSchema, Serialize)]
pub struct CardSpecificFeatures {
    /// Indicates whether three_ds card payments are supported
    #[schema(value_type = FeatureStatus)]
    pub three_ds: common_enums::FeatureStatus,
    /// Indicates whether non three_ds card payments are supported
    #[schema(value_type = FeatureStatus)]
    pub no_three_ds: common_enums::FeatureStatus,
    /// List of supported card networks
    #[schema(value_type = Vec<CardNetwork>)]
    pub supported_card_networks: Vec<common_enums::CardNetwork>,
}

#[derive(Debug, Clone, ToSchema, Serialize)]
#[serde(untagged)]
pub enum PaymentMethodSpecificFeatures {
    /// Card specific features
    Card(CardSpecificFeatures),
}

#[derive(Debug, ToSchema, Serialize)]
pub struct SupportedPaymentMethod {
    /// The payment method supported by the connector
    #[schema(value_type = PaymentMethod)]
    pub payment_method: common_enums::PaymentMethod,
    /// The payment method type supported by the connector
    #[schema(value_type = PaymentMethodType)]
    pub payment_method_type: common_enums::PaymentMethodType,
    /// The display name of the payment method type
    pub payment_method_type_display_name: String,
    /// Indicates whether the payment method supports mandates via the connector
    #[schema(value_type = FeatureStatus)]
    pub mandates: common_enums::FeatureStatus,
    /// Indicates whether the payment method supports refunds via the connector
    #[schema(value_type = FeatureStatus)]
    pub refunds: common_enums::FeatureStatus,
    /// List of supported capture methods supported by the payment method type
    #[schema(value_type = Vec<CaptureMethod>)]
    pub supported_capture_methods: Vec<common_enums::CaptureMethod>,
    /// Information on the Payment method specific payment features
    #[serde(flatten)]
    pub payment_method_specific_features: Option<PaymentMethodSpecificFeatures>,
    /// List of countries supported by the payment method type via the connector
    #[schema(value_type = Option<HashSet<CountryAlpha3>>)]
    pub supported_countries: Option<HashSet<common_enums::CountryAlpha3>>,
    /// List of currencies supported by the payment method type via the connector
    #[schema(value_type = Option<HashSet<Currency>>)]
    pub supported_currencies: Option<HashSet<common_enums::Currency>>,
}

#[derive(Debug, ToSchema, Serialize)]
pub struct ConnectorFeatureMatrixResponse {
    /// The name of the connector
    pub name: String,
    /// The display name of the connector
    pub display_name: String,
    /// The description of the connector
    pub description: String,
    /// The category of the connector
    #[schema(value_type = HyperswitchConnectorCategory, example = "payment_gateway")]
    pub category: common_enums::HyperswitchConnectorCategory,
    /// The integration status of the connector
    #[schema(value_type = ConnectorIntegrationStatus, example = "live")]
    pub integration_status: common_enums::ConnectorIntegrationStatus,
    /// The list of payment methods supported by the connector
    pub supported_payment_methods: Option<Vec<SupportedPaymentMethod>>,
    /// The list of webhook flows supported by the connector
    #[schema(value_type = Option<Vec<EventClass>>)]
    pub supported_webhook_flows: Option<Vec<common_enums::EventClass>>,
}

#[derive(Debug, Serialize, ToSchema)]
pub struct FeatureMatrixListResponse {
    /// The number of connectors included in the response
    pub connector_count: usize,
    // The list of payments response objects
    pub connectors: Vec<ConnectorFeatureMatrixResponse>,
}

impl common_utils::events::ApiEventMetric for FeatureMatrixListResponse {}
impl common_utils::events::ApiEventMetric for FeatureMatrixRequest {}


Documentation: api-reference/v2/api-key/api-key--update.mdx
# Type: Doc File

---
openapi: put /v2/api-keys/{id}
---


pub struct PayoutEnabledPaymentMethodsInfo {
    pub payment_method: common_enums::PaymentMethod,
    pub payment_method_types_info: Vec<PaymentMethodTypeInfo>,
}


impl api::RefundExecute for Payeezy {}


pub struct Customer {
    pub customer_id: id_type::CustomerId,
    pub merchant_id: id_type::MerchantId,
    #[encrypt]
    pub name: Option<Encryptable<Secret<String>>>,
    #[encrypt]
    pub email: Option<Encryptable<Secret<String, pii::EmailStrategy>>>,
    #[encrypt]
    pub phone: Option<Encryptable<Secret<String>>>,
    pub phone_country_code: Option<String>,
    pub description: Option<Description>,
    pub created_at: PrimitiveDateTime,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub modified_at: PrimitiveDateTime,
    pub connector_customer: Option<pii::SecretSerdeValue>,
    pub address_id: Option<String>,
    pub default_payment_method_id: Option<String>,
    pub updated_by: Option<String>,
    pub version: common_enums::ApiVersion,
    #[encrypt]
    pub tax_registration_id: Option<Encryptable<Secret<String>>>,
}


impl api::MandateSetup for Adyenplatform {}

impl api::MandateSetup for for Adyenplatform


impl IncomingWebhook for Klarna {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        Ok(IncomingWebhookEvent::EventNotSupported)
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}


pub struct BluecodePaymentsRequest {
    pub amount: FloatMajorUnit,
    pub currency: enums::Currency,
    pub payment_provider: String,
    pub shop_name: String,
    pub reference: String,
    pub ip_address: Option<Secret<String, IpAddress>>,
    pub first_name: Secret<String>,
    pub last_name: Secret<String>,
    pub billing_address_country_code_iso: enums::CountryAlpha2,
    pub billing_address_city: String,
    pub billing_address_line1: Secret<String>,
    pub billing_address_postal_code: Secret<String>,
    pub webhook_url: String,
    pub success_url: String,
    pub failure_url: String,
}


pub async fn authentication_authenticate(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    json_payload: web::Json<AuthenticationAuthenticateRequest>,
    path: web::Path<common_utils::id_type::AuthenticationId>,
) -> impl Responder {
    let flow = Flow::AuthenticationAuthenticate;
    let authentication_id = path.into_inner();
    let api_auth = auth::ApiKeyAuth::default();
    let payload = AuthenticationAuthenticateRequest {
        authentication_id,
        ..json_payload.into_inner()
    };

    let (auth, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok((auth, auth_flow)) => (auth, auth_flow),
            Err(e) => return api::log_and_return_error_response(e),
        };

    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),
            ));
            unified_authentication_service::authentication_authenticate_core(
                state,
                merchant_context,
                req,
                auth_flow,
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


impl api::ConnectorAccessToken for Mollie {}

impl api::ConnectorAccessToken for for Mollie


pub async fn find_by_profile_id_connector_name(
        conn: &PgPooledConn,
        profile_id: &common_utils::id_type::ProfileId,
        connector_name: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::profile_id
                .eq(profile_id.to_owned())
                .and(dsl::connector_name.eq(connector_name.to_owned())),
        )
        .await
    }


impl<T> FromStr for DBEnumWrapper<T>
where
    T: FromStr + Display,
{
    type Err = Report<ParsingError>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        T::from_str(s)
            .map_err(|_er| report!(ParsingError::EnumParseFailure(std::any::type_name::<T>())))
            .map(DBEnumWrapper)
            .attach_printable_lazy(|| format!("raw_value: {s}"))
    }
}


impl ConnectorValidation for ConnectorEnum {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<common_enums::CaptureMethod>,
        payment_method: common_enums::PaymentMethod,
        pmt: Option<common_enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_connector_against_payment_request(
                capture_method,
                payment_method,
                pmt,
            ),
            Self::New(connector) => connector.validate_connector_against_payment_request(
                capture_method,
                payment_method,
                pmt,
            ),
        }
    }

    fn validate_mandate_payment(
        &self,
        pm_type: Option<common_enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_mandate_payment(pm_type, pm_data),
            Self::New(connector) => connector.validate_mandate_payment(pm_type, pm_data),
        }
    }

    fn validate_psync_reference_id(
        &self,
        data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData,
        is_three_ds: bool,
        status: common_enums::enums::AttemptStatus,
        connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        match self {
            Self::Old(connector) => connector.validate_psync_reference_id(
                data,
                is_three_ds,
                status,
                connector_meta_data,
            ),
            Self::New(connector) => connector.validate_psync_reference_id(
                data,
                is_three_ds,
                status,
                connector_meta_data,
            ),
        }
    }

    fn is_webhook_source_verification_mandatory(&self) -> bool {
        match self {
            Self::Old(connector) => connector.is_webhook_source_verification_mandatory(),
            Self::New(connector) => connector.is_webhook_source_verification_mandatory(),
        }
    }
}

impl ConnectorValidation for for ConnectorEnum


pub fn is_success_rate_routing_enabled(&self) -> bool {
        self.success_based_algorithm
            .as_ref()
            .map(|success_based_routing| {
                success_based_routing.enabled_feature
                    == DynamicRoutingFeatures::DynamicConnectorSelection
                    || success_based_routing.enabled_feature == DynamicRoutingFeatures::Metrics
            })
            .unwrap_or_default()
    }


pub struct ProducerSettings {
    pub upper_fetch_limit: i64,
    pub lower_fetch_limit: i64,

    pub lock_key: String,
    pub lock_ttl: i64,
    pub batch_size: usize,
}


pub struct CustomerUpdateRequest {
    /// The identifier for the Merchant Account
    #[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")]
    #[serde(skip)]
    pub merchant_id: id_type::MerchantId,
    /// The customer's name
    #[schema(max_length = 255, value_type = Option<String>, example = "Jon Test")]
    pub name: Option<Secret<String>>,
    /// The customer's email address
    #[schema(value_type = Option<String>, max_length = 255, example = "JonTest@test.com")]
    pub email: Option<pii::Email>,
    /// The customer's phone number
    #[schema(value_type = Option<String>, max_length = 255, example = "9123456789")]
    pub phone: Option<Secret<String>>,
    /// An arbitrary string that you can attach to a customer object.
    #[schema(max_length = 255, example = "First Customer", value_type = Option<String>)]
    pub description: Option<Description>,
    /// The country code for the customer phone number
    #[schema(max_length = 255, example = "+65")]
    pub phone_country_code: Option<String>,
    /// The address for the customer
    #[schema(value_type = Option<AddressDetails>)]
    pub address: Option<payments::AddressDetails>,
    /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500
    /// characters long. Metadata is useful for storing additional, structured information on an
    /// object.
    #[schema(value_type = Option<Object>,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,
    /// Customer's tax registration ID
    #[schema(max_length = 255, value_type = Option<String>, example = "123456789")]
    pub tax_registration_id: Option<Secret<String>>,
}


pub struct SantanderRefundResponse {
    pub id: Secret<String>,
    pub rtr_id: Secret<String>,
    #[serde(rename = "valor")]
    pub value: StringMajorUnit,
    #[serde(rename = "horario")]
    pub time: SantanderTime,
    pub status: SantanderRefundStatus,
    #[serde(rename = "motivo")]
    pub reason: String,
}


File: crates/router/src/core/routing.rs
 Public functions: 34
 Public structs: 4

pub mod helpers;
pub mod transformers;
use std::collections::HashSet;

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use api_models::routing::DynamicRoutingAlgoAccessor;
use api_models::{
    enums, mandates as mandates_api,
    open_router::{
        DecideGatewayResponse, OpenRouterDecideGatewayRequest, UpdateScorePayload,
        UpdateScoreResponse,
    },
    routing,
    routing::{
        self as routing_types, RoutingRetrieveQuery, RuleMigrationError, RuleMigrationResponse,
    },
};
use async_trait::async_trait;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use common_utils::ext_traits::AsyncExt;
use common_utils::request::Method;
use diesel_models::routing_algorithm::RoutingAlgorithm;
use error_stack::ResultExt;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use external_services::grpc_client::dynamic_routing::{
    contract_routing_client::ContractBasedDynamicRouting,
    elimination_based_client::EliminationBasedRouting,
    success_rate_client::SuccessBasedDynamicRouting,
};
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use helpers::{
    enable_decision_engine_dynamic_routing_setup, update_decision_engine_dynamic_routing_setup,
};
use hyperswitch_domain_models::{mandates, payment_address};
use payment_methods::helpers::StorageErrorExt;
use rustc_hash::FxHashSet;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use storage_impl::redis::cache;

#[cfg(feature = "payouts")]
use super::payouts;
use super::{
    errors::RouterResult,
    payments::{
        routing::{
            utils::*,
            {self as payments_routing},
        },
        OperationSessionGetters,
    },
};
#[cfg(feature = "v1")]
use crate::utils::ValueExt;
#[cfg(feature = "v2")]
use crate::{core::admin, utils::ValueExt};
use crate::{
    core::{
        errors::{self, CustomResult, RouterResponse},
        metrics, utils as core_utils,
    },
    db::StorageInterface,
    routes::SessionState,
    services::api as service_api,
    types::{
        api, domain,
        storage::{self, enums as storage_enums},
        transformers::{ForeignInto, ForeignTryFrom},
    },
    utils::{self, OptionExt},
};

pub enum TransactionData<'a> {
    Payment(PaymentsDslInput<'a>),
    #[cfg(feature = "payouts")]
    Payout(&'a payouts::PayoutData),
}

#[derive(Clone)]
pub struct PaymentsDslInput<'a> {
    pub setup_mandate: Option<&'a mandates::MandateData>,
    pub payment_attempt: &'a storage::PaymentAttempt,
    pub payment_intent: &'a storage::PaymentIntent,
    pub payment_method_data: Option<&'a domain::PaymentMethodData>,
    pub address: &'a payment_address::PaymentAddress,
    pub recurring_details: Option<&'a mandates_api::RecurringDetails>,
    pub currency: storage_enums::Currency,
}

impl<'a> PaymentsDslInput<'a> {
    pub fn new(
        setup_mandate: Option<&'a mandates::MandateData>,
        payment_attempt: &'a storage::PaymentAttempt,
        payment_intent: &'a storage::PaymentIntent,
        payment_method_data: Option<&'a domain::PaymentMethodData>,
        address: &'a payment_address::PaymentAddress,
        recurring_details: Option<&'a mandates_api::RecurringDetails>,
        currency: storage_enums::Currency,
    ) -> Self {
        Self {
            setup_mandate,
            payment_attempt,
            payment_intent,
            payment_method_data,
            address,
            recurring_details,
            currency,
        }
    }
}

#[cfg(feature = "v2")]
struct RoutingAlgorithmUpdate(RoutingAlgorithm);

#[cfg(feature = "v2")]
impl RoutingAlgorithmUpdate {
    pub fn create_new_routing_algorithm(
        request: &routing_types::RoutingConfigRequest,
        merchant_id: &common_utils::id_type::MerchantId,
        profile_id: common_utils::id_type::ProfileId,
        transaction_type: enums::TransactionType,
    ) -> Self {
        let algorithm_id = common_utils::generate_routing_id_of_default_length();
        let timestamp = common_utils::date_time::now();
        let algo = RoutingAlgorithm {
            algorithm_id,
            profile_id,
            merchant_id: merchant_id.clone(),
            name: request.name.clone(),
            description: Some(request.description.clone()),
            kind: request.algorithm.get_kind().foreign_into(),
            algorithm_data: serde_json::json!(request.algorithm),
            created_at: timestamp,
            modified_at: timestamp,
            algorithm_for: transaction_type,
            decision_engine_routing_id: None,
        };
        Self(algo)
    }
    pub async fn fetch_routing_algo(
        merchant_id: &common_utils::id_type::MerchantId,
        algorithm_id: &common_utils::id_type::RoutingId,
        db: &dyn StorageInterface,
    ) -> RouterResult<Self> {
        let routing_algo = db
            .find_routing_algorithm_by_algorithm_id_merchant_id(algorithm_id, merchant_id)
            .await
            .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
        Ok(Self(routing_algo))
    }
}

pub async fn retrieve_merchant_routing_dictionary(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<common_utils::id_type::ProfileId>>,
    query_params: RoutingRetrieveQuery,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingKind> {
    metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE.add(1, &[]);

    let routing_metadata: Vec<diesel_models::routing_algorithm::RoutingProfileMetadata> = state
        .store
        .list_routing_algorithm_metadata_by_merchant_id_transaction_type(
            merchant_context.get_merchant_account().get_id(),
            &transaction_type,
            i64::from(query_params.limit.unwrap_or_default()),
            i64::from(query_params.offset.unwrap_or_default()),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
    let routing_metadata = super::utils::filter_objects_based_on_profile_id_list(
        profile_id_list.clone(),
        routing_metadata,
    );

    let mut result = routing_metadata
        .into_iter()
        .map(ForeignInto::foreign_into)
        .collect::<Vec<_>>();

    if let Some(profile_ids) = profile_id_list {
        let mut de_result: Vec<routing_types::RoutingDictionaryRecord> = vec![];
        // DE_TODO: need to replace this with batch API call to reduce the number of network calls
        for profile_id in &profile_ids {
            let list_request = ListRountingAlgorithmsRequest {
                created_by: profile_id.get_string_repr().to_string(),
            };
            list_de_euclid_routing_algorithms(&state, list_request)
                .await
                .map_err(|e| {
                    router_env::logger::error!(decision_engine_error=?e, "decision_engine_euclid");
                })
                .ok() // Avoid throwing error if Decision Engine is not available or other errors
                .map(|mut de_routing| de_result.append(&mut de_routing));
            // filter de_result based on transaction type
            de_result.retain(|record| record.algorithm_for == Some(transaction_type));
            // append dynamic routing algorithms to de_result
            de_result.append(
                &mut result
                    .clone()
                    .into_iter()
                    .filter(|record: &routing_types::RoutingDictionaryRecord| {
                        record.kind == routing_types::RoutingAlgorithmKind::Dynamic
                    })
                    .collect::<Vec<_>>(),
            );
        }
        compare_and_log_result(
            de_result.clone(),
            result.clone(),
            "list_routing".to_string(),
        );
        result = build_list_routing_result(
            &state,
            merchant_context,
            &result,
            &de_result,
            profile_ids.clone(),
        )
        .await?;
    }

    metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_types::RoutingKind::RoutingAlgorithm(result),
    ))
}

async fn build_list_routing_result(
    state: &SessionState,
    merchant_context: domain::MerchantContext,
    hs_results: &[routing_types::RoutingDictionaryRecord],
    de_results: &[routing_types::RoutingDictionaryRecord],
    profile_ids: Vec<common_utils::id_type::ProfileId>,
) -> RouterResult<Vec<routing_types::RoutingDictionaryRecord>> {
    let db = state.store.as_ref();
    let key_manager_state = &state.into();
    let mut list_result: Vec<routing_types::RoutingDictionaryRecord> = vec![];
    for profile_id in profile_ids.iter() {
        let by_profile =
            |rec: &&routing_types::RoutingDictionaryRecord| &rec.profile_id == profile_id;
        let de_result_for_profile = de_results.iter().filter(by_profile).cloned().collect();
        let hs_result_for_profile = hs_results.iter().filter(by_profile).cloned().collect();
        let business_profile = core_utils::validate_and_get_business_profile(
            db,
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            Some(profile_id),
            merchant_context.get_merchant_account().get_id(),
        )
        .await?
        .get_required_value("Profile")
        .change_context(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

        list_result.append(
            &mut select_routing_result(
                state,
                &business_profile,
                hs_result_for_profile,
                de_result_for_profile,
            )
            .await,
        );
    }
    Ok(list_result)
}

#[cfg(feature = "v2")]
pub async fn create_routing_algorithm_under_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    request: routing_types::RoutingConfigRequest,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(1, &[]);
    let db = &*state.store;
    let key_manager_state = &(&state).into();

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&request.profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;
    let merchant_id = merchant_context.get_merchant_account().get_id();
    core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?;
    let all_mcas = state
        .store
        .find_merchant_connector_account_by_merchant_id_and_disabled_list(
            key_manager_state,
            merchant_id,
            true,
            merchant_context.get_merchant_key_store(),
        )
        .await
        .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound {
            id: merchant_id.get_string_repr().to_owned(),
        })?;

    let name_mca_id_set = helpers::ConnectNameAndMCAIdForProfile(
        all_mcas.filter_by_profile(business_profile.get_id(), |mca| {
            (&mca.connector_name, mca.get_id())
        }),
    );

    let name_set = helpers::ConnectNameForProfile(
        all_mcas.filter_by_profile(business_profile.get_id(), |mca| &mca.connector_name),
    );

    let algorithm_helper = helpers::RoutingAlgorithmHelpers {
        name_mca_id_set,
        name_set,
        routing_algorithm: &request.algorithm,
    };

    algorithm_helper.validate_connectors_in_routing_config()?;

    let algo = RoutingAlgorithmUpdate::create_new_routing_algorithm(
        &request,
        merchant_context.get_merchant_account().get_id(),
        business_profile.get_id().to_owned(),
        transaction_type,
    );

    let record = state
        .store
        .as_ref()
        .insert_routing_algorithm(algo.0)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let new_record = record.foreign_into();

    metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(new_record))
}

#[cfg(feature = "v1")]
pub async fn create_routing_algorithm_under_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    request: routing_types::RoutingConfigRequest,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    use api_models::routing::StaticRoutingAlgorithm as EuclidAlgorithm;

    metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let name = request
        .name
        .get_required_value("name")
        .change_context(errors::ApiErrorResponse::MissingRequiredField { field_name: "name" })
        .attach_printable("Name of config not given")?;

    let description = request
        .description
        .get_required_value("description")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "description",
        })
        .attach_printable("Description of config not given")?;

    let algorithm = request
        .algorithm
        .clone()
        .get_required_value("algorithm")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "algorithm",
        })
        .attach_printable("Algorithm of config not given")?;

    let algorithm_id = common_utils::generate_routing_id_of_default_length();

    let profile_id = request
        .profile_id
        .get_required_value("profile_id")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "profile_id",
        })
        .attach_printable("Profile_id not provided")?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;

    core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?;

    if algorithm.should_validate_connectors_in_routing_config() {
        helpers::validate_connectors_in_routing_config(
            &state,
            merchant_context.get_merchant_key_store(),
            merchant_context.get_merchant_account().get_id(),
            &profile_id,
            &algorithm,
        )
        .await?;
    }

    let mut decision_engine_routing_id: Option<String> = None;

    if let Some(euclid_algorithm) = request.algorithm.clone() {
        let maybe_static_algorithm: Option<StaticRoutingAlgorithm> = match euclid_algorithm {
            EuclidAlgorithm::Advanced(program) => match program.try_into() {
                Ok(internal_program) => Some(StaticRoutingAlgorithm::Advanced(internal_program)),
                Err(e) => {
                    router_env::logger::error!(decision_engine_error = ?e, "decision_engine_euclid");
                    None
                }
            },
            EuclidAlgorithm::Single(conn) => {
                Some(StaticRoutingAlgorithm::Single(Box::new(conn.into())))
            }
            EuclidAlgorithm::Priority(connectors) => {
                let converted: Vec<ConnectorInfo> =
                    connectors.into_iter().map(Into::into).collect();
                Some(StaticRoutingAlgorithm::Priority(converted))
            }
            EuclidAlgorithm::VolumeSplit(splits) => {
                let converted: Vec<VolumeSplit<ConnectorInfo>> =
                    splits.into_iter().map(Into::into).collect();
                Some(StaticRoutingAlgorithm::VolumeSplit(converted))
            }
            EuclidAlgorithm::ThreeDsDecisionRule(_) => {
                router_env::logger::error!(
                    "decision_engine_euclid: ThreeDsDecisionRules are not yet implemented"
                );
                None
            }
        };

        if let Some(static_algorithm) = maybe_static_algorithm {
            let routing_rule = RoutingRule {
                rule_id: Some(algorithm_id.clone().get_string_repr().to_owned()),
                name: name.clone(),
                description: Some(description.clone()),
                created_by: profile_id.get_string_repr().to_string(),
                algorithm: static_algorithm,
                algorithm_for: transaction_type.into(),
                metadata: Some(RoutingMetadata {
                    kind: algorithm.get_kind().foreign_into(),
                }),
            };

            match create_de_euclid_routing_algo(&state, &routing_rule).await {
                Ok(id) => {
                    decision_engine_routing_id = Some(id);
                }
                Err(e)
                    if matches!(
                        e.current_context(),
                        errors::RoutingError::DecisionEngineValidationError(_)
                    ) =>
                {
                    if let errors::RoutingError::DecisionEngineValidationError(msg) =
                        e.current_context()
                    {
                        router_env::logger::error!(
                            decision_engine_euclid_error = ?msg,
                            decision_engine_euclid_request = ?routing_rule,
                            "failed to create rule in decision_engine with validation error"
                        );
                    }
                }
                Err(e) => {
                    router_env::logger::error!(
                        decision_engine_euclid_error = ?e,
                        decision_engine_euclid_request = ?routing_rule,
                        "failed to create rule in decision_engine"
                    );
                }
            }
        }
    }

    if decision_engine_routing_id.is_some() {
        router_env::logger::info!(routing_flow=?"create_euclid_routing_algorithm", is_equal=?"true", "decision_engine_euclid");
    } else {
        router_env::logger::info!(routing_flow=?"create_euclid_routing_algorithm", is_equal=?"false", "decision_engine_euclid");
    }

    let timestamp = common_utils::date_time::now();
    let algo = RoutingAlgorithm {
        algorithm_id: algorithm_id.clone(),
        profile_id,
        merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
        name: name.clone(),
        description: Some(description.clone()),
        kind: algorithm.get_kind().foreign_into(),
        algorithm_data: serde_json::json!(algorithm),
        created_at: timestamp,
        modified_at: timestamp,
        algorithm_for: transaction_type.to_owned(),
        decision_engine_routing_id,
    };
    let record = db
        .insert_routing_algorithm(algo)
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let new_record = record.foreign_into();

    metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(new_record))
}

#[cfg(feature = "v2")]
pub async fn link_routing_config_under_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    algorithm_id: common_utils::id_type::RoutingId,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_LINK_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let routing_algorithm = RoutingAlgorithmUpdate::fetch_routing_algo(
        merchant_context.get_merchant_account().get_id(),
        &algorithm_id,
        db,
    )
    .await?;

    utils::when(routing_algorithm.0.profile_id != profile_id, || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: "Profile Id is invalid for the routing config".to_string(),
        })
    })?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;

    utils::when(
        routing_algorithm.0.algorithm_for != *transaction_type,
        || {
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: format!(
                    "Cannot use {}'s routing algorithm for {} operation",
                    routing_algorithm.0.algorithm_for, transaction_type
                ),
            })
        },
    )?;

    utils::when(
        business_profile.routing_algorithm_id == Some(algorithm_id.clone())
            || business_profile.payout_routing_algorithm_id == Some(algorithm_id.clone()),
        || {
            Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "Algorithm is already active".to_string(),
            })
        },
    )?;
    admin::ProfileWrapper::new(business_profile)
        .update_profile_and_invalidate_routing_config_for_active_algorithm_id_update(
            db,
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            algorithm_id,
            transaction_type,
        )
        .await?;

    metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_algorithm.0.foreign_into(),
    ))
}

#[cfg(feature = "v1")]
pub async fn link_routing_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    algorithm_id: common_utils::id_type::RoutingId,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_LINK_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let routing_algorithm = db
        .find_routing_algorithm_by_algorithm_id_merchant_id(
            &algorithm_id,
            merchant_context.get_merchant_account().get_id(),
        )
        .await
        .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&routing_algorithm.profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ProfileNotFound {
        id: routing_algorithm.profile_id.get_string_repr().to_owned(),
    })?;

    core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?;

    match routing_algorithm.kind {
        diesel_models::enums::RoutingAlgorithmKind::Dynamic => {
            let mut dynamic_routing_ref: routing_types::DynamicRoutingAlgorithmRef =
                business_profile
                    .dynamic_routing_algorithm
                    .clone()
                    .map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "unable to deserialize Dynamic routing algorithm ref from business profile",
                    )?
                    .unwrap_or_default();

            utils::when(
                matches!(
                    dynamic_routing_ref.success_based_algorithm,
                    Some(routing::SuccessBasedAlgorithm {
                        algorithm_id_with_timestamp:
                        routing_types::DynamicAlgorithmWithTimestamp {
                            algorithm_id: Some(ref id),
                            timestamp: _
                        },
                        enabled_feature: _
                    }) if id == &algorithm_id
                ) || matches!(
                    dynamic_routing_ref.elimination_routing_algorithm,
                    Some(routing::EliminationRoutingAlgorithm {
                        algorithm_id_with_timestamp:
                        routing_types::DynamicAlgorithmWithTimestamp {
                            algorithm_id: Some(ref id),
                            timestamp: _
                        },
                        enabled_feature: _
                    }) if id == &algorithm_id
                ) || matches!(
                    dynamic_routing_ref.contract_based_routing,
                    Some(routing::ContractRoutingAlgorithm {
                        algorithm_id_with_timestamp:
                        routing_types::DynamicAlgorithmWithTimestamp {
                            algorithm_id: Some(ref id),
                            timestamp: _
                        },
                        enabled_feature: _
                    }) if id == &algorithm_id
                ),
                || {
                    Err(errors::ApiErrorResponse::PreconditionFailed {
                        message: "Algorithm is already active".to_string(),
                    })
                },
            )?;

            if routing_algorithm.name == helpers::SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM {
                dynamic_routing_ref.update_algorithm_id(
                algorithm_id,
                dynamic_routing_ref
                    .success_based_algorithm
                    .clone()
                    .ok_or(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "missing success_based_algorithm in dynamic_algorithm_ref from business_profile table",
                    )?
                    .enabled_feature,
                routing_types::DynamicRoutingType::SuccessRateBasedRouting,
            );

                // Call to DE here to update SR configs
                #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
                {
                    if state.conf.open_router.dynamic_routing_enabled {
                        let existing_config = helpers::get_decision_engine_active_dynamic_routing_algorithm(
                        &state,
                        business_profile.get_id(),
                        api_models::open_router::DecisionEngineDynamicAlgorithmType::SuccessRate,
                    )
                    .await;

                        if let Ok(Some(_config)) = existing_config {
                            update_decision_engine_dynamic_routing_setup(
                            &state,
                            business_profile.get_id(),
                            routing_algorithm.algorithm_data.clone(),
                            routing_types::DynamicRoutingType::SuccessRateBasedRouting,
                            &mut dynamic_routing_ref,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable(
                            "Failed to update the success rate routing config in Decision Engine",
                        )?;
                        } else {
                            let data: routing_types::SuccessBasedRoutingConfig =
                            routing_algorithm.algorithm_data
                                .clone()
                                .parse_value("SuccessBasedRoutingConfig")
                                .change_context(errors::ApiErrorResponse::InternalServerError)
                                .attach_printable(
                                    "unable to deserialize SuccessBasedRoutingConfig from routing algorithm data",
                                )?;

                            enable_decision_engine_dynamic_routing_setup(
                            &state,
                            business_profile.get_id(),
                            routing_types::DynamicRoutingType::SuccessRateBasedRouting,
                            &mut dynamic_routing_ref,
                            Some(routing_types::DynamicRoutingPayload::SuccessBasedRoutingPayload(data)),
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::InternalServerError)
                        .attach_printable("Unable to setup decision engine dynamic routing")?;
                        }
                    }
                }
            } else if routing_algorithm.name == helpers::ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM
            {
                dynamic_routing_ref.update_algorithm_id(
                algorithm_id,
                dynamic_routing_ref
                    .elimination_routing_algorithm
                    .clone()
                    .ok_or(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "missing elimination_routing_algorithm in dynamic_algorithm_ref from business_profile table",
                    )?
                    .enabled_feature,
                routing_types::DynamicRoutingType::EliminationRouting,
            );
                #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
                {
                    if state.conf.open_router.dynamic_routing_enabled {
                        let existing_config = helpers::get_decision_engine_active_dynamic_routing_algorithm(
                            &state,
                            business_profile.get_id(),
                            api_models::open_router::DecisionEngineDynamicAlgorithmType::Elimination,
                        )
                        .await;

                        if let Ok(Some(_config)) = existing_config {
                            update_decision_engine_dynamic_routing_setup(
                                &state,
                                business_profile.get_id(),
                                routing_algorithm.algorithm_data.clone(),
                                routing_types::DynamicRoutingType::EliminationRouting,
                                &mut dynamic_routing_ref,
                            )
                            .await
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable(
                                "Failed to update the elimination routing config in Decision Engine",
                            )?;
                        } else {
                            let data: routing_types::EliminationRoutingConfig =
                                routing_algorithm.algorithm_data
                                    .clone()
                                    .parse_value("EliminationRoutingConfig")
                                    .change_context(errors::ApiErrorResponse::InternalServerError)
                                    .attach_printable(
                                        "unable to deserialize EliminationRoutingConfig from routing algorithm data",
                                    )?;

                            enable_decision_engine_dynamic_routing_setup(
                                &state,
                                business_profile.get_id(),
                                routing_types::DynamicRoutingType::EliminationRouting,
                                &mut dynamic_routing_ref,
                                Some(
                                    routing_types::DynamicRoutingPayload::EliminationRoutingPayload(
                                        data,
                                    ),
                                ),
                            )
                            .await
                            .change_context(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable("Unable to setup decision engine dynamic routing")?;
                        }
                    }
                }
            } else if routing_algorithm.name == helpers::CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM {
                dynamic_routing_ref.update_algorithm_id(
                algorithm_id,
                dynamic_routing_ref
                    .contract_based_routing
                    .clone()
                    .ok_or(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable(
                        "missing contract_based_routing in dynamic_algorithm_ref from business_profile table",
                    )?
                    .enabled_feature,
                routing_types::DynamicRoutingType::ContractBasedRouting,
            );
            }

            helpers::update_business_profile_active_dynamic_algorithm_ref(
                db,
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                business_profile.clone(),
                dynamic_routing_ref,
            )
            .await?;
        }
        diesel_models::enums::RoutingAlgorithmKind::Single
        | diesel_models::enums::RoutingAlgorithmKind::Priority
        | diesel_models::enums::RoutingAlgorithmKind::Advanced
        | diesel_models::enums::RoutingAlgorithmKind::VolumeSplit
        | diesel_models::enums::RoutingAlgorithmKind::ThreeDsDecisionRule => {
            let mut routing_ref: routing_types::RoutingAlgorithmRef = business_profile
                .routing_algorithm
                .clone()
                .map(|val| val.parse_value("RoutingAlgorithmRef"))
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable(
                    "unable to deserialize routing algorithm ref from business profile",
                )?
                .unwrap_or_default();

            utils::when(routing_algorithm.algorithm_for != transaction_type, || {
                Err(errors::ApiErrorResponse::PreconditionFailed {
                    message: format!(
                        "Cannot use {}'s routing algorithm for {} operation",
                        routing_algorithm.algorithm_for, transaction_type
                    ),
                })
            })?;

            utils::when(
                routing_ref.algorithm_id == Some(algorithm_id.clone()),
                || {
                    Err(errors::ApiErrorResponse::PreconditionFailed {
                        message: "Algorithm is already active".to_string(),
                    })
                },
            )?;
            routing_ref.update_algorithm_id(algorithm_id);
            helpers::update_profile_active_algorithm_ref(
                db,
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                business_profile.clone(),
                routing_ref,
                &transaction_type,
            )
            .await?;
        }
    };
    if let Some(euclid_routing_id) = routing_algorithm.decision_engine_routing_id.clone() {
        let routing_algo = ActivateRoutingConfigRequest {
            created_by: business_profile.get_id().get_string_repr().to_string(),
            routing_algorithm_id: euclid_routing_id,
        };
        let link_result = link_de_euclid_routing_algorithm(&state, routing_algo).await;
        match link_result {
            Ok(_) => {
                router_env::logger::info!(
                    routing_flow=?"link_routing_algorithm",
                    is_equal=?true,
                    "decision_engine_euclid"
                );
            }
            Err(e) => {
                router_env::logger::info!(
                    routing_flow=?"link_routing_algorithm",
                    is_equal=?false,
                    error=?e,
                    "decision_engine_euclid"
                );
            }
        }
    }
    metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(
        routing_algorithm.foreign_into(),
    ))
}

#[cfg(feature = "v2")]
pub async fn retrieve_routing_algorithm_from_algorithm_id(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    algorithm_id: common_utils::id_type::RoutingId,
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
    metrics::ROUTING_RETRIEVE_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let routing_algorithm = RoutingAlgorithmUpdate::fetch_routing_algo(
        merchant_context.get_merchant_account().get_id(),
        &algorithm_id,
        db,
    )
    .await?;
    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&routing_algorithm.0.profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;

    core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?;

    let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm.0)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to parse routing algorithm")?;

    metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(response))
}

#[cfg(feature = "v1")]
pub async fn retrieve_routing_algorithm_from_algorithm_id(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    algorithm_id: common_utils::id_type::RoutingId,
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
    metrics::ROUTING_RETRIEVE_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let routing_algorithm = db
        .find_routing_algorithm_by_algorithm_id_merchant_id(
            &algorithm_id,
            merchant_context.get_merchant_account().get_id(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&routing_algorithm.profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")
    .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;

    core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?;

    let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm)
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("unable to parse routing algorithm")?;

    metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
    Ok(service_api::ApplicationResponse::Json(response))
}

#[cfg(feature = "v2")]
pub async fn unlink_routing_config_under_profile(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: common_utils::id_type::ProfileId,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UNLINK_CONFIG.add(1, &[]);
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?
    .get_required_value("Profile")?;

    let routing_algo_id = match transaction_type {
        enums::TransactionType::Payment => business_profile.routing_algorithm_id.clone(),
        #[cfg(feature = "payouts")]
        enums::TransactionType::Payout => business_profile.payout_routing_algorithm_id.clone(),
        // TODO: Handle ThreeDsAuthentication Transaction Type for Three DS Decision Rule Algorithm configuration
        enums::TransactionType::ThreeDsAuthentication => todo!(),
    };

    if let Some(algorithm_id) = routing_algo_id {
        let record = RoutingAlgorithmUpdate::fetch_routing_algo(
            merchant_context.get_merchant_account().get_id(),
            &algorithm_id,
            db,
        )
        .await?;
        let response = record.0.foreign_into();
        admin::ProfileWrapper::new(business_profile)
            .update_profile_and_invalidate_routing_config_for_active_algorithm_id_update(
                db,
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                algorithm_id,
                transaction_type,
            )
            .await?;
        metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
        Ok(service_api::ApplicationResponse::Json(response))
    } else {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: "Algorithm is already inactive".to_string(),
        })?
    }
}

#[cfg(feature = "v1")]
pub async fn unlink_routing_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    request: routing_types::RoutingConfigRequest,
    authentication_profile_id: Option<common_utils::id_type::ProfileId>,
    transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
    metrics::ROUTING_UNLINK_CONFIG.add(1, &[]);

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

    let profile_id = request
        .profile_id
        .get_required_value("profile_id")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "profile_id",
        })
        .attach_printable("Profile_id not provided")?;

    let business_profile = core_utils::validate_and_get_business_profile(
        db,
        key_manager_state,
        merchant_context.get_merchant_key_store(),
        Some(&profile_id),
        merchant_context.get_merchant_account().get_id(),
    )
    .await?;

    match business_profile {
        Some(business_profile) => {
            core_utils::validate_profile_id_from_auth_layer(
                authentication_profile_id,
                &business_profile,
            )?;
            let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
                enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
                #[cfg(feature = "payouts")]
                enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
                enums::TransactionType::ThreeDsAuthentication => {
                    business_profile.three_ds_decision_rule_algorithm.clone()
                }
            }
            .map(|val| val.parse_value("RoutingAlgorithmRef"))
            .transpose()


impl BusinessGenericLinkConfig {
    pub fn validate(&self) -> Result<(), &str> {
        // Validate host domain name
        let host_domain_valid = self
            .domain_name
            .clone()
            .map(|host_domain| link_utils::validate_strict_domain(&host_domain))
            .unwrap_or(true);
        if !host_domain_valid {
            return Err("Invalid host domain name received in payout_link_config");
        }

        let are_allowed_domains_valid = self
            .allowed_domains
            .clone()
            .iter()
            .all(|allowed_domain| link_utils::validate_wildcard_domain(allowed_domain));
        if !are_allowed_domains_valid {
            return Err("Invalid allowed domain names received in payout_link_config");
        }

        Ok(())
    }
}