Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): enable surcharge support for all connectors #3109

Merged
merged 10 commits into from
Dec 14, 2023
4 changes: 1 addition & 3 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,7 @@ 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)
})
.map(|surcharge_details| surcharge_details.get_total_surcharge_amount())
.unwrap_or(0);
self.amount
.map(|amount| i64::from(amount) + surcharge_amount)
Expand Down
10 changes: 10 additions & 0 deletions crates/data_models/src/payments/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ pub struct PaymentAttempt {
pub unified_message: Option<String>,
}

impl PaymentAttempt {
pub fn get_total_amount(&self) -> i64 {
self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0)
}
pub fn get_total_surcharge_amount(&self) -> Option<i64> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this method return i64 instead of option, However we are always using unwrap_or(0) on the results of this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purpose of this method is to return total surcharge amount only if surcharge is present in payment_attempt. Otherwise None.

self.surcharge_amount
.map(|surcharge_amount| surcharge_amount + self.tax_amount.unwrap_or(0))
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PaymentListFilters {
pub connector: Vec<String>,
Expand Down
9 changes: 1 addition & 8 deletions crates/router/src/connector/paypal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,6 @@ impl ConnectorValidation for Paypal {
),
}
}

fn validate_if_surcharge_implemented(&self) -> CustomResult<(), errors::ConnectorError> {
Ok(())
}
}

impl
Expand Down Expand Up @@ -423,10 +419,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
let connector_router_data = paypal::PaypalRouterData::try_from((
&self.get_currency_unit(),
req.request.currency,
req.request
.surcharge_details
.as_ref()
.map_or(req.request.amount, |surcharge| surcharge.final_amount),
req.request.amount,
hrithikesh026 marked this conversation as resolved.
Show resolved Hide resolved
req,
))?;
let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?;
Expand Down
20 changes: 3 additions & 17 deletions crates/router/src/connector/trustpay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,7 @@ impl ConnectorCommon for Trustpay {
}
}

impl ConnectorValidation for Trustpay {
fn validate_if_surcharge_implemented(&self) -> CustomResult<(), errors::ConnectorError> {
Ok(())
}
}
impl ConnectorValidation for Trustpay {}

impl api::Payment for Trustpay {}

Expand Down Expand Up @@ -432,12 +428,7 @@ impl
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let currency = req.request.get_currency()?;
let amount = req
.request
.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.final_amount)
.unwrap_or(req.request.get_amount()?);
let amount = req.request.get_amount()?;
let connector_router_data = trustpay::TrustpayRouterData::try_from((
&self.get_currency_unit(),
currency,
Expand Down Expand Up @@ -544,12 +535,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = req
.request
.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.final_amount)
.unwrap_or(req.request.amount);
let amount = req.request.amount;
let connector_router_data = trustpay::TrustpayRouterData::try_from((
&self.get_currency_unit(),
req.request.currency,
Expand Down
29 changes: 27 additions & 2 deletions crates/router/src/connector/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use crate::{
},
pii::PeekInterface,
types::{
self, api, storage::payment_attempt::PaymentAttemptExt, transformers::ForeignTryFrom,
ApplePayPredecryptData, BrowserInformation, PaymentsCancelData, ResponseId,
self, api, transformers::ForeignTryFrom, ApplePayPredecryptData, BrowserInformation,
PaymentsCancelData, ResponseId,
},
utils::{OptionExt, ValueExt},
};
Expand Down Expand Up @@ -367,6 +367,10 @@ pub trait PaymentsAuthorizeRequestData {
fn get_connector_mandate_id(&self) -> Result<String, Error>;
fn get_complete_authorize_url(&self) -> Result<String, Error>;
fn get_ip_address_as_optional(&self) -> Option<Secret<String, IpAddress>>;
fn get_original_amount(&self) -> i64;
fn get_surcharge_amount(&self) -> Option<i64>;
fn get_tax_on_surcharge_amount(&self) -> Option<i64>;
fn get_total_surcharge_amount(&self) -> Option<i64>;
}

pub trait PaymentMethodTokenizationRequestData {
Expand Down Expand Up @@ -473,6 +477,27 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData {
.map(|ip| Secret::new(ip.to_string()))
})
}
fn get_original_amount(&self) -> i64 {
self.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.original_amount)
.unwrap_or(self.amount)
}
fn get_surcharge_amount(&self) -> Option<i64> {
self.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.surcharge_amount)
}
fn get_tax_on_surcharge_amount(&self) -> Option<i64> {
self.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.tax_on_surcharge_amount)
}
fn get_total_surcharge_amount(&self) -> Option<i64> {
self.surcharge_details
.as_ref()
.map(|surcharge_details| surcharge_details.get_total_surcharge_amount())
}
}

pub trait ConnectorCustomerData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ fn get_surcharge_details_from_surcharge_output(
.transpose()?
.unwrap_or(0);
Ok(types::SurchargeDetails {
original_amount: payment_attempt.amount,
surcharge: match surcharge_details.surcharge {
surcharge_decision_configs::SurchargeOutput::Fixed { amount } => {
common_utils_types::Surcharge::Fixed(amount)
Expand Down
12 changes: 8 additions & 4 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,6 @@ where
);

if should_continue_transaction {
operation
.to_domain()?
.populate_payment_data(state, &mut payment_data, &merchant_account)
.await?;
payment_data = match connector_details {
api::ConnectorCallType::PreDetermined(connector) => {
let schedule_time = if should_add_task_to_process_tracker {
Expand Down Expand Up @@ -484,6 +480,13 @@ where
.surcharge_applicable
.unwrap_or(false)
{
if let Some(surcharge_details) = payment_data.payment_attempt.get_surcharge_details() {
// if retry payment, surcharge would have been populated from the previous attempt. Use the same surcharge
let surcharge_details =
types::SurchargeDetails::from((&surcharge_details, &payment_data.payment_attempt));
payment_data.surcharge_details = Some(surcharge_details);
return Ok(());
}
let raw_card_key = payment_data
.payment_method_data
.as_ref()
Expand Down Expand Up @@ -562,6 +565,7 @@ where
payment_data.payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount;
Ok(Some(api::SessionSurchargeDetails::PreDetermined(
types::SurchargeDetails {
original_amount: payment_data.payment_attempt.amount,
surcharge: Surcharge::Fixed(surcharge_amount),
tax_on_surcharge: None,
surcharge_amount,
Expand Down
6 changes: 0 additions & 6 deletions crates/router/src/core/payments/flows/authorize_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
.connector
.validate_capture_method(self.request.capture_method)
.to_payment_failed_response()?;
if self.request.surcharge_details.is_some() {
connector
.connector
.validate_if_surcharge_implemented()
.to_payment_failed_response()?;
}

if self.should_proceed_with_authorize() {
self.decide_authentication_type();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
payment_method_type.or(payment_attempt.payment_method_type);
payment_attempt.payment_experience = request.payment_experience;
currency = payment_attempt.currency.get_required_value("currency")?;
amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

helpers::validate_customer_id_mandatory_cases(
request.setup_future_usage.is_some(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.await?;

let currency = payment_attempt.currency.get_required_value("currency")?;
let amount = payment_attempt.amount.into();
let amount = payment_attempt.get_total_amount().into();

payment_attempt.cancellation_reason = request.cancellation_reason.clone();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>

currency = payment_attempt.currency.get_required_value("currency")?;

amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

let shipping_address = helpers::create_or_find_address_for_payment_by_request(
db,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.payment_experience
.or(payment_attempt.payment_experience);
currency = payment_attempt.currency.get_required_value("currency")?;
amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

helpers::validate_customer_id_mandatory_cases(
request.setup_future_usage.is_some(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
payment_attempt.capture_method = request.capture_method.or(payment_attempt.capture_method);

currency = payment_attempt.currency.get_required_value("currency")?;
amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

helpers::validate_customer_id_mandatory_cases(
request.setup_future_usage.is_some(),
Expand Down Expand Up @@ -732,7 +732,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
m_db.update_payment_attempt_with_attempt_id(
m_payment_data_payment_attempt,
storage::PaymentAttemptUpdate::ConfirmUpdate {
amount: payment_data.amount.into(),
amount: payment_data.payment_attempt.amount,
currency: payment_data.currency,
status: attempt_status,
payment_method,
Expand Down Expand Up @@ -780,7 +780,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
m_db.update_payment_intent(
m_payment_data_payment_intent,
storage::PaymentIntentUpdate::Update {
amount: payment_data.amount.into(),
amount: payment_data.payment_intent.amount,
currency: payment_data.currency,
setup_future_usage,
status: intent_status,
Expand Down
10 changes: 9 additions & 1 deletion crates/router/src/core/payments/operations/payment_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.map(|(payment_method_data, additional_payment_data)| {
payment_method_data.apply_additional_payment_data(additional_payment_data)
});

let amount = payment_attempt.get_total_amount().into();
let payment_data = PaymentData {
flow: PhantomData,
payment_intent,
Expand Down Expand Up @@ -645,6 +645,12 @@ impl PaymentCreate {
} else {
utils::get_payment_attempt_id(payment_id, 1)
};
let surcharge_amount = request
.surcharge_details
.map(|surcharge_details| surcharge_details.surcharge_amount);
let tax_amount = request
.surcharge_details
.and_then(|surcharge_details| surcharge_details.tax_amount);

Ok((
storage::PaymentAttemptNew {
Expand All @@ -670,6 +676,8 @@ impl PaymentCreate {
payment_token: request.payment_token.clone(),
mandate_id: request.mandate_id.clone(),
business_sub_label: request.business_sub_label.clone(),
surcharge_amount,
tax_amount,
mandate_details: request
.mandate_data
.as_ref()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.await?;

let currency = payment_attempt.currency.get_required_value("currency")?;
let amount = payment_attempt.amount.into();
let amount = payment_attempt.get_total_amount().into();

let frm_response = db
.find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.merchant_id.clone())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ use crate::{
services::RedirectForm,
types::{
self, api,
storage::{
self, enums,
payment_attempt::{AttemptStatusExt, PaymentAttemptExt},
},
storage::{self, enums, payment_attempt::AttemptStatusExt},
transformers::{ForeignFrom, ForeignTryFrom},
CaptureSyncResponse,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>

payment_attempt.payment_method = Some(storage_enums::PaymentMethod::Wallet);

let amount = payment_intent.amount.into();
let amount = payment_attempt.get_total_amount().into();

let shipping_address = helpers::create_or_find_address_for_payment_by_request(
db,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

currency = payment_attempt.currency.get_required_value("currency")?;
amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

let shipping_address = helpers::create_or_find_address_for_payment_by_request(
db,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ async fn get_tracker_for_sync<
let payment_id_str = payment_attempt.payment_id.clone();

currency = payment_attempt.currency.get_required_value("currency")?;
amount = payment_attempt.amount.into();
amount = payment_attempt.get_total_amount().into();

let shipping_address = helpers::get_address_by_id(
db,
Expand Down
22 changes: 18 additions & 4 deletions crates/router/src/core/payments/operations/payment_update.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use api_models::{enums::FrmSuggestion, payments::RequestSurchargeDetails};
use async_trait::async_trait;
use common_utils::ext_traits::{AsyncExt, Encode, ValueExt};
use error_stack::{report, IntoReport, ResultExt};
Expand Down Expand Up @@ -281,11 +281,25 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
})
.await
.transpose()?;
let next_operation: BoxedOperation<'a, F, api::PaymentsRequest, Ctx> =
let (next_operation, amount): (BoxedOperation<'a, F, api::PaymentsRequest, Ctx>, _) =
if request.confirm.unwrap_or(false) {
Box::new(operations::PaymentConfirm)
let amount = {
let amount = request
.amount
.map(Into::into)
.unwrap_or(payment_attempt.amount);
payment_attempt.amount = amount;
payment_intent.amount = amount;
let surcharge_amount = request
.surcharge_details
.as_ref()
.map(RequestSurchargeDetails::get_total_surcharge_amount)
.or(payment_attempt.get_total_surcharge_amount());
(amount + surcharge_amount.unwrap_or(0)).into()
};
(Box::new(operations::PaymentConfirm), amount)
} else {
Box::new(self)
(Box::new(self), amount)
};

payment_intent.status = match request.payment_method_data.as_ref() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

let currency = payment_attempt.currency.get_required_value("currency")?;
let amount = payment_attempt.amount;
let amount = payment_attempt.get_total_amount();

let profile_id = payment_intent
.profile_id
Expand Down
Loading
Loading