mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(connector): [Checkout] unify error code, message and reason in error response (#1855)
This commit is contained in:
committed by
GitHub
parent
62461f1b38
commit
e8a51c2abe
@ -7,7 +7,9 @@ use error_stack::{IntoReport, ResultExt};
|
|||||||
use masking::PeekInterface;
|
use masking::PeekInterface;
|
||||||
|
|
||||||
use self::transformers as checkout;
|
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::{
|
use crate::{
|
||||||
configs::settings,
|
configs::settings,
|
||||||
consts,
|
consts,
|
||||||
@ -102,18 +104,27 @@ impl ConnectorCommon for Checkout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
router_env::logger::info!(error_response=?response);
|
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 {
|
Ok(types::ErrorResponse {
|
||||||
status_code: res.status_code,
|
status_code: res.status_code,
|
||||||
code: response
|
code: option_error_code_message
|
||||||
.error_type
|
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
|
.map(|error_code_message| error_code_message.error_code)
|
||||||
message: response
|
.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
|
.error_codes
|
||||||
.as_ref()
|
.map(|errors| errors.join(" & "))
|
||||||
.and_then(|error_codes| error_codes.first().cloned())
|
.or(response.error_type),
|
||||||
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
|
|
||||||
reason: response.error_codes.map(|errors| errors.join(" & ")),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1211,3 +1222,128 @@ impl services::ConnectorRedirectResponse for Checkout {
|
|||||||
Ok(connector_action)
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use url::Url;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connector::utils::{self, RouterData, WalletData},
|
connector::utils::{self, RouterData, WalletData},
|
||||||
|
consts,
|
||||||
core::errors,
|
core::errors,
|
||||||
services,
|
services,
|
||||||
types::{self, api, storage::enums, transformers::ForeignFrom},
|
types::{self, api, storage::enums, transformers::ForeignFrom},
|
||||||
@ -402,6 +403,8 @@ pub struct PaymentsResponse {
|
|||||||
links: Links,
|
links: Links,
|
||||||
balances: Option<Balances>,
|
balances: Option<Balances>,
|
||||||
reference: Option<String>,
|
reference: Option<String>,
|
||||||
|
response_code: Option<String>,
|
||||||
|
response_summary: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
|
#[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| {
|
let redirection_data = item.response.links.redirect.map(|href| {
|
||||||
services::RedirectForm::from((href.redirection_url, services::Method::Get))
|
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 {
|
Ok(Self {
|
||||||
status: enums::AttemptStatus::foreign_from((
|
status,
|
||||||
item.response.status,
|
response: error_response.map_or_else(|| Ok(payments_response_data), Err),
|
||||||
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),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
..item.data
|
..item.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -450,22 +471,38 @@ impl TryFrom<types::PaymentsSyncResponseRouterData<PaymentsResponse>>
|
|||||||
let redirection_data = item.response.links.redirect.map(|href| {
|
let redirection_data = item.response.links.redirect.map(|href| {
|
||||||
services::RedirectForm::from((href.redirection_url, services::Method::Get))
|
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 {
|
Ok(Self {
|
||||||
status: enums::AttemptStatus::foreign_from((
|
status,
|
||||||
item.response.status,
|
response: error_response.map_or_else(|| Ok(payments_response_data), Err),
|
||||||
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),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
..item.data
|
..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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user