feat(connector): [Checkout] unify error code, message and reason in error response (#1855)

This commit is contained in:
Sai Harsha Vardhan
2023-08-08 13:04:24 +05:30
committed by GitHub
parent 62461f1b38
commit e8a51c2abe
2 changed files with 221 additions and 39 deletions

View File

@ -7,7 +7,9 @@ use error_stack::{IntoReport, ResultExt};
use masking::PeekInterface;
use self::transformers as checkout;
use super::utils::{self as conn_utils, RefundsRequestData};
use super::utils::{
self as conn_utils, ConnectorErrorType, ConnectorErrorTypeMapping, RefundsRequestData,
};
use crate::{
configs::settings,
consts,
@ -102,18 +104,27 @@ impl ConnectorCommon for Checkout {
};
router_env::logger::info!(error_response=?response);
let errors_list = response.error_codes.clone().unwrap_or(vec![]);
let option_error_code_message = conn_utils::get_error_code_error_message_based_on_priority(
self.clone(),
errors_list
.into_iter()
.map(|errors| errors.into())
.collect(),
);
Ok(types::ErrorResponse {
status_code: res.status_code,
code: response
.error_type
code: option_error_code_message
.clone()
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.map(|error_code_message| error_code_message.error_code)
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
message: option_error_code_message
.map(|error_code_message| error_code_message.error_message)
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
reason: response
.error_codes
.as_ref()
.and_then(|error_codes| error_codes.first().cloned())
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: response.error_codes.map(|errors| errors.join(" & ")),
.map(|errors| errors.join(" & "))
.or(response.error_type),
})
}
}
@ -1211,3 +1222,128 @@ impl services::ConnectorRedirectResponse for Checkout {
Ok(connector_action)
}
}
impl ConnectorErrorTypeMapping for Checkout {
fn get_connector_error_type(
&self,
error_code: String,
_error_message: String,
) -> ConnectorErrorType {
match error_code.as_str() {
"action_failure_limit_exceeded" => ConnectorErrorType::BusinessError,
"address_invalid" => ConnectorErrorType::UserError,
"amount_exceeds_balance" => ConnectorErrorType::BusinessError,
"amount_invalid" => ConnectorErrorType::UserError,
"api_calls_quota_exceeded" => ConnectorErrorType::TechnicalError,
"billing_descriptor_city_invalid" => ConnectorErrorType::UserError,
"billing_descriptor_city_required" => ConnectorErrorType::UserError,
"billing_descriptor_name_invalid" => ConnectorErrorType::UserError,
"billing_descriptor_name_required" => ConnectorErrorType::UserError,
"business_invalid" => ConnectorErrorType::BusinessError,
"business_settings_missing" => ConnectorErrorType::BusinessError,
"capture_value_greater_than_authorized" => ConnectorErrorType::BusinessError,
"capture_value_greater_than_remaining_authorized" => ConnectorErrorType::BusinessError,
"card_authorization_failed" => ConnectorErrorType::UserError,
"card_disabled" => ConnectorErrorType::UserError,
"card_expired" => ConnectorErrorType::UserError,
"card_expiry_month_invalid" => ConnectorErrorType::UserError,
"card_expiry_month_required" => ConnectorErrorType::UserError,
"card_expiry_year_invalid" => ConnectorErrorType::UserError,
"card_expiry_year_required" => ConnectorErrorType::UserError,
"card_holder_invalid" => ConnectorErrorType::UserError,
"card_not_found" => ConnectorErrorType::UserError,
"card_number_invalid" => ConnectorErrorType::UserError,
"card_number_required" => ConnectorErrorType::UserError,
"channel_details_invalid" => ConnectorErrorType::BusinessError,
"channel_url_missing" => ConnectorErrorType::BusinessError,
"charge_details_invalid" => ConnectorErrorType::BusinessError,
"city_invalid" => ConnectorErrorType::BusinessError,
"country_address_invalid" => ConnectorErrorType::UserError,
"country_invalid" => ConnectorErrorType::UserError,
"country_phone_code_invalid" => ConnectorErrorType::UserError,
"country_phone_code_length_invalid" => ConnectorErrorType::UserError,
"currency_invalid" => ConnectorErrorType::UserError,
"currency_required" => ConnectorErrorType::UserError,
"customer_already_exists" => ConnectorErrorType::BusinessError,
"customer_email_invalid" => ConnectorErrorType::UserError,
"customer_id_invalid" => ConnectorErrorType::BusinessError,
"customer_not_found" => ConnectorErrorType::BusinessError,
"customer_number_invalid" => ConnectorErrorType::UserError,
"customer_plan_edit_failed" => ConnectorErrorType::BusinessError,
"customer_plan_id_invalid" => ConnectorErrorType::BusinessError,
"cvv_invalid" => ConnectorErrorType::UserError,
"email_in_use" => ConnectorErrorType::BusinessError,
"email_invalid" => ConnectorErrorType::UserError,
"email_required" => ConnectorErrorType::UserError,
"endpoint_invalid" => ConnectorErrorType::TechnicalError,
"expiry_date_format_invalid" => ConnectorErrorType::UserError,
"fail_url_invalid" => ConnectorErrorType::TechnicalError,
"first_name_required" => ConnectorErrorType::UserError,
"last_name_required" => ConnectorErrorType::UserError,
"ip_address_invalid" => ConnectorErrorType::UserError,
"issuer_network_unavailable" => ConnectorErrorType::TechnicalError,
"metadata_key_invalid" => ConnectorErrorType::BusinessError,
"parameter_invalid" => ConnectorErrorType::UserError,
"password_invalid" => ConnectorErrorType::UserError,
"payment_expired" => ConnectorErrorType::BusinessError,
"payment_invalid" => ConnectorErrorType::BusinessError,
"payment_method_invalid" => ConnectorErrorType::UserError,
"payment_source_required" => ConnectorErrorType::UserError,
"payment_type_invalid" => ConnectorErrorType::UserError,
"phone_number_invalid" => ConnectorErrorType::UserError,
"phone_number_length_invalid" => ConnectorErrorType::UserError,
"previous_payment_id_invalid" => ConnectorErrorType::BusinessError,
"recipient_account_number_invalid" => ConnectorErrorType::BusinessError,
"recipient_account_number_required" => ConnectorErrorType::UserError,
"recipient_dob_required" => ConnectorErrorType::UserError,
"recipient_last_name_required" => ConnectorErrorType::UserError,
"recipient_zip_invalid" => ConnectorErrorType::UserError,
"recipient_zip_required" => ConnectorErrorType::UserError,
"recurring_plan_exists" => ConnectorErrorType::BusinessError,
"recurring_plan_not_exist" => ConnectorErrorType::BusinessError,
"recurring_plan_removal_failed" => ConnectorErrorType::BusinessError,
"request_invalid" => ConnectorErrorType::UserError,
"request_json_invalid" => ConnectorErrorType::UserError,
"risk_enabled_required" => ConnectorErrorType::BusinessError,
"server_api_not_allowed" => ConnectorErrorType::TechnicalError,
"source_email_invalid" => ConnectorErrorType::UserError,
"source_email_required" => ConnectorErrorType::UserError,
"source_id_invalid" => ConnectorErrorType::BusinessError,
"source_id_or_email_required" => ConnectorErrorType::UserError,
"source_id_required" => ConnectorErrorType::UserError,
"source_id_unknown" => ConnectorErrorType::BusinessError,
"source_invalid" => ConnectorErrorType::BusinessError,
"source_or_destination_required" => ConnectorErrorType::BusinessError,
"source_token_invalid" => ConnectorErrorType::BusinessError,
"source_token_required" => ConnectorErrorType::UserError,
"source_token_type_required" => ConnectorErrorType::UserError,
"source_token_type_invalid" => ConnectorErrorType::BusinessError,
"source_type_required" => ConnectorErrorType::UserError,
"sub_entities_count_invalid" => ConnectorErrorType::BusinessError,
"success_url_invalid" => ConnectorErrorType::BusinessError,
"3ds_malfunction" => ConnectorErrorType::TechnicalError,
"3ds_not_configured" => ConnectorErrorType::BusinessError,
"3ds_not_enabled_for_card" => ConnectorErrorType::BusinessError,
"3ds_not_supported" => ConnectorErrorType::BusinessError,
"3ds_payment_required" => ConnectorErrorType::BusinessError,
"token_expired" => ConnectorErrorType::BusinessError,
"token_in_use" => ConnectorErrorType::BusinessError,
"token_invalid" => ConnectorErrorType::BusinessError,
"token_required" => ConnectorErrorType::UserError,
"token_type_required" => ConnectorErrorType::UserError,
"token_used" => ConnectorErrorType::BusinessError,
"void_amount_invalid" => ConnectorErrorType::BusinessError,
"wallet_id_invalid" => ConnectorErrorType::BusinessError,
"zip_invalid" => ConnectorErrorType::UserError,
"processing_key_required" => ConnectorErrorType::BusinessError,
"processing_value_required" => ConnectorErrorType::BusinessError,
"3ds_version_invalid" => ConnectorErrorType::BusinessError,
"3ds_version_not_supported" => ConnectorErrorType::BusinessError,
"processing_error" => ConnectorErrorType::TechnicalError,
"service_unavailable" => ConnectorErrorType::TechnicalError,
"token_type_invalid" => ConnectorErrorType::UserError,
"token_data_invalid" => ConnectorErrorType::UserError,
_ => ConnectorErrorType::UnknownError,
}
}
}

View File

@ -7,6 +7,7 @@ use url::Url;
use crate::{
connector::utils::{self, RouterData, WalletData},
consts,
core::errors,
services,
types::{self, api, storage::enums, transformers::ForeignFrom},
@ -402,6 +403,8 @@ pub struct PaymentsResponse {
links: Links,
balances: Option<Balances>,
reference: Option<String>,
response_code: Option<String>,
response_summary: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
@ -419,22 +422,40 @@ impl TryFrom<types::PaymentsResponseRouterData<PaymentsResponse>>
let redirection_data = item.response.links.redirect.map(|href| {
services::RedirectForm::from((href.redirection_url, services::Method::Get))
});
let status = enums::AttemptStatus::foreign_from((
item.response.status,
item.data.request.capture_method,
));
let error_response = if status == enums::AttemptStatus::Failure {
Some(types::ErrorResponse {
status_code: item.http_code,
code: item
.response
.response_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
.response_summary
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: item.response.response_summary,
})
} else {
None
};
let payments_response_data = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(
item.response.reference.unwrap_or(item.response.id),
),
};
Ok(Self {
status: enums::AttemptStatus::foreign_from((
item.response.status,
item.data.request.capture_method,
)),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(
item.response.reference.unwrap_or(item.response.id),
),
}),
status,
response: error_response.map_or_else(|| Ok(payments_response_data), Err),
..item.data
})
}
@ -450,22 +471,38 @@ impl TryFrom<types::PaymentsSyncResponseRouterData<PaymentsResponse>>
let redirection_data = item.response.links.redirect.map(|href| {
services::RedirectForm::from((href.redirection_url, services::Method::Get))
});
let status =
enums::AttemptStatus::foreign_from((item.response.status, item.response.balances));
let error_response = if status == enums::AttemptStatus::Failure {
Some(types::ErrorResponse {
status_code: item.http_code,
code: item
.response
.response_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: item
.response
.response_summary
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: item.response.response_summary,
})
} else {
None
};
let payments_response_data = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(
item.response.reference.unwrap_or(item.response.id),
),
};
Ok(Self {
status: enums::AttemptStatus::foreign_from((
item.response.status,
item.response.balances,
)),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(
item.response.reference.unwrap_or(item.response.id),
),
}),
status,
response: error_response.map_or_else(|| Ok(payments_response_data), Err),
..item.data
})
}
@ -987,3 +1024,12 @@ impl TryFrom<&types::SubmitEvidenceRouterData> for Evidence {
})
}
}
impl From<String> for utils::ErrorCodeAndMessage {
fn from(error: String) -> Self {
Self {
error_code: error.clone(),
error_message: error,
}
}
}