mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
fix: amount_captured goes to 0 for 3ds payments (#2954)
This commit is contained in:
@ -312,6 +312,18 @@ pub struct PaymentsRequest {
|
||||
pub payment_type: Option<api_enums::PaymentType>,
|
||||
}
|
||||
|
||||
impl PaymentsRequest {
|
||||
pub fn get_total_capturable_amount(&self) -> Option<i64> {
|
||||
let surcharge_amount = self
|
||||
.surcharge_details
|
||||
.map(|surcharge_details| {
|
||||
surcharge_details.surcharge_amount + surcharge_details.tax_amount.unwrap_or(0)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
self.amount
|
||||
.map(|amount| i64::from(amount) + surcharge_amount)
|
||||
}
|
||||
}
|
||||
#[derive(
|
||||
Default, Debug, Clone, serde::Serialize, serde::Deserialize, Copy, ToSchema, PartialEq,
|
||||
)]
|
||||
|
||||
@ -111,14 +111,8 @@ where
|
||||
}
|
||||
}
|
||||
enums::AttemptStatus::Charged => {
|
||||
let captured_amount = if self.request.is_psync() {
|
||||
payment_data
|
||||
.payment_attempt
|
||||
.amount_to_capture
|
||||
.or(Some(payment_data.payment_attempt.get_total_amount()))
|
||||
} else {
|
||||
types::Capturable::get_capture_amount(&self.request)
|
||||
};
|
||||
let captured_amount =
|
||||
types::Capturable::get_capture_amount(&self.request, payment_data);
|
||||
if Some(payment_data.payment_attempt.get_total_amount()) == captured_amount {
|
||||
enums::AttemptStatus::Charged
|
||||
} else if captured_amount.is_some() {
|
||||
|
||||
@ -601,19 +601,19 @@ pub fn validate_request_amount_and_amount_to_capture(
|
||||
}
|
||||
}
|
||||
|
||||
/// if confirm = true and capture method = automatic, amount_to_capture(if provided) must be equal to amount
|
||||
/// if capture method = automatic, amount_to_capture(if provided) must be equal to amount
|
||||
#[instrument(skip_all)]
|
||||
pub fn validate_amount_to_capture_in_create_call_request(
|
||||
request: &api_models::payments::PaymentsRequest,
|
||||
) -> CustomResult<(), errors::ApiErrorResponse> {
|
||||
if request.capture_method.unwrap_or_default() == api_enums::CaptureMethod::Automatic
|
||||
&& request.confirm.unwrap_or(false)
|
||||
if request.capture_method.unwrap_or_default() == api_enums::CaptureMethod::Automatic {
|
||||
let total_capturable_amount = request.get_total_capturable_amount();
|
||||
if let Some((amount_to_capture, total_capturable_amount)) =
|
||||
request.amount_to_capture.zip(total_capturable_amount)
|
||||
{
|
||||
if let Some((amount_to_capture, amount)) = request.amount_to_capture.zip(request.amount) {
|
||||
let amount_int: i64 = amount.into();
|
||||
utils::when(amount_to_capture != amount_int, || {
|
||||
utils::when(amount_to_capture != total_capturable_amount, || {
|
||||
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "amount_to_capture must be equal to amount when confirm = true and capture_method = automatic".into()
|
||||
message: "amount_to_capture must be equal to total_capturable_amount when capture_method = automatic".into()
|
||||
}))
|
||||
})
|
||||
} else {
|
||||
|
||||
@ -751,7 +751,7 @@ fn get_total_amount_captured<F: Clone, T: types::Capturable>(
|
||||
}
|
||||
None => {
|
||||
//Non multiple capture
|
||||
let amount = request.get_capture_amount();
|
||||
let amount = request.get_capture_amount(payment_data);
|
||||
amount_captured.or_else(|| {
|
||||
if router_data_status == enums::AttemptStatus::Charged {
|
||||
amount
|
||||
|
||||
@ -58,13 +58,12 @@ pub async fn refund_create_core(
|
||||
)?;
|
||||
|
||||
// Amount is not passed in request refer from payment intent.
|
||||
amount = req.amount.unwrap_or(
|
||||
payment_intent
|
||||
.amount_captured
|
||||
amount = req
|
||||
.amount
|
||||
.or(payment_intent.amount_captured)
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.into_report()
|
||||
.attach_printable("amount captured is none in a successful payment")?,
|
||||
);
|
||||
.attach_printable("amount captured is none in a successful payment")?;
|
||||
|
||||
//[#299]: Can we change the flow based on some workflow idea
|
||||
utils::when(amount <= 0, || {
|
||||
|
||||
@ -30,9 +30,10 @@ use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLO
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{self, RouterResult},
|
||||
payments::RecurringMandatePaymentData,
|
||||
payments::{PaymentData, RecurringMandatePaymentData},
|
||||
},
|
||||
services,
|
||||
types::storage::payment_attempt::PaymentAttemptExt,
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
@ -544,7 +545,10 @@ pub struct AccessTokenRequestData {
|
||||
}
|
||||
|
||||
pub trait Capturable {
|
||||
fn get_capture_amount(&self) -> Option<i64> {
|
||||
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
None
|
||||
}
|
||||
fn get_surcharge_amount(&self) -> Option<i64> {
|
||||
@ -553,13 +557,13 @@ pub trait Capturable {
|
||||
fn get_tax_on_surcharge_amount(&self) -> Option<i64> {
|
||||
None
|
||||
}
|
||||
fn is_psync(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Capturable for PaymentsAuthorizeData {
|
||||
fn get_capture_amount(&self) -> Option<i64> {
|
||||
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
let final_amount = self
|
||||
.surcharge_details
|
||||
.as_ref()
|
||||
@ -579,24 +583,44 @@ impl Capturable for PaymentsAuthorizeData {
|
||||
}
|
||||
|
||||
impl Capturable for PaymentsCaptureData {
|
||||
fn get_capture_amount(&self) -> Option<i64> {
|
||||
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
Some(self.amount_to_capture)
|
||||
}
|
||||
}
|
||||
|
||||
impl Capturable for CompleteAuthorizeData {
|
||||
fn get_capture_amount(&self) -> Option<i64> {
|
||||
fn get_capture_amount<F>(&self, _payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
Some(self.amount)
|
||||
}
|
||||
}
|
||||
impl Capturable for SetupMandateRequestData {}
|
||||
impl Capturable for PaymentsCancelData {}
|
||||
impl Capturable for PaymentsCancelData {
|
||||
fn get_capture_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
// return previously captured amount
|
||||
payment_data.payment_intent.amount_captured
|
||||
}
|
||||
}
|
||||
impl Capturable for PaymentsApproveData {}
|
||||
impl Capturable for PaymentsRejectData {}
|
||||
impl Capturable for PaymentsSessionData {}
|
||||
impl Capturable for PaymentsSyncData {
|
||||
fn is_psync(&self) -> bool {
|
||||
true
|
||||
fn get_capture_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
payment_data
|
||||
.payment_attempt
|
||||
.amount_to_capture
|
||||
.or(Some(payment_data.payment_attempt.get_total_amount()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"confirm": false,
|
||||
"capture_method": "automatic",
|
||||
"capture_on": "2022-09-10T10:11:12Z",
|
||||
"amount_to_capture": 6540,
|
||||
"amount_to_capture": 8000,
|
||||
"customer_id": "StripeCustomer",
|
||||
"email": "guest@example.com",
|
||||
"name": "John Doe",
|
||||
|
||||
Reference in New Issue
Block a user