mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
docs: standardise error types and error codes (#432)
This commit is contained in:
@ -64,7 +64,7 @@ serde_json = "1.0.91"
|
|||||||
serde_path_to_error = "0.1.9"
|
serde_path_to_error = "0.1.9"
|
||||||
serde_qs = { version = "0.11.0", optional = true }
|
serde_qs = { version = "0.11.0", optional = true }
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"]}
|
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
|
||||||
signal-hook = "0.3.14"
|
signal-hook = "0.3.14"
|
||||||
strum = { version = "0.24.1", features = ["derive"] }
|
strum = { version = "0.24.1", features = ["derive"] }
|
||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
|
|||||||
@ -325,7 +325,7 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
|
|||||||
errors::ApiErrorResponse::Unauthorized
|
errors::ApiErrorResponse::Unauthorized
|
||||||
| errors::ApiErrorResponse::InvalidJwtToken
|
| errors::ApiErrorResponse::InvalidJwtToken
|
||||||
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
||||||
| errors::ApiErrorResponse::InvalidEphermeralKey => Self::Unauthorized,
|
| errors::ApiErrorResponse::InvalidEphemeralKey => Self::Unauthorized,
|
||||||
errors::ApiErrorResponse::InvalidRequestUrl
|
errors::ApiErrorResponse::InvalidRequestUrl
|
||||||
| errors::ApiErrorResponse::InvalidHttpMethod => Self::InvalidRequestUrl,
|
| errors::ApiErrorResponse::InvalidHttpMethod => Self::InvalidRequestUrl,
|
||||||
errors::ApiErrorResponse::MissingRequiredField { field_name } => {
|
errors::ApiErrorResponse::MissingRequiredField { field_name } => {
|
||||||
|
|||||||
@ -18,128 +18,67 @@ pub enum ErrorType {
|
|||||||
#[derive(Debug, Clone, router_derive::ApiError)]
|
#[derive(Debug, Clone, router_derive::ApiError)]
|
||||||
#[error(error_type_enum = ErrorType)]
|
#[error(error_type_enum = ErrorType)]
|
||||||
pub enum ApiErrorResponse {
|
pub enum ApiErrorResponse {
|
||||||
|
#[error(error_type = ErrorType::ServerNotAvailable, code = "IR_00", message = "{message:?}")]
|
||||||
|
NotImplemented { message: NotImplementedMessage },
|
||||||
#[error(
|
#[error(
|
||||||
error_type = ErrorType::InvalidRequestError, code = "IR_01",
|
error_type = ErrorType::InvalidRequestError, code = "IR_01",
|
||||||
message = "API key not provided or invalid API key used. Provide API key in the Authorization header or create new API key, using api-key (e.g api-key: API_KEY)."
|
message = "API key not provided or invalid API key used"
|
||||||
)]
|
)]
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "Unrecognized request URL.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_02", message = "Unrecognized request URL")]
|
||||||
InvalidRequestUrl,
|
InvalidRequestUrl,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "The HTTP method is not applicable for this API.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "The HTTP method is not applicable for this API")]
|
||||||
InvalidHttpMethod,
|
InvalidHttpMethod,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_05", message = "Missing required param: {field_name}.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "Missing required param: {field_name}")]
|
||||||
MissingRequiredField { field_name: &'static str },
|
MissingRequiredField { field_name: &'static str },
|
||||||
#[error(
|
#[error(
|
||||||
error_type = ErrorType::InvalidRequestError, code = "IR_06",
|
error_type = ErrorType::InvalidRequestError, code = "IR_05",
|
||||||
message = "{field_name} contains invalid data. Expected format is {expected_format}."
|
message = "{field_name} contains invalid data. Expected format is {expected_format}"
|
||||||
)]
|
)]
|
||||||
InvalidDataFormat {
|
InvalidDataFormat {
|
||||||
field_name: String,
|
field_name: String,
|
||||||
expected_format: String,
|
expected_format: String,
|
||||||
},
|
},
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "{message}")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_06", message = "{message}")]
|
||||||
InvalidRequestData { message: String },
|
InvalidRequestData { message: String },
|
||||||
/// Typically used when a field has invalid value, or deserialization of the value contained in
|
/// Typically used when a field has invalid value, or deserialization of the value contained in a field fails.
|
||||||
/// a field fails.
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Invalid value provided: {field_name}")]
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Invalid value provided: {field_name}.")]
|
|
||||||
InvalidDataValue { field_name: &'static str },
|
InvalidDataValue { field_name: &'static str },
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Client secret was not provided")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret was not provided")]
|
||||||
ClientSecretNotGiven,
|
ClientSecretNotGiven,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "The client_secret provided does not match the client_secret associated with the Payment.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_09", message = "The client_secret provided does not match the client_secret associated with the Payment")]
|
||||||
ClientSecretInvalid,
|
ClientSecretInvalid,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Customer has existing mandate/subsciption.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Customer has active mandate/subsciption")]
|
||||||
MandateActive,
|
MandateActive,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Customer has already redacted.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_11", message = "Customer has already been redacted")]
|
||||||
CustomerRedacted,
|
CustomerRedacted,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Reached maximum refund attempts")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")]
|
||||||
MaximumRefundCount,
|
MaximumRefundCount,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Refund amount exceeds the payment amount.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "Refund amount exceeds the payment amount")]
|
||||||
RefundAmountExceedsPaymentAmount,
|
RefundAmountExceedsPaymentAmount,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_09", message = "This PaymentIntent could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")]
|
||||||
PaymentUnexpectedState {
|
PaymentUnexpectedState {
|
||||||
current_flow: String,
|
current_flow: String,
|
||||||
field_name: String,
|
field_name: String,
|
||||||
current_value: String,
|
current_value: String,
|
||||||
states: String,
|
states: String,
|
||||||
},
|
},
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Invalid Ephemeral Key for the customer")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_15", message = "Invalid Ephemeral Key for the customer")]
|
||||||
InvalidEphermeralKey,
|
InvalidEphemeralKey,
|
||||||
/// Typically used when information involving multiple fields or previously provided
|
/// Typically used when information involving multiple fields or previously provided information doesn't satisfy a condition.
|
||||||
/// information doesn't satisfy a condition.
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_16", message = "{message}")]
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "{message}")]
|
|
||||||
PreconditionFailed { message: String },
|
PreconditionFailed { message: String },
|
||||||
#[error(
|
#[error(
|
||||||
error_type = ErrorType::InvalidRequestError, code = "IR_11",
|
error_type = ErrorType::InvalidRequestError, code = "IR_17",
|
||||||
message = "Access forbidden, invalid JWT token was used."
|
message = "Access forbidden, invalid JWT token was used"
|
||||||
)]
|
)]
|
||||||
InvalidJwtToken,
|
InvalidJwtToken,
|
||||||
#[error(
|
#[error(
|
||||||
error_type = ErrorType::InvalidRequestError, code = "IR_12",
|
error_type = ErrorType::InvalidRequestError, code = "IR_18",
|
||||||
message = "{message}",
|
message = "{message}",
|
||||||
)]
|
)]
|
||||||
GenericUnauthorized { message: String },
|
GenericUnauthorized { message: String },
|
||||||
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Payment failed while processing with connector. Retry payment.")]
|
|
||||||
PaymentAuthorizationFailed { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_02", message = "Payment failed while processing with connector. Retry payment.")]
|
|
||||||
PaymentAuthenticationFailed { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_03", message = "Capture attempt failed while processing with connector.")]
|
|
||||||
PaymentCaptureFailed { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_04", message = "Capture attempt failed while processing with connector.")]
|
|
||||||
InvalidCardData { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_05", message = "Payment failed while processing with connector. Retry payment.")]
|
|
||||||
CardExpired { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_06", message = "Refund failed while processing with connector. Retry refund.")]
|
|
||||||
RefundFailed { data: Option<serde_json::Value> },
|
|
||||||
#[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Verification failed while processing with connector. Retry operation.")]
|
|
||||||
VerificationFailed { data: Option<serde_json::Value> },
|
|
||||||
|
|
||||||
#[error(error_type = ErrorType::ServerNotAvailable, code = "RE_00", message = "Something went wrong.")]
|
|
||||||
InternalServerError,
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_01", message = "Duplicate refund request. Refund already attempted with the refund ID.")]
|
|
||||||
DuplicateRefundRequest,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Refund does not exist in our records.")]
|
|
||||||
RefundNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Customer does not exist in our records.")]
|
|
||||||
CustomerNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Payment does not exist in our records.")]
|
|
||||||
PaymentNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Payment method does not exist in our records.")]
|
|
||||||
PaymentMethodNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Merchant account does not exist in our records.")]
|
|
||||||
MerchantAccountNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Merchant connector account does not exist in our records.")]
|
|
||||||
MerchantConnectorAccountNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Resource ID does not exist in our records.")]
|
|
||||||
ResourceIdNotFound,
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_01", message = "Duplicate mandate request. Mandate already attempted with the Mandate ID.")]
|
|
||||||
DuplicateMandate,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Mandate does not exist in our records.")]
|
|
||||||
MandateNotFound,
|
|
||||||
#[error(error_type = ErrorType::ValidationError, code = "RE_03", message = "Return URL is not configured and not passed in payments request.")]
|
|
||||||
ReturnUrlUnavailable,
|
|
||||||
#[error(error_type = ErrorType::ValidationError, code = "RE_03", message = "Refunds not possible through hyperswitch. Please raise Refunds through {connector} dashboard")]
|
|
||||||
RefundNotPossible { connector: String },
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The merchant account with the specified details already exists in our records.")]
|
|
||||||
DuplicateMerchantAccount,
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The merchant connector account with the specified details already exists in our records.")]
|
|
||||||
DuplicateMerchantConnectorAccount,
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The payment method with the specified details already exists in our records.")]
|
|
||||||
DuplicatePaymentMethod,
|
|
||||||
#[error(error_type = ErrorType::DuplicateRequest, code = "RE_04", message = "The payment with the specified payment_id '{payment_id}' already exists in our records.")]
|
|
||||||
DuplicatePayment { payment_id: String },
|
|
||||||
#[error(error_type= ErrorType::InvalidRequestError, code = "RE_05", message = "The payment has not succeeded yet")]
|
|
||||||
PaymentNotSucceeded,
|
|
||||||
#[error(error_type= ErrorType::ObjectNotFound, code = "RE_05", message = "Successful payment not found for the given payment id")]
|
|
||||||
SuccessfulPaymentNotFound,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_05", message = "The connector provided in the request is incorrect or not available")]
|
|
||||||
IncorrectConnectorNameGiven,
|
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "RE_05", message = "Address does not exist in our records.")]
|
|
||||||
AddressNotFound,
|
|
||||||
#[error(error_type = ErrorType::ValidationError, code = "RE_03", message = "Mandate Validation Failed" )]
|
|
||||||
MandateValidationFailed { reason: String },
|
|
||||||
#[error(error_type = ErrorType::ServerNotAvailable, code = "IR_00", message = "{message:?}")]
|
|
||||||
NotImplemented { message: NotImplementedMessage },
|
|
||||||
#[error(error_type = ErrorType::ConnectorError, code = "CE_00", message = "{code}: {message}", ignore = "status_code")]
|
#[error(error_type = ErrorType::ConnectorError, code = "CE_00", message = "{code}: {message}", ignore = "status_code")]
|
||||||
ExternalConnectorError {
|
ExternalConnectorError {
|
||||||
code: String,
|
code: String,
|
||||||
@ -147,6 +86,65 @@ pub enum ApiErrorResponse {
|
|||||||
connector: String,
|
connector: String,
|
||||||
status_code: u16,
|
status_code: u16,
|
||||||
},
|
},
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Payment failed during authorization with connector. Retry payment")]
|
||||||
|
PaymentAuthorizationFailed { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_02", message = "Payment failed during authentication with connector. Retry payment")]
|
||||||
|
PaymentAuthenticationFailed { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_03", message = "Capture attempt failed while processing with connector")]
|
||||||
|
PaymentCaptureFailed { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_04", message = "The card data is invalid")]
|
||||||
|
InvalidCardData { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_05", message = "The card has expired")]
|
||||||
|
CardExpired { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_06", message = "Refund failed while processing with connector. Retry refund")]
|
||||||
|
RefundFailed { data: Option<serde_json::Value> },
|
||||||
|
#[error(error_type = ErrorType::ProcessingError, code = "CE_07", message = "Verification failed while processing with connector. Retry operation")]
|
||||||
|
VerificationFailed { data: Option<serde_json::Value> },
|
||||||
|
|
||||||
|
#[error(error_type = ErrorType::ServerNotAvailable, code = "HE_00", message = "Something went wrong")]
|
||||||
|
InternalServerError,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate refund request. Refund already attempted with the refund ID")]
|
||||||
|
DuplicateRefundRequest,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate mandate request. Mandate already attempted with the Mandate ID")]
|
||||||
|
DuplicateMandate,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The merchant account with the specified details already exists in our records")]
|
||||||
|
DuplicateMerchantAccount,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The merchant connector account with the specified details already exists in our records")]
|
||||||
|
DuplicateMerchantConnectorAccount,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payment method with the specified details already exists in our records")]
|
||||||
|
DuplicatePaymentMethod,
|
||||||
|
#[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "The payment with the specified payment_id '{payment_id}' already exists in our records")]
|
||||||
|
DuplicatePayment { payment_id: String },
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Refund does not exist in our records")]
|
||||||
|
RefundNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Customer does not exist in our records")]
|
||||||
|
CustomerNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment does not exist in our records")]
|
||||||
|
PaymentNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment method does not exist in our records")]
|
||||||
|
PaymentMethodNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Merchant account does not exist in our records")]
|
||||||
|
MerchantAccountNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Merchant connector account does not exist in our records")]
|
||||||
|
MerchantConnectorAccountNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Resource ID does not exist in our records")]
|
||||||
|
ResourceIdNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Mandate does not exist in our records")]
|
||||||
|
MandateNotFound,
|
||||||
|
#[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Return URL is not configured and not passed in payments request")]
|
||||||
|
ReturnUrlUnavailable,
|
||||||
|
#[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "This refund is not possible through Hyperswitch. Please raise the refund through {connector} dashboard")]
|
||||||
|
RefundNotPossible { connector: String },
|
||||||
|
#[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Mandate Validation Failed" )]
|
||||||
|
MandateValidationFailed { reason: String },
|
||||||
|
#[error(error_type= ErrorType::ValidationError, code = "HE_03", message = "The payment has not succeeded yet. Please pass a successful payment to initiate refund")]
|
||||||
|
PaymentNotSucceeded,
|
||||||
|
#[error(error_type= ErrorType::ObjectNotFound, code = "HE_04", message = "Successful payment not found for the given payment id")]
|
||||||
|
SuccessfulPaymentNotFound,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "The connector provided in the request is incorrect or not available")]
|
||||||
|
IncorrectConnectorNameGiven,
|
||||||
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "Address does not exist in our records")]
|
||||||
|
AddressNotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -185,7 +183,7 @@ impl actix_web::ResponseError for ApiErrorResponse {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Unauthorized
|
Self::Unauthorized
|
||||||
| Self::InvalidEphermeralKey
|
| Self::InvalidEphemeralKey
|
||||||
| Self::InvalidJwtToken
|
| Self::InvalidJwtToken
|
||||||
| Self::GenericUnauthorized { .. } => StatusCode::UNAUTHORIZED, // 401
|
| Self::GenericUnauthorized { .. } => StatusCode::UNAUTHORIZED, // 401
|
||||||
Self::ExternalConnectorError { status_code, .. } => {
|
Self::ExternalConnectorError { status_code, .. } => {
|
||||||
|
|||||||
@ -236,7 +236,7 @@ pub async fn is_ephemeral_auth(
|
|||||||
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
if ephemeral_key.customer_id.ne(customer_id) {
|
if ephemeral_key.customer_id.ne(customer_id) {
|
||||||
return Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey));
|
return Err(report!(errors::ApiErrorResponse::InvalidEphemeralKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Box::new(MerchantIdAuth(ephemeral_key.merchant_id)))
|
Ok(Box::new(MerchantIdAuth(ephemeral_key.merchant_id)))
|
||||||
|
|||||||
Reference in New Issue
Block a user