refactor(connector): update error handling for Paypal, Checkout, Mollie to include detailed messages (#1150)

This commit is contained in:
Prasunna Soppa
2023-05-16 13:29:05 +05:30
committed by GitHub
parent da4d721424
commit e044c2fd9a
6 changed files with 75 additions and 103 deletions

View File

@ -92,17 +92,18 @@ impl ConnectorCommon for Checkout {
.parse_struct("ErrorResponse") .parse_struct("ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)? .change_context(errors::ConnectorError::ResponseDeserializationFailed)?
}; };
Ok(types::ErrorResponse { Ok(types::ErrorResponse {
status_code: res.status_code, status_code: res.status_code,
code: response code: response
.error_codes
.unwrap_or_else(|| vec![consts::NO_ERROR_CODE.to_string()])
.join(" & "),
message: response
.error_type .error_type
.clone()
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.error_codes
.as_ref()
.and_then(|error_codes| error_codes.first().cloned())
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()), .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None, reason: response.error_codes.map(|errors| errors.join(" & ")),
}) })
} }
} }

View File

@ -7,6 +7,7 @@ use transformers as mollie;
use crate::{ use crate::{
configs::settings, configs::settings,
consts,
core::{ core::{
errors::{self, CustomResult}, errors::{self, CustomResult},
payments, payments,
@ -82,10 +83,12 @@ impl ConnectorCommon for Mollie {
.parse_struct("MollieErrorResponse") .parse_struct("MollieErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(ErrorResponse { Ok(ErrorResponse {
status_code: response.status,
code: response
.title
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response.detail, message: response.detail,
reason: response.field, reason: response.field,
status_code: response.status,
..Default::default()
}) })
} }
} }

View File

@ -71,37 +71,32 @@ impl Paypal {
.parse_struct("Paypal ErrorResponse") .parse_struct("Paypal ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
let message = match response.details { let error_reason = match response.details {
Some(mes) => { Some(order_errors) => order_errors
let mut des = "".to_owned(); .iter()
for item in mes.iter() { .map(|error| {
let mut description = format!("description - {}", item.to_owned().description); let mut reason = format!("description - {}", error.description);
if let Some(value) = &error.value {
if let Some(data) = &item.value { reason.push_str(&format!(", value - {value}"));
description.push_str(format!(", value - {}", data.to_owned()).as_str());
} }
if let Some(field) = error
if let Some(data) = &item.field { .field
let field = data .as_ref()
.clone() .and_then(|field| field.split('/').last())
.split('/') {
.last() reason.push_str(&format!(", field - {field}"));
.unwrap_or_default()
.to_owned();
description.push_str(format!(", field - {};", field).as_str());
} }
des.push_str(description.as_str()) reason.push(';');
} reason
des })
} .collect::<String>(),
None => consts::NO_ERROR_MESSAGE.to_string(), None => consts::NO_ERROR_MESSAGE.to_string(),
}; };
Ok(ErrorResponse { Ok(ErrorResponse {
status_code: res.status_code, status_code: res.status_code,
code: response.name, code: response.name,
message, message: response.message,
reason: None, reason: Some(error_reason),
}) })
} }
} }
@ -168,24 +163,18 @@ impl ConnectorCommon for Paypal {
.parse_struct("Paypal ErrorResponse") .parse_struct("Paypal ErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?; .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
let message = match response.details { let error_reason = match response.details {
Some(mes) => { Some(error_details) => error_details
let mut des = "".to_owned(); .iter()
for item in mes.iter() { .map(|error| format!("description - {} ; ", error.description))
let x = item.clone().description; .collect::<String>(),
let st = format!("description - {} ; ", x);
des.push_str(&st);
}
des
}
None => consts::NO_ERROR_MESSAGE.to_string(), None => consts::NO_ERROR_MESSAGE.to_string(),
}; };
Ok(ErrorResponse { Ok(ErrorResponse {
status_code: res.status_code, status_code: res.status_code,
code: response.name, code: response.name,
message, message: response.message,
reason: None, reason: Some(error_reason),
}) })
} }
} }

View File

@ -1,6 +1,3 @@
use std::str::FromStr;
use cards::CardNumber;
use masking::Secret; use masking::Secret;
use router::types::{self, api, storage::enums}; use router::types::{self, api, storage::enums};
@ -313,28 +310,6 @@ async fn should_sync_refund() {
} }
// Cards Negative scenerios // Cards Negative scenerios
// Creates a payment with incorrect card number.
#[serial_test::serial]
#[actix_web::test]
async fn should_fail_payment_for_incorrect_card_number() {
let response = CONNECTOR
.make_payment(
Some(types::PaymentsAuthorizeData {
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
card_number: CardNumber::from_str("1234567891011").unwrap(),
..utils::CCardType::default().0
}),
..utils::PaymentAuthorizeType::default().0
}),
get_default_payment_info(),
)
.await
.unwrap();
assert_eq!(
response.response.unwrap_err().code,
"card_number_invalid".to_string(),
);
}
// Creates a payment with incorrect CVC. // Creates a payment with incorrect CVC.
#[serial_test::serial] #[serial_test::serial]
@ -354,7 +329,7 @@ async fn should_fail_payment_for_incorrect_cvc() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().code, response.response.unwrap_err().message,
"cvv_invalid".to_string(), "cvv_invalid".to_string(),
); );
} }
@ -377,7 +352,7 @@ async fn should_fail_payment_for_invalid_exp_month() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().code, response.response.unwrap_err().message,
"card_expiry_month_invalid".to_string(), "card_expiry_month_invalid".to_string(),
); );
} }
@ -400,7 +375,7 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().code, response.response.unwrap_err().message,
"card_expired".to_string(), "card_expired".to_string(),
); );
} }
@ -450,7 +425,7 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().code, response.response.unwrap_err().message,
"refund_amount_exceeds_balance", "refund_amount_exceeds_balance",
); );
} }

View File

@ -428,27 +428,6 @@ async fn should_sync_refund() {
} }
// Cards Negative scenerios // Cards Negative scenerios
// Creates a payment with incorrect card number.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_card_number() {
let response = CONNECTOR
.make_payment(
Some(types::PaymentsAuthorizeData {
payment_method_data: types::api::PaymentMethodData::Card(api::Card {
card_number: cards::CardNumber::from_str("1234567891011").unwrap(),
..utils::CCardType::default().0
}),
..utils::PaymentAuthorizeType::default().0
}),
get_default_payment_info(),
)
.await
.unwrap();
assert_eq!(
response.response.unwrap_err().message,
"description - UNPROCESSABLE_ENTITY",
);
}
// Creates a payment with incorrect CVC. // Creates a payment with incorrect CVC.
#[actix_web::test] #[actix_web::test]
@ -467,7 +446,11 @@ async fn should_fail_payment_for_incorrect_cvc() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().message, response.response.clone().unwrap_err().message,
"Request is not well-formed, syntactically incorrect, or violates schema.",
);
assert_eq!(
response.response.unwrap_err().reason.unwrap(),
"description - The value of a field does not conform to the expected format., value - 12345, field - security_code;", "description - The value of a field does not conform to the expected format., value - 12345, field - security_code;",
); );
} }
@ -489,7 +472,11 @@ async fn should_fail_payment_for_invalid_exp_month() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().message, response.response.clone().unwrap_err().message,
"Request is not well-formed, syntactically incorrect, or violates schema.",
);
assert_eq!(
response.response.unwrap_err().reason.unwrap(),
"description - The value of a field does not conform to the expected format., value - 2025-20, field - expiry;", "description - The value of a field does not conform to the expected format., value - 2025-20, field - expiry;",
); );
} }
@ -511,7 +498,11 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
response.response.unwrap_err().message, response.response.clone().unwrap_err().message,
"The requested action could not be performed, semantically incorrect, or failed business validation.",
);
assert_eq!(
response.response.unwrap_err().reason.unwrap(),
"description - The card is expired., field - expiry;", "description - The card is expired., field - expiry;",
); );
} }
@ -551,7 +542,11 @@ async fn should_fail_void_payment_for_auto_capture() {
.await .await
.expect("Void payment response"); .expect("Void payment response");
assert_eq!( assert_eq!(
void_response.response.unwrap_err().message, void_response.response.clone().unwrap_err().message,
"The requested action could not be performed, semantically incorrect, or failed business validation."
);
assert_eq!(
void_response.response.unwrap_err().reason.unwrap(),
"description - Authorization has been previously captured and hence cannot be voided. ; " "description - Authorization has been previously captured and hence cannot be voided. ; "
); );
} }
@ -576,7 +571,11 @@ async fn should_fail_capture_for_invalid_payment() {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
capture_response.response.unwrap_err().message, capture_response.response.clone().unwrap_err().message,
"The specified resource does not exist.",
);
assert_eq!(
capture_response.response.unwrap_err().reason.unwrap(),
"description - Specified resource ID does not exist. Please check the resource ID and try again. ; ", "description - Specified resource ID does not exist. Please check the resource ID and try again. ; ",
); );
} }
@ -600,7 +599,12 @@ async fn should_fail_for_refund_amount_higher_than_payment_amount() {
) )
.await .await
.unwrap(); .unwrap();
assert_eq!(&response.response.unwrap_err().message, "description - The refund amount must be less than or equal to the capture amount that has not yet been refunded. ; "); assert_eq!(&response.response.clone().unwrap_err().message, "The requested action could not be performed, semantically incorrect, or failed business validation.");
assert_eq!(
response.response.unwrap_err().reason.unwrap(),
"description - The refund amount must be less than or equal to the capture amount that has not yet been refunded. ; ",
);
} }
// Connector dependent test cases goes here // Connector dependent test cases goes here

View File

@ -14,8 +14,8 @@ api_key = "MyMerchantName"
key1 = "MyTransactionKey" key1 = "MyTransactionKey"
[checkout] [checkout]
api_key = "Bearer PublicKey" api_key = "PublicKey"
api_secret = "Bearer SecretKey" api_secret = "SecretKey"
key1 = "MyProcessingChannelId" key1 = "MyProcessingChannelId"
[cybersource] [cybersource]