mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
fix(payments): update error handling for payment void v2 (#9595)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -11060,6 +11060,11 @@
|
||||
"type": "string",
|
||||
"description": "The error message"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "The detailed error reason that was returned by the connector.",
|
||||
"nullable": true
|
||||
},
|
||||
"unified_code": {
|
||||
"type": "string",
|
||||
"description": "The unified error code across all connectors.\nThis can be relied upon for taking decisions based on the error.",
|
||||
|
||||
@ -4992,6 +4992,9 @@ pub struct PaymentsCancelResponse {
|
||||
/// The url to which user must be redirected to after completion of the purchase
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub return_url: Option<common_utils::types::Url>,
|
||||
|
||||
/// Error details for the payment
|
||||
pub error: Option<ErrorDetails>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, Eq, PartialEq, serde::Serialize)]
|
||||
@ -6284,6 +6287,8 @@ pub struct ErrorDetails {
|
||||
pub code: String,
|
||||
/// The error message
|
||||
pub message: String,
|
||||
/// The detailed error reason that was returned by the connector.
|
||||
pub reason: Option<String>,
|
||||
/// The unified error code across all connectors.
|
||||
/// This can be relied upon for taking decisions based on the error.
|
||||
pub unified_code: Option<String>,
|
||||
|
||||
@ -1855,10 +1855,11 @@ impl
|
||||
{
|
||||
fn get_payment_intent_update(
|
||||
&self,
|
||||
_payment_data: &payments::PaymentCancelData<router_flow_types::Void>,
|
||||
payment_data: &payments::PaymentCancelData<router_flow_types::Void>,
|
||||
storage_scheme: common_enums::MerchantStorageScheme,
|
||||
) -> PaymentIntentUpdate {
|
||||
let intent_status = common_enums::IntentStatus::from(self.status);
|
||||
let intent_status =
|
||||
common_enums::IntentStatus::from(self.get_attempt_status_for_db_update(payment_data));
|
||||
PaymentIntentUpdate::VoidUpdate {
|
||||
status: intent_status,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
@ -1870,10 +1871,55 @@ impl
|
||||
payment_data: &payments::PaymentCancelData<router_flow_types::Void>,
|
||||
storage_scheme: common_enums::MerchantStorageScheme,
|
||||
) -> PaymentAttemptUpdate {
|
||||
PaymentAttemptUpdate::VoidUpdate {
|
||||
status: self.status,
|
||||
cancellation_reason: payment_data.payment_attempt.cancellation_reason.clone(),
|
||||
updated_by: storage_scheme.to_string(),
|
||||
match &self.response {
|
||||
Err(ref error_response) => {
|
||||
let ErrorResponse {
|
||||
code,
|
||||
message,
|
||||
reason,
|
||||
status_code: _,
|
||||
attempt_status: _,
|
||||
connector_transaction_id,
|
||||
network_decline_code,
|
||||
network_advice_code,
|
||||
network_error_message,
|
||||
connector_metadata: _,
|
||||
} = error_response.clone();
|
||||
|
||||
// Handle errors exactly
|
||||
let status = match error_response.attempt_status {
|
||||
// Use the status sent by connector in error_response if it's present
|
||||
Some(status) => status,
|
||||
None => match error_response.status_code {
|
||||
500..=511 => common_enums::AttemptStatus::Pending,
|
||||
_ => common_enums::AttemptStatus::VoidFailed,
|
||||
},
|
||||
};
|
||||
|
||||
let error_details = ErrorDetails {
|
||||
code,
|
||||
message,
|
||||
reason,
|
||||
unified_code: None,
|
||||
unified_message: None,
|
||||
network_advice_code,
|
||||
network_decline_code,
|
||||
network_error_message,
|
||||
};
|
||||
|
||||
PaymentAttemptUpdate::ErrorUpdate {
|
||||
status,
|
||||
amount_capturable: Some(MinorUnit::zero()),
|
||||
error: error_details,
|
||||
updated_by: storage_scheme.to_string(),
|
||||
connector_payment_id: connector_transaction_id,
|
||||
}
|
||||
}
|
||||
Ok(ref _response) => PaymentAttemptUpdate::VoidUpdate {
|
||||
status: self.status,
|
||||
cancellation_reason: payment_data.payment_attempt.cancellation_reason.clone(),
|
||||
updated_by: storage_scheme.to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1897,7 +1943,16 @@ impl
|
||||
&self,
|
||||
_payment_data: &payments::PaymentCancelData<router_flow_types::Void>,
|
||||
) -> common_enums::AttemptStatus {
|
||||
// For void operations, return Voided status
|
||||
common_enums::AttemptStatus::Voided
|
||||
// For void operations, determine status based on response
|
||||
match &self.response {
|
||||
Err(ref error_response) => match error_response.attempt_status {
|
||||
Some(status) => status,
|
||||
None => match error_response.status_code {
|
||||
500..=511 => common_enums::AttemptStatus::Pending,
|
||||
_ => common_enums::AttemptStatus::VoidFailed,
|
||||
},
|
||||
},
|
||||
Ok(ref _response) => self.status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ pub struct PaymentFlowData {
|
||||
pub merchant_id: common_utils::id_type::MerchantId,
|
||||
pub customer_id: Option<common_utils::id_type::CustomerId>,
|
||||
pub connector_customer: Option<String>,
|
||||
pub connector: String,
|
||||
pub payment_id: String,
|
||||
pub attempt_id: String,
|
||||
pub status: common_enums::AttemptStatus,
|
||||
|
||||
@ -219,6 +219,7 @@ impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PaymentF
|
||||
merchant_id: old_router_data.merchant_id.clone(),
|
||||
customer_id: old_router_data.customer_id.clone(),
|
||||
connector_customer: old_router_data.connector_customer.clone(),
|
||||
connector: old_router_data.connector.clone(),
|
||||
payment_id: old_router_data.payment_id.clone(),
|
||||
attempt_id: old_router_data.attempt_id.clone(),
|
||||
status: old_router_data.status,
|
||||
@ -265,6 +266,7 @@ impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PaymentF
|
||||
merchant_id,
|
||||
customer_id,
|
||||
connector_customer,
|
||||
connector,
|
||||
payment_id,
|
||||
attempt_id,
|
||||
status,
|
||||
@ -300,6 +302,7 @@ impl<T, Req: Clone, Resp: Clone> RouterDataConversion<T, Req, Resp> for PaymentF
|
||||
router_data.merchant_id = merchant_id;
|
||||
router_data.customer_id = customer_id;
|
||||
router_data.connector_customer = connector_customer;
|
||||
router_data.connector = connector;
|
||||
router_data.payment_id = payment_id;
|
||||
router_data.attempt_id = attempt_id;
|
||||
router_data.status = status;
|
||||
|
||||
@ -1017,6 +1017,7 @@ pub async fn construct_cancel_router_data_v2<'a>(
|
||||
request: types::PaymentsCancelData,
|
||||
connector_request_reference_id: String,
|
||||
customer_id: Option<common_utils::id_type::CustomerId>,
|
||||
connector_id: &str,
|
||||
header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
|
||||
) -> RouterResult<
|
||||
RouterDataV2<
|
||||
@ -1035,6 +1036,7 @@ pub async fn construct_cancel_router_data_v2<'a>(
|
||||
merchant_id: merchant_account.get_id().clone(),
|
||||
customer_id,
|
||||
connector_customer: None,
|
||||
connector: connector_id.to_owned(),
|
||||
payment_id: payment_data
|
||||
.payment_attempt
|
||||
.payment_id
|
||||
@ -1149,6 +1151,7 @@ pub async fn construct_router_data_for_cancel<'a>(
|
||||
request,
|
||||
connector_request_reference_id.clone(),
|
||||
customer_id.clone(),
|
||||
connector_id,
|
||||
header_payload.clone(),
|
||||
)
|
||||
.await?;
|
||||
@ -2183,6 +2186,11 @@ where
|
||||
.connector
|
||||
.as_ref()
|
||||
.and_then(|conn| api_enums::Connector::from_str(conn).ok());
|
||||
let error = payment_attempt
|
||||
.error
|
||||
.as_ref()
|
||||
.map(api_models::payments::ErrorDetails::foreign_from);
|
||||
|
||||
let response = api_models::payments::PaymentsCancelResponse {
|
||||
id: payment_intent.id.clone(),
|
||||
status: payment_intent.status,
|
||||
@ -2195,6 +2203,7 @@ where
|
||||
payment_method_subtype: Some(payment_attempt.payment_method_subtype),
|
||||
attempts: None,
|
||||
return_url: payment_intent.return_url.clone(),
|
||||
error,
|
||||
};
|
||||
|
||||
let headers = connector_http_status_code
|
||||
@ -6000,6 +6009,7 @@ impl ForeignFrom<&hyperswitch_domain_models::payments::payment_attempt::ErrorDet
|
||||
Self {
|
||||
code: error_details.code.to_owned(),
|
||||
message: error_details.message.to_owned(),
|
||||
reason: error_details.reason.clone(),
|
||||
unified_code: error_details.unified_code.clone(),
|
||||
unified_message: error_details.unified_message.clone(),
|
||||
network_advice_code: error_details.network_advice_code.clone(),
|
||||
|
||||
Reference in New Issue
Block a user