mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +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 => {
|
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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user