Skip to content

Commit

Permalink
fix: update amount_capturable based on intent_status and payment flow (
Browse files Browse the repository at this point in the history
  • Loading branch information
hrithikesh026 authored Jan 11, 2024
1 parent cc3eefd commit 469ea20
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 46 deletions.
2 changes: 1 addition & 1 deletion crates/router/src/connector/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ where
}
enums::AttemptStatus::Charged => {
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();
if Some(total_capturable_amount) == captured_amount {
enums::AttemptStatus::Charged
Expand Down
68 changes: 30 additions & 38 deletions crates/router/src/core/payments/operations/payment_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
services::RedirectForm,
types::{
self, api,
storage::{self, enums, payment_attempt::AttemptStatusExt},
storage::{self, enums},
transformers::{ForeignFrom, ForeignTryFrom},
CaptureSyncResponse,
},
Expand Down Expand Up @@ -499,15 +499,9 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
error_message: Some(Some(err.message)),
error_code: Some(Some(err.code)),
error_reason: Some(err.reason),
amount_capturable: if status.is_terminal_status()
|| router_data
.status
.maps_to_intent_status(enums::IntentStatus::Processing)
{
Some(0)
} else {
None
},
amount_capturable: router_data
.request
.get_amount_capturable(&payment_data, status),
updated_by: storage_scheme.to_string(),
unified_code: option_gsm.clone().map(|gsm| gsm.unified_code),
unified_message: option_gsm.map(|gsm| gsm.unified_message),
Expand Down Expand Up @@ -598,27 +592,33 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
payment_data.payment_attempt.merchant_id.clone(),
);

let (capture_updates, payment_attempt_update) =
match payment_data.multiple_capture_data {
Some(multiple_capture_data) => {
let capture_update = storage::CaptureUpdate::ResponseUpdate {
status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
connector_capture_id: connector_transaction_id.clone(),
connector_response_reference_id,
};
let capture_update_list = vec![(
multiple_capture_data.get_latest_capture().clone(),
capture_update,
)];
(Some((multiple_capture_data, capture_update_list)), None)
}
None => (
let (capture_updates, payment_attempt_update) = match payment_data
.multiple_capture_data
{
Some(multiple_capture_data) => {
let capture_update = storage::CaptureUpdate::ResponseUpdate {
status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
connector_capture_id: connector_transaction_id.clone(),
connector_response_reference_id,
};
let capture_update_list = vec![(
multiple_capture_data.get_latest_capture().clone(),
capture_update,
)];
(Some((multiple_capture_data, capture_update_list)), None)
}
None => {
let status = router_data.get_attempt_status_for_db_update(&payment_data);
(
None,
Some(storage::PaymentAttemptUpdate::ResponseUpdate {
status: router_data.get_attempt_status_for_db_update(&payment_data),
status,
connector: None,
connector_transaction_id: connector_transaction_id.clone(),
authentication_type: None,
amount_capturable: router_data
.request
.get_amount_capturable(&payment_data, status),
payment_method_id: Some(router_data.payment_method_id),
mandate_id: payment_data
.mandate_id
Expand All @@ -632,21 +632,13 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
unified_code: error_status.clone(),
unified_message: error_status,
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(),
authentication_data,
encoded_data,
}),
),
};
)
}
};

(capture_updates, payment_attempt_update)
}
Expand Down Expand Up @@ -900,7 +892,7 @@ fn get_total_amount_captured<F: Clone, T: types::Capturable>(
}
None => {
//Non multiple capture
let amount = request.get_capture_amount(payment_data);
let amount = request.get_captured_amount(payment_data);
amount_captured.or_else(|| {
if router_data_status == enums::AttemptStatus::Charged {
amount
Expand Down
168 changes: 161 additions & 7 deletions crates/router/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,17 @@ pub struct AccessTokenRequestData {
}

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
F: Clone,
{
Expand All @@ -608,7 +618,7 @@ pub trait Capturable {
}

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
F: Clone,
{
Expand All @@ -618,41 +628,171 @@ impl Capturable for PaymentsAuthorizeData {
.map(|surcharge_details| surcharge_details.final_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 {
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,
{
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 {
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,
{
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 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
F: Clone,
{
// return previously captured amount
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 PaymentsRejectData {}
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 {
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,
{
Expand All @@ -661,6 +801,20 @@ impl Capturable for PaymentsSyncData {
.amount_to_capture
.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 {
Expand Down

0 comments on commit 469ea20

Please sign in to comment.