Bug: [FEATURE] Integrate global SR 



### Feature Description

In dynamic routing, we need to keep track of global success rates as well. We need to support the integration for tracking global SR

### Possible Implementation

The dynamic routing modules and corresponding proto file would need to be changed

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs
index 2e570816ab4..8e502767575 100644
--- a/crates/api_models/src/routing.rs
+++ b/crates/api_models/src/routing.rs
@@ -779,6 +779,7 @@ impl Default for SuccessBasedRoutingConfig {
                     duration_in_mins: Some(5),
                     max_total_count: Some(2),
                 }),
+                specificity_level: SuccessRateSpecificityLevel::default(),
             }),
         }
     }
@@ -801,6 +802,8 @@ pub struct SuccessBasedRoutingConfigBody {
     pub default_success_rate: Option<f64>,
     pub max_aggregates_size: Option<u32>,
     pub current_block_threshold: Option<CurrentBlockThreshold>,
+    #[serde(default)]
+    pub specificity_level: SuccessRateSpecificityLevel,
 }
 
 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
@@ -809,6 +812,14 @@ pub struct CurrentBlockThreshold {
     pub max_total_count: Option<u64>,
 }
 
+#[derive(serde::Serialize, serde::Deserialize, Debug, Default, Clone, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SuccessRateSpecificityLevel {
+    #[default]
+    Merchant,
+    Global,
+}
+
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct SuccessBasedRoutingPayloadWrapper {
     pub updated_config: SuccessBasedRoutingConfig,
@@ -849,6 +860,7 @@ impl SuccessBasedRoutingConfigBody {
                 .as_mut()
                 .map(|threshold| threshold.update(current_block_threshold));
         }
+        self.specificity_level = new.specificity_level
     }
 }
 
diff --git a/crates/diesel_models/src/dynamic_routing_stats.rs b/crates/diesel_models/src/dynamic_routing_stats.rs
index c055359d8b0..90cf4689080 100644
--- a/crates/diesel_models/src/dynamic_routing_stats.rs
+++ b/crates/diesel_models/src/dynamic_routing_stats.rs
@@ -20,6 +20,7 @@ pub struct DynamicRoutingStatsNew {
     pub conclusive_classification: common_enums::SuccessBasedRoutingConclusiveState,
     pub created_at: time::PrimitiveDateTime,
     pub payment_method_type: Option<common_enums::PaymentMethodType>,
+    pub global_success_based_connector: Option<String>,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Queryable, Selectable, Insertable)]
@@ -40,4 +41,5 @@ pub struct DynamicRoutingStats {
     pub conclusive_classification: common_enums::SuccessBasedRoutingConclusiveState,
     pub created_at: time::PrimitiveDateTime,
     pub payment_method_type: Option<common_enums::PaymentMethodType>,
+    pub global_success_based_connector: Option<String>,
 }
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index dfe3d5acc23..7930ad5d9ae 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -435,6 +435,8 @@ diesel::table! {
         created_at -> Timestamp,
         #[max_length = 64]
         payment_method_type -> Nullable<Varchar>,
+        #[max_length = 64]
+        global_success_based_connector -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs
index b5361f4c95c..13abf8ee64b 100644
--- a/crates/diesel_models/src/schema_v2.rs
+++ b/crates/diesel_models/src/schema_v2.rs
@@ -447,6 +447,8 @@ diesel::table! {
         created_at -> Timestamp,
         #[max_length = 64]
         payment_method_type -> Nullable<Varchar>,
+        #[max_length = 64]
+        global_success_based_connector -> Nullable<Varchar>,
     }
 }
 
diff --git a/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs b/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs
index fca4ff61fcc..278a2b50d15 100644
--- a/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs
+++ b/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs
@@ -1,15 +1,17 @@
 use api_models::routing::{
     CurrentBlockThreshold, RoutableConnectorChoice, RoutableConnectorChoiceWithStatus,
-    SuccessBasedRoutingConfig, SuccessBasedRoutingConfigBody,
+    SuccessBasedRoutingConfig, SuccessBasedRoutingConfigBody, SuccessRateSpecificityLevel,
 };
 use common_utils::{ext_traits::OptionExt, transformers::ForeignTryFrom};
 use error_stack::ResultExt;
 use router_env::{instrument, logger, tracing};
 pub use success_rate::{
-    success_rate_calculator_client::SuccessRateCalculatorClient, CalSuccessRateConfig,
+    success_rate_calculator_client::SuccessRateCalculatorClient, CalGlobalSuccessRateConfig,
+    CalGlobalSuccessRateRequest, CalGlobalSuccessRateResponse, CalSuccessRateConfig,
     CalSuccessRateRequest, CalSuccessRateResponse,
     CurrentBlockThreshold as DynamicCurrentThreshold, InvalidateWindowsRequest,
-    InvalidateWindowsResponse, LabelWithStatus, UpdateSuccessRateWindowConfig,
+    InvalidateWindowsResponse, LabelWithStatus,
+    SuccessRateSpecificityLevel as ProtoSpecificityLevel, UpdateSuccessRateWindowConfig,
     UpdateSuccessRateWindowRequest, UpdateSuccessRateWindowResponse,
 };
 #[allow(
@@ -51,6 +53,15 @@ pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync {
         id: String,
         headers: GrpcHeaders,
     ) -> DynamicRoutingResult<InvalidateWindowsResponse>;
+    /// To calculate both global and merchant specific success rate for the list of chosen connectors
+    async fn calculate_entity_and_global_success_rate(
+        &self,
+        id: String,
+        success_rate_based_config: SuccessBasedRoutingConfig,
+        params: String,
+        label_input: Vec<RoutableConnectorChoice>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<CalGlobalSuccessRateResponse>;
 }
 
 #[async_trait::async_trait]
@@ -113,6 +124,7 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {
             .transpose()?;
 
         let labels_with_status = label_input
+            .clone()
             .into_iter()
             .map(|conn_choice| LabelWithStatus {
                 label: conn_choice.routable_connector_choice.to_string(),
@@ -120,12 +132,21 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {
             })
             .collect();
 
+        let global_labels_with_status = label_input
+            .into_iter()
+            .map(|conn_choice| LabelWithStatus {
+                label: conn_choice.routable_connector_choice.connector.to_string(),
+                status: conn_choice.status,
+            })
+            .collect();
+
         let request = grpc_client::create_grpc_request(
             UpdateSuccessRateWindowRequest {
                 id,
                 params,
                 labels_with_status,
                 config,
+                global_labels_with_status,
             },
             headers,
         );
@@ -165,6 +186,55 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient<Client> {
 
         Ok(response)
     }
+
+    async fn calculate_entity_and_global_success_rate(
+        &self,
+        id: String,
+        success_rate_based_config: SuccessBasedRoutingConfig,
+        params: String,
+        label_input: Vec<RoutableConnectorChoice>,
+        headers: GrpcHeaders,
+    ) -> DynamicRoutingResult<CalGlobalSuccessRateResponse> {
+        let labels = label_input
+            .clone()
+            .into_iter()
+            .map(|conn_choice| conn_choice.to_string())
+            .collect::<Vec<_>>();
+
+        let global_labels = label_input
+            .into_iter()
+            .map(|conn_choice| conn_choice.connector.to_string())
+            .collect::<Vec<_>>();
+
+        let config = success_rate_based_config
+            .config
+            .map(ForeignTryFrom::foreign_try_from)
+            .transpose()?;
+
+        let request = grpc_client::create_grpc_request(
+            CalGlobalSuccessRateRequest {
+                entity_id: id,
+                entity_params: params,
+                entity_labels: labels,
+                global_labels,
+                config,
+            },
+            headers,
+        );
+
+        let response = self
+            .clone()
+            .fetch_entity_and_global_success_rate(request)
+            .await
+            .change_context(DynamicRoutingError::SuccessRateBasedRoutingFailure(
+                "Failed to fetch the entity and global success rate".to_string(),
+            ))?
+            .into_inner();
+
+        logger::info!(dynamic_routing_response=?response);
+
+        Ok(response)
+    }
 }
 
 impl ForeignTryFrom<CurrentBlockThreshold> for DynamicCurrentThreshold {
@@ -216,6 +286,30 @@ impl ForeignTryFrom<SuccessBasedRoutingConfigBody> for CalSuccessRateConfig {
                 .change_context(DynamicRoutingError::MissingRequiredField {
                     field: "default_success_rate".to_string(),
                 })?,
+            specificity_level: match config.specificity_level {
+                SuccessRateSpecificityLevel::Merchant => Some(ProtoSpecificityLevel::Entity.into()),
+                SuccessRateSpecificityLevel::Global => Some(ProtoSpecificityLevel::Global.into()),
+            },
+        })
+    }
+}
+
+impl ForeignTryFrom<SuccessBasedRoutingConfigBody> for CalGlobalSuccessRateConfig {
+    type Error = error_stack::Report<DynamicRoutingError>;
+    fn foreign_try_from(config: SuccessBasedRoutingConfigBody) -> Result<Self, Self::Error> {
+        Ok(Self {
+            entity_min_aggregates_size: config
+                .min_aggregates_size
+                .get_required_value("min_aggregate_size")
+                .change_context(DynamicRoutingError::MissingRequiredField {
+                    field: "min_aggregates_size".to_string(),
+                })?,
+            entity_default_success_rate: config
+                .default_success_rate
+                .get_required_value("default_success_rate")
+                .change_context(DynamicRoutingError::MissingRequiredField {
+                    field: "default_success_rate".to_string(),
+                })?,
         })
     }
 }
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index 32c2402e108..9ad89500eb9 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -624,6 +624,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::routing::StraightThroughAlgorithm,
         api_models::routing::ConnectorVolumeSplit,
         api_models::routing::ConnectorSelection,
+        api_models::routing::SuccessRateSpecificityLevel,
         api_models::routing::ToggleDynamicRoutingQuery,
         api_models::routing::ToggleDynamicRoutingPath,
         api_models::routing::ast::RoutableChoiceKind,
diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs
index 1395563a30a..ca14e3e7e48 100644
--- a/crates/router/src/core/routing/helpers.rs
+++ b/crates/router/src/core/routing/helpers.rs
@@ -714,7 +714,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
             );
 
         let success_based_connectors = client
-            .calculate_success_rate(
+            .calculate_entity_and_global_success_rate(
                 business_profile.get_id().get_string_repr().into(),
                 success_based_routing_configs.clone(),
                 success_based_routing_config_params.clone(),
@@ -730,28 +730,34 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
         let payment_status_attribute =
             get_desired_payment_status_for_success_routing_metrics(payment_attempt.status);
 
-        let first_success_based_connector_label = &success_based_connectors
-            .labels_with_score
+        let first_merchant_success_based_connector = &success_based_connectors
+            .entity_scores_with_labels
             .first()
             .ok_or(errors::ApiErrorResponse::InternalServerError)
             .attach_printable(
                 "unable to fetch the first connector from list of connectors obtained from dynamic routing service",
-            )?
-            .label
-            .to_string();
+            )?;
 
-        let (first_success_based_connector, _) = first_success_based_connector_label
+        let (first_merchant_success_based_connector_label, _) = first_merchant_success_based_connector.label
             .split_once(':')
             .ok_or(errors::ApiErrorResponse::InternalServerError)
             .attach_printable(format!(
                 "unable to split connector_name and mca_id from the first connector {:?} obtained from dynamic routing service",
-                first_success_based_connector_label
+                first_merchant_success_based_connector.label
             ))?;
 
+        let first_global_success_based_connector = &success_based_connectors
+            .global_scores_with_labels
+            .first()
+            .ok_or(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable(
+                "unable to fetch the first global connector from list of connectors obtained from dynamic routing service",
+            )?;
+
         let outcome = get_success_based_metrics_outcome_for_payment(
             payment_status_attribute,
             payment_connector.to_string(),
-            first_success_based_connector.to_string(),
+            first_merchant_success_based_connector_label.to_string(),
         );
 
         let dynamic_routing_stats = DynamicRoutingStatsNew {
@@ -760,7 +766,8 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
             merchant_id: payment_attempt.merchant_id.to_owned(),
             profile_id: payment_attempt.profile_id.to_owned(),
             amount: payment_attempt.get_total_amount(),
-            success_based_routing_connector: first_success_based_connector.to_string(),
+            success_based_routing_connector: first_merchant_success_based_connector_label
+                .to_string(),
             payment_connector: payment_connector.to_string(),
             payment_method_type: payment_attempt.payment_method_type,
             currency: payment_attempt.currency,
@@ -770,6 +777,9 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
             payment_status: payment_attempt.status,
             conclusive_classification: outcome,
             created_at: common_utils::date_time::now(),
+            global_success_based_connector: Some(
+                first_global_success_based_connector.label.to_string(),
+            ),
         };
 
         core_metrics::DYNAMIC_SUCCESS_BASED_ROUTING.add(
@@ -788,8 +798,20 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
                     ),
                 ),
                 (
-                    "success_based_routing_connector",
-                    first_success_based_connector.to_string(),
+                    "merchant_specific_success_based_routing_connector",
+                    first_merchant_success_based_connector_label.to_string(),
+                ),
+                (
+                    "merchant_specific_success_based_routing_connector_score",
+                    first_merchant_success_based_connector.score.to_string(),
+                ),
+                (
+                    "global_success_based_routing_connector",
+                    first_global_success_based_connector.label.to_string(),
+                ),
+                (
+                    "global_success_based_routing_connector_score",
+                    first_global_success_based_connector.score.to_string(),
                 ),
                 ("payment_connector", payment_connector.to_string()),
                 (
diff --git a/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/down.sql b/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/down.sql
new file mode 100644
index 00000000000..c99db9a384d
--- /dev/null
+++ b/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/down.sql
@@ -0,0 +1,3 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE dynamic_routing_stats
+DROP COLUMN IF EXISTS global_success_based_connector;
\ No newline at end of file
diff --git a/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/up.sql b/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/up.sql
new file mode 100644
index 00000000000..81e00a9753c
--- /dev/null
+++ b/migrations/2025-01-07-101337_global_sr_connector_dynamic_routing/up.sql
@@ -0,0 +1,3 @@
+-- Your SQL goes here
+ALTER TABLE dynamic_routing_stats
+ADD COLUMN IF NOT EXISTS global_success_based_connector VARCHAR(64);
\ No newline at end of file
diff --git a/proto/success_rate.proto b/proto/success_rate.proto
index f49618bc4b6..17d566af356 100644
--- a/proto/success_rate.proto
+++ b/proto/success_rate.proto
@@ -7,6 +7,8 @@ service SuccessRateCalculator {
     rpc UpdateSuccessRateWindow (UpdateSuccessRateWindowRequest) returns (UpdateSuccessRateWindowResponse);
 
     rpc InvalidateWindows (InvalidateWindowsRequest) returns (InvalidateWindowsResponse);
+
+    rpc FetchEntityAndGlobalSuccessRate (CalGlobalSuccessRateRequest) returns (CalGlobalSuccessRateResponse);
 }
 
 // API-1 types
@@ -20,6 +22,12 @@ message CalSuccessRateRequest {
 message CalSuccessRateConfig {
     uint32 min_aggregates_size = 1;
     double default_success_rate = 2;
+    optional SuccessRateSpecificityLevel specificity_level = 3;
+}
+
+enum SuccessRateSpecificityLevel {
+    ENTITY = 0;
+    GLOBAL = 1;
 }
 
 message CalSuccessRateResponse {
@@ -31,12 +39,13 @@ message LabelWithScore {
     string label = 2;
 }
 
- // API-2 types
+// API-2 types
 message UpdateSuccessRateWindowRequest {
     string id = 1;
     string params = 2;
     repeated LabelWithStatus labels_with_status = 3;
     UpdateSuccessRateWindowConfig config = 4;
+    repeated LabelWithStatus global_labels_with_status = 5;
 }
 
 message LabelWithStatus {
@@ -68,9 +77,28 @@ message InvalidateWindowsRequest {
 }
 
 message InvalidateWindowsResponse {
-   enum InvalidationStatus {
+    enum InvalidationStatus {
       WINDOW_INVALIDATION_SUCCEEDED = 0;
       WINDOW_INVALIDATION_FAILED = 1;
    }
    InvalidationStatus status = 1;
+}
+
+// API-4 types
+message CalGlobalSuccessRateRequest {
+    string entity_id = 1;
+    string entity_params = 2;
+    repeated string entity_labels = 3;
+    repeated string global_labels = 4;
+    CalGlobalSuccessRateConfig config = 5;
+}
+
+message CalGlobalSuccessRateConfig {
+    uint32 entity_min_aggregates_size = 1;
+    double entity_default_success_rate = 2;
+}
+
+message CalGlobalSuccessRateResponse {
+    repeated LabelWithScore entity_scores_with_labels = 1;
+    repeated LabelWithScore global_scores_with_labels = 2;
 }
\ No newline at end of file

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

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

## Description
<!-- Describe your changes in detail -->
Integrate global Success rates for dynamic routing


### 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)?
-->
1. toggle success based routing with dynamic connector selection enabled
```
curl --location --request POST 'http://localhost:8080/account/sarthak2/business_profile/pro_F5mC1GGXHPLTtfcBnZEP/dynamic_routing/success_based/toggle?enable=dynamic_connector_selection' \
--header 'api-key: dev_RQxKf8JqHb5tTfTp13JMnI7TyWgXIHaa2AP5VkjLtst1CRJPArSfqasfGQI1oIAQ'
```
2. Set volume split for dynamic routing
```
curl --location --request POST 'http://localhost:8080/account/sarthak2/business_profile/pro_F5mC1GGXHPLTtfcBnZEP/dynamic_routing/set_volume_split?split=80' \
--header 'api-key: dev_RQxKf8JqHb5tTfTp13JMnI7TyWgXIHaa2AP5VkjLtst1CRJPArSfqasfGQI1oIAQ'
```
3. Make a payment
4. Global SR connector should be populated in DB
<img width="639" alt="image" src="https://github.com/user-attachments/assets/1fadcb60-21d1-4958-8511-9a3872bb3daf" />


## 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`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
