From a78fed73babace05b4f668ef219909277045ba85 Mon Sep 17 00:00:00 2001 From: Sampras Lopes Date: Sat, 16 Dec 2023 20:39:05 +0530 Subject: [PATCH 1/2] chore(events): remove duplicate logs (#3148) --- crates/router/src/services/api.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index ea254ee4fab..ecce3b9e7c6 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -279,15 +279,6 @@ where // If needed add an error stack as follows // connector_integration.build_request(req).attach_printable("Failed to build request"); logger::debug!(connector_request=?connector_request); - let masked_conn_req = connector_request.as_ref().map(|req| match &req.body { - Some(RequestContent::Json(payload)) - | Some(RequestContent::FormUrlEncoded(payload)) - | Some(RequestContent::Xml(payload)) => payload.masked_serialize().unwrap_or_default(), - Some(RequestContent::FormData(_)) => json!({"request_type": "FORM_DATA"}), - None => serde_json::Value::Null, - }); - logger::debug!(connector_request_body=?masked_conn_req); - logger::debug!(payment_id=?req.payment_id); let mut router_data = req.clone(); match call_connector_action { payments::CallConnectorAction::HandleResponse(res) => { From 62c0c47e99f154399687a32caf9999b365da60ae Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com> Date: Sun, 17 Dec 2023 12:42:11 +0530 Subject: [PATCH 2/2] fix: [CYBERSOURCE] Fix Status Mapping (#3144) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/router/src/connector/cybersource.rs | 75 +-- .../src/connector/cybersource/transformers.rs | 452 +++++++++++++----- 2 files changed, 359 insertions(+), 168 deletions(-) diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index a2f14801a48..06be499fae4 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -418,15 +418,11 @@ impl ConnectorIntegration CustomResult { - let response: cybersource::CybersourcePaymentsResponse = res + let response: cybersource::CybersourceRefundResponse = res .response - .parse_struct("Cybersource PaymentResponse") + .parse_struct("Cybersource RefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, }) - .change_context(errors::ConnectorError::ResponseHandlingFailed) } fn get_error_response( &self, @@ -826,17 +805,15 @@ impl ConnectorIntegration CustomResult { - let response: cybersource::CybersourceTransactionResponse = res + let response: cybersource::CybersourceRsyncResponse = res .response - .parse_struct("Cybersource RefundsSyncResponse") + .parse_struct("Cybersource RefundSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - types::ResponseRouterData { + types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), http_code: res.status_code, - } - .try_into() - .change_context(errors::ConnectorError::ResponseHandlingFailed) + }) } fn get_error_response( &self, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index a5b55f111b9..ef31b977075 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ self, AddressDetailsData, CardData, PaymentsAuthorizeRequestData, - PaymentsSetupMandateRequestData, RouterData, + PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RouterData, }, consts, core::errors, @@ -15,6 +15,7 @@ use crate::{ self, api::{self, enums as api_enums}, storage::enums, + transformers::ForeignFrom, }, }; @@ -684,7 +685,8 @@ impl TryFrom<&types::ConnectorAuthType> for CybersourceAuthType { } } } -#[derive(Debug, Default, Clone, Deserialize)] + +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CybersourcePaymentStatus { Authorized, @@ -696,8 +698,6 @@ pub enum CybersourcePaymentStatus { Declined, AuthorizedPendingReview, Transmitted, - #[default] - Processing, } #[derive(Debug, Clone, Deserialize)] @@ -708,18 +708,30 @@ pub enum CybersourceIncrementalAuthorizationStatus { AuthorizedPendingReview, } -impl From for enums::AttemptStatus { - fn from(item: CybersourcePaymentStatus) -> Self { - match item { +impl ForeignFrom<(CybersourcePaymentStatus, bool)> for enums::AttemptStatus { + fn foreign_from((status, capture): (CybersourcePaymentStatus, bool)) -> Self { + match status { CybersourcePaymentStatus::Authorized - | CybersourcePaymentStatus::AuthorizedPendingReview => Self::Authorized, + | CybersourcePaymentStatus::AuthorizedPendingReview => { + if capture { + // Because Cybersource will return Payment Status as Authorized even in AutoCapture Payment + Self::Charged + } else { + Self::Authorized + } + } + CybersourcePaymentStatus::Pending => { + if capture { + Self::Charged + } else { + Self::Pending + } + } CybersourcePaymentStatus::Succeeded | CybersourcePaymentStatus::Transmitted => { Self::Charged } CybersourcePaymentStatus::Voided | CybersourcePaymentStatus::Reversed => Self::Voided, CybersourcePaymentStatus::Failed | CybersourcePaymentStatus::Declined => Self::Failure, - CybersourcePaymentStatus::Processing => Self::Authorizing, - CybersourcePaymentStatus::Pending => Self::Pending, } } } @@ -746,16 +758,29 @@ impl From for enums::RefundStatus { } } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum CybersourcePaymentsResponse { + ClientReferenceInformation(CybersourceClientReferenceResponse), + ErrorInformation(CybersourceErrorInformationResponse), +} + +#[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct CybersourcePaymentsResponse { +pub struct CybersourceClientReferenceResponse { id: String, status: CybersourcePaymentStatus, - error_information: Option, - client_reference_information: Option, + client_reference_information: ClientReferenceInformation, token_information: Option, } +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceErrorInformationResponse { + id: String, + error_information: CybersourceErrorInformation, +} + #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CybersourcePaymentsIncrementalAuthorizationResponse { @@ -787,69 +812,201 @@ pub struct CybersourceTokenInformation { #[derive(Debug, Clone, Deserialize)] pub struct CybersourceErrorInformation { - reason: String, - message: String, + reason: Option, + message: Option, } impl - TryFrom<( + From<( + &CybersourceErrorInformationResponse, types::ResponseRouterData, - bool, )> for types::RouterData { - type Error = error_stack::Report; - fn try_from( - data: ( + fn from( + (error_response, item): ( + &CybersourceErrorInformationResponse, types::ResponseRouterData< F, CybersourcePaymentsResponse, T, types::PaymentsResponseData, >, - bool, ), + ) -> Self { + Self { + response: Err(types::ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: error_response + .error_information + .message + .clone() + .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), + reason: error_response.error_information.reason.clone(), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: None, + }), + ..item.data + } + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, ) -> Result { - let item = data.0; - let is_capture = data.1; - let mandate_reference = - item.response - .token_information - .map(|token_info| types::MandateReference { - connector_mandate_id: Some(token_info.instrument_identifier.id), - payment_method_id: None, - }); - let status = get_payment_status(is_capture, item.response.status.into()); - Ok(Self { - status, - response: match item.response.error_information { - Some(error) => Err(types::ErrorResponse { - code: consts::NO_ERROR_CODE.to_string(), - message: error.message, - reason: Some(error.reason), - status_code: item.http_code, - attempt_status: None, - connector_transaction_id: Some(item.response.id), - }), - _ => Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId( - item.response.id.clone(), - ), - redirection_data: None, - mandate_reference, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: item - .response + match item.response { + CybersourcePaymentsResponse::ClientReferenceInformation(info_response) => { + let status = enums::AttemptStatus::foreign_from(( + info_response.status, + item.data.request.is_auto_capture()?, + )); + let incremental_authorization_allowed = + Some(status == enums::AttemptStatus::Authorized); + let mandate_reference = + info_response + .token_information + .map(|token_info| types::MandateReference { + connector_mandate_id: Some(token_info.instrument_identifier.id), + payment_method_id: None, + }); + let connector_response_reference_id = Some( + info_response .client_reference_information - .map(|cref| cref.code) - .unwrap_or(Some(item.response.id)), - incremental_authorization_allowed: Some( - status == enums::AttemptStatus::Authorized, - ), - }), - }, - ..item.data - }) + .code + .unwrap_or(info_response.id.clone()), + ); + Ok(Self { + status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(info_response.id), + redirection_data: None, + mandate_reference, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id, + incremental_authorization_allowed, + }), + ..item.data + }) + } + CybersourcePaymentsResponse::ErrorInformation(ref error_response) => { + Ok(Self::from((&error_response.clone(), item))) + } + } + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + ) -> Result { + match item.response { + CybersourcePaymentsResponse::ClientReferenceInformation(info_response) => { + let status = enums::AttemptStatus::foreign_from((info_response.status, true)); + let connector_response_reference_id = Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id.clone()), + ); + Ok(Self { + status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(info_response.id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id, + incremental_authorization_allowed: None, + }), + ..item.data + }) + } + CybersourcePaymentsResponse::ErrorInformation(ref error_response) => { + Ok(Self::from((&error_response.clone(), item))) + } + } + } +} + +impl + TryFrom< + types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + CybersourcePaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + ) -> Result { + match item.response { + CybersourcePaymentsResponse::ClientReferenceInformation(info_response) => { + let status = enums::AttemptStatus::foreign_from((info_response.status, false)); + let connector_response_reference_id = Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id.clone()), + ); + Ok(Self { + status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(info_response.id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id, + incremental_authorization_allowed: None, + }), + ..item.data + }) + } + CybersourcePaymentsResponse::ErrorInformation(ref error_response) => { + Ok(Self::from((&error_response.clone(), item))) + } + } } } @@ -879,7 +1036,7 @@ impl connector_mandate_id: Some(token_info.instrument_identifier.id), payment_method_id: None, }); - let mut mandate_status: enums::AttemptStatus = item.response.status.into(); + let mut mandate_status = enums::AttemptStatus::foreign_from((item.response.status, false)); if matches!(mandate_status, enums::AttemptStatus::Authorized) { //In case of zero auth mandates we want to make the payment reach the terminal status so we are converting the authorized status to charged as well. mandate_status = enums::AttemptStatus::Charged @@ -889,8 +1046,10 @@ impl response: match item.response.error_information { Some(error) => Err(types::ErrorResponse { code: consts::NO_ERROR_CODE.to_string(), - message: error.message, - reason: Some(error.reason), + message: error + .message + .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), + reason: error.reason, status_code: item.http_code, attempt_status: None, connector_transaction_id: Some(item.response.id), @@ -947,8 +1106,8 @@ impl Some(error) => Ok( types::PaymentsResponseData::IncrementalAuthorizationResponse { status: common_enums::AuthorizationStatus::Failure, - error_code: Some(error.reason), - error_message: Some(error.message), + error_code: error.reason, + error_message: error.message, connector_authorization_id: None, }, ), @@ -966,9 +1125,16 @@ impl } } +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum CybersourceTransactionResponse { + ApplicationInformation(CybersourceApplicationInfoResponse), + ErrorInformation(CybersourceErrorInformationResponse), +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct CybersourceTransactionResponse { +pub struct CybersourceApplicationInfoResponse { id: String, application_information: ApplicationInformation, client_reference_information: Option, @@ -980,61 +1146,68 @@ pub struct ApplicationInformation { status: CybersourcePaymentStatus, } -fn get_payment_status(is_capture: bool, status: enums::AttemptStatus) -> enums::AttemptStatus { - let is_authorized = matches!(status, enums::AttemptStatus::Authorized); - let is_pending = matches!(status, enums::AttemptStatus::Pending); - if is_capture && (is_authorized || is_pending) { - return enums::AttemptStatus::Charged; - } - status -} - -impl - TryFrom<( +impl + TryFrom< types::ResponseRouterData< F, CybersourceTransactionResponse, - T, + types::PaymentsSyncData, types::PaymentsResponseData, >, - bool, - )> for types::RouterData + > for types::RouterData { type Error = error_stack::Report; fn try_from( - data: ( - types::ResponseRouterData< - F, - CybersourceTransactionResponse, - T, - types::PaymentsResponseData, - >, - bool, - ), + item: types::ResponseRouterData< + F, + CybersourceTransactionResponse, + types::PaymentsSyncData, + types::PaymentsResponseData, + >, ) -> Result { - let item = data.0; - let is_capture = data.1; - let status = get_payment_status( - is_capture, - item.response.application_information.status.into(), - ); - Ok(Self { - status, - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), - redirection_data: None, - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: item - .response - .client_reference_information - .map(|cref| cref.code) - .unwrap_or(Some(item.response.id)), - incremental_authorization_allowed: Some(status == enums::AttemptStatus::Authorized), + match item.response { + CybersourceTransactionResponse::ApplicationInformation(app_response) => { + let status = enums::AttemptStatus::foreign_from(( + app_response.application_information.status, + item.data.request.is_auto_capture()?, + )); + let incremental_authorization_allowed = + Some(status == enums::AttemptStatus::Authorized); + Ok(Self { + status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + app_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + incremental_authorization_allowed, + connector_response_reference_id: app_response + .client_reference_information + .map(|cref| cref.code) + .unwrap_or(Some(app_response.id)), + }), + ..item.data + }) + } + CybersourceTransactionResponse::ErrorInformation(error_response) => Ok(Self { + status: item.data.status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + error_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some(error_response.id), + incremental_authorization_allowed: None, + }), + ..item.data }), - ..item.data - }) + } } } @@ -1103,30 +1276,71 @@ impl TryFrom<&CybersourceRouterData<&types::RefundsRouterData>> for Cybers } } -impl TryFrom> +impl From for enums::RefundStatus { + fn from(item: CybersourceRefundStatus) -> Self { + match item { + CybersourceRefundStatus::Succeeded | CybersourceRefundStatus::Transmitted => { + Self::Success + } + CybersourceRefundStatus::Failed | CybersourceRefundStatus::Voided => Self::Failure, + CybersourceRefundStatus::Pending => Self::Pending, + } + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum CybersourceRefundStatus { + Succeeded, + Transmitted, + Failed, + Pending, + Voided, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceRefundResponse { + id: String, + status: CybersourceRefundStatus, +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { - let refund_status = enums::RefundStatus::from(item.response.status); Ok(Self { response: Ok(types::RefundsResponseData { connector_refund_id: item.response.id, - refund_status, + refund_status: enums::RefundStatus::from(item.response.status), }), ..item.data }) } } -impl TryFrom> +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RsyncApplicationInformation { + status: CybersourceRefundStatus, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceRsyncResponse { + id: String, + application_information: RsyncApplicationInformation, +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData {