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

fix(core): Include surcharge amount in frm request #3398

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2a7c6a8
feat(core): enable surcharge support for all connectors
hrithikesh026 Dec 11, 2023
22ca09d
fix: calculate display_total_surcharge_amount from int
hrithikesh026 Dec 12, 2023
e4f0c42
address comments
hrithikesh026 Dec 12, 2023
54d4287
remove redundant call to populate_payment_data() and handle surcharge…
hrithikesh026 Dec 12, 2023
4bcd7f6
fix: add net_amount field in PaymentResponse
hrithikesh026 Dec 12, 2023
2231313
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Dec 12, 2023
6885131
Merge branch 'main' into surcharge-for-all-connectors
hrithikesh026 Dec 12, 2023
82cc438
do amount operation for PaymentsSessionData
hrithikesh026 Dec 12, 2023
f47cd02
Merge branch 'main' into surcharge-for-all-connectors
hrithikesh026 Dec 12, 2023
04a31f4
Merge branch 'main' into surcharge-for-all-connectors
hrithikesh026 Dec 13, 2023
a907bb5
chore: update Cargo.lock
hyperswitch-bot[bot] Dec 13, 2023
0e3cb3c
Merge branch 'main' into surcharge-for-all-connectors
hrithikesh026 Dec 13, 2023
a9ce9fb
Merge branch 'surcharge-for-all-connectors' into introduce-net-amount…
hrithikesh026 Dec 13, 2023
c567755
Merge branch 'main' into introduce-net-amount-in-payment_response
hrithikesh026 Dec 14, 2023
5964b05
Merge branch 'main' into introduce-net-amount-in-payment_response
hrithikesh026 Dec 15, 2023
3c386e1
create net_amount field in payment_attempt
hrithikesh026 Dec 16, 2023
bb45695
address comments
hrithikesh026 Dec 18, 2023
acb3f23
add comments in up.sql file
hrithikesh026 Dec 18, 2023
80f6ad1
use net_amount field directly
hrithikesh026 Dec 18, 2023
f107d6a
do not populate surcharge and tax amount for manually retried payment
hrithikesh026 Dec 18, 2023
40a4f3b
address comments
hrithikesh026 Dec 18, 2023
395a93b
make net_amount field optional in payment_attempt
hrithikesh026 Dec 18, 2023
7fa1ac5
refactor: refactor amount field in PaymentAttempt data_model
hrithikesh026 Dec 19, 2023
15f8b83
Merge branch 'main' into refactor-amount-in-attempt
hrithikesh026 Jan 9, 2024
06f0834
Merge branch 'main' into refactor-amount-in-attempt
hrithikesh026 Jan 12, 2024
f134a69
resolve merge related issues
hrithikesh026 Jan 12, 2024
b5a9ebd
fix(core): include surcharge_amount in frm request
hrithikesh026 Jan 19, 2024
c67c9fe
chore: update Cargo.lock
hyperswitch-bot[bot] Jan 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion crates/data_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ pub struct PaymentIntent {
pub payment_id: String,
pub merchant_id: String,
pub status: storage_enums::IntentStatus,
pub amount: i64,
/// This amount may not be equal to authorized_amount due to surcharge<br>
/// Authorized amount can be fetched from PaymentAttempt
pub original_amount: i64,
pub currency: Option<storage_enums::Currency>,
pub amount_captured: Option<i64>,
pub customer_id: Option<String>,
Expand Down
67 changes: 60 additions & 7 deletions crates/data_models/src/payments/payment_attempt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use api_models::enums::Connector;
use common_enums as storage_enums;
use diesel_models::PaymentAttempt as DieselPaymentAttempt;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;

Expand Down Expand Up @@ -106,15 +107,12 @@ pub struct PaymentAttempt {
pub merchant_id: String,
pub attempt_id: String,
pub status: storage_enums::AttemptStatus,
pub amount: i64,
pub net_amount: i64,
pub amount: AttemptAmount,
pub currency: Option<storage_enums::Currency>,
pub save_to_locker: Option<bool>,
pub connector: Option<String>,
pub error_message: Option<String>,
pub offer_amount: Option<i64>,
pub surcharge_amount: Option<i64>,
pub tax_amount: Option<i64>,
pub payment_method_id: Option<String>,
pub payment_method: Option<storage_enums::PaymentMethod>,
pub connector_transaction_id: Option<String>,
Expand Down Expand Up @@ -157,9 +155,64 @@ 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)
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AttemptAmount {
amount: i64,
surcharge_amount: Option<i64>,
tax_amount: Option<i64>,
net_amount: i64,
}

pub trait GetAttemptAmount {
fn get_attempt_amount(&self) -> AttemptAmount;
}

impl GetAttemptAmount for DieselPaymentAttempt {
fn get_attempt_amount(&self) -> AttemptAmount {
AttemptAmount {
amount: self.amount,
surcharge_amount: self.surcharge_amount,
tax_amount: self.tax_amount,
net_amount: self.get_or_calculate_net_amount(),
}
}
}

impl GetAttemptAmount for PaymentAttemptNew {
fn get_attempt_amount(&self) -> AttemptAmount {
AttemptAmount {
amount: self.amount,
surcharge_amount: self.surcharge_amount,
tax_amount: self.tax_amount,
net_amount: self.calculate_net_amount(),
}
}
}

impl AttemptAmount {
pub fn set_original_amount(&mut self, amount: i64) {
self.amount = amount;
self.net_amount = self.amount + self.get_total_surcharge_amount().unwrap_or(0);
}
pub fn set_surcharge_amount(&mut self, surcharge_amount: i64) {
self.surcharge_amount = Some(surcharge_amount);
self.net_amount = self.amount + self.get_total_surcharge_amount().unwrap_or(0);
}
pub fn set_tax_amount(&mut self, tax_amount: i64) {
self.tax_amount = Some(tax_amount);
self.net_amount = self.amount + self.get_total_surcharge_amount().unwrap_or(0);
}
pub fn get_authorize_amount(&self) -> i64 {
self.net_amount
}
pub fn get_original_amount(&self) -> i64 {
self.amount
}
pub fn get_surcharge_amount(&self) -> Option<i64> {
self.surcharge_amount
}
pub fn get_tax_amount_on_surcharge(&self) -> Option<i64> {
self.tax_amount
}
pub fn get_total_surcharge_amount(&self) -> Option<i64> {
self.surcharge_amount
Expand Down
5 changes: 0 additions & 5 deletions crates/diesel_models/src/payment_attempt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,6 @@ impl PaymentAttemptNew {
self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0)
}

pub fn get_or_calculate_net_amount(&self) -> i64 {
self.net_amount
.unwrap_or_else(|| self.calculate_net_amount())
}

pub fn populate_derived_fields(self) -> Self {
let mut payment_attempt_new = self;
payment_attempt_new.net_amount = Some(payment_attempt_new.calculate_net_amount());
Expand Down
6 changes: 3 additions & 3 deletions crates/router/src/connector/cybersource/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,9 +1986,9 @@ impl<F>
resource_id: types::ResponseId::NoResponseId,
redirection_data,
mandate_reference: None,
connector_metadata: Some(
serde_json::json!({"three_ds_data":three_ds_data}),
),
connector_metadata: Some(serde_json::json!({
"three_ds_data": three_ds_data
})),
network_txn_id: None,
connector_response_reference_id,
incremental_authorization_allowed: None,
Expand Down
3 changes: 2 additions & 1 deletion crates/router/src/connector/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ where
enums::AttemptStatus::Charged => {
let captured_amount =
types::Capturable::get_captured_amount(&self.request, payment_data);
let total_capturable_amount = payment_data.payment_attempt.get_total_amount();
let total_capturable_amount =
payment_data.payment_attempt.amount.get_authorize_amount();
if Some(total_capturable_amount) == captured_amount {
enums::AttemptStatus::Charged
} else if captured_amount.is_some() {
Expand Down
10 changes: 10 additions & 0 deletions crates/router/src/core/fraud_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ where
.payment_attempt
.connector_transaction_id
.clone();
if let Some(surcharge_details) = &payment_data.surcharge_details {
frm_data
.payment_attempt
.amount
.set_surcharge_amount(surcharge_details.surcharge_amount);
frm_data
.payment_attempt
.amount
.set_tax_amount(surcharge_details.tax_on_surcharge_amount);
}

let mut router_data = frm_data
.construct_router_data(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl ConstructFlowSpecificData<frm_api::Checkout, FraudCheckCheckoutData, FraudC
connector_meta_data: None,
amount_captured: None,
request: FraudCheckCheckoutData {
amount: self.payment_attempt.amount,
amount: self.payment_attempt.amount.get_authorize_amount(),
order_details: self.order_details.clone(),
currency: self.payment_attempt.currency,
browser_info,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub async fn construct_fulfillment_router_data<'a>(
connector_meta_data: merchant_connector_account.get_metadata(),
amount_captured: payment_intent.amount_captured,
request: FraudCheckFulfillmentData {
amount: payment_attempt.amount,
amount: payment_attempt.amount.get_authorize_amount(),
order_details: payment_intent.order_details.clone(),
fulfillment_req: fulfillment_request,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl ConstructFlowSpecificData<RecordReturn, FraudCheckRecordReturnData, FraudCh
connector_meta_data: None,
amount_captured: None,
request: FraudCheckRecordReturnData {
amount: self.payment_attempt.amount,
amount: self.payment_attempt.amount.get_authorize_amount(),
refund_method: RefundMethod::OriginalPaymentInstrument, //we dont consume this data now in payments...hence hardcoded
currency,
refund_transaction_id: self.refund.clone().map(|refund| refund.refund_id),
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/core/fraud_check/flows/sale_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl ConstructFlowSpecificData<frm_api::Sale, FraudCheckSaleData, FraudCheckResp
connector_meta_data: None,
amount_captured: None,
request: FraudCheckSaleData {
amount: self.payment_attempt.amount,
amount: self.payment_attempt.amount.get_authorize_amount(),
order_details: self.order_details.clone(),
},
response: Ok(FraudCheckResponseData::TransactionResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl
connector_meta_data: None,
amount_captured: None,
request: FraudCheckTransactionData {
amount: self.payment_attempt.amount,
amount: self.payment_attempt.amount.get_authorize_amount(),
order_details: self.order_details.clone(),
currency,
payment_method,
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/core/payment_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub async fn intiate_payment_link_flow(
payment_intent.client_secret.clone(),
)?;
let amount = currency
.to_currency_base_unit(payment_intent.amount)
.to_currency_base_unit(payment_intent.original_amount)
.into_report()
.change_context(errors::ApiErrorResponse::CurrencyConversionFailed)?;
let order_details = validate_order_details(payment_intent.order_details.clone(), currency)?;
Expand Down
20 changes: 13 additions & 7 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ pub async fn list_payment_methods(
.transpose()?;

let payment_type = payment_attempt.as_ref().map(|pa| {
let amount = api::Amount::from(pa.amount);
let amount = api::Amount::from(pa.amount.get_authorize_amount());
let mandate_type = if pa.mandate_id.is_some() {
Some(api::MandateTransactionType::RecurringMandateTransaction)
} else if pa.mandate_details.is_some() {
Expand Down Expand Up @@ -1088,8 +1088,14 @@ pub async fn list_payment_methods(
)
.await?;

// filter out connectors based on the business country
let filtered_mcas = helpers::filter_mca_based_on_business_profile(all_mcas, profile_id);
// filter out connectors based on the business profile and connector types
let filtered_mcas = {
let filter_list = helpers::filter_mca_based_on_business_profile(all_mcas, profile_id);
filter_list
.into_iter()
.filter(|mca| mca.connector_type == common_enums::ConnectorType::FizOperations)
.collect::<Vec<_>>()
};

logger::debug!(mca_before_filtering=?filtered_mcas);

Expand Down Expand Up @@ -1767,7 +1773,7 @@ pub async fn call_surcharge_decision_management(
billing_address: Option<domain::Address>,
response_payment_method_types: &mut [ResponsePaymentMethodsEnabled],
) -> errors::RouterResult<api_surcharge_decision_configs::MerchantSurchargeConfigs> {
if payment_attempt.surcharge_amount.is_some() {
if payment_attempt.amount.get_surcharge_amount().is_some() {
Ok(api_surcharge_decision_configs::MerchantSurchargeConfigs::default())
} else {
let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
Expand Down Expand Up @@ -1820,7 +1826,7 @@ pub async fn call_surcharge_decision_management_for_saved_card(
payment_intent: storage::PaymentIntent,
customer_payment_method_response: &mut api::CustomerPaymentMethodsListResponse,
) -> errors::RouterResult<()> {
if payment_attempt.surcharge_amount.is_some() {
if payment_attempt.amount.get_surcharge_amount().is_some() {
Ok(())
} else {
let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
Expand Down Expand Up @@ -2311,10 +2317,10 @@ fn filter_payment_amount_based(
payment_intent: &storage::PaymentIntent,
pm: &RequestPaymentMethodTypes,
) -> bool {
let amount = payment_intent.amount;
let amount = payment_intent.original_amount;
(pm.maximum_amount.map_or(true, |amt| amount <= amt.into())
&& pm.minimum_amount.map_or(true, |amt| amount >= amt.into()))
|| payment_intent.amount == 0
|| payment_intent.original_amount == 0
}

async fn filter_payment_mandate_based(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ fn get_surcharge_details_from_surcharge_output(
let surcharge_amount = match surcharge_details.surcharge.clone() {
surcharge_decision_configs::SurchargeOutput::Fixed { amount } => amount,
surcharge_decision_configs::SurchargeOutput::Rate(percentage) => percentage
.apply_and_ceil_result(payment_attempt.amount)
.apply_and_ceil_result(payment_attempt.amount.get_original_amount())
.change_context(ConfigError::DslExecutionError)
.attach_printable("Failed to Calculate surcharge amount by applying percentage")?,
};
Expand All @@ -309,8 +309,9 @@ fn get_surcharge_details_from_surcharge_output(
})
.transpose()?
.unwrap_or(0);
let original_amount = payment_attempt.amount.get_original_amount();
Ok(types::SurchargeDetails {
original_amount: payment_attempt.amount,
original_amount,
surcharge: match surcharge_details.surcharge {
surcharge_decision_configs::SurchargeOutput::Fixed { amount } => {
common_utils_types::Surcharge::Fixed(amount)
Expand All @@ -322,7 +323,7 @@ fn get_surcharge_details_from_surcharge_output(
tax_on_surcharge: surcharge_details.tax_on_surcharge,
surcharge_amount,
tax_on_surcharge_amount,
final_amount: payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount,
final_amount: original_amount + surcharge_amount + tax_on_surcharge_amount,
})
}

Expand Down
43 changes: 22 additions & 21 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ where
let mut connector_http_status_code = None;
let mut external_latency = None;
if let Some(connector_details) = connector {
operation
.to_domain()?
.populate_payment_data(state, &mut payment_data, &merchant_account)
.await?;
// Fetch and check FRM configs
#[cfg(feature = "frm")]
let mut frm_info = None;
Expand Down Expand Up @@ -499,7 +503,9 @@ where
.surcharge_applicable
.unwrap_or(false)
{
if let Some(surcharge_details) = payment_data.payment_attempt.get_surcharge_details() {
if let Some(surcharge_details) =
payment_data.payment_attempt.get_request_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));
Expand Down Expand Up @@ -541,16 +547,12 @@ where

payment_data.surcharge_details = calculated_surcharge_details;
} else {
let surcharge_details =
payment_data
.payment_attempt
.get_surcharge_details()
.map(|surcharge_details| {
types::SurchargeDetails::from((
&surcharge_details,
&payment_data.payment_attempt,
))
});
let surcharge_details = payment_data
.payment_attempt
.get_request_surcharge_details()
.map(|surcharge_details| {
types::SurchargeDetails::from((&surcharge_details, &payment_data.payment_attempt))
});
payment_data.surcharge_details = surcharge_details;
}
Ok(())
Expand Down Expand Up @@ -578,13 +580,17 @@ pub async fn call_surcharge_decision_management_for_session_flow<O>(
where
O: Send + Clone + Sync,
{
if let Some(surcharge_amount) = payment_data.payment_attempt.surcharge_amount {
let tax_on_surcharge_amount = payment_data.payment_attempt.tax_amount.unwrap_or(0);
let final_amount =
payment_data.payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount;
if let Some(surcharge_amount) = payment_data.payment_attempt.amount.get_surcharge_amount() {
let tax_on_surcharge_amount = payment_data
.payment_attempt
.amount
.get_tax_amount_on_surcharge()
.unwrap_or(0);
let original_amount = payment_data.payment_attempt.amount.get_original_amount();
let final_amount = original_amount + surcharge_amount + tax_on_surcharge_amount;
Ok(Some(api::SessionSurchargeDetails::PreDetermined(
types::SurchargeDetails {
original_amount: payment_data.payment_attempt.amount,
original_amount,
surcharge: Surcharge::Fixed(surcharge_amount),
tax_on_surcharge: None,
surcharge_amount,
Expand Down Expand Up @@ -1029,11 +1035,6 @@ where
merchant_connector_account.get_mca_id();
}

operation
.to_domain()?
.populate_payment_data(state, payment_data, merchant_account)
.await?;

let (pd, tokenization_action) = get_connector_tokenization_action_when_confirm_true(
state,
operation,
Expand Down
Loading
Loading