diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 08ed89c7fb6..1e8a167a179 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -1597,6 +1597,7 @@ fn get_error_response_if_failure( Some(types::ErrorResponse::from(( &info_response.error_information, &info_response.risk_information, + Some(status), http_code, info_response.id.clone(), ))) @@ -1995,6 +1996,7 @@ impl let response = Err(types::ErrorResponse::from(( &info_response.error_information, &risk_info, + Some(status), item.http_code, info_response.id.clone(), ))); @@ -2420,6 +2422,7 @@ impl response: Err(types::ErrorResponse::from(( &app_response.error_information, &risk_info, + Some(status), item.http_code, app_response.id.clone(), ))), @@ -2611,6 +2614,7 @@ impl From for enums::RefundStatus { pub struct BankOfAmericaRefundResponse { id: String, status: BankofamericaRefundStatus, + error_information: Option, } impl TryFrom> @@ -2620,17 +2624,30 @@ impl TryFrom, ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { + let refund_status = enums::RefundStatus::from(item.response.status.clone()); + let response = if utils::is_refund_failure(refund_status) { + Err(types::ErrorResponse::from(( + &item.response.error_information, + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } else { + Ok(types::RefundsResponseData { connector_refund_id: item.response.id, refund_status: enums::RefundStatus::from(item.response.status), - }), + }) + }; + + Ok(Self { + response, ..item.data }) } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum BankofamericaRefundStatus { Succeeded, @@ -2644,21 +2661,15 @@ pub enum BankofamericaRefundStatus { #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct RsyncApplicationInformation { - status: BankofamericaRefundStatus, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -pub enum BankOfAmericaRsyncResponse { - RsyncApplicationResponse(Box), - ErrorInformation(BankOfAmericaErrorInformationResponse), + status: Option, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct BankOfAmericaRsyncApplicationResponse { +pub struct BankOfAmericaRsyncResponse { id: String, - application_information: RsyncApplicationInformation, + application_information: Option, + error_information: Option, } impl TryFrom> @@ -2668,25 +2679,56 @@ impl TryFrom, ) -> Result { - match item.response { - BankOfAmericaRsyncResponse::RsyncApplicationResponse(rsync_response) => Ok(Self { - response: Ok(types::RefundsResponseData { - connector_refund_id: rsync_response.id, - refund_status: enums::RefundStatus::from( - rsync_response.application_information.status, - ), - }), - ..item.data - }), - BankOfAmericaRsyncResponse::ErrorInformation(error_response) => Ok(Self { - status: item.data.status, - response: Ok(types::RefundsResponseData { - refund_status: common_enums::RefundStatus::Pending, - connector_refund_id: error_response.id.clone(), - }), - ..item.data + let response = match item + .response + .application_information + .and_then(|application_information| application_information.status) + { + Some(status) => { + let refund_status: common_enums::RefundStatus = + enums::RefundStatus::from(status.clone()); + if utils::is_refund_failure(refund_status) { + if status == BankofamericaRefundStatus::Voided { + Err(types::ErrorResponse::from(( + &Some(BankOfAmericaErrorInformation { + message: Some(consts::REFUND_VOIDED.to_string()), + reason: None, + }), + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } else { + Err(types::ErrorResponse::from(( + &item.response.error_information, + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } + } else { + Ok(types::RefundsResponseData { + connector_refund_id: item.response.id, + refund_status, + }) + } + } + + None => Ok(types::RefundsResponseData { + connector_refund_id: item.response.id.clone(), + refund_status: match item.data.response { + Ok(response) => response.refund_status, + Err(_) => common_enums::RefundStatus::Pending, + }, }), - } + }; + + Ok(Self { + response, + ..item.data + }) } } @@ -2750,14 +2792,16 @@ impl From<( &Option, &Option, + Option, u16, String, )> for types::ErrorResponse { fn from( - (error_data, risk_information, status_code, transaction_id): ( + (error_data, risk_information, attempt_status, status_code, transaction_id): ( &Option, &Option, + Option, u16, String, ), @@ -2794,7 +2838,7 @@ impl .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: Some(error_reason.clone()), status_code, - attempt_status: Some(enums::AttemptStatus::Failure), + attempt_status, connector_transaction_id: Some(transaction_id.clone()), } } diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 2a444576b09..517d406a9d5 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -1729,6 +1729,7 @@ fn get_error_response_if_failure( Some(types::ErrorResponse::from(( &info_response.error_information, &info_response.risk_information, + Some(status), http_code, info_response.id.clone(), ))) @@ -2191,6 +2192,7 @@ impl let response = Err(types::ErrorResponse::from(( &info_response.error_information, &risk_info, + Some(status), item.http_code, info_response.id.clone(), ))); @@ -2616,6 +2618,7 @@ impl response: Err(types::ErrorResponse::from(( &app_response.error_information, &risk_info, + Some(status), item.http_code, app_response.id.clone(), ))), @@ -2702,7 +2705,7 @@ impl From for enums::RefundStatus { } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CybersourceRefundStatus { Succeeded, @@ -2718,6 +2721,7 @@ pub enum CybersourceRefundStatus { pub struct CybersourceRefundResponse { id: String, status: CybersourceRefundStatus, + error_information: Option, } impl TryFrom> @@ -2727,11 +2731,24 @@ impl TryFrom, ) -> Result { - Ok(Self { - response: Ok(types::RefundsResponseData { + let refund_status = enums::RefundStatus::from(item.response.status.clone()); + let response = if utils::is_refund_failure(refund_status) { + Err(types::ErrorResponse::from(( + &item.response.error_information, + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } else { + Ok(types::RefundsResponseData { connector_refund_id: item.response.id, refund_status: enums::RefundStatus::from(item.response.status), - }), + }) + }; + + Ok(Self { + response, ..item.data }) } @@ -2740,21 +2757,15 @@ impl TryFrom, } #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] -pub struct CybersourceRsyncApplicationResponse { +pub struct CybersourceRsyncResponse { id: String, - application_information: RsyncApplicationInformation, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -pub enum CybersourceRsyncResponse { - RsyncApplicationResponse(Box), - ErrorInformation(CybersourceErrorInformationResponse), + application_information: Option, + error_information: Option, } impl TryFrom> @@ -2764,25 +2775,55 @@ impl TryFrom, ) -> Result { - match item.response { - CybersourceRsyncResponse::RsyncApplicationResponse(rsync_reponse) => Ok(Self { - response: Ok(types::RefundsResponseData { - connector_refund_id: rsync_reponse.id, - refund_status: enums::RefundStatus::from( - rsync_reponse.application_information.status, - ), - }), - ..item.data - }), - CybersourceRsyncResponse::ErrorInformation(error_response) => Ok(Self { - status: item.data.status, - response: Ok(types::RefundsResponseData { - refund_status: common_enums::RefundStatus::Pending, - connector_refund_id: error_response.id.clone(), - }), - ..item.data + let response = match item + .response + .application_information + .and_then(|application_information| application_information.status) + { + Some(status) => { + let refund_status = enums::RefundStatus::from(status.clone()); + if utils::is_refund_failure(refund_status) { + if status == CybersourceRefundStatus::Voided { + Err(types::ErrorResponse::from(( + &Some(CybersourceErrorInformation { + message: Some(consts::REFUND_VOIDED.to_string()), + reason: None, + }), + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } else { + Err(types::ErrorResponse::from(( + &item.response.error_information, + &None, + None, + item.http_code, + item.response.id.clone(), + ))) + } + } else { + Ok(types::RefundsResponseData { + connector_refund_id: item.response.id, + refund_status, + }) + } + } + + None => Ok(types::RefundsResponseData { + connector_refund_id: item.response.id.clone(), + refund_status: match item.data.response { + Ok(response) => response.refund_status, + Err(_) => common_enums::RefundStatus::Pending, + }, }), - } + }; + + Ok(Self { + response, + ..item.data + }) } } @@ -3079,14 +3120,16 @@ impl From<( &Option, &Option, + Option, u16, String, )> for types::ErrorResponse { fn from( - (error_data, risk_information, status_code, transaction_id): ( + (error_data, risk_information, attempt_status, status_code, transaction_id): ( &Option, &Option, + Option, u16, String, ), @@ -3123,7 +3166,7 @@ impl .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), reason: Some(error_reason.clone()), status_code, - attempt_status: Some(enums::AttemptStatus::Failure), + attempt_status, connector_transaction_id: Some(transaction_id.clone()), } } diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 301cd64b011..3a94a4e7655 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -36,6 +36,8 @@ pub(crate) const NO_ERROR_CODE: &str = "No error code"; pub(crate) const UNSUPPORTED_ERROR_MESSAGE: &str = "Unsupported response type"; pub(crate) const LOW_BALANCE_ERROR_MESSAGE: &str = "Insufficient balance in the payment method"; pub(crate) const CONNECTOR_UNAUTHORIZED_ERROR: &str = "Authentication Error from the connector"; +pub(crate) const REFUND_VOIDED: &str = "Refund request has been voided."; + pub(crate) const CANNOT_CONTINUE_AUTH: &str = "Cannot continue with Authorization due to failed Liability Shift."; #[cfg(feature = "payouts")] diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 2ddc5e1f6c2..fa6b06ba3a5 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -493,12 +493,19 @@ pub async fn sync_refund_with_gateway( }; let refund_update = match router_data_res.response { - Err(error_message) => storage::RefundUpdate::ErrorUpdate { - refund_status: None, - refund_error_message: error_message.reason.or(Some(error_message.message)), - refund_error_code: Some(error_message.code), - updated_by: storage_scheme.to_string(), - }, + Err(error_message) => { + let refund_status = match error_message.status_code { + // marking failure for 2xx because this is genuine refund failure + 200..=299 => Some(enums::RefundStatus::Failure), + _ => None, + }; + storage::RefundUpdate::ErrorUpdate { + refund_status, + refund_error_message: error_message.reason.or(Some(error_message.message)), + refund_error_code: Some(error_message.code), + updated_by: storage_scheme.to_string(), + } + } Ok(response) => storage::RefundUpdate::Update { connector_refund_id: response.connector_refund_id, refund_status: response.refund_status,