Bug: [FEATURE] Introduce routing in V2



- Implement rule based profile level activation ,  straight through routing and default fallback in V2 apis.
diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs
index d200d352fa4..ae2710c50e9 100644
--- a/crates/diesel_models/src/payment_intent.rs
+++ b/crates/diesel_models/src/payment_intent.rs
@@ -346,6 +346,7 @@ pub struct PaymentIntentNew {
     pub force_3ds_challenge: Option<bool>,
     pub force_3ds_challenge_trigger: Option<bool>,
     pub processor_merchant_id: Option<common_utils::id_type::MerchantId>,
+    pub routing_algorithm_id: Option<common_utils::id_type::RoutingId>,
     pub created_by: Option<String>,
     pub is_iframe_redirection_enabled: Option<bool>,
 }
diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs
index 44b75e6e647..5b1201027b7 100644
--- a/crates/diesel_models/src/schema_v2.rs
+++ b/crates/diesel_models/src/schema_v2.rs
@@ -1344,6 +1344,8 @@ diesel::table! {
         created_at -> Timestamp,
         modified_at -> Timestamp,
         algorithm_for -> TransactionType,
+        #[max_length = 64]
+        decision_engine_routing_id -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs
index 8e5de204fba..992bd58784f 100644
--- a/crates/hyperswitch_domain_models/src/business_profile.rs
+++ b/crates/hyperswitch_domain_models/src/business_profile.rs
@@ -1892,20 +1892,19 @@ impl super::behaviour::Conversion for Profile {
                 authentication_product_ids: item.authentication_product_ids,
                 three_ds_decision_manager_config: item.three_ds_decision_manager_config,
                 card_testing_guard_config: item.card_testing_guard_config,
-                card_testing_secret_key: item
-                    .card_testing_secret_key
-                    .async_lift(|inner| async {
-                        crypto_operation(
-                            state,
-                            type_name!(Self::DstType),
-                            CryptoOperation::DecryptOptional(inner),
-                            key_manager_identifier.clone(),
-                            key.peek(),
-                        )
-                        .await
-                        .and_then(|val| val.try_into_optionaloperation())
-                    })
-                    .await?,
+                card_testing_secret_key: match item.card_testing_secret_key {
+                    Some(encrypted_value) => crypto_operation(
+                        state,
+                        type_name!(Self::DstType),
+                        CryptoOperation::DecryptOptional(Some(encrypted_value)),
+                        key_manager_identifier.clone(),
+                        key.peek(),
+                    )
+                    .await
+                    .and_then(|val| val.try_into_optionaloperation())
+                    .unwrap_or_default(),
+                    None => None,
+                },
                 is_clear_pan_retries_enabled: item.is_clear_pan_retries_enabled,
                 is_debit_routing_enabled: item.is_debit_routing_enabled,
                 merchant_business_country: item.merchant_business_country,
diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs
index 0abc626bd19..2240df7241f 100644
--- a/crates/hyperswitch_domain_models/src/payment_method_data.rs
+++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs
@@ -1,3 +1,6 @@
+#[cfg(feature = "v2")]
+use std::str::FromStr;
+
 use api_models::{
     mandates,
     payment_methods::{self},
@@ -1904,6 +1907,7 @@ pub enum PaymentMethodsData {
 }
 
 impl PaymentMethodsData {
+    #[cfg(feature = "v1")]
     pub fn get_co_badged_card_data(&self) -> Option<payment_methods::CoBadgedCardData> {
         if let Self::Card(card) = self {
             card.co_badged_card_data.clone()
@@ -1911,6 +1915,10 @@ impl PaymentMethodsData {
             None
         }
     }
+    #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+    pub fn get_co_badged_card_data(&self) -> Option<payment_methods::CoBadgedCardData> {
+        todo!()
+    }
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -1933,6 +1941,10 @@ fn saved_in_locker_default() -> bool {
     true
 }
 
+#[cfg(all(
+    any(feature = "v1", feature = "v2"),
+    not(feature = "payment_methods_v2")
+))]
 #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
 pub struct CardDetailsPaymentMethod {
     pub last4_digits: Option<String>,
@@ -1950,6 +1962,54 @@ pub struct CardDetailsPaymentMethod {
     pub co_badged_card_data: Option<payment_methods::CoBadgedCardData>,
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct CardDetailsPaymentMethod {
+    pub last4_digits: Option<String>,
+    pub issuer_country: Option<String>,
+    pub expiry_month: Option<Secret<String>>,
+    pub expiry_year: Option<Secret<String>>,
+    pub nick_name: Option<Secret<String>>,
+    pub card_holder_name: Option<Secret<String>>,
+    pub card_isin: Option<String>,
+    pub card_issuer: Option<String>,
+    pub card_network: Option<api_enums::CardNetwork>,
+    pub card_type: Option<String>,
+    #[serde(default = "saved_in_locker_default")]
+    pub saved_to_locker: bool,
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl CardDetailsPaymentMethod {
+    pub fn to_card_details_from_locker(self) -> payment_methods::CardDetailFromLocker {
+        payment_methods::CardDetailFromLocker {
+            card_number: None,
+            card_holder_name: self.card_holder_name.clone(),
+            card_issuer: self.card_issuer.clone(),
+            card_network: self.card_network.clone(),
+            card_type: self.card_type.clone(),
+            issuer_country: self.clone().get_issuer_country_alpha2(),
+            last4_digits: self.last4_digits,
+            expiry_month: self.expiry_month,
+            expiry_year: self.expiry_year,
+            card_fingerprint: None,
+            nick_name: self.nick_name,
+            card_isin: self.card_isin,
+            saved_to_locker: self.saved_to_locker,
+        }
+    }
+
+    pub fn get_issuer_country_alpha2(self) -> Option<common_enums::CountryAlpha2> {
+        self.issuer_country
+            .as_ref()
+            .map(|c| api_enums::CountryAlpha2::from_str(c))
+            .transpose()
+            .ok()
+            .flatten()
+    }
+}
+
+#[cfg(feature = "v1")]
 impl From<payment_methods::CardDetail> for CardDetailsPaymentMethod {
     fn from(item: payment_methods::CardDetail) -> Self {
         Self {
@@ -1969,6 +2029,25 @@ impl From<payment_methods::CardDetail> for CardDetailsPaymentMethod {
     }
 }
 
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+impl From<payment_methods::CardDetail> for CardDetailsPaymentMethod {
+    fn from(item: payment_methods::CardDetail) -> Self {
+        Self {
+            issuer_country: item.card_issuing_country.map(|c| c.to_string()),
+            last4_digits: Some(item.card_number.get_last4()),
+            expiry_month: Some(item.card_exp_month),
+            expiry_year: Some(item.card_exp_year),
+            card_holder_name: item.card_holder_name,
+            nick_name: item.nick_name,
+            card_isin: None,
+            card_issuer: item.card_issuer,
+            card_network: item.card_network,
+            card_type: item.card_type.map(|card| card.to_string()),
+            saved_to_locker: true,
+        }
+    }
+}
+
 #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
 impl From<NetworkTokenDetails> for NetworkTokenDetailsPaymentMethod {
     fn from(item: NetworkTokenDetails) -> Self {
diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
index 8df0ff705e7..c08a8130266 100644
--- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
+++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs
@@ -1936,6 +1936,7 @@ impl behaviour::Conversion for PaymentIntent {
             processor_merchant_id: Some(self.processor_merchant_id),
             created_by: self.created_by.map(|cb| cb.to_string()),
             is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
+            routing_algorithm_id: self.routing_algorithm_id,
         })
     }
 }
diff --git a/crates/hyperswitch_domain_models/src/routing.rs b/crates/hyperswitch_domain_models/src/routing.rs
index 4a802250630..baa2d45ab4e 100644
--- a/crates/hyperswitch_domain_models/src/routing.rs
+++ b/crates/hyperswitch_domain_models/src/routing.rs
@@ -1,18 +1,32 @@
 use std::collections::HashMap;
 
 use api_models::{enums as api_enums, routing};
+use common_utils::id_type;
 use serde;
 
+#[cfg(feature = "v1")]
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct RoutingData {
     pub routed_through: Option<String>,
 
-    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
+    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
 
     pub routing_info: PaymentRoutingInfo,
     pub algorithm: Option<routing::StraightThroughAlgorithm>,
 }
 
+#[cfg(feature = "v2")]
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+pub struct RoutingData {
+    // TODO: change this to RoutableConnectors enum
+    pub routed_through: Option<String>,
+    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,
+
+    pub pre_routing_connector_choice: Option<PreRoutingConnectorChoice>,
+
+    pub algorithm_requested: Option<id_type::RoutingId>,
+}
+
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
 #[serde(from = "PaymentRoutingInfoSerde", into = "PaymentRoutingInfoSerde")]
 pub struct PaymentRoutingInfo {
diff --git a/crates/payment_methods/Cargo.toml b/crates/payment_methods/Cargo.toml
index 26572c9e32f..73f3fe8ef96 100644
--- a/crates/payment_methods/Cargo.toml
+++ b/crates/payment_methods/Cargo.toml
@@ -35,7 +35,7 @@ workspace = true
 
 [features]
 customer_v2 = ["api_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"]
-default = ["dummy_connector", "payouts", "v1"]
+default = ["dummy_connector", "payouts"]
 v1 = ["hyperswitch_domain_models/v1", "storage_impl/v1", "common_utils/v1", "scheduler/v1", "common_types/v1"]
 v2 = ["customer_v2", "payment_methods_v2", "common_utils/v2", "scheduler/v2", "common_types/v2"]
 payment_methods_v2 = [ "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"]
diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs
index 7e13dbbed16..96ac030f57f 100644
--- a/crates/router/src/core/payment_methods.rs
+++ b/crates/router/src/core/payment_methods.rs
@@ -1889,7 +1889,8 @@ pub async fn vault_payment_method_external(
 
     let connector_name = merchant_connector_account
         .get_connector_name()
-        .unwrap_or_default(); // always get the connector name from this call
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Connector name not present for external vault")?; // always get the connector name from this call
 
     let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
         &state.conf.connectors,
diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs
index c545bcac503..911b4ece00e 100644
--- a/crates/router/src/core/payment_methods/vault.rs
+++ b/crates/router/src/core/payment_methods/vault.rs
@@ -1409,7 +1409,8 @@ pub async fn retrieve_payment_method_from_vault_external(
 
     let connector_name = merchant_connector_account
         .get_connector_name()
-        .unwrap_or_default(); // always get the connector name from this call
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Connector name not present for external vault")?; // always get the connector name from this call
 
     let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
         &state.conf.connectors,
@@ -1569,7 +1570,8 @@ pub async fn delete_payment_method_data_from_vault_external(
 
     let connector_name = merchant_connector_account
         .get_connector_name()
-        .unwrap_or_default(); // always get the connector name from this call
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Connector name not present for external vault")?; // always get the connector name from this call
 
     let connector_data = api::ConnectorData::get_external_vault_connector_by_name(
         &state.conf.connectors,
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 0993a6eee3f..38a53d9b2d1 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -241,7 +241,55 @@ where
                 )
                 .await?
         }
-        ConnectorCallType::Retryable(vec) => todo!(),
+        ConnectorCallType::Retryable(connectors) => {
+            let mut connectors = connectors.clone().into_iter();
+            let connector_data = get_connector_data(&mut connectors)?;
+            let router_data = call_connector_service(
+                state,
+                req_state.clone(),
+                &merchant_context,
+                connector_data.connector_data.clone(),
+                &operation,
+                &mut payment_data,
+                &customer,
+                call_connector_action.clone(),
+                None,
+                header_payload.clone(),
+                #[cfg(feature = "frm")]
+                None,
+                #[cfg(not(feature = "frm"))]
+                None,
+                profile,
+                false,
+                false, //should_retry_with_pan is set to false in case of PreDetermined ConnectorCallType
+                req.get_all_keys_required(),
+            )
+            .await?;
+
+            let payments_response_operation = Box::new(PaymentResponse);
+
+            payments_response_operation
+                .to_post_update_tracker()?
+                .save_pm_and_mandate(
+                    state,
+                    &router_data,
+                    &merchant_context,
+                    &mut payment_data,
+                    profile,
+                )
+                .await?;
+
+            payments_response_operation
+                .to_post_update_tracker()?
+                .update_tracker(
+                    state,
+                    payment_data,
+                    router_data,
+                    merchant_context.get_merchant_key_store(),
+                    merchant_context.get_merchant_account().storage_scheme,
+                )
+                .await?
+        }
         ConnectorCallType::SessionMultiple(vec) => todo!(),
         ConnectorCallType::Skip => payment_data,
     };
@@ -6574,23 +6622,167 @@ where
     Ok(decided_connector)
 }
 
-#[allow(clippy::too_many_arguments)]
 #[cfg(feature = "v2")]
-pub async fn decide_connector<F, D>(
-    state: SessionState,
+#[allow(clippy::too_many_arguments)]
+pub async fn connector_selection<F, D>(
+    state: &SessionState,
     merchant_context: &domain::MerchantContext,
     business_profile: &domain::Profile,
     payment_data: &mut D,
-    request_straight_through: Option<api::routing::StraightThroughAlgorithm>,
-    routing_data: &mut storage::RoutingData,
-    eligible_connectors: Option<Vec<enums::RoutableConnectors>>,
     mandate_type: Option<api::MandateTransactionType>,
 ) -> RouterResult<ConnectorCallType>
 where
     F: Send + Clone,
     D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
 {
-    todo!()
+    let mut routing_data = storage::RoutingData {
+        routed_through: payment_data.get_payment_attempt().connector.clone(),
+
+        merchant_connector_id: payment_data
+            .get_payment_attempt()
+            .merchant_connector_id
+            .clone(),
+        pre_routing_connector_choice: payment_data.get_pre_routing_result().and_then(
+            |pre_routing_results| {
+                pre_routing_results
+                    .get(&payment_data.get_payment_attempt().payment_method_subtype)
+                    .cloned()
+            },
+        ),
+
+        algorithm_requested: payment_data
+            .get_payment_intent()
+            .routing_algorithm_id
+            .clone(),
+    };
+
+    let payment_dsl_input = core_routing::PaymentsDslInput::new(
+        None,
+        payment_data.get_payment_attempt(),
+        payment_data.get_payment_intent(),
+        payment_data.get_payment_method_data(),
+        payment_data.get_address(),
+        None,
+        payment_data.get_currency(),
+    );
+
+    let decided_connector = decide_connector(
+        state.clone(),
+        merchant_context,
+        business_profile,
+        &mut routing_data,
+        payment_dsl_input,
+        mandate_type,
+    )
+    .await?;
+
+    payment_data.set_connector_in_payment_attempt(routing_data.routed_through);
+
+    payment_data.set_merchant_connector_id_in_attempt(routing_data.merchant_connector_id);
+
+    Ok(decided_connector)
+}
+
+#[allow(clippy::too_many_arguments)]
+#[cfg(feature = "v2")]
+pub async fn decide_connector(
+    state: SessionState,
+    merchant_context: &domain::MerchantContext,
+    business_profile: &domain::Profile,
+    routing_data: &mut storage::RoutingData,
+    payment_dsl_input: core_routing::PaymentsDslInput<'_>,
+    mandate_type: Option<api::MandateTransactionType>,
+) -> RouterResult<ConnectorCallType> {
+    // If the connector was already decided previously, use the same connector
+    // This is in case of flows like payments_sync, payments_cancel where the successive operations
+    // with the connector have to be made using the same connector account.
+
+    let predetermined_info_cloned = routing_data
+        .routed_through
+        .as_ref()
+        .zip(routing_data.merchant_connector_id.as_ref())
+        .map(|(cn_ref, mci_ref)| (cn_ref.clone(), mci_ref.clone()));
+
+    match (
+        predetermined_info_cloned,
+        routing_data.pre_routing_connector_choice.as_ref(),
+    ) {
+        // Condition 1: Connector was already decided previously
+        (Some((owned_connector_name, owned_merchant_connector_id)), _) => {
+            api::ConnectorData::get_connector_by_name(
+                &state.conf.connectors,
+                &owned_connector_name,
+                api::GetToken::Connector,
+                Some(owned_merchant_connector_id.clone()),
+            )
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Invalid connector name received in 'routed_through'")
+            .map(|connector_data| {
+                routing_data.routed_through = Some(owned_connector_name);
+                ConnectorCallType::PreDetermined(connector_data.into())
+            })
+        }
+        // Condition 2: Pre-routing connector choice
+        (None, Some(routable_connector_choice)) => {
+            let routable_connector_list = match routable_connector_choice {
+                storage::PreRoutingConnectorChoice::Single(routable_connector) => {
+                    vec![routable_connector.clone()]
+                }
+                storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => {
+                    routable_connector_list.clone()
+                }
+            };
+
+            routable_connector_list
+                .first()
+                .ok_or_else(|| {
+                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
+                        .attach_printable("No first routable connector in pre_routing_connector_choice")
+                })
+                .and_then(|first_routable_connector| {
+                    routing_data.routed_through = Some(first_routable_connector.connector.to_string());
+                    routing_data
+                        .merchant_connector_id
+                        .clone_from(&first_routable_connector.merchant_connector_id);
+
+                    let pre_routing_connector_data_list_result: RouterResult<Vec<api::ConnectorData>> = routable_connector_list
+                        .iter()
+                        .map(|connector_choice| {
+                            api::ConnectorData::get_connector_by_name(
+                                &state.conf.connectors,
+                                &connector_choice.connector.to_string(),
+                                api::GetToken::Connector,
+                                connector_choice.merchant_connector_id.clone(),
+                            )
+                            .change_context(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable("Invalid connector name received while processing pre_routing_connector_choice")
+                        })
+                        .collect::<Result<Vec<_>, _>>(); // Collects into RouterResult<Vec<ConnectorData>>
+
+                    pre_routing_connector_data_list_result
+                        .and_then(|list| {
+                            list.first()
+                                .cloned()
+                                .ok_or_else(|| {
+                                    report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
+                                        .attach_printable("Empty pre_routing_connector_data_list after mapping")
+                                })
+                                .map(|first_data| ConnectorCallType::PreDetermined(first_data.into()))
+                        })
+                })
+        }
+        (None, None) => {
+            route_connector_v2_for_payments(
+                &state,
+                merchant_context,
+                business_profile,
+                payment_dsl_input,
+                routing_data,
+                mandate_type,
+            )
+            .await
+        }
+    }
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -6837,21 +7029,6 @@ where
     .await
 }
 
-#[cfg(feature = "v2")]
-pub async fn decide_multiplex_connector_for_normal_or_recurring_payment<F: Clone, D>(
-    state: &SessionState,
-    payment_data: &mut D,
-    routing_data: &mut storage::RoutingData,
-    connectors: Vec<api::ConnectorData>,
-    mandate_type: Option<api::MandateTransactionType>,
-    is_connector_agnostic_mit_enabled: Option<bool>,
-) -> RouterResult<ConnectorCallType>
-where
-    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
-{
-    todo!()
-}
-
 #[cfg(feature = "v1")]
 #[allow(clippy::too_many_arguments)]
 pub async fn decide_multiplex_connector_for_normal_or_recurring_payment<F: Clone, D>(
@@ -7005,7 +7182,6 @@ where
                 Err(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration.into())
             }
         }
-
         _ => {
             helpers::override_setup_future_usage_to_on_session(&*state.store, payment_data).await?;
 
@@ -7025,22 +7201,6 @@ where
     }
 }
 
-#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
-#[allow(clippy::too_many_arguments)]
-pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
-    state: &SessionState,
-    payment_data: &mut D,
-    routing_data: &mut storage::RoutingData,
-    connectors: Vec<api::ConnectorData>,
-    is_connector_agnostic_mit_enabled: Option<bool>,
-    payment_method_info: &domain::PaymentMethod,
-) -> RouterResult<ConnectorCallType>
-where
-    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
-{
-    todo!()
-}
-
 #[cfg(all(
     any(feature = "v2", feature = "v1"),
     not(feature = "payment_methods_v2")
@@ -7400,6 +7560,60 @@ where
     })
 }
 
+#[cfg(feature = "v2")]
+#[allow(clippy::too_many_arguments)]
+pub async fn route_connector_v2_for_payments(
+    state: &SessionState,
+    merchant_context: &domain::MerchantContext,
+    business_profile: &domain::Profile,
+    transaction_data: core_routing::PaymentsDslInput<'_>,
+    routing_data: &mut storage::RoutingData,
+    _mandate_type: Option<api::MandateTransactionType>,
+) -> RouterResult<ConnectorCallType> {
+    let routing_algorithm_id = routing_data
+        .algorithm_requested
+        .as_ref()
+        .or(business_profile.routing_algorithm_id.as_ref());
+
+    let connectors = routing::perform_static_routing_v1(
+        state,
+        merchant_context.get_merchant_account().get_id(),
+        routing_algorithm_id,
+        business_profile,
+        &TransactionData::Payment(transaction_data.clone()),
+    )
+    .await
+    .change_context(errors::ApiErrorResponse::InternalServerError)?;
+
+    let connectors = routing::perform_eligibility_analysis_with_fallback(
+        &state.clone(),
+        merchant_context.get_merchant_key_store(),
+        connectors,
+        &TransactionData::Payment(transaction_data),
+        None,
+        business_profile,
+    )
+    .await
+    .change_context(errors::ApiErrorResponse::InternalServerError)
+    .attach_printable("failed eligibility analysis and fallback")?;
+
+    connectors
+        .first()
+        .map(|conn| {
+            routing_data.routed_through = Some(conn.connector.to_string());
+            routing_data.merchant_connector_id = conn.merchant_connector_id.clone();
+            api::ConnectorData::get_connector_by_name(
+                &state.conf.connectors,
+                &conn.connector.to_string(),
+                api::GetToken::Connector,
+                conn.merchant_connector_id.clone(),
+            )
+        })
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Invalid connector name received")?
+        .map(|connector_data| ConnectorCallType::PreDetermined(connector_data.into()))
+}
+
 #[cfg(feature = "v1")]
 #[allow(clippy::too_many_arguments)]
 pub async fn route_connector_v1_for_payments<F, D>(
@@ -8259,6 +8473,11 @@ pub trait OperationSessionGetters<F> {
 
     #[cfg(feature = "v2")]
     fn get_optional_payment_attempt(&self) -> Option<&storage::PaymentAttempt>;
+
+    #[cfg(feature = "v2")]
+    fn get_pre_routing_result(
+        &self,
+    ) -> Option<HashMap<enums::PaymentMethodType, domain::PreRoutingConnectorChoice>>;
 }
 
 pub trait OperationSessionSetters<F> {
@@ -8307,6 +8526,13 @@ pub trait OperationSessionSetters<F> {
         &mut self,
         straight_through_algorithm: serde_json::Value,
     );
+
+    #[cfg(feature = "v2")]
+    fn set_prerouting_algorithm_in_payment_intent(
+        &mut self,
+        straight_through_algorithm: storage::PaymentRoutingInfo,
+    );
+
     fn set_connector_in_payment_attempt(&mut self, connector: Option<String>);
 
     #[cfg(feature = "v1")]
@@ -8758,10 +8984,22 @@ impl<F: Clone> OperationSessionGetters<F> for PaymentIntentData<F> {
     fn get_whole_connector_response(&self) -> Option<String> {
         todo!();
     }
+
+    fn get_pre_routing_result(
+        &self,
+    ) -> Option<HashMap<enums::PaymentMethodType, domain::PreRoutingConnectorChoice>> {
+        None
+    }
 }
 
 #[cfg(feature = "v2")]
 impl<F: Clone> OperationSessionSetters<F> for PaymentIntentData<F> {
+    fn set_prerouting_algorithm_in_payment_intent(
+        &mut self,
+        straight_through_algorithm: storage::PaymentRoutingInfo,
+    ) {
+        self.payment_intent.prerouting_algorithm = Some(straight_through_algorithm);
+    }
     // Setters Implementation
     fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) {
         self.payment_intent = payment_intent;
@@ -9001,10 +9239,26 @@ impl<F: Clone> OperationSessionGetters<F> for PaymentConfirmData<F> {
     fn get_whole_connector_response(&self) -> Option<String> {
         todo!()
     }
+
+    fn get_pre_routing_result(
+        &self,
+    ) -> Option<HashMap<enums::PaymentMethodType, domain::PreRoutingConnectorChoice>> {
+        self.get_payment_intent()
+            .prerouting_algorithm
+            .clone()
+            .and_then(|pre_routing_algorithm| pre_routing_algorithm.pre_routing_results)
+    }
 }
 
 #[cfg(feature = "v2")]
 impl<F: Clone> OperationSessionSetters<F> for PaymentConfirmData<F> {
+    #[cfg(feature = "v2")]
+    fn set_prerouting_algorithm_in_payment_intent(
+        &mut self,
+        straight_through_algorithm: storage::PaymentRoutingInfo,
+    ) {
+        self.payment_intent.prerouting_algorithm = Some(straight_through_algorithm);
+    }
     // Setters Implementation
     fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) {
         self.payment_intent = payment_intent;
@@ -9245,10 +9499,23 @@ impl<F: Clone> OperationSessionGetters<F> for PaymentStatusData<F> {
     fn get_whole_connector_response(&self) -> Option<String> {
         todo!()
     }
+
+    fn get_pre_routing_result(
+        &self,
+    ) -> Option<HashMap<enums::PaymentMethodType, domain::PreRoutingConnectorChoice>> {
+        None
+    }
 }
 
 #[cfg(feature = "v2")]
 impl<F: Clone> OperationSessionSetters<F> for PaymentStatusData<F> {
+    #[cfg(feature = "v2")]
+    fn set_prerouting_algorithm_in_payment_intent(
+        &mut self,
+        straight_through_algorithm: storage::PaymentRoutingInfo,
+    ) {
+        self.payment_intent.prerouting_algorithm = Some(straight_through_algorithm);
+    }
     fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) {
         self.payment_intent = payment_intent;
     }
@@ -9490,10 +9757,22 @@ impl<F: Clone> OperationSessionGetters<F> for PaymentCaptureData<F> {
     fn get_whole_connector_response(&self) -> Option<String> {
         todo!();
     }
+    fn get_pre_routing_result(
+        &self,
+    ) -> Option<HashMap<enums::PaymentMethodType, domain::PreRoutingConnectorChoice>> {
+        None
+    }
 }
 
 #[cfg(feature = "v2")]
 impl<F: Clone> OperationSessionSetters<F> for PaymentCaptureData<F> {
+    #[cfg(feature = "v2")]
+    fn set_prerouting_algorithm_in_payment_intent(
+        &mut self,
+        straight_through_algorithm: storage::PaymentRoutingInfo,
+    ) {
+        self.payment_intent.prerouting_algorithm = Some(straight_through_algorithm);
+    }
     fn set_payment_intent(&mut self, payment_intent: storage::PaymentIntent) {
         self.payment_intent = payment_intent;
     }
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 220a48cd1e1..a166c54139d 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -3949,6 +3949,7 @@ impl MerchantConnectorAccountType {
         }
     }
 
+    #[cfg(feature = "v1")]
     pub fn get_connector_name(&self) -> Option<String> {
         match self {
             Self::DbVal(db_val) => Some(db_val.connector_name.to_string()),
@@ -3956,6 +3957,14 @@ impl MerchantConnectorAccountType {
         }
     }
 
+    #[cfg(feature = "v2")]
+    pub fn get_connector_name(&self) -> Option<api_enums::Connector> {
+        match self {
+            Self::DbVal(db_val) => Some(db_val.connector_name),
+            Self::CacheVal(_) => None,
+        }
+    }
+
     pub fn get_additional_merchant_data(
         &self,
     ) -> Option<Encryptable<masking::Secret<serde_json::Value>>> {
@@ -5289,12 +5298,6 @@ where
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Failed to get merchant default fallback connectors config")?;
 
-        #[cfg(feature = "v2")]
-        let fallback_connetors_list = core_admin::ProfileWrapper::new(business_profile)
-            .get_default_fallback_list_of_connector_under_profile()
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Failed to get merchant default fallback connectors config")?;
-
         let mut routing_connector_data_list = Vec::new();
 
         pre_routing_connector_data_list.iter().for_each(|pre_val| {
@@ -6876,6 +6879,34 @@ where
     Ok(())
 }
 
+pub async fn validate_routing_id_with_profile_id(
+    db: &dyn StorageInterface,
+    routing_id: &id_type::RoutingId,
+    profile_id: &id_type::ProfileId,
+) -> CustomResult<(), errors::ApiErrorResponse> {
+    let _routing_id = db
+        .find_routing_algorithm_metadata_by_algorithm_id_profile_id(routing_id, profile_id)
+        .await
+        .map_err(|err| {
+            if err.current_context().is_db_not_found() {
+                logger::warn!(
+                    "Routing id not found for routing id - {:?} and profile id - {:?}",
+                    routing_id,
+                    profile_id
+                );
+                err.change_context(errors::ApiErrorResponse::InvalidDataFormat {
+                    field_name: "routing_algorithm_id".to_string(),
+                    expected_format: "A valid routing_id that belongs to the business_profile"
+                        .to_string(),
+                })
+            } else {
+                err.change_context(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("Failed to validate routing id")
+            }
+        })?;
+    Ok(())
+}
+
 #[cfg(feature = "v1")]
 pub async fn validate_merchant_connector_ids_in_connector_mandate_details(
     state: &SessionState,
diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs
index 2074b28fd00..7084fc14bb8 100644
--- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs
@@ -338,34 +338,14 @@ impl<F: Clone + Send + Sync> Domain<F, PaymentsConfirmIntentRequest, PaymentConf
         // TODO: do not take the whole payment data here
         payment_data: &mut PaymentConfirmData<F>,
     ) -> CustomResult<ConnectorCallType, errors::ApiErrorResponse> {
-        use crate::core::payments::OperationSessionSetters;
-
-        let fallback_config = admin::ProfileWrapper::new(business_profile.clone())
-            .get_default_fallback_list_of_connector_under_profile()
-            .change_context(errors::RoutingError::FallbackConfigFetchFailed)
-            .change_context(errors::ApiErrorResponse::InternalServerError)?;
-
-        let first_chosen_connector = fallback_config
-            .first()
-            .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?;
-
-        let connector_name = first_chosen_connector.connector.to_string();
-        let merchant_connector_id = first_chosen_connector
-            .merchant_connector_id
-            .clone()
-            .get_required_value("merchant_connector_id")?;
-
-        payment_data.set_connector_in_payment_attempt(Some(connector_name.to_string()));
-        payment_data.set_merchant_connector_id_in_attempt(Some(merchant_connector_id.clone()));
-
-        let connector_data = api::ConnectorData::get_connector_by_name(
-            &state.conf.connectors,
-            &connector_name,
-            api::GetToken::Connector,
-            Some(merchant_connector_id),
-        )?;
-
-        Ok(ConnectorCallType::PreDetermined(connector_data.into()))
+        payments::connector_selection(
+            state,
+            merchant_context,
+            business_profile,
+            payment_data,
+            None,
+        )
+        .await
     }
 }
 
diff --git a/crates/router/src/core/payments/operations/payment_create_intent.rs b/crates/router/src/core/payments/operations/payment_create_intent.rs
index c5cb9cdba12..1cb95a2b92e 100644
--- a/crates/router/src/core/payments/operations/payment_create_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_create_intent.rs
@@ -4,7 +4,7 @@ use api_models::{enums::FrmSuggestion, payments::PaymentsCreateIntentRequest};
 use async_trait::async_trait;
 use common_utils::{
     errors::CustomResult,
-    ext_traits::{AsyncExt, Encode, ValueExt},
+    ext_traits::Encode,
     types::{authentication, keymanager::ToEncryptable},
 };
 use error_stack::ResultExt;
@@ -15,10 +15,9 @@ use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, Valida
 use crate::{
     core::{
         errors::{self, RouterResult, StorageErrorExt},
-        payments::{self, cards::create_encrypted_data, helpers, operations},
+        payments::{self, helpers, operations},
     },
     routes::{app::ReqState, SessionState},
-    services,
     types::{
         api,
         domain::{self, types as domain_types},
@@ -100,6 +99,14 @@ impl<F: Send + Clone + Sync>
         _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
     ) -> RouterResult<operations::GetTrackerResponse<payments::PaymentIntentData<F>>> {
         let db = &*state.store;
+        if let Some(routing_algorithm_id) = request.routing_algorithm_id.as_ref() {
+            helpers::validate_routing_id_with_profile_id(
+                db,
+                routing_algorithm_id,
+                profile.get_id(),
+            )
+            .await?;
+        }
         let key_manager_state = &state.into();
 
         let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs
index c605f9b3421..b791dab22c1 100644
--- a/crates/router/src/core/payments/operations/payment_update_intent.rs
+++ b/crates/router/src/core/payments/operations/payment_update_intent.rs
@@ -136,10 +136,18 @@ impl<F: Send + Clone> GetTracker<F, payments::PaymentIntentData<F>, PaymentsUpda
         payment_id: &common_utils::id_type::GlobalPaymentId,
         request: &PaymentsUpdateIntentRequest,
         merchant_context: &domain::MerchantContext,
-        _profile: &domain::Profile,
+        profile: &domain::Profile,
         _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
     ) -> RouterResult<operations::GetTrackerResponse<payments::PaymentIntentData<F>>> {
         let db = &*state.store;
+        if let Some(routing_algorithm_id) = request.routing_algorithm_id.as_ref() {
+            helpers::validate_routing_id_with_profile_id(
+                db,
+                routing_algorithm_id,
+                profile.get_id(),
+            )
+            .await?;
+        }
         let key_manager_state = &state.into();
         let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
         let payment_intent = db
diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs
index a7d8fecfe87..df36f9f5117 100644
--- a/crates/router/src/core/payments/retry.rs
+++ b/crates/router/src/core/payments/retry.rs
@@ -703,7 +703,6 @@ pub fn make_new_payment_attempt(
     todo!()
 }
 
-#[cfg(feature = "v1")]
 pub async fn get_merchant_config_for_gsm(
     db: &dyn StorageInterface,
     merchant_id: &common_utils::id_type::MerchantId,
diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs
index c3c4050f1d5..712d092fc77 100644
--- a/crates/router/src/core/utils.rs
+++ b/crates/router/src/core/utils.rs
@@ -2044,7 +2044,8 @@ pub async fn construct_vault_router_data<F>(
 ) -> RouterResult<VaultRouterDataV2<F>> {
     let connector_name = merchant_connector_account
         .get_connector_name()
-        .unwrap_or_default(); // always get the connector name from the merchant_connector_account
+        .ok_or(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Connector name not present for external vault")?; // always get the connector name from the merchant_connector_account
     let connector_auth_type: types::ConnectorAuthType = merchant_connector_account
         .get_connector_account_details()
         .parse_value("ConnectorAuthType")
diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs
index 2df62a36e17..12930b680ee 100644
--- a/crates/router/src/routes/payments.rs
+++ b/crates/router/src/routes/payments.rs
@@ -2764,9 +2764,16 @@ pub async fn payment_confirm_intent(
             ))
             .await
         },
-        &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
-            global_payment_id,
-        )),
+        auth::api_or_client_auth(
+            &auth::V2ApiKeyAuth {
+                is_connected_allowed: false,
+                is_platform_allowed: false,
+            },
+            &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
+                global_payment_id,
+            )),
+            req.headers(),
+        ),
         locking_action,
     ))
     .await
diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs
index f41fa33bb2d..1e9a0d17320 100644
--- a/crates/router/src/types/api.rs
+++ b/crates/router/src/types/api.rs
@@ -301,20 +301,24 @@ impl ConnectorData {
     #[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
     pub fn get_external_vault_connector_by_name(
         _connectors: &Connectors,
-        name: &str,
+        connector: &api_enums::Connector,
         connector_type: GetToken,
         connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
     ) -> CustomResult<Self, errors::ApiErrorResponse> {
-        let connector = Self::convert_connector(name)?;
-        let external_vault_connector_name = api_enums::VaultConnectors::from_str(name)
-            .change_context(errors::ConnectorError::InvalidConnectorName)
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable_lazy(|| {
-                format!("unable to parse external vault connector name {name}")
-            })?;
+        let connector_enum = Self::convert_connector(&connector.to_string())?;
+        let external_vault_connector_name =
+            api_enums::VaultConnectors::from_str(&connector.to_string())
+                .change_context(errors::ConnectorError::InvalidConnectorName)
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable_lazy(|| {
+                    format!(
+                        "unable to parse external vault connector name {:?}",
+                        connector
+                    )
+                })?;
         let connector_name = api_enums::Connector::from(external_vault_connector_name);
         Ok(Self {
-            connector,
+            connector: connector_enum,
             connector_name,
             get_token: connector_type,
             merchant_connector_id: connector_id,
diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs
index 21ddf6079cf..1d1f0be637a 100644
--- a/crates/router/src/types/domain.rs
+++ b/crates/router/src/types/domain.rs
@@ -56,6 +56,10 @@ pub mod vault {
     pub use hyperswitch_domain_models::vault::*;
 }
 
+mod routing {
+    pub use hyperswitch_domain_models::routing::*;
+}
+
 pub mod payments;
 pub mod types;
 #[cfg(feature = "olap")]
@@ -73,6 +77,7 @@ pub use merchant_key_store::*;
 pub use network_tokenization::*;
 pub use payment_method_data::*;
 pub use payment_methods::*;
+pub use routing::*;
 #[cfg(feature = "olap")]
 pub use user::*;
 pub use user_key_store::*;
diff --git a/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql b/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql
index 0e086500cc1..e912b9f9c53 100644
--- a/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql
+++ b/v2_migrations/2025-01-13-081847_drop_v1_columns/up.sql
@@ -129,6 +129,4 @@ ALTER TABLE refund
     DROP COLUMN IF EXISTS refund_id,
     DROP COLUMN IF EXISTS merchant_connector_id;
 
--- Run below queries only when V1 is deprecated
-ALTER TABLE routing_algorithm
-    DROP COLUMN IF EXISTS decision_engine_routing_id;
+

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
Implemented rule based routing in payments v2.
Straight through routing in v2 will be achieved by first configuring a straight through algorithm using `routing` endpoint and then passing the `routing_algorithm_id` field in payment create intent request.

* Added validation for `routing_algorithm_id` in payment_inten_create and payment_intent_update.

Three diff types of routing
- Straight through - routing id sent in request
- routing id activated in profile
- Default fallback - in the order the MCA's are created.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->

Please use this log for checking the final list of connectors decided by routing module - `final_selected_connectors_for_routing`

- Straight through routing - 
Create a routing algo entry, with rule and use that routing id in payment intent create 
create routing algorithm - 
<img width="1030" alt="Screenshot 2025-05-27 at 2 42 36 PM" src="https://github.com/user-attachments/assets/14786801-f860-4f0c-940c-14e3ff9d9c29" />
request - 

```
curl --location 'http://localhost:8080/v2/routing-algorithm' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'X-Profile-Id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data '{
    "name": "newalgo",
    "description": "It is my ADVANCED config",
    "algorithm": {
        "type": "advanced",
        "data": {
            "defaultSelection": {
                "type": "priority",
                "data": [
                   {"connector": "bluesnap",
                    "merchant_connector_id":"mca_xMJUJRcvk333cIPrJtdZ"}
                ]
            },

            "rules": [
                {
                    "name": "bluesnap",
                    "connectorSelection": {
                        "type": "priority",
                        "data": [
                       { "connector": "bluesnap",
                    "merchant_connector_id":"mca_xMJUJRcvk333cIPrJtdZ"},
                    {"connector": "adyen",
                    "merchant_connector_id":"mca_IpsaFXCRJa2TNYWuVkNx"},
                    {
                        "connector": "stripe",
                    "merchant_connector_id":"mca_DfyZDqHCZErTASCW170f"
                    }

                        ]
                    },
                    "statements": [
                        {
                            "condition": [
                                {
                                    "lhs": "amount",
                                    "comparison": "greater_than",
                                    "value": {
                                        "type": "number",
                                        "value": 100077
                                    },
                                    "metadata": {}
                                }
                            ],
                            "nested": null
                        }
                    ]
                }
            ],
            "metadata": {}
        }
    },
    "profile_id":"pro_kQqIhnCrULOPt4tmuP5v"
}'
```

Response - 
```
{
    "id": "routing_bFY4UYfNYLR0pQozeoPX",
    "profile_id": "pro_kQqIhnCrULOPt4tmuP5v",
    "name": "newalgo",
    "kind": "advanced",
    "description": "It is my ADVANCED config",
    "created_at": 1748843132,
    "modified_at": 1748843132,
    "algorithm_for": "payment",
    "decision_engine_routing_id": null
}
```

Payment intent create - 
```
curl --location 'http://localhost:8080/v2/payments/create-intent' \
--header 'Content-Type: application/json' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data-raw '{
    "amount_details": {
        "order_amount": 100,
        "currency": "USD"
    },
    "capture_method":"manual",
    "authentication_type": "no_three_ds",
    "billing": {
        "address": {
            "first_name": "John",
            "last_name": "Dough"
        },
        "email": "example@example.com"
    },
    "shipping": {
        "address": {
            "first_name": "John",
            "last_name": "Dough",
            "city": "Karwar",
            "zip": "581301",
            "state": "Karnataka"
        },
        "email": "example@example.com"
    },
    "routing_algorithm_id":"routing_bFY4UYfNYLR0pQozeoPX"
}'
```
Confirm intent - 
```
curl --location 'http://localhost:8080/v2/payments/12345_pay_01972f2d4b007c119e0373142eac05d3/confirm-intent' \
--header 'x-client-secret: cs_01972f2d4b1a7fa091ec990921d3bea7' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'Content-Type: application/json' \
--header 'api-key: pk_dev_47a62e65c5124ddab58d82c3bb4d0072' \
--data-raw '{
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "01",
            "card_exp_year": "25",
            "card_holder_name": "John Doe",
            "card_cvc": "100"
        },
        "billing": {
            "address": {
                "first_name": "John",
                "last_name": "Dough"
            },
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "credit",
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    }
}'
```
final list of connectors decided by routing module
<img width="1361" alt="Screenshot 2025-06-02 at 11 17 12 AM" src="https://github.com/user-attachments/assets/53b8c78d-ebe6-496f-ba6f-65be7bdccfd0" />
payment happened with Bluesnap

**Rule based routing activation at profile level -** 
Creating new routing algo , with adyen as priority.
<img width="986" alt="Screenshot 2025-06-02 at 11 20 29 AM" src="https://github.com/user-attachments/assets/93361a49-b17f-46c1-9bc0-158ed63a0fb8" />

Activate the routing algorithm id 
req - 
```
curl --location --request PATCH 'http://localhost:8080/v2/profiles/pro_kQqIhnCrULOPt4tmuP5v/activate-routing-algorithm' \
--header 'Content-Type: application/json' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'X-Profile-Id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data '{
    "routing_algorithm_id": "routing_VdGQBkci7Wt5eoFRMJ6L"
}'
```
response - 
```
{
    "id": "routing_VdGQBkci7Wt5eoFRMJ6L",
    "profile_id": "pro_kQqIhnCrULOPt4tmuP5v",
    "name": "newalgo",
    "kind": "advanced",
    "description": "It is my ADVANCED config",
    "created_at": 1748843407,
    "modified_at": 1748843407,
    "algorithm_for": "payment",
    "decision_engine_routing_id": null
}
```
verify from profile entry, the routing algo id will be populated
<img width="1540" alt="Screenshot 2025-06-02 at 11 22 11 AM" src="https://github.com/user-attachments/assets/21ec88db-6b48-45c1-b062-93b93be5d2f2" />

Create intent without passing routing id at all in the request

<img width="1103" alt="Screenshot 2025-06-02 at 11 23 47 AM" src="https://github.com/user-attachments/assets/80d7bdf2-623b-4e29-b4c1-c57981f1e6d2" />

Final list of connectors - 
<img width="1558" alt="Screenshot 2025-06-02 at 11 24 27 AM" src="https://github.com/user-attachments/assets/f259e9e5-b40d-4c02-b78f-8798329d31fa" />

Confirm intent
<img width="1182" alt="Screenshot 2025-06-02 at 11 24 10 AM" src="https://github.com/user-attachments/assets/577bbf47-ae94-4d7e-88ac-60aad4fb764b" />




**Default Fallback -** 
Verify in profile
<img width="1728" alt="Screenshot 2025-06-02 at 11 10 08 AM" src="https://github.com/user-attachments/assets/3d154892-c583-4c55-ae82-c3048c7debcc" />
<img width="1411" alt="Screenshot 2025-06-02 at 11 11 19 AM" src="https://github.com/user-attachments/assets/c4405bfb-f066-45dd-af07-6ab88f786492" />

Payment Intent create - 
```
curl --location 'http://localhost:8080/v2/payments/create-intent' \
--header 'Content-Type: application/json' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'api-key: dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--data-raw '{
    "amount_details": {
        "order_amount": 100,
        "currency": "USD"
    },
    "capture_method":"manual",
    "authentication_type": "no_three_ds",
    "billing": {
        "address": {
            "first_name": "John",
            "last_name": "Dough"
        },
        "email": "example@example.com"
    },
    "shipping": {
        "address": {
            "first_name": "John",
            "last_name": "Dough",
            "city": "Karwar",
            "zip": "581301",
            "state": "Karnataka"
        },
        "email": "example@example.com"
    }
    
}'
```
use the client secret to confirm the intent 
Confirm request - 
```
curl --location 'http://localhost:8080/v2/payments/12345_pay_01972f2414617513a2b2387237f61eb9/confirm-intent' \
--header 'x-client-secret: cs_01972f2414cb7720be7f596e088f9bfd' \
--header 'x-profile-id: pro_kQqIhnCrULOPt4tmuP5v' \
--header 'Authorization: api-key=dev_Ku6SmaxCKhT5paf7bvG0QVtZq9skqpqkn2NznC6Ef5jWLD2aidCDeaby0i4DWThq' \
--header 'Content-Type: application/json' \
--header 'api-key: pk_dev_47a62e65c5124ddab58d82c3bb4d0072' \
--data-raw '{
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "01",
            "card_exp_year": "25",
            "card_holder_name": "John Doe",
            "card_cvc": "100"
        },
        "billing": {
            "address": {
                "first_name": "John",
                "last_name": "Dough"
            },
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "credit",
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    }
}'
```
Confirm Intent Response - 
here the payment went through stripe - 
```
{
    "id": "12345_pay_01972f2414617513a2b2387237f61eb9",
    "status": "failed",
    "amount": {
        "order_amount": 100,
        "currency": "USD",
        "shipping_cost": null,
        "order_tax_amount": null,
        "external_tax_calculation": "skip",
        "surcharge_calculation": "skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null,
        "net_amount": 100,
        "amount_to_capture": null,
        "amount_capturable": 0,
        "amount_captured": null
    },
    "customer_id": null,
    "connector": "stripe",
    "created": "2025-06-02T05:36:23.178Z",
    "payment_method_data": {
        "billing": {
            "address": {
                "city": null,
                "country": null,
                "line1": null,
                "line2": null,
                "line3": null,
                "zip": null,
                "state": null,
                "first_name": "John",
                "last_name": "Dough"
            },
            "phone": null,
            "email": "example@example.com"
        }
    },
    "payment_method_type": "card",
    "payment_method_subtype": "credit",
    "connector_transaction_id": null,
    "connector_reference_id": null,
    "merchant_connector_id": "mca_DfyZDqHCZErTASCW170f",
    "browser_info": null,
    "error": {
        "code": "No error code",
        "message": "No error message",
        "unified_code": null,
        "unified_message": null,
        "network_advice_code": null,
        "network_decline_code": null,
        "network_error_message": null
    },
    "shipping": null,
    "billing": null,
    "attempts": null,
    "connector_token_details": null,
    "payment_method_id": null,
    "next_action": null,
    "return_url": "https://google.com/success",
    "authentication_type": "no_three_ds",
    "authentication_type_applied": "no_three_ds",
    "is_iframe_redirection_enabled": null
}
```


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
