fix: update amount_capturable based on intent_status and payment flow (#3278)

This commit is contained in:
Hrithikesh
2024-01-11 21:00:58 +05:30
committed by GitHub
parent cc3eefd317
commit 469ea20214
3 changed files with 192 additions and 46 deletions

View File

@ -117,7 +117,7 @@ where
} }
enums::AttemptStatus::Charged => { enums::AttemptStatus::Charged => {
let captured_amount = let captured_amount =
types::Capturable::get_capture_amount(&self.request, payment_data); types::Capturable::get_captured_amount(&self.request, payment_data);
let total_capturable_amount = payment_data.payment_attempt.get_total_amount(); let total_capturable_amount = payment_data.payment_attempt.get_total_amount();
if Some(total_capturable_amount) == captured_amount { if Some(total_capturable_amount) == captured_amount {
enums::AttemptStatus::Charged enums::AttemptStatus::Charged

View File

@ -24,7 +24,7 @@ use crate::{
services::RedirectForm, services::RedirectForm,
types::{ types::{
self, api, self, api,
storage::{self, enums, payment_attempt::AttemptStatusExt}, storage::{self, enums},
transformers::{ForeignFrom, ForeignTryFrom}, transformers::{ForeignFrom, ForeignTryFrom},
CaptureSyncResponse, CaptureSyncResponse,
}, },
@ -499,15 +499,9 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
error_message: Some(Some(err.message)), error_message: Some(Some(err.message)),
error_code: Some(Some(err.code)), error_code: Some(Some(err.code)),
error_reason: Some(err.reason), error_reason: Some(err.reason),
amount_capturable: if status.is_terminal_status() amount_capturable: router_data
|| router_data .request
.status .get_amount_capturable(&payment_data, status),
.maps_to_intent_status(enums::IntentStatus::Processing)
{
Some(0)
} else {
None
},
updated_by: storage_scheme.to_string(), updated_by: storage_scheme.to_string(),
unified_code: option_gsm.clone().map(|gsm| gsm.unified_code), unified_code: option_gsm.clone().map(|gsm| gsm.unified_code),
unified_message: option_gsm.map(|gsm| gsm.unified_message), unified_message: option_gsm.map(|gsm| gsm.unified_message),
@ -598,8 +592,9 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
payment_data.payment_attempt.merchant_id.clone(), payment_data.payment_attempt.merchant_id.clone(),
); );
let (capture_updates, payment_attempt_update) = let (capture_updates, payment_attempt_update) = match payment_data
match payment_data.multiple_capture_data { .multiple_capture_data
{
Some(multiple_capture_data) => { Some(multiple_capture_data) => {
let capture_update = storage::CaptureUpdate::ResponseUpdate { let capture_update = storage::CaptureUpdate::ResponseUpdate {
status: enums::CaptureStatus::foreign_try_from(router_data.status)?, status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
@ -612,13 +607,18 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
)]; )];
(Some((multiple_capture_data, capture_update_list)), None) (Some((multiple_capture_data, capture_update_list)), None)
} }
None => ( None => {
let status = router_data.get_attempt_status_for_db_update(&payment_data);
(
None, None,
Some(storage::PaymentAttemptUpdate::ResponseUpdate { Some(storage::PaymentAttemptUpdate::ResponseUpdate {
status: router_data.get_attempt_status_for_db_update(&payment_data), status,
connector: None, connector: None,
connector_transaction_id: connector_transaction_id.clone(), connector_transaction_id: connector_transaction_id.clone(),
authentication_type: None, authentication_type: None,
amount_capturable: router_data
.request
.get_amount_capturable(&payment_data, status),
payment_method_id: Some(router_data.payment_method_id), payment_method_id: Some(router_data.payment_method_id),
mandate_id: payment_data mandate_id: payment_data
.mandate_id .mandate_id
@ -632,20 +632,12 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
unified_code: error_status.clone(), unified_code: error_status.clone(),
unified_message: error_status, unified_message: error_status,
connector_response_reference_id, connector_response_reference_id,
amount_capturable: if router_data.status.is_terminal_status()
|| router_data
.status
.maps_to_intent_status(enums::IntentStatus::Processing)
{
Some(0)
} else {
None
},
updated_by: storage_scheme.to_string(), updated_by: storage_scheme.to_string(),
authentication_data, authentication_data,
encoded_data, encoded_data,
}), }),
), )
}
}; };
(capture_updates, payment_attempt_update) (capture_updates, payment_attempt_update)
@ -900,7 +892,7 @@ fn get_total_amount_captured<F: Clone, T: types::Capturable>(
} }
None => { None => {
//Non multiple capture //Non multiple capture
let amount = request.get_capture_amount(payment_data); let amount = request.get_captured_amount(payment_data);
amount_captured.or_else(|| { amount_captured.or_else(|| {
if router_data_status == enums::AttemptStatus::Charged { if router_data_status == enums::AttemptStatus::Charged {
amount amount

View File

@ -599,7 +599,17 @@ pub struct AccessTokenRequestData {
} }
pub trait Capturable { pub trait Capturable {
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
where
F: Clone,
{
None
}
fn get_amount_capturable<F>(
&self,
_payment_data: &PaymentData<F>,
_attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
@ -608,7 +618,7 @@ pub trait Capturable {
} }
impl Capturable for PaymentsAuthorizeData { impl Capturable for PaymentsAuthorizeData {
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
@ -618,41 +628,171 @@ impl Capturable for PaymentsAuthorizeData {
.map(|surcharge_details| surcharge_details.final_amount); .map(|surcharge_details| surcharge_details.final_amount);
final_amount.or(Some(self.amount)) final_amount.or(Some(self.amount))
} }
fn get_amount_capturable<F>(
&self,
payment_data: &PaymentData<F>,
attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
match payment_data
.payment_attempt
.capture_method
.unwrap_or_default()
{
common_enums::CaptureMethod::Automatic => {
let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
match intent_status {
common_enums::IntentStatus::Succeeded
| common_enums::IntentStatus::Failed
| common_enums::IntentStatus::Processing => Some(0),
common_enums::IntentStatus::Cancelled
| common_enums::IntentStatus::PartiallyCaptured
| common_enums::IntentStatus::RequiresCustomerAction
| common_enums::IntentStatus::RequiresMerchantAction
| common_enums::IntentStatus::RequiresPaymentMethod
| common_enums::IntentStatus::RequiresConfirmation
| common_enums::IntentStatus::RequiresCapture
| common_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
}
},
common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount()),
// In case of manual multiple, amount capturable must be inferred from all captures.
common_enums::CaptureMethod::ManualMultiple |
// Scheduled capture is not supported as of now
common_enums::CaptureMethod::Scheduled => None,
}
}
} }
impl Capturable for PaymentsCaptureData { impl Capturable for PaymentsCaptureData {
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
Some(self.amount_to_capture) Some(self.amount_to_capture)
} }
fn get_amount_capturable<F>(
&self,
_payment_data: &PaymentData<F>,
attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
match intent_status {
common_enums::IntentStatus::Succeeded
| common_enums::IntentStatus::PartiallyCaptured
| common_enums::IntentStatus::Processing => Some(0),
common_enums::IntentStatus::Cancelled
| common_enums::IntentStatus::Failed
| common_enums::IntentStatus::RequiresCustomerAction
| common_enums::IntentStatus::RequiresMerchantAction
| common_enums::IntentStatus::RequiresPaymentMethod
| common_enums::IntentStatus::RequiresConfirmation
| common_enums::IntentStatus::RequiresCapture
| common_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
}
}
} }
impl Capturable for CompleteAuthorizeData { impl Capturable for CompleteAuthorizeData {
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
Some(self.amount) Some(self.amount)
} }
fn get_amount_capturable<F>(
&self,
payment_data: &PaymentData<F>,
attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
match payment_data
.payment_attempt
.capture_method
.unwrap_or_default()
{
common_enums::CaptureMethod::Automatic => {
let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
match intent_status {
common_enums::IntentStatus::Succeeded|
common_enums::IntentStatus::Failed|
common_enums::IntentStatus::Processing => Some(0),
common_enums::IntentStatus::Cancelled
| common_enums::IntentStatus::PartiallyCaptured
| common_enums::IntentStatus::RequiresCustomerAction
| common_enums::IntentStatus::RequiresMerchantAction
| common_enums::IntentStatus::RequiresPaymentMethod
| common_enums::IntentStatus::RequiresConfirmation
| common_enums::IntentStatus::RequiresCapture
| common_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
}
},
common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount()),
// In case of manual multiple, amount capturable must be inferred from all captures.
common_enums::CaptureMethod::ManualMultiple |
// Scheduled capture is not supported as of now
common_enums::CaptureMethod::Scheduled => None,
}
}
} }
impl Capturable for SetupMandateRequestData {} impl Capturable for SetupMandateRequestData {}
impl Capturable for PaymentsCancelData { impl Capturable for PaymentsCancelData {
fn get_capture_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
// return previously captured amount // return previously captured amount
payment_data.payment_intent.amount_captured payment_data.payment_intent.amount_captured
} }
fn get_amount_capturable<F>(
&self,
_payment_data: &PaymentData<F>,
attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
let intent_status = common_enums::IntentStatus::foreign_from(attempt_status);
match intent_status {
common_enums::IntentStatus::Cancelled
| common_enums::IntentStatus::Processing
| common_enums::IntentStatus::PartiallyCaptured => Some(0),
common_enums::IntentStatus::Succeeded
| common_enums::IntentStatus::Failed
| common_enums::IntentStatus::RequiresCustomerAction
| common_enums::IntentStatus::RequiresMerchantAction
| common_enums::IntentStatus::RequiresPaymentMethod
| common_enums::IntentStatus::RequiresConfirmation
| common_enums::IntentStatus::RequiresCapture
| common_enums::IntentStatus::PartiallyCapturedAndCapturable => None,
}
}
} }
impl Capturable for PaymentsApproveData {} impl Capturable for PaymentsApproveData {}
impl Capturable for PaymentsRejectData {} impl Capturable for PaymentsRejectData {}
impl Capturable for PaymentsSessionData {} impl Capturable for PaymentsSessionData {}
impl Capturable for PaymentsIncrementalAuthorizationData {} impl Capturable for PaymentsIncrementalAuthorizationData {
fn get_amount_capturable<F>(
&self,
_payment_data: &PaymentData<F>,
_attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
Some(self.total_amount)
}
}
impl Capturable for PaymentsSyncData { impl Capturable for PaymentsSyncData {
fn get_capture_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64> fn get_captured_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64>
where where
F: Clone, F: Clone,
{ {
@ -661,6 +801,20 @@ impl Capturable for PaymentsSyncData {
.amount_to_capture .amount_to_capture
.or_else(|| Some(payment_data.payment_attempt.get_total_amount())) .or_else(|| Some(payment_data.payment_attempt.get_total_amount()))
} }
fn get_amount_capturable<F>(
&self,
_payment_data: &PaymentData<F>,
attempt_status: common_enums::AttemptStatus,
) -> Option<i64>
where
F: Clone,
{
if attempt_status.is_terminal_status() {
Some(0)
} else {
None
}
}
} }
pub struct AddAccessTokenResult { pub struct AddAccessTokenResult {