Bug: [FEATURE] populate status_code in response



Populate UCS status_code (connector HTTP status code) in response headers
diff --git a/Cargo.lock b/Cargo.lock
index 3e1101fdbf4..60f064c45e4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3525,7 +3525,7 @@ dependencies = [
 [[package]]
 name = "grpc-api-types"
 version = "0.1.0"
-source = "git+https://github.com/juspay/connector-service?rev=0409f6aa1014dd1b9827fabfa4fa424e16d07ebc#0409f6aa1014dd1b9827fabfa4fa424e16d07ebc"
+source = "git+https://github.com/juspay/connector-service?rev=4387a6310dc9c2693b453b455a8032623f3d6a81#4387a6310dc9c2693b453b455a8032623f3d6a81"
 dependencies = [
  "axum 0.8.4",
  "error-stack 0.5.0",
@@ -6899,7 +6899,7 @@ dependencies = [
 [[package]]
 name = "rust-grpc-client"
 version = "0.1.0"
-source = "git+https://github.com/juspay/connector-service?rev=0409f6aa1014dd1b9827fabfa4fa424e16d07ebc#0409f6aa1014dd1b9827fabfa4fa424e16d07ebc"
+source = "git+https://github.com/juspay/connector-service?rev=4387a6310dc9c2693b453b455a8032623f3d6a81#4387a6310dc9c2693b453b455a8032623f3d6a81"
 dependencies = [
  "grpc-api-types",
 ]
diff --git a/config/config.example.toml b/config/config.example.toml
index 55ab8675605..9cb4065b5f9 100644
--- a/config/config.example.toml
+++ b/config/config.example.toml
@@ -1171,3 +1171,6 @@ allow_connected_merchants = false # Enable or disable connected merchant account
 [chat]
 enabled = false                                # Enable or disable chat features
 hyperswitch_ai_host = "http://0.0.0.0:8000"    # Hyperswitch ai workflow host
+
+[proxy_status_mapping]
+proxy_connector_http_status_code = false    # If enabled, the http status code of the connector will be proxied in the response
\ No newline at end of file
diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml
index 23ff9f16e70..6ce8611ef02 100644
--- a/config/deployments/env_specific.toml
+++ b/config/deployments/env_specific.toml
@@ -386,3 +386,6 @@ connection_timeout = 10                 # Connection Timeout Duration in Seconds
 [chat]
 enabled = false                                # Enable or disable chat features
 hyperswitch_ai_host = "http://0.0.0.0:8000"    # Hyperswitch ai workflow host
+
+[proxy_status_mapping]
+proxy_connector_http_status_code = false    # If enabled, the http status code of the connector will be proxied in the response
\ No newline at end of file
diff --git a/config/development.toml b/config/development.toml
index b3aa9fcb9a5..403a55552b2 100644
--- a/config/development.toml
+++ b/config/development.toml
@@ -1269,3 +1269,6 @@ version = "HOSTNAME"
 [chat]
 enabled = false
 hyperswitch_ai_host = "http://0.0.0.0:8000"
+
+[proxy_status_mapping]
+proxy_connector_http_status_code = false    # If enabled, the http status code of the connector will be proxied in the response
\ No newline at end of file
diff --git a/config/docker_compose.toml b/config/docker_compose.toml
index 09ef545f584..b6ef24fcd2c 100644
--- a/config/docker_compose.toml
+++ b/config/docker_compose.toml
@@ -1160,3 +1160,6 @@ connector_names = "stripe, adyen"               # Comma-separated list of allowe
 [infra_values]
 cluster = "CLUSTER"         # value of CLUSTER from deployment
 version = "HOSTNAME"        # value of HOSTNAME from deployment which tells its version
+
+[proxy_status_mapping]
+proxy_connector_http_status_code = false    # If enabled, the http status code of the connector will be proxied in the response
\ No newline at end of file
diff --git a/crates/external_services/Cargo.toml b/crates/external_services/Cargo.toml
index bfa0a8b6132..9e83f331b94 100644
--- a/crates/external_services/Cargo.toml
+++ b/crates/external_services/Cargo.toml
@@ -53,7 +53,7 @@ reqwest = { version = "0.11.27", features = ["rustls-tls"] }
 http = "0.2.12"
 url = { version = "2.5.4", features = ["serde"] }
 quick-xml = { version = "0.31.0", features = ["serialize"] }
-unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "0409f6aa1014dd1b9827fabfa4fa424e16d07ebc", package = "rust-grpc-client" }
+unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "4387a6310dc9c2693b453b455a8032623f3d6a81", package = "rust-grpc-client" }
 
 
 # First party crates
diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml
index 3f3e574fcdd..4d1e9917ad1 100644
--- a/crates/router/Cargo.toml
+++ b/crates/router/Cargo.toml
@@ -88,7 +88,7 @@ reqwest = { version = "0.11.27", features = ["json", "rustls-tls", "gzip", "mult
 ring = "0.17.14"
 rust_decimal = { version = "1.37.1", features = ["serde-with-float", "serde-with-str"] }
 rust-i18n = { git = "https://github.com/kashif-m/rust-i18n", rev = "f2d8096aaaff7a87a847c35a5394c269f75e077a" }
-unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "0409f6aa1014dd1b9827fabfa4fa424e16d07ebc", package = "rust-grpc-client" }
+unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "4387a6310dc9c2693b453b455a8032623f3d6a81", package = "rust-grpc-client" }
 rustc-hash = "1.1.0"
 rustls = "0.22"
 rustls-pemfile = "2"
diff --git a/crates/router/src/compatibility/wrap.rs b/crates/router/src/compatibility/wrap.rs
index f517cb69739..d00ece8360a 100644
--- a/crates/router/src/compatibility/wrap.rs
+++ b/crates/router/src/compatibility/wrap.rs
@@ -87,7 +87,7 @@ where
             let response = S::try_from(response);
             match response {
                 Ok(response) => match serde_json::to_string(&response) {
-                    Ok(res) => api::http_response_json_with_headers(res, headers, None),
+                    Ok(res) => api::http_response_json_with_headers(res, headers, None, None),
                     Err(_) => api::http_response_err(
                         r#"{
                                 "error": {
diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs
index 4f53a2ef2df..6cb720650a2 100644
--- a/crates/router/src/configs/secrets_transformers.rs
+++ b/crates/router/src/configs/secrets_transformers.rs
@@ -548,5 +548,6 @@ pub(crate) async fn fetch_raw_secrets(
         merchant_id_auth: conf.merchant_id_auth,
         infra_values: conf.infra_values,
         enhancement: conf.enhancement,
+        proxy_status_mapping: conf.proxy_status_mapping,
     }
 }
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index 1604b7eaa9d..29c3d3598c6 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -164,6 +164,7 @@ pub struct Settings<S: SecretState> {
     pub infra_values: Option<HashMap<String, String>>,
     #[serde(default)]
     pub enhancement: Option<HashMap<String, String>>,
+    pub proxy_status_mapping: ProxyStatusMapping,
 }
 
 #[derive(Debug, Deserialize, Clone, Default)]
@@ -844,6 +845,12 @@ pub struct MerchantIdAuthSettings {
     pub merchant_id_auth_enabled: bool,
 }
 
+#[derive(Debug, Clone, Default, Deserialize)]
+#[serde(default)]
+pub struct ProxyStatusMapping {
+    pub proxy_connector_http_status_code: bool,
+}
+
 #[derive(Debug, Clone, Default, Deserialize)]
 #[serde(default)]
 pub struct WebhooksSettings {
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index a912dc27b82..106db14fdef 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -214,6 +214,7 @@ where
         .perform_routing(&merchant_context, profile, state, &mut payment_data)
         .await?;
 
+    let mut connector_http_status_code = None;
     let (payment_data, connector_response_data) = match connector {
         ConnectorCallType::PreDetermined(connector_data) => {
             let (mca_type_details, updated_customer, router_data) =
@@ -265,6 +266,9 @@ where
 
             let payments_response_operation = Box::new(PaymentResponse);
 
+            connector_http_status_code = router_data.connector_http_status_code;
+            add_connector_http_status_code_metrics(connector_http_status_code);
+
             payments_response_operation
                 .to_post_update_tracker()?
                 .save_pm_and_mandate(
@@ -342,6 +346,9 @@ where
 
             let payments_response_operation = Box::new(PaymentResponse);
 
+            connector_http_status_code = router_data.connector_http_status_code;
+            add_connector_http_status_code_metrics(connector_http_status_code);
+
             payments_response_operation
                 .to_post_update_tracker()?
                 .save_pm_and_mandate(
@@ -395,7 +402,7 @@ where
         payment_data,
         req,
         customer,
-        None,
+        connector_http_status_code,
         None,
         connector_response_data,
     ))
@@ -492,6 +499,9 @@ where
 
     let payments_response_operation = Box::new(PaymentResponse);
 
+    let connector_http_status_code = router_data.connector_http_status_code;
+    add_connector_http_status_code_metrics(connector_http_status_code);
+
     let payment_data = payments_response_operation
         .to_post_update_tracker()?
         .update_tracker(
@@ -503,7 +513,13 @@ where
         )
         .await?;
 
-    Ok((payment_data, req, None, None, connector_response_data))
+    Ok((
+        payment_data,
+        req,
+        connector_http_status_code,
+        None,
+        connector_response_data,
+    ))
 }
 
 #[cfg(feature = "v1")]
diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs
index e077362cc1f..2aa69e6a36b 100644
--- a/crates/router/src/core/payments/flows/authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/authorize_flow.rs
@@ -860,7 +860,7 @@ async fn call_unified_connector_service_authorize(
 
     let payment_authorize_response = response.into_inner();
 
-    let (status, router_data_response) =
+    let (status, router_data_response, status_code) =
         handle_unified_connector_service_response_for_payment_authorize(
             payment_authorize_response.clone(),
         )
@@ -872,6 +872,7 @@ async fn call_unified_connector_service_authorize(
     router_data.raw_connector_response = payment_authorize_response
         .raw_connector_response
         .map(Secret::new);
+    router_data.connector_http_status_code = Some(status_code);
 
     Ok(())
 }
@@ -916,7 +917,7 @@ async fn call_unified_connector_service_repeat_payment(
 
     let payment_repeat_response = response.into_inner();
 
-    let (status, router_data_response) =
+    let (status, router_data_response, status_code) =
         handle_unified_connector_service_response_for_payment_repeat(
             payment_repeat_response.clone(),
         )
@@ -928,6 +929,7 @@ async fn call_unified_connector_service_repeat_payment(
     router_data.raw_connector_response = payment_repeat_response
         .raw_connector_response
         .map(Secret::new);
+    router_data.connector_http_status_code = Some(status_code);
 
     Ok(())
 }
diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs
index 84752f16520..641af1d397e 100644
--- a/crates/router/src/core/payments/flows/psync_flow.rs
+++ b/crates/router/src/core/payments/flows/psync_flow.rs
@@ -250,7 +250,7 @@ impl Feature<api::PSync, types::PaymentsSyncData>
 
         let payment_get_response = response.into_inner();
 
-        let (status, router_data_response) =
+        let (status, router_data_response, status_code) =
             handle_unified_connector_service_response_for_payment_get(payment_get_response.clone())
                 .change_context(ApiErrorResponse::InternalServerError)
                 .attach_printable("Failed to deserialize UCS response")?;
@@ -258,6 +258,7 @@ impl Feature<api::PSync, types::PaymentsSyncData>
         self.status = status;
         self.response = router_data_response;
         self.raw_connector_response = payment_get_response.raw_connector_response.map(Secret::new);
+        self.connector_http_status_code = Some(status_code);
 
         Ok(())
     }
diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
index 6204d521680..c89f04aa69a 100644
--- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs
+++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
@@ -246,7 +246,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
 
         let payment_register_response = response.into_inner();
 
-        let (status, router_data_response) =
+        let (status, router_data_response, status_code) =
             handle_unified_connector_service_response_for_payment_register(
                 payment_register_response.clone(),
             )
@@ -255,6 +255,7 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
 
         self.status = status;
         self.response = router_data_response;
+        self.connector_http_status_code = Some(status_code);
         // UCS does not return raw connector response for setup mandate right now
         // self.raw_connector_response = payment_register_response
         //     .raw_connector_response
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index dd9b41ee9d2..55bf4fe4298 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -36,7 +36,7 @@ use crate::{
         payments::{self, helpers},
         utils as core_utils,
     },
-    headers::X_PAYMENT_CONFIRM_SOURCE,
+    headers::{X_CONNECTOR_HTTP_STATUS_CODE, X_PAYMENT_CONFIRM_SOURCE},
     routes::{metrics, SessionState},
     services::{self, RedirectForm},
     types::{
@@ -1586,9 +1586,17 @@ where
             status: payment_intent.status,
         };
 
+        let headers = connector_http_status_code
+            .map(|status_code| {
+                vec![(
+                    X_CONNECTOR_HTTP_STATUS_CODE.to_string(),
+                    Maskable::new_normal(status_code.to_string()),
+                )]
+            })
+            .unwrap_or_default();
+
         Ok(services::ApplicationResponse::JsonWithHeaders((
-            response,
-            vec![],
+            response, headers,
         )))
     }
 }
@@ -1969,6 +1977,15 @@ where
             .clone()
             .or(profile.return_url.clone());
 
+        let headers = connector_http_status_code
+            .map(|status_code| {
+                vec![(
+                    X_CONNECTOR_HTTP_STATUS_CODE.to_string(),
+                    Maskable::new_normal(status_code.to_string()),
+                )]
+            })
+            .unwrap_or_default();
+
         let response = api_models::payments::PaymentsResponse {
             id: payment_intent.id.clone(),
             status: payment_intent.status,
@@ -2000,8 +2017,7 @@ where
         };
 
         Ok(services::ApplicationResponse::JsonWithHeaders((
-            response,
-            vec![],
+            response, headers,
         )))
     }
 }
@@ -2066,6 +2082,15 @@ where
 
         let return_url = payment_intent.return_url.or(profile.return_url.clone());
 
+        let headers = connector_http_status_code
+            .map(|status_code| {
+                vec![(
+                    X_CONNECTOR_HTTP_STATUS_CODE.to_string(),
+                    Maskable::new_normal(status_code.to_string()),
+                )]
+            })
+            .unwrap_or_default();
+
         let response = api_models::payments::PaymentsResponse {
             id: payment_intent.id.clone(),
             status: payment_intent.status,
@@ -2101,8 +2126,7 @@ where
         };
 
         Ok(services::ApplicationResponse::JsonWithHeaders((
-            response,
-            vec![],
+            response, headers,
         )))
     }
 }
@@ -2540,7 +2564,7 @@ where
     let mut headers = connector_http_status_code
         .map(|status_code| {
             vec![(
-                "connector_http_status_code".to_string(),
+                X_CONNECTOR_HTTP_STATUS_CODE.to_string(),
                 Maskable::new_normal(status_code.to_string()),
             )]
         })
diff --git a/crates/router/src/core/unified_connector_service.rs b/crates/router/src/core/unified_connector_service.rs
index 79dd1cc7013..107b6e7e7a2 100644
--- a/crates/router/src/core/unified_connector_service.rs
+++ b/crates/router/src/core/unified_connector_service.rs
@@ -241,161 +241,79 @@ pub fn build_unified_connector_service_auth_metadata(
 pub fn handle_unified_connector_service_response_for_payment_authorize(
     response: PaymentServiceAuthorizeResponse,
 ) -> CustomResult<
-    (AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
+    (
+        AttemptStatus,
+        Result<PaymentsResponseData, ErrorResponse>,
+        u16,
+    ),
     UnifiedConnectorServiceError,
 > {
     let status = AttemptStatus::foreign_try_from(response.status())?;
 
-    // <<<<<<< HEAD
-    //     let connector_response_reference_id =
-    //         response.response_ref_id.as_ref().and_then(|identifier| {
-    //             identifier
-    //                 .id_type
-    //                 .clone()
-    //                 .and_then(|id_type| match id_type {
-    //                     payments_grpc::identifier::IdType::Id(id) => Some(id),
-    //                     payments_grpc::identifier::IdType::EncodedData(encoded_data) => {
-    //                         Some(encoded_data)
-    //                     }
-    //                     payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
-    //                 })
-    //         });
-
-    //     let transaction_id = response.transaction_id.as_ref().and_then(|id| {
-    //         id.id_type.clone().and_then(|id_type| match id_type {
-    //             payments_grpc::identifier::IdType::Id(id) => Some(id),
-    //             payments_grpc::identifier::IdType::EncodedData(encoded_data) => Some(encoded_data),
-    //             payments_grpc::identifier::IdType::NoResponseIdMarker(_) => None,
-    //         })
-    //     });
-
-    //     let (connector_metadata, redirection_data) = match response.redirection_data.clone() {
-    //         Some(redirection_data) => match redirection_data.form_type {
-    //             Some(ref form_type) => match form_type {
-    //                 payments_grpc::redirect_form::FormType::Uri(uri) => {
-    //                     let image_data = QrImage::new_from_data(uri.uri.clone())
-    //                         .change_context(UnifiedConnectorServiceError::ParsingFailed)?;
-    //                     let image_data_url = Url::parse(image_data.data.clone().as_str())
-    //                         .change_context(UnifiedConnectorServiceError::ParsingFailed)?;
-    //                     let qr_code_info = QrCodeInformation::QrDataUrl {
-    //                         image_data_url,
-    //                         display_to_timestamp: None,
-    //                     };
-    //                     (
-    //                         Some(qr_code_info.encode_to_value())
-    //                             .transpose()
-    //                             .change_context(UnifiedConnectorServiceError::ParsingFailed)?,
-    //                         None,
-    //                     )
-    //                 }
-    //                 _ => (
-    //                     None,
-    //                     Some(RedirectForm::foreign_try_from(redirection_data)).transpose()?,
-    //                 ),
-    //             },
-    //             None => (None, None),
-    //         },
-    //         None => (None, None),
-    //     };
-
-    //     let router_data_response = match status {
-    //         AttemptStatus::Charged |
-    //                 AttemptStatus::Authorized |
-    //                 AttemptStatus::AuthenticationPending |
-    //                 AttemptStatus::DeviceDataCollectionPending |
-    //                 AttemptStatus::Started |
-    //                 AttemptStatus::AuthenticationSuccessful |
-    //                 AttemptStatus::Authorizing |
-    //                 AttemptStatus::ConfirmationAwaited |
-    //                 AttemptStatus::Pending => Ok(PaymentsResponseData::TransactionResponse {
-    //                     resource_id: match transaction_id.as_ref() {
-    //                         Some(transaction_id) => hyperswitch_domain_models::router_request_types::ResponseId::ConnectorTransactionId(transaction_id.clone()),
-    //                         None => hyperswitch_domain_models::router_request_types::ResponseId::NoResponseId,
-    //                     },
-    //                     redirection_data: Box::new(
-    //                             redirection_data
-    //                     ),
-    //                     mandate_reference: Box::new(None),
-    //                     connector_metadata,
-    //                     network_txn_id: response.network_txn_id.clone(),
-    //                     connector_response_reference_id,
-    //                     incremental_authorization_allowed: response.incremental_authorization_allowed,
-    //                     charges: None,
-    //                 }),
-    //         AttemptStatus::AuthenticationFailed
-    //                 | AttemptStatus::AuthorizationFailed
-    //                 | AttemptStatus::Unresolved
-    //                 | AttemptStatus::Failure => Err(ErrorResponse {
-    //                     code: response.error_code().to_owned(),
-    //                     message: response.error_message().to_owned(),
-    //                     reason: Some(response.error_message().to_owned()),
-    //                     status_code: 500,
-    //                     attempt_status: Some(status),
-    //                     connector_transaction_id: connector_response_reference_id,
-    //                     network_decline_code: None,
-    //                     network_advice_code: None,
-    //                     network_error_message: None,
-    //                 }),
-    //         AttemptStatus::RouterDeclined |
-    //                     AttemptStatus::CodInitiated |
-    //                     AttemptStatus::Voided |
-    //                     AttemptStatus::VoidInitiated |
-    //                     AttemptStatus::CaptureInitiated |
-    //                     AttemptStatus::VoidFailed |
-    //                     AttemptStatus::AutoRefunded |
-    //                     AttemptStatus::PartialCharged |
-    //                     AttemptStatus::PartialChargedAndChargeable |
-    //                     AttemptStatus::PaymentMethodAwaited |
-    //                     AttemptStatus::CaptureFailed |
-    //                     AttemptStatus::IntegrityFailure => return Err(UnifiedConnectorServiceError::NotImplemented(format!(
-    //                         "AttemptStatus {status:?} is not implemented for Unified Connector Service"
-    //                     )).into()),
-    //                 };
-    // =======
+    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;
+
     let router_data_response =
         Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
 
-    Ok((status, router_data_response))
+    Ok((status, router_data_response, status_code))
 }
 
 pub fn handle_unified_connector_service_response_for_payment_get(
     response: payments_grpc::PaymentServiceGetResponse,
 ) -> CustomResult<
-    (AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
+    (
+        AttemptStatus,
+        Result<PaymentsResponseData, ErrorResponse>,
+        u16,
+    ),
     UnifiedConnectorServiceError,
 > {
     let status = AttemptStatus::foreign_try_from(response.status())?;
 
+    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;
+
     let router_data_response =
         Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
 
-    Ok((status, router_data_response))
+    Ok((status, router_data_response, status_code))
 }
 
 pub fn handle_unified_connector_service_response_for_payment_register(
     response: payments_grpc::PaymentServiceRegisterResponse,
 ) -> CustomResult<
-    (AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
+    (
+        AttemptStatus,
+        Result<PaymentsResponseData, ErrorResponse>,
+        u16,
+    ),
     UnifiedConnectorServiceError,
 > {
     let status = AttemptStatus::foreign_try_from(response.status())?;
 
+    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;
+
     let router_data_response =
         Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
 
-    Ok((status, router_data_response))
+    Ok((status, router_data_response, status_code))
 }
 
 pub fn handle_unified_connector_service_response_for_payment_repeat(
     response: payments_grpc::PaymentServiceRepeatEverythingResponse,
 ) -> CustomResult<
-    (AttemptStatus, Result<PaymentsResponseData, ErrorResponse>),
+    (
+        AttemptStatus,
+        Result<PaymentsResponseData, ErrorResponse>,
+        u16,
+    ),
     UnifiedConnectorServiceError,
 > {
     let status = AttemptStatus::foreign_try_from(response.status())?;
 
+    let status_code = transformers::convert_connector_service_status_code(response.status_code)?;
+
     let router_data_response =
         Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
 
-    Ok((status, router_data_response))
+    Ok((status, router_data_response, status_code))
 }
diff --git a/crates/router/src/core/unified_connector_service/transformers.rs b/crates/router/src/core/unified_connector_service/transformers.rs
index 267896ce8ea..9f8ad6c128e 100644
--- a/crates/router/src/core/unified_connector_service/transformers.rs
+++ b/crates/router/src/core/unified_connector_service/transformers.rs
@@ -397,12 +397,14 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceAuthorizeResponse>
             None => (None, None),
         };
 
+        let status_code = convert_connector_service_status_code(response.status_code)?;
+
         let response = if response.error_code.is_some() {
             Err(ErrorResponse {
                 code: response.error_code().to_owned(),
                 message: response.error_message().to_owned(),
                 reason: Some(response.error_message().to_owned()),
-                status_code: 500, //TODO: To be handled once UCS sends proper status codes
+                status_code,
                 attempt_status: Some(status),
                 connector_transaction_id: connector_response_reference_id,
                 network_decline_code: None,
@@ -455,12 +457,14 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceGetResponse>
                     })
             });
 
+        let status_code = convert_connector_service_status_code(response.status_code)?;
+
         let response = if response.error_code.is_some() {
             Err(ErrorResponse {
                 code: response.error_code().to_owned(),
                 message: response.error_message().to_owned(),
                 reason: Some(response.error_message().to_owned()),
-                status_code: 500, //TODO: To be handled once UCS sends proper status codes
+                status_code,
                 attempt_status: Some(status),
                 connector_transaction_id: connector_response_reference_id,
                 network_decline_code: None,
@@ -514,12 +518,14 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceRegisterResponse>
                     })
             });
 
+        let status_code = convert_connector_service_status_code(response.status_code)?;
+
         let response = if response.error_code.is_some() {
             Err(ErrorResponse {
                 code: response.error_code().to_owned(),
                 message: response.error_message().to_owned(),
                 reason: Some(response.error_message().to_owned()),
-                status_code: 500, //TODO: To be handled once UCS sends proper status codes
+                status_code,
                 attempt_status: Some(status),
                 connector_transaction_id: connector_response_reference_id,
                 network_decline_code: None,
@@ -603,12 +609,14 @@ impl ForeignTryFrom<payments_grpc::PaymentServiceRepeatEverythingResponse>
             })
         });
 
+        let status_code = convert_connector_service_status_code(response.status_code)?;
+
         let response = if response.error_code.is_some() {
             Err(ErrorResponse {
                 code: response.error_code().to_owned(),
                 message: response.error_message().to_owned(),
                 reason: Some(response.error_message().to_owned()),
-                status_code: 500, //TODO: To be handled once UCS sends proper status codes
+                status_code,
                 attempt_status: Some(status),
                 connector_transaction_id: transaction_id,
                 network_decline_code: None,
@@ -994,3 +1002,14 @@ impl ForeignTryFrom<common_types::payments::CustomerAcceptance>
         })
     }
 }
+
+pub fn convert_connector_service_status_code(
+    status_code: u32,
+) -> Result<u16, error_stack::Report<UnifiedConnectorServiceError>> {
+    u16::try_from(status_code).map_err(|err| {
+        UnifiedConnectorServiceError::RequestEncodingFailedWithReason(format!(
+            "Failed to convert connector service status code to u16: {err}"
+        ))
+        .into()
+    })
+}
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 01676a11d49..828986aeeee 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -92,6 +92,12 @@ pub mod headers {
     pub const X_CLIENT_SECRET: &str = "X-Client-Secret";
     pub const X_CUSTOMER_ID: &str = "X-Customer-Id";
     pub const X_CONNECTED_MERCHANT_ID: &str = "x-connected-merchant-id";
+    // Header value for X_CONNECTOR_HTTP_STATUS_CODE differs by version.
+    // Constant name is kept the same for consistency across versions.
+    #[cfg(feature = "v1")]
+    pub const X_CONNECTOR_HTTP_STATUS_CODE: &str = "connector_http_status_code";
+    #[cfg(feature = "v2")]
+    pub const X_CONNECTOR_HTTP_STATUS_CODE: &str = "x-connector-http-status-code";
 }
 
 pub mod pii {
diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs
index 780093fd012..e738747f6a4 100644
--- a/crates/router/src/services/api.rs
+++ b/crates/router/src/services/api.rs
@@ -898,8 +898,45 @@ where
                     None
                 }
             });
+            let proxy_connector_http_status_code = if state
+                .conf
+                .proxy_status_mapping
+                .proxy_connector_http_status_code
+            {
+                headers
+                    .iter()
+                    .find(|(key, _)| key == headers::X_CONNECTOR_HTTP_STATUS_CODE)
+                    .and_then(|(_, value)| {
+                        match value.clone().into_inner().parse::<u16>() {
+                            Ok(code) => match http::StatusCode::from_u16(code) {
+                                Ok(status_code) => Some(status_code),
+                                Err(err) => {
+                                    logger::error!(
+                                        "Invalid HTTP status code parsed from connector_http_status_code: {:?}",
+                                        err
+                                    );
+                                    None
+                                }
+                            },
+                            Err(err) => {
+                                logger::error!(
+                                    "Failed to parse connector_http_status_code from header: {:?}",
+                                    err
+                                );
+                                None
+                            }
+                        }
+                    })
+            } else {
+                None
+            };
             match serde_json::to_string(&response) {
-                Ok(res) => http_response_json_with_headers(res, headers, request_elapsed_time),
+                Ok(res) => http_response_json_with_headers(
+                    res,
+                    headers,
+                    request_elapsed_time,
+                    proxy_connector_http_status_code,
+                ),
                 Err(_) => http_response_err(
                     r#"{
                         "error": {
@@ -991,8 +1028,9 @@ pub fn http_response_json_with_headers<T: body::MessageBody + 'static>(
     response: T,
     headers: Vec<(String, Maskable<String>)>,
     request_duration: Option<Duration>,
+    status_code: Option<http::StatusCode>,
 ) -> HttpResponse {
-    let mut response_builder = HttpResponse::Ok();
+    let mut response_builder = HttpResponse::build(status_code.unwrap_or(http::StatusCode::OK));
     for (header_name, header_value) in headers {
         let is_sensitive_header = header_value.is_masked();
         let mut header_value = header_value.into_inner();

## 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 -->
This PR populates UCS status_code (connector HTTP code) in response headers.
It also introduces proxy_connector_http_status_code which when enabled, configures Hyperswitch to mirror (proxy) the connector’s HTTP status code in its own response.

### 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)?
-->
Enable UCS Config
```
curl --location 'http://localhost:8080/configs/' \
--header 'api-key: test_admin' \
--header 'x-tenant-id: public' \
--header 'Content-Type: application/json' \
--data '{
    "key": "ucs_rollout_config_{{merchant_id}}_razorpay_upi_Authorize",
    "value": "1.0"
}'
```
Make a payment
```
curl --location 'http://localhost:8080/v2/payments' \
--header 'api-key: dev_mwKUL6tWhsO8FbwWtKjNllSa7GnZutXqKsv9oLxJRv4c7l8UX9muRdsC4r9Wh2A5' \
--header 'Content-Type: application/json' \
--header 'x-profile-id: pro_cQ4q1A6oYpi89Zi7zyxW' \
--header 'Authorization: api-key=dev_mwKUL6tWhsO8FbwWtKjNllSa7GnZutXqKsv9oLxJRv4c7l8UX9muRdsC4r9Wh2A5' \
--data-raw '{
    "amount_details": {
        "order_amount": 100,
        "currency": "INR"
    },
    "capture_method":"automatic",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "payment_method_data": {
        "upi": {
            "upi_collect": {
                "vpa_id": "success"
            }
        },
        "billing": {
            "address": {
                "line1": "1467",
                "line2": "Harrison Street",
                "line3": "Harrison Street",
                "city": "San Fransico",
                "state": "California",
                "zip": "94122",
                "country": "IN",
                "first_name": "Swangi",
                "last_name": "Kumari"
            },
            "phone": {
                "number": "8056594427",
                "country_code": "+91"
            },
            "email": "swangi.kumari@juspay.in"
        }
    },
    "payment_method_subtype": "upi_collect",
    "payment_method_type": "upi",
    "return_raw_connector_response": true
}'
```
Response 
```
{
    "id": "12345_pay_019860443e3d7f939e80e9a98effba3d",
    "status": "requires_merchant_action",
    "amount": {
        "order_amount": 100,
        "currency": "INR",
        "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": "razorpay",
    "created": "2025-07-31T11:35:41.885Z",
    "payment_method_data": {
        "billing": {
            "address": {
                "city": "San Fransico",
                "country": "IN",
                "line1": "1467",
                "line2": "Harrison Street",
                "line3": "Harrison Street",
                "zip": "94122",
                "state": "California",
                "first_name": "Swangi",
                "last_name": "Kumari"
            },
            "phone": {
                "number": "8056594427",
                "country_code": "+91"
            },
            "email": "swangi.kumari@juspay.in"
        }
    },
    "payment_method_type": "upi",
    "payment_method_subtype": "upi_collect",
    "connector_transaction_id": "order_QzezHblnRQM45h",
    "connector_reference_id": null,
    "merchant_connector_id": "mca_BIFmH1dr6Vh5UoYu2srb",
    "browser_info": null,
    "error": {
        "code": "BAD_REQUEST_ERROR",
        "message": "Invalid VPA. Please enter a valid Virtual Payment Address",
        "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/",
    "authentication_type": "no_three_ds",
    "authentication_type_applied": "no_three_ds",
    "is_iframe_redirection_enabled": null,
    "merchant_reference_id": null,
    "raw_connector_response": "{\"error\":{\"code\":\"BAD_REQUEST_ERROR\",\"description\":\"Invalid VPA. Please enter a valid Virtual Payment Address\",\"source\":\"customer\",\"step\":\"payment_initiation\",\"reason\":\"invalid_vpa\",\"metadata\":{},\"field\":\"vpa\"}}",
    "feature_metadata": null
}
```
Verify response headers for connector_http_status_code
<img width="806" height="373" alt="image" src="https://github.com/user-attachments/assets/d948782f-d7d2-45f8-bff5-ef62c7a93111" />

If proxy_connector_http_status_code is enabled
Hyperswitch resoponse status code will also be 400
<img width="825" height="277" alt="image" src="https://github.com/user-attachments/assets/518446a8-5989-4862-b441-81c20c8dab1a" />



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