From e8d948efeed3e9e4475ebc01d2be2ce3addd92a6 Mon Sep 17 00:00:00 2001 From: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:35:18 +0530 Subject: [PATCH] fix: handle 5xx during multiple capture call (#2148) --- .../src/connector/adyen/transformers.rs | 7 ++++ .../src/connector/checkout/transformers.rs | 10 +++++ .../src/connector/globalpay/transformers.rs | 6 +++ crates/router/src/connector/utils.rs | 2 + .../src/core/payments/flows/psync_flow.rs | 1 + .../payments/operations/payment_response.rs | 38 +++++++++++++++++++ .../router/src/core/payments/transformers.rs | 2 + crates/router/src/core/payments/types.rs | 6 +++ crates/router/src/types.rs | 19 ++++++++++ 9 files changed, 91 insertions(+) diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 7e85d8d42d..791f51b0be 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -278,6 +278,7 @@ pub struct Response { refusal_reason: Option, refusal_reason_code: Option, additional_data: Option, + // event_code will be available only in webhook body event_code: Option, } @@ -3710,6 +3711,12 @@ impl utils::MultipleCaptureSyncResponse for Response { fn get_connector_reference_id(&self) -> Option { Some(self.merchant_reference.clone()) } + + fn get_amount_captured(&self) -> Option { + self.amount + .as_ref() + .map(|amount_struct| amount_struct.value) + } } // Payouts diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index f61ed158ee..70d7dce908 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -872,6 +872,10 @@ impl utils::MultipleCaptureSyncResponse for ActionResponse { fn is_capture_response(&self) -> bool { self.action_type == ActionType::Capture } + + fn get_amount_captured(&self) -> Option { + Some(self.amount) + } } impl utils::MultipleCaptureSyncResponse for Box { @@ -890,6 +894,12 @@ impl utils::MultipleCaptureSyncResponse for Box { fn is_capture_response(&self) -> bool { self.status == CheckoutPaymentStatus::Captured } + fn get_amount_captured(&self) -> Option { + match self.amount { + Some(amount) => amount.try_into().ok(), + None => None, + } + } } #[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)] diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index 2100aa34f6..7a35a5f578 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -506,6 +506,12 @@ impl utils::MultipleCaptureSyncResponse for GlobalpayPaymentsResponse { true } + fn get_amount_captured(&self) -> Option { + match self.amount.clone() { + Some(amount) => amount.parse().ok(), + None => None, + } + } fn get_connector_reference_id(&self) -> Option { self.reference.clone() } diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 4d9b550783..07c04e05cf 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -1380,6 +1380,7 @@ pub trait MultipleCaptureSyncResponse { fn get_connector_reference_id(&self) -> Option { None } + fn get_amount_captured(&self) -> Option; } pub fn construct_captures_response_hashmap( @@ -1401,6 +1402,7 @@ where status: capture_sync_response.get_capture_attempt_status(), connector_response_reference_id: capture_sync_response .get_connector_reference_id(), + amount: capture_sync_response.get_amount_captured(), }, ); } diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 1c01af79f5..36d418a3ae 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -191,6 +191,7 @@ impl types::RouterData { diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 3d91faf119..4c62dd5caa 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -621,16 +621,54 @@ fn response_to_capture_update( response_list: HashMap, ) -> RouterResult> { let mut capture_update_list = vec![]; + let mut unmapped_captures = vec![]; for (connector_capture_id, capture_sync_response) in response_list { let capture = multiple_capture_data.get_capture_by_connector_capture_id(connector_capture_id); if let Some(capture) = capture { capture_update_list.push((capture.clone(), capture_sync_response.try_into()?)) + } else { + // connector_capture_id may not be populated in the captures table in some case + // if so, we try to map the unmapped capture response and captures in DB. + unmapped_captures.push(capture_sync_response) } } + capture_update_list.extend(get_capture_update_for_unmapped_capture_responses( + unmapped_captures, + multiple_capture_data, + )?); + Ok(capture_update_list) } +fn get_capture_update_for_unmapped_capture_responses( + unmapped_capture_sync_response_list: Vec, + multiple_capture_data: &MultipleCaptureData, +) -> RouterResult> { + let mut result = Vec::new(); + let captures_without_connector_capture_id: Vec<_> = multiple_capture_data + .get_pending_captures_without_connector_capture_id() + .into_iter() + .cloned() + .collect(); + for capture_sync_response in unmapped_capture_sync_response_list { + if let Some(capture) = captures_without_connector_capture_id + .iter() + .find(|capture| { + capture_sync_response.get_connector_response_reference_id() + == Some(capture.capture_id.clone()) + || capture_sync_response.get_amount_captured() == Some(capture.amount) + }) + { + result.push(( + capture.clone(), + storage::CaptureUpdate::try_from(capture_sync_response)?, + )) + } + } + Ok(result) +} + fn get_total_amount_captured( request: T, amount_captured: Option, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 6da9ef5707..2606b81ae3 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1206,6 +1206,7 @@ impl TryFrom for storage::CaptureUpdate { resource_id, status, connector_response_reference_id, + .. } => { let connector_capture_id = match resource_id { types::ResponseId::ConnectorTransactionId(id) => Some(id), @@ -1222,6 +1223,7 @@ impl TryFrom for storage::CaptureUpdate { message, reason, status_code, + .. } => Ok(Self::ErrorUpdate { status: match status_code { 500..=511 => storage::enums::CaptureStatus::Pending, diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs index 7157deffc1..f420a4b87a 100644 --- a/crates/router/src/core/payments/types.rs +++ b/crates/router/src/core/payments/types.rs @@ -157,4 +157,10 @@ impl MultipleCaptureData { .collect(); pending_connector_capture_ids } + pub fn get_pending_captures_without_connector_capture_id(&self) -> Vec<&storage::Capture> { + self.get_pending_captures() + .into_iter() + .filter(|capture| capture.connector_capture_id.is_none()) + .collect() + } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index b1488d6df3..77c053d47c 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -572,15 +572,34 @@ pub enum CaptureSyncResponse { resource_id: ResponseId, status: storage_enums::AttemptStatus, connector_response_reference_id: Option, + amount: Option, }, Error { code: String, message: String, reason: Option, status_code: u16, + amount: Option, }, } +impl CaptureSyncResponse { + pub fn get_amount_captured(&self) -> Option { + match self { + Self::Success { amount, .. } | Self::Error { amount, .. } => *amount, + } + } + pub fn get_connector_response_reference_id(&self) -> Option { + match self { + Self::Success { + connector_response_reference_id, + .. + } => connector_response_reference_id.clone(), + Self::Error { .. } => None, + } + } +} + #[derive(Debug, Clone)] pub enum PaymentsResponseData { TransactionResponse {