Bug: chore: Conditoinal config for invoking DE routing



There should be a conditional check for Decision Engine (DE) Euclid API integration within the static routing flow. Right now, the system always attempted to perform Euclid-based routing regardless of configuration. With this change, the Euclid routing flow is invoked only if the OpenRouter URL is configured.  
diff --git a/config/config.example.toml b/config/config.example.toml
index 3c6da56b240..02224da9024 100644
--- a/config/config.example.toml
+++ b/config/config.example.toml
@@ -1094,7 +1094,8 @@ background_color = "#FFFFFF"
 billing_connectors_which_require_payment_sync = "stripebilling, recurly" # List of billing connectors which has payment sync api call
 
 [open_router]
-enabled = true                          # Enable or disable Open Router
+dynamic_routing_enabled = true                          # Enable or disable Open Router for dynamic routing
+static_routing_enabled = true           # Enable or disable Open Router for static routing
 url = "http://localhost:8080"           # Open Router URL
 
 [grpc_client.unified_connector_service]
diff --git a/config/development.toml b/config/development.toml
index ab885503c85..ece582a388f 100644
--- a/config/development.toml
+++ b/config/development.toml
@@ -1189,7 +1189,8 @@ allow_connected_merchants = true
 click_to_pay = {connector_list = "adyen, cybersource"}
 
 [open_router]
-enabled = false
+dynamic_routing_enabled = false
+static_routing_enabled = false
 url = "http://localhost:8080"
 
 [grpc_client.unified_connector_service]
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index ee7b3ed820e..6157a4fab20 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -175,7 +175,8 @@ pub struct DebitRoutingConfig {
 
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct OpenRouter {
-    pub enabled: bool,
+    pub dynamic_routing_enabled: bool,
+    pub static_routing_enabled: bool,
     pub url: String,
 }
 
@@ -1080,6 +1081,8 @@ impl Settings<SecuredSecret> {
 
         self.platform.validate()?;
 
+        self.open_router.validate()?;
+
         Ok(())
     }
 }
diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs
index ab5f2af4160..4fc254760a0 100644
--- a/crates/router/src/configs/validations.rs
+++ b/crates/router/src/configs/validations.rs
@@ -327,3 +327,19 @@ impl super::settings::Platform {
         })
     }
 }
+
+impl super::settings::OpenRouter {
+    pub fn validate(&self) -> Result<(), ApplicationError> {
+        use common_utils::fp_utils::when;
+
+        when(
+            (self.dynamic_routing_enabled || self.static_routing_enabled)
+                && self.url.is_default_or_empty(),
+            || {
+                Err(ApplicationError::InvalidConfigurationValueError(
+                    "OpenRouter base URL must not be empty when it is enabled".into(),
+                ))
+            },
+        )
+    }
+}
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index f362d413d3e..18772bd7100 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -1230,7 +1230,7 @@ pub async fn merchant_account_delete(
     // Call to DE here
     #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
     {
-        if state.conf.open_router.enabled && is_deleted {
+        if state.conf.open_router.dynamic_routing_enabled && is_deleted {
             merchant_account
                 .default_profile
                 .as_ref()
diff --git a/crates/router/src/core/debit_routing.rs b/crates/router/src/core/debit_routing.rs
index d4f7c8bd753..651f1ea04c3 100644
--- a/crates/router/src/core/debit_routing.rs
+++ b/crates/router/src/core/debit_routing.rs
@@ -123,7 +123,7 @@ where
     F: Send + Clone,
     D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
 {
-    if business_profile.is_debit_routing_enabled && state.conf.open_router.enabled {
+    if business_profile.is_debit_routing_enabled && state.conf.open_router.dynamic_routing_enabled {
         logger::info!("Debit routing is enabled for the profile");
 
         let debit_routing_config = &state.conf.debit_routing_config;
diff --git a/crates/router/src/core/health_check.rs b/crates/router/src/core/health_check.rs
index 49138d3bda0..7bc65e4d0e4 100644
--- a/crates/router/src/core/health_check.rs
+++ b/crates/router/src/core/health_check.rs
@@ -194,7 +194,7 @@ impl HealthCheckInterface for app::SessionState {
     async fn health_check_decision_engine(
         &self,
     ) -> CustomResult<HealthState, errors::HealthCheckDecisionEngineError> {
-        if self.conf.open_router.enabled {
+        if self.conf.open_router.dynamic_routing_enabled {
             let url = format!("{}/{}", &self.conf.open_router.url, "health");
             let request = services::Request::new(services::Method::Get, &url);
             let _ = services::call_connector_api(self, request, "health_check_for_decision_engine")
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 32b6aa1bfbd..8a0058b79e4 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -8409,7 +8409,7 @@ where
             .attach_printable("failed to perform volume split on routing type")?;
 
         if routing_choice.routing_type.is_dynamic_routing() {
-            if state.conf.open_router.enabled {
+            if state.conf.open_router.dynamic_routing_enabled {
                 routing::perform_dynamic_routing_with_open_router(
                     state,
                     connectors.clone(),
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index e72555b2ede..7fa8a4858f8 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -2167,7 +2167,8 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                 );
             tokio::spawn(
                 async move {
-                    let should_route_to_open_router = state.conf.open_router.enabled;
+                    let should_route_to_open_router =
+                        state.conf.open_router.dynamic_routing_enabled;
 
                     if should_route_to_open_router {
                         routing_helpers::update_gateway_score_helper_with_open_router(
diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs
index c3823aa2f30..7feea3286ca 100644
--- a/crates/router/src/core/payments/routing.rs
+++ b/crates/router/src/core/payments/routing.rs
@@ -490,29 +490,33 @@ pub async fn perform_static_routing_v1(
             .to_string(),
     };
 
-    let routing_events_wrapper = utils::RoutingEventsWrapper::new(
-        state.tenant.tenant_id.clone(),
-        state.request_id,
-        payment_id,
-        business_profile.get_id().to_owned(),
-        business_profile.merchant_id.to_owned(),
-        "DecisionEngine: Euclid Static Routing".to_string(),
-        None,
-        true,
-        false,
-    );
+    let de_euclid_connectors = if state.conf.open_router.static_routing_enabled {
+        let routing_events_wrapper = utils::RoutingEventsWrapper::new(
+            state.tenant.tenant_id.clone(),
+            state.request_id,
+            payment_id,
+            business_profile.get_id().to_owned(),
+            business_profile.merchant_id.to_owned(),
+            "DecisionEngine: Euclid Static Routing".to_string(),
+            None,
+            true,
+            false,
+        );
 
-    let de_euclid_connectors = perform_decision_euclid_routing(
-        state,
-        backend_input.clone(),
-        business_profile.get_id().get_string_repr().to_string(),
-        routing_events_wrapper
-    )
-    .await
-    .map_err(|e|
-        // errors are ignored as this is just for diff checking as of now (optional flow).
-        logger::error!(decision_engine_euclid_evaluate_error=?e, "decision_engine_euclid: error in evaluation of rule")
-    ).unwrap_or_default();
+        perform_decision_euclid_routing(
+            state,
+            backend_input.clone(),
+            business_profile.get_id().get_string_repr().to_string(),
+            routing_events_wrapper
+        )
+        .await
+        .map_err(|e|
+            // errors are ignored as this is just for diff checking as of now (optional flow).
+            logger::error!(decision_engine_euclid_evaluate_error=?e, "decision_engine_euclid: error in evaluation of rule")
+        ).unwrap_or_default()
+    } else {
+        Vec::default()
+    };
 
     let (routable_connectors, routing_approach) = match cached_algorithm.as_ref() {
         CachedAlgorithm::Single(conn) => (
diff --git a/crates/router/src/core/payments/routing/utils.rs b/crates/router/src/core/payments/routing/utils.rs
index bef71d5fc66..d2167f88498 100644
--- a/crates/router/src/core/payments/routing/utils.rs
+++ b/crates/router/src/core/payments/routing/utils.rs
@@ -335,8 +335,6 @@ pub async fn perform_decision_euclid_routing(
     routing_event.set_routable_connectors(euclid_response.evaluated_output.clone());
     state.event_handler.log_event(&routing_event);
 
-    // Need to log euclid response event here
-
     logger::debug!(decision_engine_euclid_response=?euclid_response,"decision_engine_euclid");
     logger::debug!(decision_engine_euclid_selected_connector=?euclid_response.evaluated_output,"decision_engine_euclid");
 
diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs
index ffd5f60e79d..52a3b4f1a7b 100644
--- a/crates/router/src/core/routing.rs
+++ b/crates/router/src/core/routing.rs
@@ -687,7 +687,7 @@ pub async fn link_routing_config(
                 // Call to DE here to update SR configs
                 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
                 {
-                    if state.conf.open_router.enabled {
+                    if state.conf.open_router.dynamic_routing_enabled {
                         update_decision_engine_dynamic_routing_setup(
                             &state,
                             business_profile.get_id(),
@@ -718,7 +718,7 @@ pub async fn link_routing_config(
             );
                 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
                 {
-                    if state.conf.open_router.enabled {
+                    if state.conf.open_router.dynamic_routing_enabled {
                         update_decision_engine_dynamic_routing_setup(
                             &state,
                             business_profile.get_id(),
@@ -1844,7 +1844,7 @@ pub async fn success_based_routing_update_configs(
         router_env::metric_attributes!(("profile_id", profile_id.clone())),
     );
 
-    if !state.conf.open_router.enabled {
+    if !state.conf.open_router.dynamic_routing_enabled {
         state
             .grpc_client
             .dynamic_routing
@@ -1948,7 +1948,7 @@ pub async fn elimination_routing_update_configs(
         router_env::metric_attributes!(("profile_id", profile_id.clone())),
     );
 
-    if !state.conf.open_router.enabled {
+    if !state.conf.open_router.dynamic_routing_enabled {
         state
             .grpc_client
             .dynamic_routing
diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs
index df9c11efe45..8b4c747d13f 100644
--- a/crates/router/src/core/routing/helpers.rs
+++ b/crates/router/src/core/routing/helpers.rs
@@ -1923,7 +1923,7 @@ pub async fn disable_dynamic_routing_algorithm(
         };
 
     // Call to DE here
-    if state.conf.open_router.enabled {
+    if state.conf.open_router.dynamic_routing_enabled {
         disable_decision_engine_dynamic_routing_setup(
             state,
             business_profile.get_id(),
@@ -2111,11 +2111,12 @@ pub async fn default_specific_dynamic_routing_setup(
     let timestamp = common_utils::date_time::now();
     let algo = match dynamic_routing_type {
         routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
-            let default_success_based_routing_config = if state.conf.open_router.enabled {
-                routing_types::SuccessBasedRoutingConfig::open_router_config_default()
-            } else {
-                routing_types::SuccessBasedRoutingConfig::default()
-            };
+            let default_success_based_routing_config =
+                if state.conf.open_router.dynamic_routing_enabled {
+                    routing_types::SuccessBasedRoutingConfig::open_router_config_default()
+                } else {
+                    routing_types::SuccessBasedRoutingConfig::default()
+                };
 
             routing_algorithm::RoutingAlgorithm {
                 algorithm_id: algorithm_id.clone(),
@@ -2132,11 +2133,12 @@ pub async fn default_specific_dynamic_routing_setup(
             }
         }
         routing_types::DynamicRoutingType::EliminationRouting => {
-            let default_elimination_routing_config = if state.conf.open_router.enabled {
-                routing_types::EliminationRoutingConfig::open_router_config_default()
-            } else {
-                routing_types::EliminationRoutingConfig::default()
-            };
+            let default_elimination_routing_config =
+                if state.conf.open_router.dynamic_routing_enabled {
+                    routing_types::EliminationRoutingConfig::open_router_config_default()
+                } else {
+                    routing_types::EliminationRoutingConfig::default()
+                };
             routing_algorithm::RoutingAlgorithm {
                 algorithm_id: algorithm_id.clone(),
                 profile_id: profile_id.clone(),
@@ -2162,7 +2164,7 @@ pub async fn default_specific_dynamic_routing_setup(
 
     // Call to DE here
     // Need to map out the cases if this call should always be made or not
-    if state.conf.open_router.enabled {
+    if state.conf.open_router.dynamic_routing_enabled {
         enable_decision_engine_dynamic_routing_setup(
             state,
             business_profile.get_id(),

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

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


## Description

This PR introduces a conditional check for Decision Engine (DE) Euclid API integration within the static routing flow. Previously, the system always attempted to perform Euclid-based routing regardless of configuration. With this change, the Euclid routing flow is invoked only if the OpenRouter URL is configured.  

Additionally, some unused or redundant code around logging and comments has been cleaned up in `utils.rs`.  

## Outcomes

- Prevents unnecessary calls to the Euclid API when the OpenRouter URL is not configured, reducing overhead and potential timeouts.
- Ensures cleaner and more consistent logs by removing redundant debug statements and placeholders.
- Improves maintainability by scoping the Euclid integration behind a clear configuration flag.  

## Diff Hunk Explanation

### `crates/router/src/core/payments/routing.rs`
- Wrapped the `perform_decision_euclid_routing` call with a check for `state.conf.open_router.url`.
- Added a fallback to return an empty connector list if OpenRouter is not configured.

### `crates/router/src/core/payments/routing/utils.rs`
- Removed a redundant placeholder comment about logging Euclid response events.
- Streamlined debug logs for Euclid responses.

This PR addresses an integration improvement for Euclid static routing checks in the decision engine.  


### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [x] 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)?
-->
Can be tested directly in prod as that is the only environment where this env in missing.

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

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