Skip to content

Commit

Permalink
fix(connector): [CYBERSOURCE] Display 2XX Failure Errors (#3201)
Browse files Browse the repository at this point in the history
  • Loading branch information
deepanshu-iiitu authored Dec 22, 2023
1 parent 07fd9be commit 86c2622
Showing 1 changed file with 157 additions and 76 deletions.
233 changes: 157 additions & 76 deletions crates/router/src/connector/cybersource/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,13 +842,14 @@ pub enum CybersourcePaymentsResponse {
ErrorInformation(CybersourceErrorInformationResponse),
}

#[derive(Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CybersourceClientReferenceResponse {
id: String,
status: CybersourcePaymentStatus,
client_reference_information: ClientReferenceInformation,
token_information: Option<CybersourceTokenInformation>,
error_information: Option<CybersourceErrorInformation>,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down Expand Up @@ -928,6 +929,107 @@ impl<F, T>
}
}

fn get_error_response_if_failure(
(info_response, status, http_code): (
&CybersourceClientReferenceResponse,
enums::AttemptStatus,
u16,
),
) -> Option<types::ErrorResponse> {
if is_payment_failure(status) {
let (message, reason) = match info_response.error_information.as_ref() {
Some(error_info) => (
error_info
.message
.clone()
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
error_info.reason.clone(),
),
None => (consts::NO_ERROR_MESSAGE.to_string(), None),
};

Some(types::ErrorResponse {
code: consts::NO_ERROR_CODE.to_string(),
message,
reason,
status_code: http_code,
attempt_status: Some(enums::AttemptStatus::Failure),
connector_transaction_id: Some(info_response.id.clone()),
})
} else {
None
}
}

fn is_payment_failure(status: enums::AttemptStatus) -> bool {
match status {
common_enums::AttemptStatus::AuthenticationFailed
| common_enums::AttemptStatus::AuthorizationFailed
| common_enums::AttemptStatus::CaptureFailed
| common_enums::AttemptStatus::VoidFailed
| common_enums::AttemptStatus::Failure => true,
common_enums::AttemptStatus::Started
| common_enums::AttemptStatus::RouterDeclined
| common_enums::AttemptStatus::AuthenticationPending
| common_enums::AttemptStatus::AuthenticationSuccessful
| common_enums::AttemptStatus::Authorized
| common_enums::AttemptStatus::Charged
| common_enums::AttemptStatus::Authorizing
| common_enums::AttemptStatus::CodInitiated
| common_enums::AttemptStatus::Voided
| common_enums::AttemptStatus::VoidInitiated
| common_enums::AttemptStatus::CaptureInitiated
| common_enums::AttemptStatus::AutoRefunded
| common_enums::AttemptStatus::PartialCharged
| common_enums::AttemptStatus::PartialChargedAndChargeable
| common_enums::AttemptStatus::Unresolved
| common_enums::AttemptStatus::Pending
| common_enums::AttemptStatus::PaymentMethodAwaited
| common_enums::AttemptStatus::ConfirmationAwaited
| common_enums::AttemptStatus::DeviceDataCollectionPending => false,
}
}

fn get_payment_response(
(info_response, status, http_code): (
&CybersourceClientReferenceResponse,
enums::AttemptStatus,
u16,
),
) -> Result<types::PaymentsResponseData, types::ErrorResponse> {
let error_response = get_error_response_if_failure((info_response, status, http_code));
match error_response {
Some(error) => Err(error),
None => {
let incremental_authorization_allowed =
Some(status == enums::AttemptStatus::Authorized);
let mandate_reference =
info_response
.token_information
.clone()
.map(|token_info| types::MandateReference {
connector_mandate_id: Some(token_info.instrument_identifier.id),
payment_method_id: None,
});
Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(info_response.id.clone()),
redirection_data: None,
mandate_reference,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(
info_response
.client_reference_information
.code
.clone()
.unwrap_or(info_response.id.clone()),
),
incremental_authorization_allowed,
})
}
}
}

impl<F>
TryFrom<
types::ResponseRouterData<
Expand All @@ -950,35 +1052,13 @@ impl<F>
match item.response {
CybersourcePaymentsResponse::ClientReferenceInformation(info_response) => {
let status = enums::AttemptStatus::foreign_from((
info_response.status,
info_response.status.clone(),
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
.code
.unwrap_or(info_response.id.clone()),
);
let response = get_payment_response((&info_response, status, item.http_code));
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,
}),
response,
..item.data
})
}
Expand Down Expand Up @@ -1010,24 +1090,12 @@ impl<F>
) -> Result<Self, Self::Error> {
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()),
);
let status =
enums::AttemptStatus::foreign_from((info_response.status.clone(), true));
let response = get_payment_response((&info_response, status, item.http_code));
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,
}),
response,
..item.data
})
}
Expand Down Expand Up @@ -1059,24 +1127,12 @@ impl<F>
) -> Result<Self, Self::Error> {
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()),
);
let status =
enums::AttemptStatus::foreign_from((info_response.status.clone(), false));
let response = get_payment_response((&info_response, status, item.http_code));
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,
}),
response,
..item.data
})
}
Expand Down Expand Up @@ -1215,9 +1271,10 @@ pub struct CybersourceApplicationInfoResponse {
id: String,
application_information: ApplicationInformation,
client_reference_information: Option<ClientReferenceInformation>,
error_information: Option<CybersourceErrorInformation>,
}

#[derive(Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationInformation {
status: CybersourcePaymentStatus,
Expand Down Expand Up @@ -1250,24 +1307,48 @@ impl<F>
));
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(),
if is_payment_failure(status) {
let (message, reason) = match app_response.error_information {
Some(error_info) => (
error_info
.message
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
error_info.reason,
),
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
})
None => (consts::NO_ERROR_MESSAGE.to_string(), None),
};
Ok(Self {
response: Err(types::ErrorResponse {
code: consts::NO_ERROR_CODE.to_string(),
message,
reason,
status_code: item.http_code,
attempt_status: Some(enums::AttemptStatus::Failure),
connector_transaction_id: Some(app_response.id),
}),
status: enums::AttemptStatus::Failure,
..item.data
})
} else {
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,
connector_response_reference_id: app_response
.client_reference_information
.map(|cref| cref.code)
.unwrap_or(Some(app_response.id)),
incremental_authorization_allowed,
}),
..item.data
})
}
}
CybersourceTransactionResponse::ErrorInformation(error_response) => Ok(Self {
status: item.data.status,
Expand Down

0 comments on commit 86c2622

Please sign in to comment.