fix: status goes from pending to partially captured in psync (#2915)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Hrithikesh
2023-11-21 20:25:38 +05:30
committed by GitHub
parent 8f610f4cf1
commit 3f3b797dc6
15 changed files with 112 additions and 37 deletions

View File

@ -321,12 +321,15 @@ pub enum PaymentAttemptUpdate {
error_message: Option<Option<String>>, error_message: Option<Option<String>>,
error_reason: Option<Option<String>>, error_reason: Option<Option<String>>,
amount_capturable: Option<i64>, amount_capturable: Option<i64>,
surcharge_amount: Option<i64>,
tax_amount: Option<i64>,
updated_by: String, updated_by: String,
unified_code: Option<Option<String>>, unified_code: Option<Option<String>>,
unified_message: Option<Option<String>>, unified_message: Option<Option<String>>,
}, },
MultipleCaptureCountUpdate { CaptureUpdate {
multiple_capture_count: i16, amount_to_capture: Option<i64>,
multiple_capture_count: Option<i16>,
updated_by: String, updated_by: String,
}, },
AmountToCaptureUpdate { AmountToCaptureUpdate {

View File

@ -238,12 +238,15 @@ pub enum PaymentAttemptUpdate {
error_message: Option<Option<String>>, error_message: Option<Option<String>>,
error_reason: Option<Option<String>>, error_reason: Option<Option<String>>,
amount_capturable: Option<i64>, amount_capturable: Option<i64>,
surcharge_amount: Option<i64>,
tax_amount: Option<i64>,
updated_by: String, updated_by: String,
unified_code: Option<Option<String>>, unified_code: Option<Option<String>>,
unified_message: Option<Option<String>>, unified_message: Option<Option<String>>,
}, },
MultipleCaptureCountUpdate { CaptureUpdate {
multiple_capture_count: i16, amount_to_capture: Option<i64>,
multiple_capture_count: Option<i16>,
updated_by: String, updated_by: String,
}, },
AmountToCaptureUpdate { AmountToCaptureUpdate {
@ -535,6 +538,8 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
error_message, error_message,
error_reason, error_reason,
amount_capturable, amount_capturable,
surcharge_amount,
tax_amount,
updated_by, updated_by,
unified_code, unified_code,
unified_message, unified_message,
@ -547,6 +552,8 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
error_reason, error_reason,
amount_capturable, amount_capturable,
updated_by, updated_by,
surcharge_amount,
tax_amount,
unified_code, unified_code,
unified_message, unified_message,
..Default::default() ..Default::default()
@ -618,12 +625,14 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
updated_by, updated_by,
..Default::default() ..Default::default()
}, },
PaymentAttemptUpdate::MultipleCaptureCountUpdate { PaymentAttemptUpdate::CaptureUpdate {
multiple_capture_count, multiple_capture_count,
updated_by, updated_by,
amount_to_capture,
} => Self { } => Self {
multiple_capture_count: Some(multiple_capture_count), multiple_capture_count,
updated_by, updated_by,
amount_to_capture,
..Default::default() ..Default::default()
}, },
PaymentAttemptUpdate::AmountToCaptureUpdate { PaymentAttemptUpdate::AmountToCaptureUpdate {

View File

@ -24,7 +24,10 @@ use crate::{
payments::PaymentData, payments::PaymentData,
}, },
pii::PeekInterface, pii::PeekInterface,
types::{self, api, transformers::ForeignTryFrom, PaymentsCancelData, ResponseId}, types::{
self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom,
PaymentsCancelData, ResponseId,
},
utils::{OptionExt, ValueExt}, utils::{OptionExt, ValueExt},
}; };
@ -108,11 +111,20 @@ where
} }
} }
enums::AttemptStatus::Charged => { enums::AttemptStatus::Charged => {
let captured_amount = types::Capturable::get_capture_amount(&self.request); let captured_amount = if self.request.is_psync() {
if Some(payment_data.payment_intent.amount) == captured_amount { payment_data
enums::AttemptStatus::Charged .payment_attempt
.amount_to_capture
.or(Some(payment_data.payment_attempt.get_total_amount()))
} else { } else {
types::Capturable::get_capture_amount(&self.request)
};
if Some(payment_data.payment_attempt.get_total_amount()) == captured_amount {
enums::AttemptStatus::Charged
} else if captured_amount.is_some() {
enums::AttemptStatus::PartialCharged enums::AttemptStatus::PartialCharged
} else {
self.status
} }
} }
_ => self.status, _ => self.status,

View File

@ -600,6 +600,29 @@ 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
#[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, || {
Err(report!(errors::ApiErrorResponse::PreconditionFailed {
message: "amount_to_capture must be equal to amount when confirm = true and capture_method = automatic".into()
}))
})
} else {
Ok(())
}
} else {
Ok(())
}
}
#[instrument(skip_all)] #[instrument(skip_all)]
pub fn validate_card_data( pub fn validate_card_data(
payment_method_data: Option<api::PaymentMethodData>, payment_method_data: Option<api::PaymentMethodData>,

View File

@ -251,20 +251,29 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
where where
F: 'b + Send, F: 'b + Send,
{ {
payment_data.payment_attempt = match &payment_data.multiple_capture_data { payment_data.payment_attempt = if payment_data.multiple_capture_data.is_some()
Some(multiple_capture_data) => db || payment_data.payment_attempt.amount_to_capture.is_some()
.store {
let multiple_capture_count = payment_data
.multiple_capture_data
.as_ref()
.map(|multiple_capture_data| multiple_capture_data.get_captures_count())
.transpose()?;
let amount_to_capture = payment_data.payment_attempt.amount_to_capture;
db.store
.update_payment_attempt_with_attempt_id( .update_payment_attempt_with_attempt_id(
payment_data.payment_attempt, payment_data.payment_attempt,
storage::PaymentAttemptUpdate::MultipleCaptureCountUpdate { storage::PaymentAttemptUpdate::CaptureUpdate {
multiple_capture_count: multiple_capture_data.get_captures_count()?, amount_to_capture,
multiple_capture_count,
updated_by: storage_scheme.to_string(), updated_by: storage_scheme.to_string(),
}, },
storage_scheme, storage_scheme,
) )
.await .await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?
None => payment_data.payment_attempt, } else {
payment_data.payment_attempt
}; };
Ok((Box::new(self), payment_data)) Ok((Box::new(self), payment_data))
} }

View File

@ -1,6 +1,6 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use api_models::{enums::FrmSuggestion, payment_methods}; use api_models::enums::FrmSuggestion;
use async_trait::async_trait; use async_trait::async_trait;
use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; use common_utils::ext_traits::{AsyncExt, Encode, ValueExt};
use data_models::{mandates::MandateData, payments::payment_attempt::PaymentAttempt}; use data_models::{mandates::MandateData, payments::payment_attempt::PaymentAttempt};
@ -279,15 +279,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
let setup_mandate = setup_mandate.map(MandateData::from); let setup_mandate = setup_mandate.map(MandateData::from);
let surcharge_details = request.surcharge_details.map(|surcharge_details| { let surcharge_details = request.surcharge_details.map(|surcharge_details| {
payment_methods::SurchargeDetailsResponse { surcharge_details.get_surcharge_details_object(payment_attempt.amount)
surcharge: payment_methods::Surcharge::Fixed(surcharge_details.surcharge_amount),
tax_on_surcharge: None,
surcharge_amount: surcharge_details.surcharge_amount,
tax_on_surcharge_amount: surcharge_details.tax_amount.unwrap_or(0),
final_amount: payment_attempt.amount
+ surcharge_details.surcharge_amount
+ surcharge_details.tax_amount.unwrap_or(0),
}
}); });
let payment_data = PaymentData { let payment_data = PaymentData {
@ -546,6 +538,8 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
expected_format: "amount_to_capture lesser than amount".to_string(), expected_format: "amount_to_capture lesser than amount".to_string(),
})?; })?;
helpers::validate_amount_to_capture_in_create_call_request(request)?;
helpers::validate_card_data(request.payment_method_data.clone())?; helpers::validate_card_data(request.payment_method_data.clone())?;
helpers::validate_payment_method_fields_present(request)?; helpers::validate_payment_method_fields_present(request)?;

View File

@ -372,6 +372,8 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
} else { } else {
None None
}, },
surcharge_amount: router_data.request.get_surcharge_amount(),
tax_amount: router_data.request.get_tax_on_surcharge_amount(),
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),

View File

@ -410,6 +410,8 @@ where
status: storage_enums::AttemptStatus::Failure, status: storage_enums::AttemptStatus::Failure,
error_reason: Some(error_response.reason.clone()), error_reason: Some(error_response.reason.clone()),
amount_capturable: Some(0), amount_capturable: Some(0),
surcharge_amount: None,
tax_amount: 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),

View File

@ -545,7 +545,7 @@ pub struct AccessTokenRequestData {
pub trait Capturable { pub trait Capturable {
fn get_capture_amount(&self) -> Option<i64> { fn get_capture_amount(&self) -> Option<i64> {
Some(0) None
} }
fn get_surcharge_amount(&self) -> Option<i64> { fn get_surcharge_amount(&self) -> Option<i64> {
None None
@ -553,6 +553,9 @@ 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 {
@ -591,7 +594,11 @@ impl Capturable for PaymentsCancelData {}
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 {
true
}
}
pub struct AddAccessTokenResult { pub struct AddAccessTokenResult {
pub access_token_result: Result<Option<AccessToken>, ErrorResponse>, pub access_token_result: Result<Option<AccessToken>, ErrorResponse>,

View File

@ -135,6 +135,8 @@ impl ProcessTrackerWorkflow<AppState> for PaymentsSyncWorkflow {
consts::REQUEST_TIMEOUT_ERROR_MESSAGE_FROM_PSYNC.to_string(), consts::REQUEST_TIMEOUT_ERROR_MESSAGE_FROM_PSYNC.to_string(),
)), )),
amount_capturable: Some(0), amount_capturable: Some(0),
surcharge_amount: None,
tax_amount: None,
updated_by: merchant_account.storage_scheme.to_string(), updated_by: merchant_account.storage_scheme.to_string(),
unified_code: None, unified_code: None,
unified_message: None, unified_message: None,

View File

@ -1320,6 +1320,8 @@ impl DataModelExt for PaymentAttemptUpdate {
error_message, error_message,
error_reason, error_reason,
amount_capturable, amount_capturable,
tax_amount,
surcharge_amount,
updated_by, updated_by,
unified_code, unified_code,
unified_message, unified_message,
@ -1330,16 +1332,20 @@ impl DataModelExt for PaymentAttemptUpdate {
error_message, error_message,
error_reason, error_reason,
amount_capturable, amount_capturable,
surcharge_amount,
tax_amount,
updated_by, updated_by,
unified_code, unified_code,
unified_message, unified_message,
}, },
Self::MultipleCaptureCountUpdate { Self::CaptureUpdate {
multiple_capture_count, multiple_capture_count,
updated_by, updated_by,
} => DieselPaymentAttemptUpdate::MultipleCaptureCountUpdate { amount_to_capture,
} => DieselPaymentAttemptUpdate::CaptureUpdate {
multiple_capture_count, multiple_capture_count,
updated_by, updated_by,
amount_to_capture,
}, },
Self::PreprocessingUpdate { Self::PreprocessingUpdate {
status, status,
@ -1577,6 +1583,8 @@ impl DataModelExt for PaymentAttemptUpdate {
error_message, error_message,
error_reason, error_reason,
amount_capturable, amount_capturable,
surcharge_amount,
tax_amount,
updated_by, updated_by,
unified_code, unified_code,
unified_message, unified_message,
@ -1588,13 +1596,17 @@ impl DataModelExt for PaymentAttemptUpdate {
error_reason, error_reason,
amount_capturable, amount_capturable,
updated_by, updated_by,
surcharge_amount,
tax_amount,
unified_code, unified_code,
unified_message, unified_message,
}, },
DieselPaymentAttemptUpdate::MultipleCaptureCountUpdate { DieselPaymentAttemptUpdate::CaptureUpdate {
amount_to_capture,
multiple_capture_count, multiple_capture_count,
updated_by, updated_by,
} => Self::MultipleCaptureCountUpdate { } => Self::CaptureUpdate {
amount_to_capture,
multiple_capture_count, multiple_capture_count,
updated_by, updated_by,
}, },

View File

@ -25,7 +25,7 @@
"business_label": "default", "business_label": "default",
"capture_method": "automatic", "capture_method": "automatic",
"capture_on": "2022-09-10T10:11:12Z", "capture_on": "2022-09-10T10:11:12Z",
"amount_to_capture": 1, "amount_to_capture": 6540,
"customer_id": "bernard123", "customer_id": "bernard123",
"email": "guest@example.com", "email": "guest@example.com",
"name": "John Doe", "name": "John Doe",

View File

@ -23,7 +23,7 @@
"confirm": true, "confirm": true,
"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": 6570,
"customer_id": "StripeCustomer", "customer_id": "StripeCustomer",
"email": "guest@example.com", "email": "guest@example.com",
"name": "John Doe", "name": "John Doe",

View File

@ -25,7 +25,7 @@
"business_label": "default", "business_label": "default",
"capture_method": "automatic", "capture_method": "automatic",
"capture_on": "2022-09-10T10:11:12Z", "capture_on": "2022-09-10T10:11:12Z",
"amount_to_capture": 1, "amount_to_capture": 6540,
"customer_id": "bernard123", "customer_id": "bernard123",
"email": "guest@example.com", "email": "guest@example.com",
"name": "John Doe", "name": "John Doe",

View File

@ -25,7 +25,7 @@
"business_label": "default", "business_label": "default",
"capture_method": "automatic", "capture_method": "automatic",
"capture_on": "2022-09-10T10:11:12Z", "capture_on": "2022-09-10T10:11:12Z",
"amount_to_capture": 1, "amount_to_capture": 6540,
"customer_id": "bernard123", "customer_id": "bernard123",
"email": "guest@example.com", "email": "guest@example.com",
"name": "John Doe", "name": "John Doe",