diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index ad48797d93..db60a55245 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -934,15 +934,13 @@ async fn call_unified_connector_service_authorize( let payment_authorize_response = response.into_inner(); - let (router_data_response, status_code) = - handle_unified_connector_service_response_for_payment_authorize( - &mut router_data, - payment_authorize_response.clone(), - ) - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize UCS response")?; + let ucs_data = handle_unified_connector_service_response_for_payment_authorize( + payment_authorize_response.clone(), + ) + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)| { + let router_data_response = ucs_data.router_data_response.map(|(response, status)| { router_data.status = status; response }); @@ -951,7 +949,16 @@ async fn call_unified_connector_service_authorize( .raw_connector_response .clone() .map(|raw_connector_response| raw_connector_response.expose().into()); - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); + + // Populate connector_customer_id if present + ucs_data.connector_customer_id.map(|connector_customer_id| { + router_data.connector_customer = Some(connector_customer_id); + }); + + ucs_data.connector_response.map(|customer_response| { + router_data.connector_response = Some(customer_response); + }); Ok((router_data, payment_authorize_response)) }, @@ -1028,15 +1035,13 @@ async fn call_unified_connector_service_repeat_payment( let payment_repeat_response = response.into_inner(); - let (router_data_response, status_code) = - handle_unified_connector_service_response_for_payment_repeat( - &mut router_data, - payment_repeat_response.clone(), - ) - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize UCS response")?; + let ucs_data = handle_unified_connector_service_response_for_payment_repeat( + payment_repeat_response.clone(), + ) + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)| { + let router_data_response = ucs_data.router_data_response.map(|(response, status)| { router_data.status = status; response }); @@ -1045,7 +1050,17 @@ async fn call_unified_connector_service_repeat_payment( .raw_connector_response .clone() .map(|raw_connector_response| raw_connector_response.expose().into()); - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); + + // Populate connector_customer_id if present + ucs_data.connector_customer_id.map(|connector_customer_id| { + router_data.connector_customer = Some(connector_customer_id); + }); + + // Populate connector_response if present + ucs_data.connector_response.map(|connector_response| { + router_data.connector_response = Some(connector_response); + }); Ok((router_data, payment_repeat_response)) }, diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index 5702778761..0f0f59f601 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -229,19 +229,19 @@ impl Feature let payment_void_response = response.into_inner(); - let (router_data_response, status_code) = - handle_unified_connector_service_response_for_payment_cancel( - payment_void_response.clone(), - ) - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize UCS response")?; + let ucs_data = handle_unified_connector_service_response_for_payment_cancel( + payment_void_response.clone(), + ) + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)| { - router_data.status = status; - response - }); + let router_data_response = + ucs_data.router_data_response.map(|(response, status)| { + router_data.status = status; + response + }); router_data.response = router_data_response; - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); Ok((router_data, payment_void_response)) }, diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index 581cf6ad83..97901dc967 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -239,23 +239,23 @@ impl Feature let payment_capture_response = response.into_inner(); - let (router_data_response, status_code) = - handle_unified_connector_service_response_for_payment_capture( - payment_capture_response.clone(), - ) - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize UCS response")?; + let ucs_data = handle_unified_connector_service_response_for_payment_capture( + payment_capture_response.clone(), + ) + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)| { - router_data.status = status; - response - }); + let router_data_response = + ucs_data.router_data_response.map(|(response, status)| { + router_data.status = status; + response + }); router_data.response = router_data_response; router_data.amount_captured = payment_capture_response.captured_amount; router_data.minor_amount_captured = payment_capture_response .minor_captured_amount .map(MinorUnit::new); - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); Ok((router_data, payment_capture_response)) }, diff --git a/crates/router/src/core/payments/flows/external_proxy_flow.rs b/crates/router/src/core/payments/flows/external_proxy_flow.rs index b044ee0876..3c8437d8d6 100644 --- a/crates/router/src/core/payments/flows/external_proxy_flow.rs +++ b/crates/router/src/core/payments/flows/external_proxy_flow.rs @@ -436,15 +436,14 @@ impl Feature let payment_authorize_response = response.into_inner(); - let (router_data_response, status_code) = + let ucs_data = unified_connector_service::handle_unified_connector_service_response_for_payment_authorize( - &mut router_data, payment_authorize_response.clone(), ) .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)|{ + let router_data_response = ucs_data.router_data_response.map(|(response, status)|{ router_data.status = status; response }); @@ -453,7 +452,7 @@ impl Feature .raw_connector_response .clone() .map(|raw_connector_response| raw_connector_response.expose().into()); - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); Ok((router_data, payment_authorize_response)) } 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 ffe5e501fe..ffb4a14a31 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -342,20 +342,26 @@ impl Feature for types::Setup let payment_register_response = response.into_inner(); - let (router_data_response, status_code) = - handle_unified_connector_service_response_for_payment_register( - &mut router_data, - payment_register_response.clone(), - ) - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize UCS response")?; + let ucs_data = handle_unified_connector_service_response_for_payment_register( + payment_register_response.clone(), + ) + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize UCS response")?; - let router_data_response = router_data_response.map(|(response, status)| { - router_data.status = status; - response - }); + let router_data_response = + ucs_data.router_data_response.map(|(response, status)| { + router_data.status = status; + response + }); router_data.response = router_data_response; - router_data.connector_http_status_code = Some(status_code); + router_data.connector_http_status_code = Some(ucs_data.status_code); + + // Populate connector_customer_id if present + ucs_data.connector_customer_id.map(|connector_customer_id| { + router_data.connector_customer = Some(connector_customer_id); + }); + + // Note: Register flow doesn't populate connector_response Ok((router_data, payment_register_response)) }, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e3f0b18d87..8830b92136 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -7905,18 +7905,17 @@ where /// Helper function to populate connector_customer_id from database before calling UCS /// This checks if a connector_customer_id already exists in the database and populates it into router_data /// Returns true if connector_customer_id was found and populated from DB, false otherwise -async fn populate_connector_customer_from_db_before_ucs( +async fn populate_connector_customer_from_db_before_ucs( state: &SessionState, - router_data: &mut RouterData, connector_label: Option<&str>, payment_data: &D, customer: &Option, merchant_connector_account: &MerchantConnectorAccountType, -) -> RouterResult +) -> RouterResult> where D: OperationSessionGetters, { - let mut connector_customer_id_was_populated = false; + let mut connector_customer_id_was_populated = None; if let (Some(connector_label), Some(connector_name)) = ( connector_label, @@ -7955,8 +7954,7 @@ where payment_data.get_payment_intent().payment_id.get_string_repr(), connector_label ); - router_data.connector_customer = Some(connector_customer_id.to_string()); - connector_customer_id_was_populated = true; + connector_customer_id_was_populated = Some(connector_customer_id.to_string()); } None => { router_env::logger::info!( @@ -7978,14 +7976,14 @@ where async fn save_connector_customer_id_after_ucs( state: &SessionState, router_data: &RouterData, - connector_customer_id_was_populated_from_db: bool, + connector_customer_id_was_populated_from_db: Option, connector_label: &Option, customer: &Option, merchant_context: &domain::MerchantContext, payment_id: String, ) -> RouterResult<()> { // Process only if connector_customer_id was NOT already in DB - if !connector_customer_id_was_populated_from_db { + if !connector_customer_id_was_populated_from_db.is_some() { // Extract necessary fields and save if both are present match (&router_data.connector_customer, connector_label.as_ref()) { (Some(connector_customer_id), Some(connector_label_str)) => { @@ -8019,7 +8017,6 @@ async fn save_connector_customer_id_after_ucs( payment_id ); } - Ok(()) } @@ -8188,7 +8185,6 @@ where let connector_customer_id_was_populated_from_db = populate_connector_customer_from_db_before_ucs( state, - &mut router_data, connector_label.as_deref(), payment_data, customer, @@ -8196,6 +8192,12 @@ where ) .await?; + connector_customer_id_was_populated_from_db + .as_ref() + .map(|id| { + router_data.connector_customer = Some(id.clone()); + }); + // Based on the preprocessing response, decide whether to continue with UCS call if should_continue { router_data @@ -8380,17 +8382,20 @@ where // Populate connector_customer_id from database for shadow UCS call // Use the pre-calculated connector_label // Track whether ID was found in DB to avoid redundant save later - let _connector_customer_id_was_populated_from_db = + let connector_customer_id_was_populated_from_db = populate_connector_customer_from_db_before_ucs( state, - &mut unified_connector_service_router_data, unified_connector_service_connector_label.as_deref(), payment_data, customer, &merchant_connector_account, ) .await - .unwrap_or(false); // Don't fail the main flow if shadow UCS populate fails + .unwrap_or_default(); // Don't fail the main flow if shadow UCS populate fails + + connector_customer_id_was_populated_from_db.map(|id| { + unified_connector_service_router_data.connector_customer = Some(id); + }); let lineage_ids = grpc_client::LineageIds::new( business_profile.merchant_id.clone(), diff --git a/crates/router/src/core/unified_connector_service.rs b/crates/router/src/core/unified_connector_service.rs index 8f21972d9d..4109f93976 100644 --- a/crates/router/src/core/unified_connector_service.rs +++ b/crates/router/src/core/unified_connector_service.rs @@ -60,7 +60,7 @@ use crate::{ events::connector_api_logs::ConnectorEvent, headers::{CONTENT_TYPE, X_REQUEST_ID}, routes::SessionState, - types::transformers::ForeignTryFrom, + types::{transformers::ForeignTryFrom, UcsResponseData}, }; pub mod transformers; @@ -69,13 +69,7 @@ pub mod transformers; pub use transformers::{WebhookTransformData, WebhookTransformationStatus}; /// Type alias for return type used by unified connector service response handlers -type UnifiedConnectorServiceResult = CustomResult< - ( - Result<(PaymentsResponseData, AttemptStatus), ErrorResponse>, - u16, - ), - UnifiedConnectorServiceError, ->; +type UnifiedConnectorServiceResult = CustomResult; /// Checks if the Unified Connector Service (UCS) is available for use async fn check_ucs_availability(state: &SessionState) -> UcsAvailability { @@ -759,8 +753,7 @@ pub fn build_unified_connector_service_external_vault_proxy_metadata( } } -pub fn handle_unified_connector_service_response_for_payment_authorize( - router_data: &mut RouterData, +pub fn handle_unified_connector_service_response_for_payment_authorize( response: PaymentServiceAuthorizeResponse, ) -> UnifiedConnectorServiceResult { let status_code = transformers::convert_connector_service_status_code(response.status_code)?; @@ -770,17 +763,17 @@ pub fn handle_unified_connector_service_response_for_payment_authorize( response.clone(), )?; - // Populate connector_customer_id from UCS state - populate_connector_customer_id_from_ucs_state(router_data, response.state.as_ref()); + let connector_customer_id = + extract_connector_customer_id_from_ucs_state(response.state.as_ref()); + let connector_response = + extract_connector_response_from_ucs(response.connector_response.as_ref()); - // Populate connector_response from UCS response (log errors, don't fail) - populate_connector_response_from_ucs(router_data, response.connector_response.as_ref()) - .inspect_err(|err| { - router_env::logger::warn!("Failed to populate connector_response from UCS: {:?}", err); - }) - .ok(); - - Ok((router_data_response, status_code)) + Ok(UcsResponseData { + router_data_response, + status_code, + connector_customer_id, + connector_response, + }) } pub fn handle_unified_connector_service_response_for_payment_capture( @@ -791,11 +784,15 @@ pub fn handle_unified_connector_service_response_for_payment_capture( let router_data_response = Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?; - Ok((router_data_response, status_code)) + Ok(UcsResponseData { + router_data_response, + status_code, + connector_customer_id: None, + connector_response: None, + }) } -pub fn handle_unified_connector_service_response_for_payment_register( - router_data: &mut RouterData, +pub fn handle_unified_connector_service_response_for_payment_register( response: payments_grpc::PaymentServiceRegisterResponse, ) -> UnifiedConnectorServiceResult { let status_code = transformers::convert_connector_service_status_code(response.status_code)?; @@ -805,14 +802,18 @@ pub fn handle_unified_connector_service_response_for_payment_register( response.clone(), )?; - // Populate connector_customer_id from UCS state - populate_connector_customer_id_from_ucs_state(router_data, response.state.as_ref()); + let connector_customer_id = + extract_connector_customer_id_from_ucs_state(response.state.as_ref()); - Ok((router_data_response, status_code)) + Ok(UcsResponseData { + router_data_response, + status_code, + connector_customer_id, + connector_response: None, // Register doesn't need connector_response + }) } -pub fn handle_unified_connector_service_response_for_payment_repeat( - router_data: &mut RouterData, +pub fn handle_unified_connector_service_response_for_payment_repeat( response: payments_grpc::PaymentServiceRepeatEverythingResponse, ) -> UnifiedConnectorServiceResult { let status_code = transformers::convert_connector_service_status_code(response.status_code)?; @@ -822,45 +823,46 @@ pub fn handle_unified_connector_service_response_for_payment_repeat( response.clone(), )?; - // Populate connector_customer_id from UCS state - populate_connector_customer_id_from_ucs_state(router_data, response.state.as_ref()); + let connector_customer_id = + extract_connector_customer_id_from_ucs_state(response.state.as_ref()); + let connector_response = + extract_connector_response_from_ucs(response.connector_response.as_ref()); - // Populate connector_response from UCS response (log errors, don't fail) - populate_connector_response_from_ucs(router_data, response.connector_response.as_ref()) - .inspect_err(|err| { - router_env::logger::warn!("Failed to populate connector_response from UCS: {:?}", err); - }) - .ok(); - - Ok((router_data_response, status_code)) + Ok(UcsResponseData { + router_data_response, + status_code, + connector_customer_id, + connector_response, + }) } -/// Extracts and populates connector_customer_id from UCS state into router_data -pub fn populate_connector_customer_id_from_ucs_state( - router_data: &mut RouterData, +/// Extracts connector_customer_id from UCS state +pub fn extract_connector_customer_id_from_ucs_state( ucs_state: Option<&payments_grpc::ConnectorState>, -) { - ucs_state.map(|state| { +) -> Option { + ucs_state.and_then(|state| { state .connector_customer_id .as_ref() - .map(|id| router_data.connector_customer = Some(id.to_string())); - }); + .map(|id| id.to_string()) + }) } -/// Extracts and populates connector_response from UCS response into router_data -pub fn populate_connector_response_from_ucs( - router_data: &mut RouterData, +/// Extracts connector_response from UCS response +pub fn extract_connector_response_from_ucs( connector_response: Option<&payments_grpc::ConnectorResponseData>, -) -> RouterResult<()> { - router_data.connector_response = connector_response - .map(|data| { - >::foreign_try_from(data.clone()) - }) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize connector_response from UCS")?; - Ok(()) +) -> Option { + connector_response.and_then(|data| { + >::foreign_try_from(data.clone()) + .map_err(|e| { + logger::warn!( + error=?e, + "Failed to deserialize connector_response from UCS" + ); + e + }) + .ok() + }) } pub fn handle_unified_connector_service_response_for_payment_cancel( @@ -871,7 +873,12 @@ pub fn handle_unified_connector_service_response_for_payment_cancel( let router_data_response = Result::<(PaymentsResponseData, AttemptStatus), ErrorResponse>::foreign_try_from(response)?; - Ok((router_data_response, status_code)) + Ok(UcsResponseData { + router_data_response, + status_code, + connector_customer_id: None, + connector_response: None, + }) } pub fn build_webhook_secrets_from_merchant_connector_account( diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 0292595897..36591a33c8 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -721,6 +721,15 @@ pub struct PspTokenResult { pub token: Result, } +/// Data extracted from UCS response +pub struct UcsResponseData { + pub router_data_response: + Result<(PaymentsResponseData, common_enums::AttemptStatus), ErrorResponse>, + pub status_code: u16, + pub connector_customer_id: Option, + pub connector_response: Option, +} + #[derive(Debug, Clone, Copy)] pub enum Redirection { Redirect,