mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
fix: update amount_capturable based on intent_status and payment flow (#3278)
This commit is contained in:
@ -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
|
||||
|
||||
@ -24,7 +24,7 @@ use crate::{
|
||||
services::RedirectForm,
|
||||
types::{
|
||||
self, api,
|
||||
storage::{self, enums, payment_attempt::AttemptStatusExt},
|
||||
storage::{self, enums},
|
||||
transformers::{ForeignFrom, ForeignTryFrom},
|
||||
CaptureSyncResponse,
|
||||
},
|
||||
@ -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),
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
{
|
||||
@ -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,
|
||||
{
|
||||
@ -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,
|
||||
{
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user