Skip to content

feat(core): populate UCS status_code in response headers #8788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Aug 5, 2025
Merged
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/external_services/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "a9f7cd96693fa034ea69d8e21125ea0f76182fae", package = "rust-grpc-client" }
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "434690566b625262acbd10db56a23435c5bb1735", package = "rust-grpc-client" }


# First party crates
Expand Down
2 changes: 1 addition & 1 deletion crates/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "a9f7cd96693fa034ea69d8e21125ea0f76182fae", package = "rust-grpc-client" }
unified-connector-service-client = { git = "https://github.com/juspay/connector-service", rev = "434690566b625262acbd10db56a23435c5bb1735", package = "rust-grpc-client" }
rustc-hash = "1.1.0"
rustls = "0.22"
rustls-pemfile = "2"
Expand Down
23 changes: 21 additions & 2 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down Expand Up @@ -265,6 +266,10 @@ where

let payments_response_operation = Box::new(PaymentResponse);

connector_http_status_code = router_data.connector_http_status_code;
//add connector http status code metrics
add_connector_http_status_code_metrics(connector_http_status_code);

payments_response_operation
.to_post_update_tracker()?
.save_pm_and_mandate(
Expand Down Expand Up @@ -342,6 +347,10 @@ where

let payments_response_operation = Box::new(PaymentResponse);

connector_http_status_code = router_data.connector_http_status_code;
//add connector http status code metrics
add_connector_http_status_code_metrics(connector_http_status_code);

payments_response_operation
.to_post_update_tracker()?
.save_pm_and_mandate(
Expand Down Expand Up @@ -395,7 +404,7 @@ where
payment_data,
req,
customer,
None,
connector_http_status_code,
None,
connector_response_data,
))
Expand Down Expand Up @@ -492,6 +501,10 @@ 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
add_connector_http_status_code_metrics(connector_http_status_code);

let payment_data = payments_response_operation
.to_post_update_tracker()?
.update_tracker(
Expand All @@ -503,7 +516,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")]
Expand Down
6 changes: 4 additions & 2 deletions crates/router/src/core/payments/flows/authorize_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand All @@ -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(())
}
Expand Down Expand Up @@ -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(),
)
Expand All @@ -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(())
}
3 changes: 2 additions & 1 deletion crates/router/src/core/payments/flows/psync_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,15 @@ 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")?;

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(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand All @@ -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
Expand Down
36 changes: 30 additions & 6 deletions crates/router/src/core/payments/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1585,9 +1585,17 @@ where
status: payment_intent.status,
};

let headers = connector_http_status_code
.map(|status_code| {
vec![(
"connector_http_status_code".to_string(),
Maskable::new_normal(status_code.to_string()),
)]
})
.unwrap_or_default();

Ok(services::ApplicationResponse::JsonWithHeaders((
response,
vec![],
response, headers,
)))
}
}
Expand Down Expand Up @@ -1964,6 +1972,15 @@ where
.clone()
.or(profile.return_url.clone());

let headers = connector_http_status_code
.map(|status_code| {
vec![(
"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,
Expand Down Expand Up @@ -1995,8 +2012,7 @@ where
};

Ok(services::ApplicationResponse::JsonWithHeaders((
response,
vec![],
response, headers,
)))
}
}
Expand Down Expand Up @@ -2061,6 +2077,15 @@ where

let return_url = payment_intent.return_url.or(profile.return_url.clone());

let headers = connector_http_status_code
.map(|status_code| {
vec![(
"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,
Expand Down Expand Up @@ -2096,8 +2121,7 @@ where
};

Ok(services::ApplicationResponse::JsonWithHeaders((
response,
vec![],
response, headers,
)))
}
}
Expand Down
48 changes: 36 additions & 12 deletions crates/router/src/core/unified_connector_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,55 +233,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())?;

let router_data_response =
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response.clone())?;

Ok((status, router_data_response))
let status_code = u16::foreign_try_from(response.status_code)?;

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 router_data_response =
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response.clone())?;

let status_code = u16::foreign_try_from(response.status_code)?;

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 router_data_response =
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response.clone())?;

Ok((status, router_data_response))
let status_code = u16::foreign_try_from(response.status_code)?;

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 router_data_response =
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response)?;
Result::<PaymentsResponseData, ErrorResponse>::foreign_try_from(response.clone())?;

let status_code = u16::foreign_try_from(response.status_code)?;

Ok((status, router_data_response))
Ok((status, router_data_response, status_code))
}
Loading
Loading