fix: handle 5xx during multiple capture call (#2148)

This commit is contained in:
Hrithikesh
2023-09-18 17:35:18 +05:30
committed by GitHub
parent 1ea823b048
commit e8d948efee
9 changed files with 91 additions and 0 deletions

View File

@ -278,6 +278,7 @@ pub struct Response {
refusal_reason: Option<String>,
refusal_reason_code: Option<String>,
additional_data: Option<AdditionalData>,
// event_code will be available only in webhook body
event_code: Option<WebhookEventCode>,
}
@ -3710,6 +3711,12 @@ impl utils::MultipleCaptureSyncResponse for Response {
fn get_connector_reference_id(&self) -> Option<String> {
Some(self.merchant_reference.clone())
}
fn get_amount_captured(&self) -> Option<i64> {
self.amount
.as_ref()
.map(|amount_struct| amount_struct.value)
}
}
// Payouts

View File

@ -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<i64> {
Some(self.amount)
}
}
impl utils::MultipleCaptureSyncResponse for Box<PaymentsResponse> {
@ -890,6 +894,12 @@ impl utils::MultipleCaptureSyncResponse for Box<PaymentsResponse> {
fn is_capture_response(&self) -> bool {
self.status == CheckoutPaymentStatus::Captured
}
fn get_amount_captured(&self) -> Option<i64> {
match self.amount {
Some(amount) => amount.try_into().ok(),
None => None,
}
}
}
#[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)]

View File

@ -506,6 +506,12 @@ impl utils::MultipleCaptureSyncResponse for GlobalpayPaymentsResponse {
true
}
fn get_amount_captured(&self) -> Option<i64> {
match self.amount.clone() {
Some(amount) => amount.parse().ok(),
None => None,
}
}
fn get_connector_reference_id(&self) -> Option<String> {
self.reference.clone()
}

View File

@ -1380,6 +1380,7 @@ pub trait MultipleCaptureSyncResponse {
fn get_connector_reference_id(&self) -> Option<String> {
None
}
fn get_amount_captured(&self) -> Option<i64>;
}
pub fn construct_captures_response_hashmap<T>(
@ -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(),
},
);
}

View File

@ -191,6 +191,7 @@ impl types::RouterData<api::PSync, types::PaymentsSyncData, types::PaymentsRespo
message: err.message,
reason: err.reason,
status_code: err.status_code,
amount: None,
});
},
Ok(types::PaymentsResponseData::MultipleCaptureResponse { capture_sync_response_list })=> {

View File

@ -621,16 +621,54 @@ fn response_to_capture_update(
response_list: HashMap<String, CaptureSyncResponse>,
) -> RouterResult<Vec<(storage::Capture, storage::CaptureUpdate)>> {
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<CaptureSyncResponse>,
multiple_capture_data: &MultipleCaptureData,
) -> RouterResult<Vec<(storage::Capture, storage::CaptureUpdate)>> {
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<F: Clone, T: types::Capturable>(
request: T,
amount_captured: Option<i64>,

View File

@ -1206,6 +1206,7 @@ impl TryFrom<types::CaptureSyncResponse> 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<types::CaptureSyncResponse> for storage::CaptureUpdate {
message,
reason,
status_code,
..
} => Ok(Self::ErrorUpdate {
status: match status_code {
500..=511 => storage::enums::CaptureStatus::Pending,

View File

@ -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()
}
}

View File

@ -572,15 +572,34 @@ pub enum CaptureSyncResponse {
resource_id: ResponseId,
status: storage_enums::AttemptStatus,
connector_response_reference_id: Option<String>,
amount: Option<i64>,
},
Error {
code: String,
message: String,
reason: Option<String>,
status_code: u16,
amount: Option<i64>,
},
}
impl CaptureSyncResponse {
pub fn get_amount_captured(&self) -> Option<i64> {
match self {
Self::Success { amount, .. } | Self::Error { amount, .. } => *amount,
}
}
pub fn get_connector_response_reference_id(&self) -> Option<String> {
match self {
Self::Success {
connector_response_reference_id,
..
} => connector_response_reference_id.clone(),
Self::Error { .. } => None,
}
}
}
#[derive(Debug, Clone)]
pub enum PaymentsResponseData {
TransactionResponse {