Skip to content

Commit

Permalink
fix: amount_captured goes to 0 for 3ds payments
Browse files Browse the repository at this point in the history
  • Loading branch information
hrithikesh026 committed Nov 22, 2023
1 parent 50e5d0b commit 1b971c2
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 35 deletions.
12 changes: 12 additions & 0 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)]
Expand Down
10 changes: 2 additions & 8 deletions crates/router/src/connector/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
16 changes: 8 additions & 8 deletions crates/router/src/core/payments/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,19 +600,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 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, || {
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)
{
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,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
Expand Down
13 changes: 6 additions & 7 deletions crates/router/src/core/refunds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
.ok_or(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable("amount captured is none in a successful payment")?,
);
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")?;

//[#299]: Can we change the flow based on some workflow idea
utils::when(amount <= 0, || {
Expand Down
46 changes: 35 additions & 11 deletions crates/router/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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> {
Expand All @@ -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()
Expand All @@ -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()))
}
}

Expand Down

0 comments on commit 1b971c2

Please sign in to comment.