Bug: Integration of ER in core flow




diff --git a/config/development.toml b/config/development.toml
index 3a041db3574..c83713e2a07 100644
--- a/config/development.toml
+++ b/config/development.toml
@@ -991,7 +991,7 @@ connector_list = "cybersource"
 
 [grpc_client.dynamic_routing_client]
 host = "localhost"
-port = 7000
+port = 8000
 service = "dynamo"
 
 [theme.storage]
diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs
index 693e9585ddb..d3529edd29e 100644
--- a/crates/api_models/src/routing.rs
+++ b/crates/api_models/src/routing.rs
@@ -811,7 +811,7 @@ pub struct EliminationRoutingConfig {
     pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
 }
 
-#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, ToSchema)]
 pub struct EliminationAnalyserConfig {
     pub bucket_size: Option<u64>,
     pub bucket_leak_interval_in_secs: Option<u64>,
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index cb711c52515..87c64747c69 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -7795,6 +7795,17 @@ pub enum ErrorCategory {
     ProcessorDeclineIncorrectData,
 }
 
+impl ErrorCategory {
+    pub fn should_perform_elimination_routing(self) -> bool {
+        match self {
+            Self::ProcessorDowntime | Self::ProcessorDeclineUnauthorized => true,
+            Self::IssueWithPaymentMethod
+            | Self::ProcessorDeclineIncorrectData
+            | Self::FrmDecline => false,
+        }
+    }
+}
+
 #[derive(
     Clone,
     Debug,
diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs
index a574d84fdcb..9621e619148 100644
--- a/crates/router/src/core/errors.rs
+++ b/crates/router/src/core/errors.rs
@@ -387,6 +387,18 @@ pub enum RoutingError {
     SuccessRateCalculationError,
     #[error("Success rate client from dynamic routing gRPC service not initialized")]
     SuccessRateClientInitializationError,
+    #[error("Elimination client from dynamic routing gRPC service not initialized")]
+    EliminationClientInitializationError,
+    #[error("Unable to analyze elimination routing config from dynamic routing service")]
+    EliminationRoutingCalculationError,
+    #[error("Params not found in elimination based routing config")]
+    EliminationBasedRoutingParamsNotFoundError,
+    #[error("Unable to retrieve elimination based routing config")]
+    EliminationRoutingConfigError,
+    #[error(
+        "Invalid elimination based connector label received from dynamic routing service: '{0}'"
+    )]
+    InvalidEliminationBasedConnectorLabel(String),
     #[error("Unable to convert from '{from}' to '{to}'")]
     GenericConversionError { from: String, to: String },
     #[error("Invalid success based connector label received from dynamic routing service: '{0}'")]
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index f5f95288eea..7988a625e7a 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -1411,7 +1411,13 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             .as_mut()
             .map(|info| info.status = status)
     });
-    let (capture_update, mut payment_attempt_update) = match router_data.response.clone() {
+
+    // TODO: refactor of gsm_error_category with respective feature flag
+    #[allow(unused_variables)]
+    let (capture_update, mut payment_attempt_update, gsm_error_category) = match router_data
+        .response
+        .clone()
+    {
         Err(err) => {
             let auth_update = if Some(router_data.auth_type)
                 != payment_data.payment_attempt.authentication_type
@@ -1420,123 +1426,127 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             } else {
                 None
             };
-            let (capture_update, attempt_update) = match payment_data.multiple_capture_data {
-                Some(multiple_capture_data) => {
-                    let capture_update = storage::CaptureUpdate::ErrorUpdate {
-                        status: match err.status_code {
-                            500..=511 => enums::CaptureStatus::Pending,
-                            _ => enums::CaptureStatus::Failed,
-                        },
-                        error_code: Some(err.code),
-                        error_message: Some(err.message),
-                        error_reason: err.reason,
-                    };
-                    let capture_update_list = vec![(
-                        multiple_capture_data.get_latest_capture().clone(),
-                        capture_update,
-                    )];
-                    (
-                        Some((multiple_capture_data, capture_update_list)),
-                        auth_update.map(|auth_type| {
-                            storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
-                                authentication_type: auth_type,
-                                updated_by: storage_scheme.to_string(),
-                            }
-                        }),
-                    )
-                }
-                None => {
-                    let connector_name = router_data.connector.to_string();
-                    let flow_name = core_utils::get_flow_name::<F>()?;
-                    let option_gsm = payments_helpers::get_gsm_record(
-                        state,
-                        Some(err.code.clone()),
-                        Some(err.message.clone()),
-                        connector_name,
-                        flow_name.clone(),
-                    )
-                    .await;
-
-                    let gsm_unified_code =
-                        option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone());
-                    let gsm_unified_message = option_gsm.and_then(|gsm| gsm.unified_message);
-
-                    let (unified_code, unified_message) = if let Some((code, message)) =
-                        gsm_unified_code.as_ref().zip(gsm_unified_message.as_ref())
-                    {
-                        (code.to_owned(), message.to_owned())
-                    } else {
+            let (capture_update, attempt_update, gsm_error_category) =
+                match payment_data.multiple_capture_data {
+                    Some(multiple_capture_data) => {
+                        let capture_update = storage::CaptureUpdate::ErrorUpdate {
+                            status: match err.status_code {
+                                500..=511 => enums::CaptureStatus::Pending,
+                                _ => enums::CaptureStatus::Failed,
+                            },
+                            error_code: Some(err.code),
+                            error_message: Some(err.message),
+                            error_reason: err.reason,
+                        };
+                        let capture_update_list = vec![(
+                            multiple_capture_data.get_latest_capture().clone(),
+                            capture_update,
+                        )];
                         (
-                            consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
-                            consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
+                            Some((multiple_capture_data, capture_update_list)),
+                            auth_update.map(|auth_type| {
+                                storage::PaymentAttemptUpdate::AuthenticationTypeUpdate {
+                                    authentication_type: auth_type,
+                                    updated_by: storage_scheme.to_string(),
+                                }
+                            }),
+                            None,
                         )
-                    };
-                    let unified_translated_message = locale
-                        .as_ref()
-                        .async_and_then(|locale_str| async {
-                            payments_helpers::get_unified_translation(
-                                state,
-                                unified_code.to_owned(),
-                                unified_message.to_owned(),
-                                locale_str.to_owned(),
+                    }
+                    None => {
+                        let connector_name = router_data.connector.to_string();
+                        let flow_name = core_utils::get_flow_name::<F>()?;
+                        let option_gsm = payments_helpers::get_gsm_record(
+                            state,
+                            Some(err.code.clone()),
+                            Some(err.message.clone()),
+                            connector_name,
+                            flow_name.clone(),
+                        )
+                        .await;
+
+                        let gsm_unified_code =
+                            option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone());
+                        let gsm_unified_message =
+                            option_gsm.clone().and_then(|gsm| gsm.unified_message);
+
+                        let (unified_code, unified_message) = if let Some((code, message)) =
+                            gsm_unified_code.as_ref().zip(gsm_unified_message.as_ref())
+                        {
+                            (code.to_owned(), message.to_owned())
+                        } else {
+                            (
+                                consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
+                                consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
                             )
+                        };
+                        let unified_translated_message = locale
+                            .as_ref()
+                            .async_and_then(|locale_str| async {
+                                payments_helpers::get_unified_translation(
+                                    state,
+                                    unified_code.to_owned(),
+                                    unified_message.to_owned(),
+                                    locale_str.to_owned(),
+                                )
+                                .await
+                            })
                             .await
-                        })
-                        .await
-                        .or(Some(unified_message));
+                            .or(Some(unified_message));
 
-                    let status = match err.attempt_status {
-                        // Use the status sent by connector in error_response if it's present
-                        Some(status) => status,
-                        None =>
-                        // mark previous attempt status for technical failures in PSync flow
-                        {
-                            if flow_name == "PSync" {
-                                match err.status_code {
-                                    // marking failure for 2xx because this is genuine payment failure
-                                    200..=299 => enums::AttemptStatus::Failure,
-                                    _ => router_data.status,
-                                }
-                            } else if flow_name == "Capture" {
-                                match err.status_code {
-                                    500..=511 => enums::AttemptStatus::Pending,
-                                    // don't update the status for 429 error status
-                                    429 => router_data.status,
-                                    _ => enums::AttemptStatus::Failure,
-                                }
-                            } else {
-                                match err.status_code {
-                                    500..=511 => enums::AttemptStatus::Pending,
-                                    _ => enums::AttemptStatus::Failure,
+                        let status = match err.attempt_status {
+                            // Use the status sent by connector in error_response if it's present
+                            Some(status) => status,
+                            None =>
+                            // mark previous attempt status for technical failures in PSync flow
+                            {
+                                if flow_name == "PSync" {
+                                    match err.status_code {
+                                        // marking failure for 2xx because this is genuine payment failure
+                                        200..=299 => enums::AttemptStatus::Failure,
+                                        _ => router_data.status,
+                                    }
+                                } else if flow_name == "Capture" {
+                                    match err.status_code {
+                                        500..=511 => enums::AttemptStatus::Pending,
+                                        // don't update the status for 429 error status
+                                        429 => router_data.status,
+                                        _ => enums::AttemptStatus::Failure,
+                                    }
+                                } else {
+                                    match err.status_code {
+                                        500..=511 => enums::AttemptStatus::Pending,
+                                        _ => enums::AttemptStatus::Failure,
+                                    }
                                 }
                             }
-                        }
-                    };
-                    (
-                        None,
-                        Some(storage::PaymentAttemptUpdate::ErrorUpdate {
-                            connector: None,
-                            status,
-                            error_message: Some(Some(err.message)),
-                            error_code: Some(Some(err.code)),
-                            error_reason: Some(err.reason),
-                            amount_capturable: router_data
-                                .request
-                                .get_amount_capturable(&payment_data, status)
-                                .map(MinorUnit::new),
-                            updated_by: storage_scheme.to_string(),
-                            unified_code: Some(Some(unified_code)),
-                            unified_message: Some(unified_translated_message),
-                            connector_transaction_id: err.connector_transaction_id,
-                            payment_method_data: additional_payment_method_data,
-                            authentication_type: auth_update,
-                            issuer_error_code: err.network_decline_code,
-                            issuer_error_message: err.network_error_message,
-                        }),
-                    )
-                }
-            };
-            (capture_update, attempt_update)
+                        };
+                        (
+                            None,
+                            Some(storage::PaymentAttemptUpdate::ErrorUpdate {
+                                connector: None,
+                                status,
+                                error_message: Some(Some(err.message)),
+                                error_code: Some(Some(err.code)),
+                                error_reason: Some(err.reason),
+                                amount_capturable: router_data
+                                    .request
+                                    .get_amount_capturable(&payment_data, status)
+                                    .map(MinorUnit::new),
+                                updated_by: storage_scheme.to_string(),
+                                unified_code: Some(Some(unified_code)),
+                                unified_message: Some(unified_translated_message),
+                                connector_transaction_id: err.connector_transaction_id,
+                                payment_method_data: additional_payment_method_data,
+                                authentication_type: auth_update,
+                                issuer_error_code: err.network_decline_code,
+                                issuer_error_message: err.network_error_message,
+                            }),
+                            option_gsm.and_then(|option_gsm| option_gsm.error_category),
+                        )
+                    }
+                };
+            (capture_update, attempt_update, gsm_error_category)
         }
 
         Ok(payments_response) => {
@@ -1572,6 +1582,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                             issuer_error_code: None,
                             issuer_error_message: None,
                         }),
+                        None,
                     )
                 }
                 Ok(()) => {
@@ -1627,7 +1638,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                     updated_by: storage_scheme.to_string(),
                                 };
 
-                            (None, Some(payment_attempt_update))
+                            (None, Some(payment_attempt_update), None)
                         }
                         types::PaymentsResponseData::TransactionResponse {
                             resource_id,
@@ -1845,7 +1856,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                 ),
                             };
 
-                            (capture_updates, payment_attempt_update)
+                            (capture_updates, payment_attempt_update, None)
                         }
                         types::PaymentsResponseData::TransactionUnresolvedResponse {
                             resource_id,
@@ -1873,23 +1884,30 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                     connector_response_reference_id,
                                     updated_by: storage_scheme.to_string(),
                                 }),
+                                None,
                             )
                         }
-                        types::PaymentsResponseData::SessionResponse { .. } => (None, None),
-                        types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
-                        types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
+                        types::PaymentsResponseData::SessionResponse { .. } => (None, None, None),
+                        types::PaymentsResponseData::SessionTokenResponse { .. } => {
+                            (None, None, None)
+                        }
+                        types::PaymentsResponseData::TokenizationResponse { .. } => {
+                            (None, None, None)
+                        }
                         types::PaymentsResponseData::ConnectorCustomerResponse { .. } => {
-                            (None, None)
+                            (None, None, None)
                         }
                         types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => {
-                            (None, None)
+                            (None, None, None)
+                        }
+                        types::PaymentsResponseData::PostProcessingResponse { .. } => {
+                            (None, None, None)
                         }
-                        types::PaymentsResponseData::PostProcessingResponse { .. } => (None, None),
                         types::PaymentsResponseData::IncrementalAuthorizationResponse {
                             ..
-                        } => (None, None),
+                        } => (None, None, None),
                         types::PaymentsResponseData::PaymentResourceUpdateResponse { .. } => {
-                            (None, None)
+                            (None, None, None)
                         }
                         types::PaymentsResponseData::MultipleCaptureResponse {
                             capture_sync_response_list,
@@ -1899,9 +1917,13 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                     &multiple_capture_data,
                                     capture_sync_response_list,
                                 )?;
-                                (Some((multiple_capture_data, capture_update_list)), None)
+                                (
+                                    Some((multiple_capture_data, capture_update_list)),
+                                    None,
+                                    None,
+                                )
                             }
-                            None => (None, None),
+                            None => (None, None, None),
                         },
                     }
                 }
@@ -2139,6 +2161,23 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                     .map_err(|e| logger::error!(success_based_routing_metrics_error=?e))
                     .ok();
 
+                    if let Some(gsm_error_category) = gsm_error_category {
+                        if gsm_error_category.should_perform_elimination_routing() {
+                            logger::info!("Performing update window for elimination routing");
+                            routing_helpers::update_window_for_elimination_routing(
+                                &state,
+                                &payment_attempt,
+                                &profile_id,
+                                dynamic_routing_algo_ref.clone(),
+                                dynamic_routing_config_params_interpolator.clone(),
+                                gsm_error_category,
+                            )
+                            .await
+                            .map_err(|e| logger::error!(dynamic_routing_metrics_error=?e))
+                            .ok();
+                        };
+                    };
+
                     routing_helpers::push_metrics_with_update_window_for_contract_based_routing(
                         &state,
                         &payment_attempt,
diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs
index 889525ec6d5..4e3e24c83f3 100644
--- a/crates/router/src/core/payments/routing.rs
+++ b/crates/router/src/core/payments/routing.rs
@@ -26,6 +26,7 @@ use euclid::{
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 use external_services::grpc_client::dynamic_routing::{
     contract_routing_client::ContractBasedDynamicRouting,
+    elimination_based_client::{EliminationBasedRouting, EliminationResponse},
     success_rate_client::{CalSuccessRateResponse, SuccessBasedDynamicRouting},
     DynamicRoutingError,
 };
@@ -1510,7 +1511,7 @@ pub async fn perform_dynamic_routing(
         profile.get_id().get_string_repr()
     );
 
-    let connector_list = match dynamic_routing_algo_ref
+    let mut connector_list = match dynamic_routing_algo_ref
         .success_based_algorithm
         .as_ref()
         .async_map(|algorithm| {
@@ -1539,7 +1540,7 @@ pub async fn perform_dynamic_routing(
                         state,
                         routable_connectors.clone(),
                         profile.get_id(),
-                        dynamic_routing_config_params_interpolator,
+                        dynamic_routing_config_params_interpolator.clone(),
                         algorithm.clone(),
                     )
                 })
@@ -1548,10 +1549,29 @@ pub async fn perform_dynamic_routing(
                 .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
                 .ok()
                 .flatten()
-                .unwrap_or(routable_connectors)
+                .unwrap_or(routable_connectors.clone())
         }
     };
 
+    connector_list = dynamic_routing_algo_ref
+        .elimination_routing_algorithm
+        .as_ref()
+        .async_map(|algorithm| {
+            perform_elimination_routing(
+                state,
+                connector_list.clone(),
+                profile.get_id(),
+                dynamic_routing_config_params_interpolator.clone(),
+                algorithm.clone(),
+            )
+        })
+        .await
+        .transpose()
+        .inspect_err(|e| logger::error!(dynamic_routing_error=?e))
+        .ok()
+        .flatten()
+        .unwrap_or(connector_list);
+
     Ok(connector_list)
 }
 
@@ -1654,6 +1674,127 @@ pub async fn perform_success_based_routing(
     }
 }
 
+/// elimination dynamic routing
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+pub async fn perform_elimination_routing(
+    state: &SessionState,
+    routable_connectors: Vec<api_routing::RoutableConnectorChoice>,
+    profile_id: &common_utils::id_type::ProfileId,
+    elimination_routing_configs_params_interpolator: routing::helpers::DynamicRoutingConfigParamsInterpolator,
+    elimination_algo_ref: api_routing::EliminationRoutingAlgorithm,
+) -> RoutingResult<Vec<api_routing::RoutableConnectorChoice>> {
+    if elimination_algo_ref.enabled_feature
+        == api_routing::DynamicRoutingFeatures::DynamicConnectorSelection
+    {
+        logger::debug!(
+            "performing elimination_routing for profile {}",
+            profile_id.get_string_repr()
+        );
+        let client = state
+            .grpc_client
+            .dynamic_routing
+            .elimination_based_client
+            .as_ref()
+            .ok_or(errors::RoutingError::EliminationClientInitializationError)
+            .attach_printable("elimination routing's gRPC client not found")?;
+
+        let elimination_routing_config = routing::helpers::fetch_dynamic_routing_configs::<
+            api_routing::EliminationRoutingConfig,
+        >(
+            state,
+            profile_id,
+            elimination_algo_ref
+                .algorithm_id_with_timestamp
+                .algorithm_id
+                .ok_or(errors::RoutingError::GenericNotFoundError {
+                    field: "elimination_routing_algorithm_id".to_string(),
+                })
+                .attach_printable(
+                    "elimination_routing_algorithm_id not found in business_profile",
+                )?,
+        )
+        .await
+        .change_context(errors::RoutingError::EliminationRoutingConfigError)
+        .attach_printable("unable to fetch elimination dynamic routing configs")?;
+
+        let elimination_routing_config_params = elimination_routing_configs_params_interpolator
+            .get_string_val(
+                elimination_routing_config
+                    .params
+                    .as_ref()
+                    .ok_or(errors::RoutingError::EliminationBasedRoutingParamsNotFoundError)?,
+            );
+
+        let elimination_based_connectors: EliminationResponse = client
+            .perform_elimination_routing(
+                profile_id.get_string_repr().to_string(),
+                elimination_routing_config_params,
+                routable_connectors.clone(),
+                elimination_routing_config.elimination_analyser_config,
+                state.get_grpc_headers(),
+            )
+            .await
+            .change_context(errors::RoutingError::EliminationRoutingCalculationError)
+            .attach_printable(
+                "unable to analyze/fetch elimination routing from dynamic routing service",
+            )?;
+        let mut connectors =
+            Vec::with_capacity(elimination_based_connectors.labels_with_status.len());
+        let mut eliminated_connectors =
+            Vec::with_capacity(elimination_based_connectors.labels_with_status.len());
+        let mut non_eliminated_connectors =
+            Vec::with_capacity(elimination_based_connectors.labels_with_status.len());
+        for labels_with_status in elimination_based_connectors.labels_with_status {
+            let (connector, merchant_connector_id) = labels_with_status.label
+                .split_once(':')
+                .ok_or(errors::RoutingError::InvalidEliminationBasedConnectorLabel(labels_with_status.label.to_string()))
+                .attach_printable(
+                    "unable to split connector_name and mca_id from the label obtained by the elimination based dynamic routing service",
+                )?;
+
+            let routable_connector = api_routing::RoutableConnectorChoice {
+                choice_kind: api_routing::RoutableChoiceKind::FullStruct,
+                connector: common_enums::RoutableConnectors::from_str(connector)
+                    .change_context(errors::RoutingError::GenericConversionError {
+                        from: "String".to_string(),
+                        to: "RoutableConnectors".to_string(),
+                    })
+                    .attach_printable("unable to convert String to RoutableConnectors")?,
+                merchant_connector_id: Some(
+                    common_utils::id_type::MerchantConnectorAccountId::wrap(
+                        merchant_connector_id.to_string(),
+                    )
+                    .change_context(errors::RoutingError::GenericConversionError {
+                        from: "String".to_string(),
+                        to: "MerchantConnectorAccountId".to_string(),
+                    })
+                    .attach_printable("unable to convert MerchantConnectorAccountId from string")?,
+                ),
+            };
+
+            if labels_with_status
+                .elimination_information
+                .is_some_and(|elimination_info| {
+                    elimination_info
+                        .entity
+                        .is_some_and(|entity_info| entity_info.is_eliminated)
+                })
+            {
+                eliminated_connectors.push(routable_connector);
+            } else {
+                non_eliminated_connectors.push(routable_connector);
+            }
+            connectors.extend(non_eliminated_connectors.clone());
+            connectors.extend(eliminated_connectors.clone());
+        }
+        logger::debug!(dynamic_eliminated_connectors=?eliminated_connectors);
+        logger::debug!(dynamic_elimination_based_routing_connectors=?connectors);
+        Ok(connectors)
+    } else {
+        Ok(routable_connectors)
+    }
+}
+
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 pub async fn perform_contract_based_routing(
     state: &SessionState,
diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs
index ec86102eba0..5caffd7e763 100644
--- a/crates/router/src/core/routing/helpers.rs
+++ b/crates/router/src/core/routing/helpers.rs
@@ -21,9 +21,10 @@ use error_stack::ResultExt;
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
 use external_services::grpc_client::dynamic_routing::{
     contract_routing_client::ContractBasedDynamicRouting,
+    elimination_based_client::EliminationBasedRouting,
     success_rate_client::SuccessBasedDynamicRouting,
 };
-#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 use hyperswitch_domain_models::api::ApplicationResponse;
 #[cfg(all(feature = "dynamic_routing", feature = "v1"))]
 use router_env::logger;
@@ -607,6 +608,43 @@ impl DynamicRoutingCache for routing_types::ContractBasedRoutingConfig {
     }
 }
 
+#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
+#[async_trait::async_trait]
+impl DynamicRoutingCache for routing_types::EliminationRoutingConfig {
+    async fn get_cached_dynamic_routing_config_for_profile(
+        state: &SessionState,
+        key: &str,
+    ) -> Option<Arc<Self>> {
+        cache::ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE
+            .get_val::<Arc<Self>>(cache::CacheKey {
+                key: key.to_string(),
+                prefix: state.tenant.redis_key_prefix.clone(),
+            })
+            .await
+    }
+
+    async fn refresh_dynamic_routing_cache<T, F, Fut>(
+        state: &SessionState,
+        key: &str,
+        func: F,
+    ) -> RouterResult<T>
+    where
+        F: FnOnce() -> Fut + Send,
+        T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
+        Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send,
+    {
+        cache::get_or_populate_in_memory(
+            state.store.get_cache_store().as_ref(),
+            key,
+            func,
+            &cache::ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE,
+        )
+        .await
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to populate ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE")
+    }
+}
+
 /// Cfetch dynamic routing configs
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 #[instrument(skip_all)]
@@ -941,6 +979,99 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
     }
 }
 
+/// update window for elimination based dynamic routing
+#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
+#[instrument(skip_all)]
+pub async fn update_window_for_elimination_routing(
+    state: &SessionState,
+    payment_attempt: &storage::PaymentAttempt,
+    profile_id: &id_type::ProfileId,
+    dynamic_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
+    elimination_routing_configs_params_interpolator: DynamicRoutingConfigParamsInterpolator,
+    gsm_error_category: common_enums::ErrorCategory,
+) -> RouterResult<()> {
+    if let Some(elimination_algo_ref) = dynamic_algo_ref.elimination_routing_algorithm {
+        if elimination_algo_ref.enabled_feature != routing_types::DynamicRoutingFeatures::None {
+            let client = state
+                .grpc_client
+                .dynamic_routing
+                .elimination_based_client
+                .as_ref()
+                .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
+                    message: "elimination_rate gRPC client not found".to_string(),
+                })?;
+
+            let elimination_routing_config = fetch_dynamic_routing_configs::<
+                routing_types::EliminationRoutingConfig,
+            >(
+                state,
+                profile_id,
+                elimination_algo_ref
+                    .algorithm_id_with_timestamp
+                    .algorithm_id
+                    .ok_or(errors::ApiErrorResponse::GenericNotFoundError {
+                        message: "elimination routing algorithm_id not found".to_string(),
+                    })
+                    .attach_printable(
+                        "elimination_routing_algorithm_id not found in business_profile",
+                    )?,
+            )
+            .await
+            .change_context(errors::ApiErrorResponse::GenericNotFoundError {
+                message: "elimination based dynamic routing configs not found".to_string(),
+            })
+            .attach_printable("unable to retrieve success_rate based dynamic routing configs")?;
+
+            let payment_connector = &payment_attempt.connector.clone().ok_or(
+                errors::ApiErrorResponse::GenericNotFoundError {
+                    message: "unable to derive payment connector from payment attempt".to_string(),
+                },
+            )?;
+
+            let elimination_routing_config_params = elimination_routing_configs_params_interpolator
+                .get_string_val(
+                    elimination_routing_config
+                        .params
+                        .as_ref()
+                        .ok_or(errors::RoutingError::EliminationBasedRoutingParamsNotFoundError)
+                        .change_context(errors::ApiErrorResponse::InternalServerError)?,
+                );
+
+            client
+                .update_elimination_bucket_config(
+                    profile_id.get_string_repr().to_string(),
+                    elimination_routing_config_params,
+                    vec![routing_types::RoutableConnectorChoiceWithBucketName::new(
+                        routing_types::RoutableConnectorChoice {
+                            choice_kind: api_models::routing::RoutableChoiceKind::FullStruct,
+                            connector: common_enums::RoutableConnectors::from_str(
+                                payment_connector.as_str(),
+                            )
+                            .change_context(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable(
+                                "unable to infer routable_connector from connector",
+                            )?,
+                            merchant_connector_id: payment_attempt.merchant_connector_id.clone(),
+                        },
+                        gsm_error_category.to_string(),
+                    )],
+                    elimination_routing_config.elimination_analyser_config,
+                    state.get_grpc_headers(),
+                )
+                .await
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable(
+                    "unable to update elimination based routing buckets in dynamic routing service",
+                )?;
+            Ok(())
+        } else {
+            Ok(())
+        }
+    } else {
+        Ok(())
+    }
+}
+
 /// metrics for contract based dynamic routing
 #[cfg(all(feature = "v1", feature = "dynamic_routing"))]
 #[instrument(skip_all)]

## 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 -->
This will integrate elimination routing in our core flows for connectors to be filtered on basis of error codes received.

### 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`
-->

## 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)?
-->
Testing performed in same way as SR routing.
1. Enable ER for merchant
```
curl --location --request POST 'http://localhost:8080/account/merchant_1744368166/business_profile/pro_GrzjTHd5Pn5hAhwTZPRn/dynamic_routing/elimination/toggle?enable=dynamic_connector_selection' \
--header 'api-key: dev_a4XMpNXd3Yriz5DPsiwU6UbxEkiRkXdqHhwnJPolnVhssLaDEbOg6f5kE6B2hx1f'
```
2. Enable Volume split
```
curl --location --request POST 'http://localhost:8080/account/merchant_1744368166/business_profile/pro_GrzjTHd5Pn5hAhwTZPRn/dynamic_routing/set_volume_split?split=100' \
--header 'api-key: dev_a4XMpNXd3Yriz5DPsiwU6UbxEkiRkXdqHhwnJPolnVhssLaDEbOg6f5kE6B2hx1f'
```
3. Create a volte Payment
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_lnvARj6hcC6DXOC3XXByk5s2qv7EWGhG0v9vFo85RQvz3Ft9IrqMXeSMpvuotZfp' \
--data-raw '{
    "amount": 8000,
    "currency": "EUR",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 8000,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://www.google.com/",
    "payment_method": "bank_redirect",
    "payment_method_type": "open_banking_uk",
    "payment_method_data": {
        "bank_redirect": {
            "open_banking_uk": {}
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "DE",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "DE",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "profile_id": "pro_KEc20awDxPq8pSIDsBV0"
}'
```
cancel the payment after going to the payment url.

4. logs checkup: 
`DEBUG router::core::payments::routing: dynamic_eliminated_connectors: []`
if the above log is present, it shows elimination is working fine.

## 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
