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>,
|
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(
|
#[derive(
|
||||||
Default, Debug, Clone, serde::Serialize, serde::Deserialize, Copy, ToSchema, PartialEq,
|
Default, Debug, Clone, serde::Serialize, serde::Deserialize, Copy, ToSchema, PartialEq,
|
||||||
)]
|
)]
|
||||||
|
|||||||
@ -111,14 +111,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
enums::AttemptStatus::Charged => {
|
enums::AttemptStatus::Charged => {
|
||||||
let captured_amount = if self.request.is_psync() {
|
let captured_amount =
|
||||||
payment_data
|
types::Capturable::get_capture_amount(&self.request, payment_data);
|
||||||
.payment_attempt
|
|
||||||
.amount_to_capture
|
|
||||||
.or(Some(payment_data.payment_attempt.get_total_amount()))
|
|
||||||
} else {
|
|
||||||
types::Capturable::get_capture_amount(&self.request)
|
|
||||||
};
|
|
||||||
if Some(payment_data.payment_attempt.get_total_amount()) == captured_amount {
|
if Some(payment_data.payment_attempt.get_total_amount()) == captured_amount {
|
||||||
enums::AttemptStatus::Charged
|
enums::AttemptStatus::Charged
|
||||||
} else if captured_amount.is_some() {
|
} 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)]
|
#[instrument(skip_all)]
|
||||||
pub fn validate_amount_to_capture_in_create_call_request(
|
pub fn validate_amount_to_capture_in_create_call_request(
|
||||||
request: &api_models::payments::PaymentsRequest,
|
request: &api_models::payments::PaymentsRequest,
|
||||||
) -> CustomResult<(), errors::ApiErrorResponse> {
|
) -> CustomResult<(), errors::ApiErrorResponse> {
|
||||||
if request.capture_method.unwrap_or_default() == api_enums::CaptureMethod::Automatic
|
if request.capture_method.unwrap_or_default() == api_enums::CaptureMethod::Automatic {
|
||||||
&& request.confirm.unwrap_or(false)
|
let total_capturable_amount = request.get_total_capturable_amount();
|
||||||
{
|
if let Some((amount_to_capture, total_capturable_amount)) =
|
||||||
if let Some((amount_to_capture, amount)) = request.amount_to_capture.zip(request.amount) {
|
request.amount_to_capture.zip(total_capturable_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 {
|
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 {
|
} else {
|
||||||
|
|||||||
@ -751,7 +751,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();
|
let amount = request.get_capture_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
|
||||||
|
|||||||
@ -58,13 +58,12 @@ pub async fn refund_create_core(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Amount is not passed in request refer from payment intent.
|
// Amount is not passed in request refer from payment intent.
|
||||||
amount = req.amount.unwrap_or(
|
amount = req
|
||||||
payment_intent
|
.amount
|
||||||
.amount_captured
|
.or(payment_intent.amount_captured)
|
||||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
.into_report()
|
.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
|
//[#299]: Can we change the flow based on some workflow idea
|
||||||
utils::when(amount <= 0, || {
|
utils::when(amount <= 0, || {
|
||||||
|
|||||||
@ -30,9 +30,10 @@ use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLO
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
errors::{self, RouterResult},
|
errors::{self, RouterResult},
|
||||||
payments::RecurringMandatePaymentData,
|
payments::{PaymentData, RecurringMandatePaymentData},
|
||||||
},
|
},
|
||||||
services,
|
services,
|
||||||
|
types::storage::payment_attempt::PaymentAttemptExt,
|
||||||
utils::OptionExt,
|
utils::OptionExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -544,7 +545,10 @@ pub struct AccessTokenRequestData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Capturable {
|
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
|
None
|
||||||
}
|
}
|
||||||
fn get_surcharge_amount(&self) -> Option<i64> {
|
fn get_surcharge_amount(&self) -> Option<i64> {
|
||||||
@ -553,13 +557,13 @@ pub trait Capturable {
|
|||||||
fn get_tax_on_surcharge_amount(&self) -> Option<i64> {
|
fn get_tax_on_surcharge_amount(&self) -> Option<i64> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn is_psync(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Capturable for PaymentsAuthorizeData {
|
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
|
let final_amount = self
|
||||||
.surcharge_details
|
.surcharge_details
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -579,24 +583,44 @@ impl Capturable for PaymentsAuthorizeData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Capturable for PaymentsCaptureData {
|
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)
|
Some(self.amount_to_capture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Capturable for CompleteAuthorizeData {
|
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)
|
Some(self.amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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>
|
||||||
|
where
|
||||||
|
F: Clone,
|
||||||
|
{
|
||||||
|
// return previously captured amount
|
||||||
|
payment_data.payment_intent.amount_captured
|
||||||
|
}
|
||||||
|
}
|
||||||
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 PaymentsSyncData {
|
impl Capturable for PaymentsSyncData {
|
||||||
fn is_psync(&self) -> bool {
|
fn get_capture_amount<F>(&self, payment_data: &PaymentData<F>) -> Option<i64>
|
||||||
true
|
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,
|
"confirm": false,
|
||||||
"capture_method": "automatic",
|
"capture_method": "automatic",
|
||||||
"capture_on": "2022-09-10T10:11:12Z",
|
"capture_on": "2022-09-10T10:11:12Z",
|
||||||
"amount_to_capture": 6540,
|
"amount_to_capture": 8000,
|
||||||
"customer_id": "StripeCustomer",
|
"customer_id": "StripeCustomer",
|
||||||
"email": "guest@example.com",
|
"email": "guest@example.com",
|
||||||
"name": "John Doe",
|
"name": "John Doe",
|
||||||
|
|||||||
Reference in New Issue
Block a user