diff --git a/crates/router/src/connector/bankofamerica.rs b/crates/router/src/connector/bankofamerica.rs index cac3bacc211d..c55d2845f904 100644 --- a/crates/router/src/connector/bankofamerica.rs +++ b/crates/router/src/connector/bankofamerica.rs @@ -356,7 +356,7 @@ impl ConnectorIntegration CustomResult { - self.build_error_response(res) + build_auth_and_capture_error_response(res) } } @@ -517,7 +517,7 @@ impl ConnectorIntegration CustomResult { - self.build_error_response(res) + build_auth_and_capture_error_response(res) } } @@ -797,3 +797,49 @@ impl api::IncomingWebhook for Bankofamerica { Err(errors::ConnectorError::WebhooksNotImplemented).into_report() } } + +fn build_auth_and_capture_error_response( + res: Response, +) -> CustomResult { + let response: bankofamerica::BankOfAmericaErrorResponse = res + .response + .parse_struct("BankOfAmerica ErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + let error_message = if res.status_code == 401 { + consts::CONNECTOR_UNAUTHORIZED_ERROR + } else { + consts::NO_ERROR_MESSAGE + }; + + let (code, message) = match response.error_information { + Some(ref error_info) => (error_info.reason.clone(), error_info.message.clone()), + None => ( + response + .reason + .map_or(consts::NO_ERROR_CODE.to_string(), |reason| { + reason.to_string() + }), + response + .message + .map_or(error_message.to_string(), |message| message), + ), + }; + let connector_reason = match response.details { + Some(details) => details + .iter() + .map(|det| format!("{} : {}", det.field, det.reason)) + .collect::>() + .join(", "), + None => message.clone(), + }; + + Ok(ErrorResponse { + status_code: res.status_code, + code, + message, + reason: Some(connector_reason), + attempt_status: None, + connector_transaction_id: response.id, + }) +} diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index bbec9022835c..0f9ae5f0ce1e 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -1,6 +1,7 @@ use api_models::payments; use base64::Engine; -use common_utils::pii; +use common_utils::{errors::CustomResult, pii}; +use error_stack::ResultExt; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -571,6 +572,23 @@ pub struct BankOfAmericaErrorInformation { message: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BOATransactionMetadata { + auth_id: String, +} + +pub fn get_connector_metadata( + auth_id: String, +) -> CustomResult, errors::ConnectorError> { + Some( + common_utils::ext_traits::Encode::::encode_to_value( + &BOATransactionMetadata { auth_id }, + ), + ) + .transpose() + .change_context(errors::ConnectorError::ResponseHandlingFailed) +} + impl TryFrom< types::ResponseRouterData< @@ -596,22 +614,28 @@ impl info_response.status, item.data.request.is_auto_capture()?, )), - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( - info_response.id.clone(), - ), - redirection_data: None, - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: Some( - info_response - .client_reference_information - .code - .unwrap_or(info_response.id), - ), - incremental_authorization_allowed: None, - }), + response: { + Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + info_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: if !item.data.request.is_auto_capture()? { + get_connector_metadata(info_response.id.clone())? + } else { + None + }, + network_txn_id: None, + connector_response_reference_id: Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id), + ), + incremental_authorization_allowed: None, + }) + }, ..item.data }), BankOfAmericaPaymentsResponse::ErrorInformation(error_response) => Ok(Self { @@ -624,7 +648,7 @@ impl reason: error_response.error_information.reason, status_code: item.http_code, attempt_status: None, - connector_transaction_id: None, + connector_transaction_id: Some(error_response.id), }), ..item.data }), @@ -682,7 +706,7 @@ impl reason: error_response.error_information.reason, status_code: item.http_code, attempt_status: None, - connector_transaction_id: None, + connector_transaction_id: Some(error_response.id), }), ..item.data }), @@ -1017,6 +1041,7 @@ impl TryFrom, pub error_information: Option, pub status: Option, pub message: Option, @@ -1042,6 +1067,7 @@ pub enum Reason { SystemError, ServerTimeout, ServiceTimeout, + ExceedsAuthAmount, } #[derive(Debug, Deserialize, Clone)] diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index f74ab55595dd..e3e5e94a633e 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -320,7 +320,45 @@ impl &self, res: types::Response, ) -> CustomResult { - self.build_error_response(res) + let response: cybersource::ErrorResponse = res + .response + .parse_struct("Cybersource ErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let details = response.details.unwrap_or_default(); + let connector_reason = details + .iter() + .map(|det| format!("{} : {}", det.field, det.reason)) + .collect::>() + .join(", "); + + let error_message = if res.status_code == 401 { + consts::CONNECTOR_UNAUTHORIZED_ERROR + } else { + consts::NO_ERROR_MESSAGE + }; + + let (code, message) = match response.error_information { + Some(ref error_info) => (error_info.reason.clone(), error_info.message.clone()), + None => ( + response + .reason + .map_or(consts::NO_ERROR_CODE.to_string(), |reason| { + reason.to_string() + }), + response + .message + .map_or(error_message.to_string(), |message| message), + ), + }; + + Ok(types::ErrorResponse { + status_code: res.status_code, + code, + message, + reason: Some(connector_reason), + attempt_status: None, + connector_transaction_id: response.id, + }) } } @@ -418,6 +456,7 @@ impl ConnectorIntegration CustomResult { - self.build_error_response(res) + build_auth_and_capture_error_response(res) } } @@ -589,6 +628,7 @@ impl ConnectorIntegration CustomResult { - self.build_error_response(res) + build_auth_and_capture_error_response(res) } } @@ -669,6 +709,7 @@ impl ConnectorIntegration CustomResult { + let response: cybersource::ErrorResponse = res + .response + .parse_struct("Cybersource ErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let details = response.details.unwrap_or_default(); + let connector_reason = details + .iter() + .map(|det| format!("{} : {}", det.field, det.reason)) + .collect::>() + .join(", "); + + let error_message = if res.status_code == 401 { + consts::CONNECTOR_UNAUTHORIZED_ERROR + } else { + consts::NO_ERROR_MESSAGE + }; + + let (code, message) = match response.error_information { + Some(ref error_info) => (error_info.reason.clone(), error_info.message.clone()), + None => ( + response + .reason + .map_or(consts::NO_ERROR_CODE.to_string(), |reason| { + reason.to_string() + }), + response + .message + .map_or(error_message.to_string(), |message| message), + ), + }; + Ok(types::ErrorResponse { + status_code: res.status_code, + code, + message, + reason: Some(connector_reason), + attempt_status: None, + connector_transaction_id: response.id, + }) +} diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index a4cea13e2184..9049a0969f86 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -1,5 +1,6 @@ use api_models::payments; -use common_utils::pii; +use common_utils::{errors::CustomResult, pii}; +use error_stack::ResultExt; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -612,10 +613,28 @@ pub struct CybersourceErrorInformation { message: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CybersourceTransactionMetadata { + auth_id: String, +} + +pub fn get_connector_metadata( + auth_id: String, +) -> CustomResult, errors::ConnectorError> { + Some(common_utils::ext_traits::Encode::< + CybersourceTransactionMetadata, + >::encode_to_value(&CybersourceTransactionMetadata { + auth_id, + })) + .transpose() + .change_context(errors::ConnectorError::ResponseHandlingFailed) +} + impl TryFrom<( types::ResponseRouterData, bool, + bool, )> for types::RouterData { type Error = error_stack::Report; @@ -628,10 +647,12 @@ impl types::PaymentsResponseData, >, bool, + bool, ), ) -> Result { let item = data.0; let is_capture = data.1; + let is_auth_call = data.2; let mandate_reference = item.response .token_information @@ -640,6 +661,13 @@ impl payment_method_id: None, }); let status = get_payment_status(is_capture, item.response.status.into()); + + let connector_metadata = if is_auth_call && !is_capture { + get_connector_metadata(item.response.id.clone())? + } else { + None + }; + Ok(Self { status, response: match item.response.error_information { @@ -649,7 +677,7 @@ impl reason: Some(error.reason), status_code: item.http_code, attempt_status: None, - connector_transaction_id: Some(item.response.id), + connector_transaction_id: Some(item.response.id.clone()), }), _ => Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId( @@ -657,7 +685,7 @@ impl ), redirection_data: None, mandate_reference, - connector_metadata: None, + connector_metadata, network_txn_id: None, connector_response_reference_id: item .response @@ -862,6 +890,7 @@ impl #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ErrorResponse { + pub id: Option, pub error_information: Option, pub status: Option, pub message: Option, @@ -887,6 +916,7 @@ pub enum Reason { SystemError, ServerTimeout, ServiceTimeout, + ExceedsAuthAmount, } #[derive(Debug, Deserialize, Clone)]