diff --git a/api-reference/v1/openapi_spec_v1.json b/api-reference/v1/openapi_spec_v1.json index 36a2896008..f1383e1761 100644 --- a/api-reference/v1/openapi_spec_v1.json +++ b/api-reference/v1/openapi_spec_v1.json @@ -633,7 +633,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -712,7 +719,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -873,7 +887,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -937,7 +958,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -977,7 +1005,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1031,7 +1066,14 @@ "description": "Payment canceled" }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1213,7 +1255,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1318,7 +1367,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1368,7 +1424,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1418,7 +1481,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1468,7 +1538,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1659,7 +1736,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1756,7 +1840,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -14742,6 +14833,28 @@ "GcashRedirection": { "type": "object" }, + "GenericErrorResponseOpenApi": { + "type": "object", + "required": [ + "error_type", + "message", + "code" + ], + "properties": { + "error_type": { + "type": "string", + "example": "invalid_request" + }, + "message": { + "type": "string", + "example": "Missing required param: {param}" + }, + "code": { + "type": "string", + "example": "IR_04" + } + } + }, "GenericLinkUiConfig": { "type": "object", "description": "Object for GenericLinkUiConfig", diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 671658dc29..6928c873be 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -1809,7 +1809,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -1999,7 +2006,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -2122,7 +2136,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -2173,7 +2194,14 @@ } }, "400": { - "description": "Missing mandatory fields" + "description": "Missing mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -3231,7 +3259,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -3289,7 +3324,14 @@ } }, "400": { - "description": "Missing Mandatory fields" + "description": "Missing Mandatory fields", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericErrorResponseOpenApi" + } + } + } } }, "security": [ @@ -11406,6 +11448,28 @@ "GcashRedirection": { "type": "object" }, + "GenericErrorResponseOpenApi": { + "type": "object", + "required": [ + "error_type", + "message", + "code" + ], + "properties": { + "error_type": { + "type": "string", + "example": "invalid_request" + }, + "message": { + "type": "string", + "example": "Missing required param: {param}" + }, + "code": { + "type": "string", + "example": "IR_04" + } + } + }, "GenericLinkUiConfig": { "type": "object", "description": "Object for GenericLinkUiConfig", diff --git a/crates/api_models/src/errors/types.rs b/crates/api_models/src/errors/types.rs index 233e0d933b..1a22a2d5cd 100644 --- a/crates/api_models/src/errors/types.rs +++ b/crates/api_models/src/errors/types.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; - use reqwest::StatusCode; +use router_derive::PolymorphicSchema; use serde::Serialize; +use utoipa::ToSchema; #[derive(Debug, serde::Serialize)] pub enum ErrorType { @@ -38,31 +38,45 @@ impl ApiError { } } -#[derive(Debug, serde::Serialize)] -struct ErrorResponse<'a> { +#[derive(Debug, serde::Serialize, ToSchema, PolymorphicSchema)] +#[generate_schemas(GenericErrorResponseOpenApi)] +pub struct ErrorResponse { #[serde(rename = "type")] - error_type: &'static str, - message: Cow<'a, str>, - code: String, + #[schema( + example = "invalid_request", + value_type = &'static str + )] + pub error_type: &'static str, + #[schema( + example = "Missing required param: {param}", + value_type = String + )] + pub message: String, + #[schema( + example = "IR_04", + value_type = String + )] + pub code: String, #[serde(flatten)] - extra: &'a Option, + pub extra: Option, + #[cfg(feature = "detailed_errors")] #[serde(skip_serializing_if = "Option::is_none")] - stacktrace: Option<&'a serde_json::Value>, + pub stacktrace: Option, } -impl<'a> From<&'a ApiErrorResponse> for ErrorResponse<'a> { - fn from(value: &'a ApiErrorResponse) -> Self { +impl From<&ApiErrorResponse> for ErrorResponse { + fn from(value: &ApiErrorResponse) -> Self { let error_info = value.get_internal_error(); let error_type = value.error_type(); Self { code: format!("{}_{:02}", error_info.sub_code, error_info.error_identifier), - message: Cow::Borrowed(value.get_internal_error().error_message.as_str()), + message: error_info.error_message.clone(), error_type, - extra: &error_info.extra, + extra: error_info.extra.clone(), #[cfg(feature = "detailed_errors")] - stacktrace: error_info.stacktrace.as_ref(), + stacktrace: error_info.stacktrace.clone(), } } } @@ -103,7 +117,7 @@ pub enum ApiErrorResponse { impl ::core::fmt::Display for ApiErrorResponse { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let error_response: ErrorResponse<'_> = self.into(); + let error_response: ErrorResponse = self.into(); write!( f, r#"{{"error":{}}}"#, diff --git a/crates/openapi/Cargo.toml b/crates/openapi/Cargo.toml index f918f73eae..ab396b10af 100644 --- a/crates/openapi/Cargo.toml +++ b/crates/openapi/Cargo.toml @@ -11,7 +11,7 @@ serde_json = "1.0.140" utoipa = { version = "4.2.3", features = ["preserve_order", "preserve_path_order", "time"] } # First party crates -api_models = { version = "0.1.0", path = "../api_models", features = ["frm", "payouts", "openapi"] } +api_models = { version = "0.1.0", path = "../api_models", features = ["frm", "payouts", "openapi", "errors"] } common_utils = { version = "0.1.0", path = "../common_utils", features = ["logs"] } common_types = { version = "0.1.0", path = "../common_types" } router_env = { version = "0.1.0", path = "../router_env" } diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 4a8f2ac9c2..54b004e32b 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -465,6 +465,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentsConfirmRequest, api_models::payments::PaymentsResponse, api_models::payments::PaymentsCreateResponseOpenApi, + api_models::errors::types::GenericErrorResponseOpenApi, api_models::payments::PaymentRetrieveBody, api_models::payments::PaymentsRetrieveRequest, api_models::payments::PaymentsCaptureRequest, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index e1cda388a3..4b88ec4c1a 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -204,6 +204,7 @@ Never share your secret api keys. Keep them guarded and secure. common_types::three_ds_decision_rule_engine::ThreeDSDecision, common_types::payments::MerchantCountryCode, common_utils::request::Method, + api_models::errors::types::GenericErrorResponseOpenApi, api_models::refunds::RefundsCreateRequest, api_models::refunds::RefundErrorDetails, api_models::refunds::RefundType, diff --git a/crates/openapi/src/routes/payments.rs b/crates/openapi/src/routes/payments.rs index 0f34b21435..70c83f2339 100644 --- a/crates/openapi/src/routes/payments.rs +++ b/crates/openapi/src/routes/payments.rs @@ -556,7 +556,7 @@ )) ) ), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi), ), tag = "Payments", operation_id = "Create a Payment", @@ -636,7 +636,7 @@ pub fn payments_retrieve() {} ), responses( (status = 200, description = "Payment updated", body = PaymentsCreateResponseOpenApi), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Update a Payment", @@ -691,7 +691,7 @@ pub fn payments_update() {} ), responses( (status = 200, description = "Payment confirmed", body = PaymentsCreateResponseOpenApi), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Confirm a Payment", @@ -731,7 +731,7 @@ pub fn payments_confirm() {} ), responses( (status = 200, description = "Payment captured", body = PaymentsResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Capture a Payment", @@ -749,7 +749,7 @@ pub fn payments_capture() {} request_body=PaymentsSessionRequest, responses( (status = 200, description = "Payment session object created or session token was retrieved from wallets", body = PaymentsSessionResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create Session tokens for a Payment", @@ -770,7 +770,7 @@ pub fn payments_connector_session() {} request_body=PaymentsSessionRequest, responses( (status = 200, description = "Payment session object created or session token was retrieved from wallets", body = PaymentsSessionResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create V2 Session tokens for a Payment", @@ -804,7 +804,7 @@ pub fn payments_connector_session() {} ), responses( (status = 200, description = "Payment canceled"), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Cancel a Payment", @@ -879,7 +879,7 @@ pub async fn profile_payments_list() {} ), responses( (status = 200, description = "Payment authorized amount incremented", body = PaymentsResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Increment authorized amount for a Payment", @@ -899,7 +899,7 @@ pub fn payments_incremental_authorization() {} ), responses( (status = 200, description = "Authentication created", body = PaymentsExternalAuthenticationResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Initiate external authentication for a Payment", @@ -917,7 +917,7 @@ pub fn payments_external_authentication() {} ), responses( (status = 200, description = "Payments Complete Authorize Success", body = PaymentsResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Complete Authorize a Payment", @@ -932,7 +932,7 @@ pub fn payments_complete_authorize() {} request_body=PaymentsDynamicTaxCalculationRequest, responses( (status = 200, description = "Tax Calculation is done", body = PaymentsDynamicTaxCalculationResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create Tax Calculation for a Payment", @@ -951,7 +951,7 @@ pub fn payments_dynamic_tax_calculation() {} request_body=PaymentsPostSessionTokensRequest, responses( (status = 200, description = "Post Session Token is done", body = PaymentsPostSessionTokensResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create Post Session Tokens for a Payment", @@ -970,7 +970,7 @@ pub fn payments_post_session_tokens() {} request_body=PaymentsUpdateMetadataRequest, responses( (status = 200, description = "Metadata updated successfully", body = PaymentsUpdateMetadataResponse), - (status = 400, description = "Missing mandatory fields") + (status = 400, description = "Missing mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Update Metadata for a Payment", @@ -998,7 +998,7 @@ pub fn payments_update_metadata() {} ), responses( (status = 200, description = "Payment created", body = PaymentsIntentResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create a Payment Intent", @@ -1102,7 +1102,7 @@ pub fn payments_update_intent() {} ), responses( (status = 200, description = "Payment created", body = PaymentsResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Confirm Payment Intent", @@ -1175,7 +1175,7 @@ pub fn payment_status() {} ), responses( (status = 200, description = "Payment created", body = PaymentsResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Payments", operation_id = "Create and Confirm Payment Intent", diff --git a/crates/openapi/src/routes/refunds.rs b/crates/openapi/src/routes/refunds.rs index be8c63e469..83e37b283f 100644 --- a/crates/openapi/src/routes/refunds.rs +++ b/crates/openapi/src/routes/refunds.rs @@ -38,7 +38,7 @@ ), responses( (status = 200, description = "Refund created", body = RefundResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Refunds", operation_id = "Create a Refund", @@ -106,7 +106,7 @@ pub async fn refunds_retrieve_with_body() {} ), responses( (status = 200, description = "Refund updated", body = RefundResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Refunds", operation_id = "Update a Refund", @@ -207,7 +207,7 @@ pub async fn refunds_filter_list() {} ), responses( (status = 200, description = "Refund created", body = RefundResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Refunds", operation_id = "Create a Refund", @@ -239,7 +239,7 @@ pub async fn refunds_create() {} ), responses( (status = 200, description = "Refund updated", body = RefundResponse), - (status = 400, description = "Missing Mandatory fields") + (status = 400, description = "Missing Mandatory fields", body = GenericErrorResponseOpenApi) ), tag = "Refunds", operation_id = "Update Refund Metadata and Reason",