fix(openapi): Added Error Response Schema for Status Code 400 (#8684)

Co-authored-by: Sayak Bhattacharya <sayak.b@Sayak-Bhattacharya-G092THXJ34.local>
This commit is contained in:
Sayak Bhattacharya
2025-07-22 12:23:36 +05:30
committed by GitHub
parent b2ab927713
commit a01d6083b0
8 changed files with 248 additions and 55 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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<Extra>,
pub extra: Option<Extra>,
#[cfg(feature = "detailed_errors")]
#[serde(skip_serializing_if = "Option::is_none")]
stacktrace: Option<&'a serde_json::Value>,
pub stacktrace: Option<serde_json::Value>,
}
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":{}}}"#,

View File

@ -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" }

View File

@ -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,

View File

@ -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,

View File

@ -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",

View File

@ -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",