From cd8d96a084ae9d516ef8394854cdd35f04f0630c Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Fri, 1 Dec 2023 01:18:41 +0530 Subject: [PATCH 1/9] refactor: create separate struct for surcharge details response --- crates/api_models/src/payment_methods.rs | 120 +------------ crates/api_models/src/payments.rs | 12 -- .../src/surcharge_decision_configs.rs | 10 +- crates/common_utils/src/types.rs | 15 +- crates/diesel_models/src/payment_intent.rs | 1 - .../surcharge_decision_configs.rs | 93 ++++++----- crates/router/src/core/payments.rs | 20 +-- crates/router/src/core/payments/helpers.rs | 40 ++++- .../payments/operations/payment_create.rs | 8 +- .../payments/operations/payment_update.rs | 20 ++- crates/router/src/core/payments/types.rs | 158 +++++++++++++++++- crates/router/src/core/utils.rs | 22 ++- crates/router/src/types.rs | 8 +- crates/router/src/types/api.rs | 12 +- 14 files changed, 319 insertions(+), 220 deletions(-) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index dfb8e8999771..3343becaaae6 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -2,8 +2,10 @@ use std::collections::HashMap; use cards::CardNumber; use common_utils::{ - consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, crypto::OptionalEncryptableName, pii, - types::Percentage, + consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, + crypto::OptionalEncryptableName, + pii, + types::{Percentage, Surcharge}, }; use serde::de; use utoipa::ToSchema; @@ -14,7 +16,7 @@ use crate::{ admin, customers::CustomerId, enums as api_enums, - payments::{self, BankCodeResponse, RequestSurchargeDetails}, + payments::{self, BankCodeResponse}, }; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] @@ -337,117 +339,11 @@ pub struct SurchargeDetailsResponse { /// tax on surcharge value pub tax_on_surcharge: Option>, /// surcharge amount for this payment - pub surcharge_amount: i64, + pub display_surcharge_amount: f64, /// tax on surcharge amount for this payment - pub tax_on_surcharge_amount: i64, + pub display_tax_on_surcharge_amount: f64, /// sum of original amount, - pub final_amount: i64, -} - -impl SurchargeDetailsResponse { - pub fn is_request_surcharge_matching( - &self, - request_surcharge_details: RequestSurchargeDetails, - ) -> bool { - request_surcharge_details.surcharge_amount == self.surcharge_amount - && request_surcharge_details.tax_amount.unwrap_or(0) == self.tax_on_surcharge_amount - } - pub fn get_total_surcharge_amount(&self) -> i64 { - self.surcharge_amount + self.tax_on_surcharge_amount - } -} - -#[derive(Clone, Debug)] -pub struct SurchargeMetadata { - surcharge_results: HashMap< - ( - common_enums::PaymentMethod, - common_enums::PaymentMethodType, - Option, - ), - SurchargeDetailsResponse, - >, - pub payment_attempt_id: String, -} - -impl SurchargeMetadata { - pub fn new(payment_attempt_id: String) -> Self { - Self { - surcharge_results: HashMap::new(), - payment_attempt_id, - } - } - pub fn is_empty_result(&self) -> bool { - self.surcharge_results.is_empty() - } - pub fn get_surcharge_results_size(&self) -> usize { - self.surcharge_results.len() - } - pub fn insert_surcharge_details( - &mut self, - payment_method: &common_enums::PaymentMethod, - payment_method_type: &common_enums::PaymentMethodType, - card_network: Option<&common_enums::CardNetwork>, - surcharge_details: SurchargeDetailsResponse, - ) { - let key = ( - payment_method.to_owned(), - payment_method_type.to_owned(), - card_network.cloned(), - ); - self.surcharge_results.insert(key, surcharge_details); - } - pub fn get_surcharge_details( - &self, - payment_method: &common_enums::PaymentMethod, - payment_method_type: &common_enums::PaymentMethodType, - card_network: Option<&common_enums::CardNetwork>, - ) -> Option<&SurchargeDetailsResponse> { - let key = &( - payment_method.to_owned(), - payment_method_type.to_owned(), - card_network.cloned(), - ); - self.surcharge_results.get(key) - } - pub fn get_surcharge_metadata_redis_key(payment_attempt_id: &str) -> String { - format!("surcharge_metadata_{}", payment_attempt_id) - } - pub fn get_individual_surcharge_key_value_pairs( - &self, - ) -> Vec<(String, SurchargeDetailsResponse)> { - self.surcharge_results - .iter() - .map(|((pm, pmt, card_network), surcharge_details)| { - let key = - Self::get_surcharge_details_redis_hashset_key(pm, pmt, card_network.as_ref()); - (key, surcharge_details.to_owned()) - }) - .collect() - } - pub fn get_surcharge_details_redis_hashset_key( - payment_method: &common_enums::PaymentMethod, - payment_method_type: &common_enums::PaymentMethodType, - card_network: Option<&common_enums::CardNetwork>, - ) -> String { - if let Some(card_network) = card_network { - format!( - "{}_{}_{}", - payment_method, payment_method_type, card_network - ) - } else { - format!("{}_{}", payment_method, payment_method_type) - } - } -} - -#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)] -#[serde(rename_all = "snake_case", tag = "type", content = "value")] -pub enum Surcharge { - /// Fixed Surcharge value - Fixed(i64), - /// Surcharge percentage - Rate(Percentage), + pub display_final_amount: f64, } /// Required fields info used while listing the payment_method_data diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index fe5ed417f350..1215cd6501c1 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -16,7 +16,6 @@ use crate::{ admin, disputes, enums::{self as api_enums}, ephemeral_key::EphemeralKeyCreateResponse, - payment_methods::{Surcharge, SurchargeDetailsResponse}, refunds, }; @@ -339,17 +338,6 @@ impl RequestSurchargeDetails { pub fn is_surcharge_zero(&self) -> bool { self.surcharge_amount == 0 && self.tax_amount.unwrap_or(0) == 0 } - pub fn get_surcharge_details_object(&self, original_amount: i64) -> SurchargeDetailsResponse { - let surcharge_amount = self.surcharge_amount; - let tax_on_surcharge_amount = self.tax_amount.unwrap_or(0); - SurchargeDetailsResponse { - surcharge: Surcharge::Fixed(self.surcharge_amount), - tax_on_surcharge: None, - surcharge_amount, - tax_on_surcharge_amount, - final_amount: original_amount + surcharge_amount + tax_on_surcharge_amount, - } - } pub fn get_total_surcharge_amount(&self) -> i64 { self.surcharge_amount + self.tax_amount.unwrap_or(0) } diff --git a/crates/api_models/src/surcharge_decision_configs.rs b/crates/api_models/src/surcharge_decision_configs.rs index 3ebf8f42744e..7ead27945584 100644 --- a/crates/api_models/src/surcharge_decision_configs.rs +++ b/crates/api_models/src/surcharge_decision_configs.rs @@ -7,21 +7,21 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] -pub struct SurchargeDetails { - pub surcharge: Surcharge, +pub struct SurchargeDetailsOutput { + pub surcharge: SurchargeOutput, pub tax_on_surcharge: Option>, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case", tag = "type", content = "value")] -pub enum Surcharge { - Fixed(i64), +pub enum SurchargeOutput { + Fixed { amount: i64 }, Rate(Percentage), } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct SurchargeDecisionConfigs { - pub surcharge_details: Option, + pub surcharge_details: Option, } impl EuclidDirFilter for SurchargeDecisionConfigs { const ALLOWED: &'static [DirKeyKind] = &[ diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 111f0f43c0f2..cf94f2fe26ce 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -2,7 +2,10 @@ use error_stack::{IntoReport, ResultExt}; use serde::{de::Visitor, Deserialize, Deserializer}; -use crate::errors::{CustomResult, PercentageError}; +use crate::{ + consts, + errors::{CustomResult, PercentageError}, +}; /// Represents Percentage Value between 0 and 100 both inclusive #[derive(Clone, Default, Debug, PartialEq, serde::Serialize)] @@ -136,3 +139,13 @@ impl<'de, const PRECISION: u8> Deserialize<'de> for Percentage { data.deserialize_map(PercentageVisitor:: {}) } } + +/// represents surcharge type and value +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "snake_case", tag = "type", content = "value")] +pub enum Surcharge { + /// Fixed Surcharge value + Fixed(i64), + /// Surcharge percentage + Rate(Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>), +} diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 8d752466103e..40d8fd92caeb 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -106,7 +106,6 @@ pub struct PaymentIntentNew { pub merchant_decision: Option, pub payment_link_id: Option, pub payment_confirm_source: Option, - pub updated_by: String, pub surcharge_applicable: Option, pub request_incremental_authorization: RequestIncrementalAuthorization, diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs index 9a65ec76f2a5..9b5c37632b0b 100644 --- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs +++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs @@ -1,12 +1,10 @@ use api_models::{ - payment_methods::{self, SurchargeDetailsResponse, SurchargeMetadata}, + payment_methods::SurchargeDetailsResponse, payments::Address, routing, - surcharge_decision_configs::{ - self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord, SurchargeDetails, - }, + surcharge_decision_configs::{self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord}, }; -use common_utils::{ext_traits::StringExt, static_cache::StaticCache}; +use common_utils::{ext_traits::StringExt, static_cache::StaticCache, types as common_utils_types}; use error_stack::{self, IntoReport, ResultExt}; use euclid::{ backend, @@ -14,7 +12,11 @@ use euclid::{ }; use router_env::{instrument, tracing}; -use crate::{core::payments::PaymentData, db::StorageInterface, types::storage as oss_storage}; +use crate::{ + core::payments::{types, PaymentData}, + db::StorageInterface, + types::{storage as oss_storage, transformers::ForeignTryFrom}, +}; static CONF_CACHE: StaticCache = StaticCache::new(); use crate::{ core::{ @@ -55,10 +57,10 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( billing_address: Option
, response_payment_method_types: &mut [api_models::payment_methods::ResponsePaymentMethodsEnabled], ) -> ConditionalConfigResult<( - SurchargeMetadata, + types::SurchargeMetadata, surcharge_decision_configs::MerchantSurchargeConfigs, )> { - let mut surcharge_metadata = SurchargeMetadata::new(payment_attempt.attempt_id.clone()); + let mut surcharge_metadata = types::SurchargeMetadata::new(payment_attempt.attempt_id.clone()); let algorithm_id = if let Some(id) = algorithm_ref.surcharge_config_algo_id { id } else { @@ -101,20 +103,25 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( Some(card_network_type.card_network.clone()); let surcharge_output = execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; + // let surcharge_details = card_network_type.surcharge_details = surcharge_output .surcharge_details .map(|surcharge_details| { - get_surcharge_details_response(surcharge_details, payment_attempt).map( - |surcharge_details_response| { - surcharge_metadata.insert_surcharge_details( - &payment_methods_enabled.payment_method, - &payment_method_type_response.payment_method_type, - Some(&card_network_type.card_network), - surcharge_details_response.clone(), - ); - surcharge_details_response - }, - ) + let surcharge_details = + get_surcharge_details_response(surcharge_details, payment_attempt)?; + surcharge_metadata.insert_surcharge_details( + &payment_methods_enabled.payment_method, + &payment_method_type_response.payment_method_type, + Some(&card_network_type.card_network), + surcharge_details.clone(), + ); + SurchargeDetailsResponse::foreign_try_from(( + &surcharge_details, + &payment_attempt, + )) + .into_report() + .change_context(ConfigError::DslExecutionError) + .attach_printable("Error while constructing Surcharge response type") }) .transpose()?; } @@ -124,17 +131,21 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( payment_method_type_response.surcharge_details = surcharge_output .surcharge_details .map(|surcharge_details| { - get_surcharge_details_response(surcharge_details, payment_attempt).map( - |surcharge_details_response| { - surcharge_metadata.insert_surcharge_details( - &payment_methods_enabled.payment_method, - &payment_method_type_response.payment_method_type, - None, - surcharge_details_response.clone(), - ); - surcharge_details_response - }, - ) + let surcharge_details = + get_surcharge_details_response(surcharge_details, payment_attempt)?; + surcharge_metadata.insert_surcharge_details( + &payment_methods_enabled.payment_method, + &payment_method_type_response.payment_method_type, + None, + surcharge_details.clone(), + ); + SurchargeDetailsResponse::foreign_try_from(( + &surcharge_details, + &payment_attempt, + )) + .into_report() + .change_context(ConfigError::DslExecutionError) + .attach_printable("Error while constructing Surcharge response type") }) .transpose()?; } @@ -148,12 +159,12 @@ pub async fn perform_surcharge_decision_management_for_session_flow( algorithm_ref: routing::RoutingAlgorithmRef, payment_data: &mut PaymentData, payment_method_type_list: &Vec, -) -> ConditionalConfigResult +) -> ConditionalConfigResult where O: Send + Clone, { let mut surcharge_metadata = - SurchargeMetadata::new(payment_data.payment_attempt.attempt_id.clone()); + types::SurchargeMetadata::new(payment_data.payment_attempt.attempt_id.clone()); let algorithm_id = if let Some(id) = algorithm_ref.surcharge_config_algo_id { id } else { @@ -200,12 +211,12 @@ where } fn get_surcharge_details_response( - surcharge_details: SurchargeDetails, + surcharge_details: surcharge_decision_configs::SurchargeDetailsOutput, payment_attempt: &oss_storage::PaymentAttempt, -) -> ConditionalConfigResult { +) -> ConditionalConfigResult { let surcharge_amount = match surcharge_details.surcharge.clone() { - surcharge_decision_configs::Surcharge::Fixed(value) => value, - surcharge_decision_configs::Surcharge::Rate(percentage) => percentage + surcharge_decision_configs::SurchargeOutput::Fixed { amount } => amount, + surcharge_decision_configs::SurchargeOutput::Rate(percentage) => percentage .apply_and_ceil_result(payment_attempt.amount) .change_context(ConfigError::DslExecutionError) .attach_printable("Failed to Calculate surcharge amount by applying percentage")?, @@ -221,13 +232,13 @@ fn get_surcharge_details_response( }) .transpose()? .unwrap_or(0); - Ok(SurchargeDetailsResponse { + Ok(types::SurchargeDetails { surcharge: match surcharge_details.surcharge { - surcharge_decision_configs::Surcharge::Fixed(surcharge_amount) => { - payment_methods::Surcharge::Fixed(surcharge_amount) + surcharge_decision_configs::SurchargeOutput::Fixed { amount } => { + common_utils_types::Surcharge::Fixed(amount) } - surcharge_decision_configs::Surcharge::Rate(percentage) => { - payment_methods::Surcharge::Rate(percentage) + surcharge_decision_configs::SurchargeOutput::Rate(percentage) => { + common_utils_types::Surcharge::Rate(percentage) } }, tax_on_surcharge: surcharge_details.tax_on_surcharge, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 33afa29397e1..b0e00bda5ca5 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -15,10 +15,9 @@ use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoI use api_models::{ self, enums, - payment_methods::{Surcharge, SurchargeDetailsResponse}, payments::{self, HeaderPayload}, }; -use common_utils::{ext_traits::AsyncExt, pii}; +use common_utils::{ext_traits::AsyncExt, pii, types::Surcharge}; use data_models::mandates::MandateData; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use error_stack::{IntoReport, ResultExt}; @@ -42,6 +41,7 @@ use self::{ helpers::get_key_params_for_surcharge_details, operations::{payment_complete_authorize, BoxedOperation, Operation}, routing::{self as self_routing, SessionFlowRoutingInput}, + types::SurchargeDetails, }; use super::{ errors::StorageErrorExt, payment_methods::surcharge_decision_configs, utils as core_utils, @@ -451,12 +451,6 @@ where .into()); } } - (None, Some(_calculated_surcharge_details)) => { - return Err(errors::ApiErrorResponse::MissingRequiredField { - field_name: "surcharge_details", - } - .into()); - } (Some(request_surcharge_details), None) => { if request_surcharge_details.is_surcharge_zero() { return Ok(()); @@ -467,6 +461,9 @@ where .into()); } } + (None, Some(calculated_surcharge_details)) => { + payment_data.surcharge_details = Some(calculated_surcharge_details); + } (None, None) => return Ok(()), }; } else { @@ -475,8 +472,7 @@ where .payment_attempt .get_surcharge_details() .map(|surcharge_details| { - surcharge_details - .get_surcharge_details_object(payment_data.payment_attempt.amount) + SurchargeDetails::from((&surcharge_details, &payment_data.payment_attempt)) }); payment_data.surcharge_details = surcharge_details; } @@ -509,7 +505,7 @@ where let final_amount = payment_data.payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount; Ok(Some(api::SessionSurchargeDetails::PreDetermined( - SurchargeDetailsResponse { + SurchargeDetails { surcharge: Surcharge::Fixed(surcharge_amount), tax_on_surcharge: None, surcharge_amount, @@ -1882,7 +1878,7 @@ where pub recurring_mandate_payment_data: Option, pub ephemeral_key: Option, pub redirect_response: Option, - pub surcharge_details: Option, + pub surcharge_details: Option, pub frm_message: Option, pub payment_link_data: Option, } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 4d11f6400f44..9f8312f864b5 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use api_models::payments::{CardToken, GetPaymentMethodType}; +use api_models::payments::{CardToken, GetPaymentMethodType, RequestSurchargeDetails}; use base64::Engine; use common_utils::{ ext_traits::{AsyncExt, ByteSliceExt, ValueExt}, @@ -572,6 +572,7 @@ pub fn validate_merchant_id( pub fn validate_request_amount_and_amount_to_capture( op_amount: Option, op_amount_to_capture: Option, + surcharge_details: Option, ) -> CustomResult<(), errors::ApiErrorResponse> { match (op_amount, op_amount_to_capture) { (None, _) => Ok(()), @@ -581,7 +582,11 @@ pub fn validate_request_amount_and_amount_to_capture( api::Amount::Value(amount_inner) => { // If both amount and amount to capture is present // then amount to be capture should be less than or equal to request amount - utils::when(!amount_to_capture.le(&amount_inner.get()), || { + let total_capturable_amount = amount_inner.get() + + surcharge_details + .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) + .unwrap_or(0); + utils::when(!amount_to_capture.le(&total_capturable_amount), || { Err(report!(errors::ApiErrorResponse::PreconditionFailed { message: format!( "amount_to_capture is greater than amount capture_amount: {amount_to_capture:?} request_amount: {amount:?}" @@ -603,13 +608,34 @@ pub fn validate_request_amount_and_amount_to_capture( /// 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( +pub fn validate_amount_to_capture_and_capture_method( + payment_attempt: Option<&PaymentAttempt>, request: &api_models::payments::PaymentsRequest, ) -> CustomResult<(), errors::ApiErrorResponse> { - 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) + let capture_method = request + .capture_method + .or(payment_attempt + .map(|payment_attempt| payment_attempt.capture_method.unwrap_or_default())) + .unwrap_or_default(); + if capture_method == api_enums::CaptureMethod::Automatic { + let original_amount = request + .amount + .map(|amount| amount.into()) + .or(payment_attempt.map(|payment_attempt| payment_attempt.amount)); + let surcharge_amount = request + .surcharge_details + .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) + .or_else(|| { + payment_attempt.map(|payment_attempt| { + payment_attempt.surcharge_amount.unwrap_or(0) + + payment_attempt.tax_amount.unwrap_or(0) + }) + }) + .unwrap_or(0); + let total_capturable_amount = + original_amount.map(|original_amount| original_amount + surcharge_amount); + if let Some((total_capturable_amount, amount_to_capture)) = + total_capturable_amount.zip(request.amount_to_capture) { utils::when(amount_to_capture != total_capturable_amount, || { Err(report!(errors::ApiErrorResponse::PreconditionFailed { diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index ac387076d1d1..bb7d0a931e1b 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -286,8 +286,8 @@ impl // The operation merges mandate data from both request and payment_attempt let setup_mandate = setup_mandate.map(MandateData::from); - let surcharge_details = request.surcharge_details.map(|surcharge_details| { - surcharge_details.get_surcharge_details_object(payment_attempt.amount) + let surcharge_details = request.surcharge_details.map(|request_surcharge_details| { + payments::SurchargeDetails::from((&request_surcharge_details, &payment_attempt)) }); let payment_data = PaymentData { @@ -540,14 +540,14 @@ impl ValidateRequest &[ storage_enums::IntentStatus::Failed, storage_enums::IntentStatus::Succeeded, + storage_enums::IntentStatus::PartiallyCaptured, storage_enums::IntentStatus::RequiresCapture, ], "update", @@ -134,6 +135,20 @@ impl .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + helpers::validate_amount_to_capture_and_capture_method(Some(&payment_attempt), request)?; + + helpers::validate_request_amount_and_amount_to_capture( + request.amount, + request.amount_to_capture, + request + .surcharge_details + .or(payment_attempt.get_surcharge_details()), + ) + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "amount_to_capture".to_string(), + expected_format: "amount_to_capture lesser than or equal to amount".to_string(), + })?; + currency = request .currency .or(payment_attempt.currency) @@ -322,7 +337,7 @@ impl })?; let surcharge_details = request.surcharge_details.map(|request_surcharge_details| { - request_surcharge_details.get_surcharge_details_object(payment_attempt.amount) + payments::SurchargeDetails::from((&request_surcharge_details, &payment_attempt)) }); let payment_data = PaymentData { @@ -629,6 +644,7 @@ impl ValidateRequest>, + /// surcharge amount for this payment + pub surcharge_amount: i64, + /// tax on surcharge amount for this payment + pub tax_on_surcharge_amount: i64, + /// sum of original amount, + pub final_amount: i64, +} + +impl From<(&RequestSurchargeDetails, &PaymentAttempt)> for SurchargeDetails { + fn from( + (request_surcharge_details, payment_attempt): (&RequestSurchargeDetails, &PaymentAttempt), + ) -> Self { + let surcharge_amount = request_surcharge_details.surcharge_amount; + let tax_on_surcharge_amount = request_surcharge_details.tax_amount.unwrap_or(0); + Self { + surcharge: common_types::Surcharge::Fixed(request_surcharge_details.surcharge_amount), + tax_on_surcharge: None, + surcharge_amount, + tax_on_surcharge_amount, + final_amount: payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount, + } + } +} + +impl ForeignTryFrom<(&SurchargeDetails, &PaymentAttempt)> for SurchargeDetailsResponse { + type Error = TryFromIntError; + fn foreign_try_from( + (surcharge_details, payment_attempt): (&SurchargeDetails, &PaymentAttempt), + ) -> Result { + let currency = payment_attempt.currency.unwrap_or_default(); + let display_surcharge_amount = + currency.to_currency_base_unit_asf64(surcharge_details.surcharge_amount)?; + let display_tax_on_surcharge_amount = + currency.to_currency_base_unit_asf64(surcharge_details.tax_on_surcharge_amount)?; + let display_final_amount = + currency.to_currency_base_unit_asf64(surcharge_details.final_amount)?; + Ok(Self { + surcharge: surcharge_details.surcharge.clone(), + tax_on_surcharge: surcharge_details.tax_on_surcharge.clone(), + display_surcharge_amount, + display_tax_on_surcharge_amount, + display_final_amount, + }) + } +} + +impl SurchargeDetails { + pub fn is_request_surcharge_matching( + &self, + request_surcharge_details: RequestSurchargeDetails, + ) -> bool { + request_surcharge_details.surcharge_amount == self.surcharge_amount + && request_surcharge_details.tax_amount.unwrap_or(0) == self.tax_on_surcharge_amount + } + pub fn get_total_surcharge_amount(&self) -> i64 { + self.surcharge_amount + self.tax_on_surcharge_amount + } +} + +#[derive(Clone, Debug)] +pub struct SurchargeMetadata { + surcharge_results: HashMap< + ( + common_enums::PaymentMethod, + common_enums::PaymentMethodType, + Option, + ), + SurchargeDetails, + >, + pub payment_attempt_id: String, +} + +impl SurchargeMetadata { + pub fn new(payment_attempt_id: String) -> Self { + Self { + surcharge_results: HashMap::new(), + payment_attempt_id, + } + } + pub fn is_empty_result(&self) -> bool { + self.surcharge_results.is_empty() + } + pub fn get_surcharge_results_size(&self) -> usize { + self.surcharge_results.len() + } + pub fn insert_surcharge_details( + &mut self, + payment_method: &common_enums::PaymentMethod, + payment_method_type: &common_enums::PaymentMethodType, + card_network: Option<&common_enums::CardNetwork>, + surcharge_details: SurchargeDetails, + ) { + let key = ( + payment_method.to_owned(), + payment_method_type.to_owned(), + card_network.cloned(), + ); + self.surcharge_results.insert(key, surcharge_details); + } + pub fn get_surcharge_details( + &self, + payment_method: &common_enums::PaymentMethod, + payment_method_type: &common_enums::PaymentMethodType, + card_network: Option<&common_enums::CardNetwork>, + ) -> Option<&SurchargeDetails> { + let key = &( + payment_method.to_owned(), + payment_method_type.to_owned(), + card_network.cloned(), + ); + self.surcharge_results.get(key) + } + pub fn get_surcharge_metadata_redis_key(payment_attempt_id: &str) -> String { + format!("surcharge_metadata_{}", payment_attempt_id) + } + pub fn get_individual_surcharge_key_value_pairs(&self) -> Vec<(String, SurchargeDetails)> { + self.surcharge_results + .iter() + .map(|((pm, pmt, card_network), surcharge_details)| { + let key = + Self::get_surcharge_details_redis_hashset_key(pm, pmt, card_network.as_ref()); + (key, surcharge_details.to_owned()) + }) + .collect() + } + pub fn get_surcharge_details_redis_hashset_key( + payment_method: &common_enums::PaymentMethod, + payment_method_type: &common_enums::PaymentMethodType, + card_network: Option<&common_enums::CardNetwork>, + ) -> String { + if let Some(card_network) = card_network { + format!( + "{}_{}_{}", + payment_method, payment_method_type, card_network + ) + } else { + format!("{}_{}", payment_method, payment_method_type) + } + } +} diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 670c25c814ed..6d82c44d803a 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -1,9 +1,6 @@ use std::{marker::PhantomData, str::FromStr}; -use api_models::{ - enums::{DisputeStage, DisputeStatus}, - payment_methods::{SurchargeDetailsResponse, SurchargeMetadata}, -}; +use api_models::enums::{DisputeStage, DisputeStatus}; use common_enums::RequestIncrementalAuthorization; #[cfg(feature = "payouts")] use common_utils::{crypto::Encryptable, pii::Email}; @@ -17,7 +14,7 @@ use redis_interface::errors::RedisError; use router_env::{instrument, tracing}; use uuid::Uuid; -use super::payments::{helpers, PaymentAddress}; +use super::payments::{helpers, types as payments_types, PaymentAddress}; #[cfg(feature = "payouts")] use super::payouts::PayoutData; #[cfg(feature = "payouts")] @@ -1075,7 +1072,7 @@ pub fn get_flow_name() -> RouterResult { pub async fn persist_individual_surcharge_details_in_redis( state: &AppState, merchant_account: &domain::MerchantAccount, - surcharge_metadata: &SurchargeMetadata, + surcharge_metadata: &payments_types::SurchargeMetadata, ) -> RouterResult<()> { if !surcharge_metadata.is_empty_result() { let redis_conn = state @@ -1083,7 +1080,7 @@ pub async fn persist_individual_surcharge_details_in_redis( .get_redis_conn() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get redis connection")?; - let redis_key = SurchargeMetadata::get_surcharge_metadata_redis_key( + let redis_key = payments_types::SurchargeMetadata::get_surcharge_metadata_redis_key( &surcharge_metadata.payment_attempt_id, ); @@ -1094,7 +1091,7 @@ pub async fn persist_individual_surcharge_details_in_redis( { value_list.push(( key, - Encode::::encode_to_string_of_json(&value) + Encode::::encode_to_string_of_json(&value) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to encode to string of json")?, )); @@ -1118,20 +1115,21 @@ pub async fn get_individual_surcharge_detail_from_redis( payment_method_type: &euclid_enums::PaymentMethodType, card_network: Option, payment_attempt_id: &str, -) -> CustomResult { +) -> CustomResult { let redis_conn = state .store .get_redis_conn() .attach_printable("Failed to get redis connection")?; - let redis_key = SurchargeMetadata::get_surcharge_metadata_redis_key(payment_attempt_id); - let value_key = SurchargeMetadata::get_surcharge_details_redis_hashset_key( + let redis_key = + payments_types::SurchargeMetadata::get_surcharge_metadata_redis_key(payment_attempt_id); + let value_key = payments_types::SurchargeMetadata::get_surcharge_details_redis_hashset_key( payment_method, payment_method_type, card_network.as_ref(), ); redis_conn - .get_hash_field_and_deserialize(&redis_key, &value_key, "SurchargeDetailsResponse") + .get_hash_field_and_deserialize(&redis_key, &value_key, "SurchargeDetails") .await } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index c267a54cc57b..595f487e7079 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -30,7 +30,7 @@ use crate::core::utils::IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_DISPUTE_FLO use crate::{ core::{ errors::{self, RouterResult}, - payments::{PaymentData, RecurringMandatePaymentData}, + payments::{types, PaymentData, RecurringMandatePaymentData}, }, services, types::{storage::payment_attempt::PaymentAttemptExt, transformers::ForeignFrom}, @@ -379,7 +379,7 @@ pub struct PaymentsAuthorizeData { pub related_transaction_id: Option, pub payment_experience: Option, pub payment_method_type: Option, - pub surcharge_details: Option, + pub surcharge_details: Option, pub customer_id: Option, pub request_incremental_authorization: bool, } @@ -441,7 +441,7 @@ pub struct PaymentsPreProcessingData { pub router_return_url: Option, pub webhook_url: Option, pub complete_authorize_url: Option, - pub surcharge_details: Option, + pub surcharge_details: Option, pub browser_info: Option, pub connector_transaction_id: Option, } @@ -517,7 +517,7 @@ pub struct PaymentsSessionData { pub amount: i64, pub currency: storage_enums::Currency, pub country: Option, - pub surcharge_details: Option, + pub surcharge_details: Option, pub order_details: Option>, } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 96bcaca3ed5d..ea2ea8b701da 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -19,7 +19,6 @@ pub mod webhooks; use std::{fmt::Debug, str::FromStr}; -use api_models::payment_methods::{SurchargeDetailsResponse, SurchargeMetadata}; use error_stack::{report, IntoReport, ResultExt}; pub use self::{ @@ -30,7 +29,10 @@ use super::ErrorResponse; use crate::{ configs::settings::Connectors, connector, consts, - core::errors::{self, CustomResult}, + core::{ + errors::{self, CustomResult}, + payments::types as payments_types, + }, services::{request, ConnectorIntegration, ConnectorRedirectResponse, ConnectorValidation}, types::{self, api::enums as api_enums}, }; @@ -222,9 +224,9 @@ pub struct SessionConnectorData { /// Session Surcharge type pub enum SessionSurchargeDetails { /// Surcharge is calculated by hyperswitch - Calculated(SurchargeMetadata), + Calculated(payments_types::SurchargeMetadata), /// Surcharge is sent by merchant - PreDetermined(SurchargeDetailsResponse), + PreDetermined(payments_types::SurchargeDetails), } impl SessionSurchargeDetails { @@ -233,7 +235,7 @@ impl SessionSurchargeDetails { payment_method: &enums::PaymentMethod, payment_method_type: &enums::PaymentMethodType, card_network: Option<&enums::CardNetwork>, - ) -> Option { + ) -> Option { match self { Self::Calculated(surcharge_metadata) => surcharge_metadata .get_surcharge_details(payment_method, payment_method_type, card_network) From f829a8cc7f58a53ce904d5c221d656f974452d96 Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Fri, 1 Dec 2023 01:42:01 +0530 Subject: [PATCH 2/9] change function name --- .../surcharge_decision_configs.rs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs index 9b5c37632b0b..38ae71754b87 100644 --- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs +++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs @@ -107,8 +107,10 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( card_network_type.surcharge_details = surcharge_output .surcharge_details .map(|surcharge_details| { - let surcharge_details = - get_surcharge_details_response(surcharge_details, payment_attempt)?; + let surcharge_details = get_surcharge_details_from_surcharge_output( + surcharge_details, + payment_attempt, + )?; surcharge_metadata.insert_surcharge_details( &payment_methods_enabled.payment_method, &payment_method_type_response.payment_method_type, @@ -117,7 +119,7 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( ); SurchargeDetailsResponse::foreign_try_from(( &surcharge_details, - &payment_attempt, + payment_attempt, )) .into_report() .change_context(ConfigError::DslExecutionError) @@ -131,8 +133,10 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( payment_method_type_response.surcharge_details = surcharge_output .surcharge_details .map(|surcharge_details| { - let surcharge_details = - get_surcharge_details_response(surcharge_details, payment_attempt)?; + let surcharge_details = get_surcharge_details_from_surcharge_output( + surcharge_details, + payment_attempt, + )?; surcharge_metadata.insert_surcharge_details( &payment_methods_enabled.payment_method, &payment_method_type_response.payment_method_type, @@ -141,7 +145,7 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( ); SurchargeDetailsResponse::foreign_try_from(( &surcharge_details, - &payment_attempt, + payment_attempt, )) .into_report() .change_context(ConfigError::DslExecutionError) @@ -197,8 +201,10 @@ where let surcharge_output = execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; if let Some(surcharge_details) = surcharge_output.surcharge_details { - let surcharge_details_response = - get_surcharge_details_response(surcharge_details, &payment_data.payment_attempt)?; + let surcharge_details_response = get_surcharge_details_from_surcharge_output( + surcharge_details, + &payment_data.payment_attempt, + )?; surcharge_metadata.insert_surcharge_details( &payment_method_type.to_owned().into(), payment_method_type, @@ -210,7 +216,7 @@ where Ok(surcharge_metadata) } -fn get_surcharge_details_response( +fn get_surcharge_details_from_surcharge_output( surcharge_details: surcharge_decision_configs::SurchargeDetailsOutput, payment_attempt: &oss_storage::PaymentAttempt, ) -> ConditionalConfigResult { From ccca67d8bcc9eb93e659a4e11e890b4498427eba Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Sat, 2 Dec 2023 00:48:18 +0530 Subject: [PATCH 3/9] fix: use card bin to get additional card details --- crates/api_models/src/payments.rs | 42 +++++++++ .../src/surcharge_decision_configs.rs | 1 - crates/router/src/core/payments.rs | 47 ++-------- crates/router/src/core/payments/helpers.rs | 50 +++++------ crates/router/src/core/payments/operations.rs | 1 - .../payments/operations/payment_confirm.rs | 20 ++++- .../payments/operations/payment_create.rs | 85 +++++++++++-------- 7 files changed, 142 insertions(+), 104 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index fe5ed417f350..02da407b093e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -720,6 +720,33 @@ pub struct Card { pub nick_name: Option>, } +impl Card { + fn apply_additional_card_info(&self, additional_card_info: AdditionalCardInfo) -> Self { + Self { + card_number: self.card_number.clone(), + card_exp_month: self.card_exp_month.clone(), + card_exp_year: self.card_exp_year.clone(), + card_holder_name: self.card_holder_name.clone(), + card_cvc: self.card_cvc.clone(), + card_issuer: self + .card_issuer + .clone() + .or(additional_card_info.card_issuer), + card_network: self + .card_network + .clone() + .or(additional_card_info.card_network), + card_type: self.card_type.clone().or(additional_card_info.card_type), + card_issuing_country: self + .card_issuing_country + .clone() + .or(additional_card_info.card_issuing_country), + bank_code: self.bank_code.clone().or(additional_card_info.bank_code), + nick_name: self.nick_name.clone(), + } + } +} + #[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] #[serde(rename_all = "snake_case")] pub struct CardToken { @@ -889,6 +916,21 @@ impl PaymentMethodData { | Self::CardToken(_) => None, } } + pub fn apply_additional_payment_data( + &self, + additional_payment_data: AdditionalPaymentData, + ) -> Self { + if let AdditionalPaymentData::Card(additional_card_info) = additional_payment_data { + match self { + Self::Card(card) => { + Self::Card(card.apply_additional_card_info(*additional_card_info)) + } + _ => self.to_owned(), + } + } else { + self.to_owned() + } + } } pub trait GetPaymentMethodType { diff --git a/crates/api_models/src/surcharge_decision_configs.rs b/crates/api_models/src/surcharge_decision_configs.rs index 3ebf8f42744e..d46bbc9c49fe 100644 --- a/crates/api_models/src/surcharge_decision_configs.rs +++ b/crates/api_models/src/surcharge_decision_configs.rs @@ -30,7 +30,6 @@ impl EuclidDirFilter for SurchargeDecisionConfigs { DirKeyKind::PaymentAmount, DirKeyKind::PaymentCurrency, DirKeyKind::BillingCountry, - DirKeyKind::CardType, DirKeyKind::CardNetwork, DirKeyKind::PayLaterType, DirKeyKind::WalletType, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 33afa29397e1..c87797a8b2e3 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -16,7 +16,7 @@ use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoI use api_models::{ self, enums, payment_methods::{Surcharge, SurchargeDetailsResponse}, - payments::{self, HeaderPayload}, + payments::HeaderPayload, }; use common_utils::{ext_traits::AsyncExt, pii}; use data_models::mandates::MandateData; @@ -175,10 +175,6 @@ 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, &req, &merchant_account) - .await?; payment_data = match connector_details { api::ConnectorCallType::PreDetermined(connector) => { let schedule_time = if should_add_task_to_process_tracker { @@ -405,7 +401,6 @@ where async fn populate_surcharge_details( state: &AppState, payment_data: &mut PaymentData, - request: &payments::PaymentsRequest, ) -> RouterResult<()> where F: Send + Clone, @@ -415,7 +410,7 @@ where .surcharge_applicable .unwrap_or(false) { - let payment_method_data = request + let payment_method_data = payment_data .payment_method_data .clone() .get_required_value("payment_method_data")?; @@ -436,39 +431,7 @@ where Err(err) => Err(err).change_context(errors::ApiErrorResponse::InternalServerError)?, }; - let request_surcharge_details = request.surcharge_details; - - match (request_surcharge_details, calculated_surcharge_details) { - (Some(request_surcharge_details), Some(calculated_surcharge_details)) => { - if calculated_surcharge_details - .is_request_surcharge_matching(request_surcharge_details) - { - payment_data.surcharge_details = Some(calculated_surcharge_details); - } else { - return Err(errors::ApiErrorResponse::InvalidRequestData { - message: "Invalid value provided: 'surcharge_details'. surcharge details provided do not match with surcharge details sent in payment_methods list response".to_string(), - } - .into()); - } - } - (None, Some(_calculated_surcharge_details)) => { - return Err(errors::ApiErrorResponse::MissingRequiredField { - field_name: "surcharge_details", - } - .into()); - } - (Some(request_surcharge_details), None) => { - if request_surcharge_details.is_surcharge_zero() { - return Ok(()); - } else { - return Err(errors::ApiErrorResponse::InvalidRequestData { - message: "Invalid value provided: 'surcharge_details'. surcharge details provided do not match with surcharge details sent in payment_methods list response".to_string(), - } - .into()); - } - } - (None, None) => return Ok(()), - }; + payment_data.surcharge_details = calculated_surcharge_details; } else { let surcharge_details = payment_data @@ -978,6 +941,10 @@ where payment_data, ) .await?; + operation + .to_domain()? + .populate_payment_data(state, payment_data, merchant_account) + .await?; let mut router_data = payment_data .construct_router_data( diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 4d11f6400f44..4ed5404c2491 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1460,6 +1460,19 @@ pub async fn retrieve_card_with_permanent_token( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to fetch card information from the permanent locker")?; + let payment_method = state + .store + .find_payment_method(token) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch payment method")?; + let card_network = payment_method + .scheme + .map(|scheme| scheme.parse_enum("CardNetwork")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed deserilaize card network")?; + let name_on_card = if let Some(name_on_card) = card.name_on_card.clone() { if card.name_on_card.unwrap_or_default().expose().is_empty() { card_token_data @@ -1480,10 +1493,12 @@ pub async fn retrieve_card_with_permanent_token( card_exp_month: card.card_exp_month, card_exp_year: card.card_exp_year, card_cvc: card_cvc.unwrap_or_default(), - card_issuer: card.card_brand, + card_issuer: card.card_brand.or(payment_method.issuer_name), nick_name: card.nick_name.map(masking::Secret::new), - card_network: None, - card_type: None, + card_network, + card_type: payment_method + .payment_method_type + .map(|payment_method_type| payment_method_type.to_string()), card_issuing_country: None, bank_code: None, }; @@ -3578,31 +3593,16 @@ pub fn get_key_params_for_surcharge_details( )> { match payment_method_data { api_models::payments::PaymentMethodData::Card(card) => { - let card_type = card - .card_type - .get_required_value("payment_method_data.card.card_type")?; let card_network = card .card_network .get_required_value("payment_method_data.card.card_network")?; - match card_type.to_lowercase().as_str() { - "credit" => Ok(( - common_enums::PaymentMethod::Card, - common_enums::PaymentMethodType::Credit, - Some(card_network), - )), - "debit" => Ok(( - common_enums::PaymentMethod::Card, - common_enums::PaymentMethodType::Debit, - Some(card_network), - )), - _ => { - logger::debug!("Invalid Card type found in payment confirm call, hence surcharge not applicable"); - Err(errors::ApiErrorResponse::InvalidDataValue { - field_name: "payment_method_data.card.card_type", - } - .into()) - } - } + // surcharge generated will always be same for credit as well as debit + // since surcharge conditions cannot be defined on card_type + Ok(( + common_enums::PaymentMethod::Card, + common_enums::PaymentMethodType::Credit, + Some(card_network), + )) } api_models::payments::PaymentMethodData::CardRedirect(card_redirect_data) => Ok(( common_enums::PaymentMethod::CardRedirect, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 809c9e925de0..e78ef8cf12a6 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -157,7 +157,6 @@ pub trait Domain: Send + Sync { &'a self, _state: &AppState, _payment_data: &mut PaymentData, - _request: &R, _merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index d718db79a6d0..d174cf60925e 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -446,6 +446,21 @@ impl ) .await?; + let additional_pm_data = request + .payment_method_data + .as_ref() + .async_map(|payment_method_data| async { + helpers::get_additional_payment_data(payment_method_data, &*state.store).await + }) + .await; + let payment_method_data_after_card_bin_call = request + .payment_method_data + .as_ref() + .zip(additional_pm_data) + .map(|(payment_method_data, additional_payment_data)| { + payment_method_data.apply_additional_payment_data(additional_payment_data) + }); + let payment_data = PaymentData { flow: PhantomData, payment_intent, @@ -462,7 +477,7 @@ impl billing: billing_address.as_ref().map(|a| a.into()), }, confirm: request.confirm, - payment_method_data: request.payment_method_data.clone(), + payment_method_data: payment_method_data_after_card_bin_call, force_sync: None, refunds: vec![], disputes: vec![], @@ -591,10 +606,9 @@ impl Domain, - request: &api::PaymentsRequest, _merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { - populate_surcharge_details(state, payment_data, request).await + populate_surcharge_details(state, payment_data).await } } diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index ac387076d1d1..512c852a5fcb 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -7,7 +7,7 @@ use data_models::{mandates::MandateData, payments::payment_attempt::PaymentAttem use diesel_models::ephemeral_key; use error_stack::{self, ResultExt}; use router_derive::PaymentOperation; -use router_env::{instrument, tracing}; +use router_env::{instrument, logger, tracing}; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ @@ -167,7 +167,7 @@ impl ) .await?; - let payment_attempt_new = Self::make_payment_attempt( + let (payment_attempt_new, additional_payment_data) = Self::make_payment_attempt( &payment_id, merchant_id, money, @@ -290,6 +290,16 @@ impl surcharge_details.get_surcharge_details_object(payment_attempt.amount) }); + let payment_method_data_after_card_bin_call = request + .payment_method_data + .as_ref() + .zip(additional_payment_data) + .map(|(payment_method_data, additional_payment_data)| { + payment_method_data.apply_additional_payment_data(additional_payment_data) + }); + + logger::debug!(payment_method_data_after_card_bin_call=?payment_method_data_after_card_bin_call); + let payment_data = PaymentData { flow: PhantomData, payment_intent, @@ -306,7 +316,7 @@ impl billing: billing_address.as_ref().map(|a| a.into()), }, confirm: request.confirm, - payment_method_data: request.payment_method_data.clone(), + payment_method_data: payment_method_data_after_card_bin_call, refunds: vec![], disputes: vec![], attempts: None, @@ -602,7 +612,10 @@ impl PaymentCreate { request: &api::PaymentsRequest, browser_info: Option, state: &AppState, - ) -> RouterResult { + ) -> RouterResult<( + storage::PaymentAttemptNew, + Option, + )> { let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now()); let status = helpers::payment_attempt_status_fsm(&request.payment_method_data, request.confirm); @@ -614,7 +627,8 @@ impl PaymentCreate { .async_map(|payment_method_data| async { helpers::get_additional_payment_data(payment_method_data, &*state.store).await }) - .await + .await; + let additional_pm_data_value = additional_pm_data .as_ref() .map(Encode::::encode_to_value) .transpose() @@ -629,35 +643,38 @@ impl PaymentCreate { utils::get_payment_attempt_id(payment_id, 1) }; - Ok(storage::PaymentAttemptNew { - payment_id: payment_id.to_string(), - merchant_id: merchant_id.to_string(), - attempt_id, - status, - currency, - amount: amount.into(), - payment_method, - capture_method: request.capture_method, - capture_on: request.capture_on, - confirm: request.confirm.unwrap_or(false), - created_at, - modified_at, - last_synced, - authentication_type: request.authentication_type, - browser_info, - payment_experience: request.payment_experience, - payment_method_type, - payment_method_data: additional_pm_data, - amount_to_capture: request.amount_to_capture, - payment_token: request.payment_token.clone(), - mandate_id: request.mandate_id.clone(), - business_sub_label: request.business_sub_label.clone(), - mandate_details: request - .mandate_data - .as_ref() - .and_then(|inner| inner.mandate_type.clone().map(Into::into)), - ..storage::PaymentAttemptNew::default() - }) + Ok(( + storage::PaymentAttemptNew { + payment_id: payment_id.to_string(), + merchant_id: merchant_id.to_string(), + attempt_id, + status, + currency, + amount: amount.into(), + payment_method, + capture_method: request.capture_method, + capture_on: request.capture_on, + confirm: request.confirm.unwrap_or(false), + created_at, + modified_at, + last_synced, + authentication_type: request.authentication_type, + browser_info, + payment_experience: request.payment_experience, + payment_method_type, + payment_method_data: additional_pm_data_value, + amount_to_capture: request.amount_to_capture, + payment_token: request.payment_token.clone(), + mandate_id: request.mandate_id.clone(), + business_sub_label: request.business_sub_label.clone(), + mandate_details: request + .mandate_data + .as_ref() + .and_then(|inner| inner.mandate_type.clone().map(Into::into)), + ..storage::PaymentAttemptNew::default() + }, + additional_pm_data, + )) } #[instrument(skip_all)] From 8043b991c59584ad4925c5490c7ec2bb2ac21f6f Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Sat, 2 Dec 2023 12:42:28 +0530 Subject: [PATCH 4/9] revert few changes --- crates/router/src/core/payments.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b0e00bda5ca5..67b1dbcc98b2 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -451,6 +451,12 @@ where .into()); } } + (None, Some(_calculated_surcharge_details)) => { + return Err(errors::ApiErrorResponse::MissingRequiredField { + field_name: "surcharge_details", + } + .into()); + } (Some(request_surcharge_details), None) => { if request_surcharge_details.is_surcharge_zero() { return Ok(()); @@ -461,9 +467,6 @@ where .into()); } } - (None, Some(calculated_surcharge_details)) => { - payment_data.surcharge_details = Some(calculated_surcharge_details); - } (None, None) => return Ok(()), }; } else { From 1133a530ca5d64a15e4aa3d97eaea3106576d488 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Sun, 3 Dec 2023 07:21:00 +0000 Subject: [PATCH 5/9] chore: run formatter --- crates/router/src/core/payments.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 379d3456057f..97a260bda6ed 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -13,10 +13,7 @@ pub mod types; use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter}; -use api_models::{ - self, enums, - payments::HeaderPayload, -}; +use api_models::{self, enums, payments::HeaderPayload}; use common_utils::{ext_traits::AsyncExt, pii, types::Surcharge}; use data_models::mandates::MandateData; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; From cc72c91bc7c0f380e8d5e2a2be1f1d3b7437bba0 Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Mon, 4 Dec 2023 14:14:58 +0530 Subject: [PATCH 6/9] address comments --- crates/router/src/core/payments/helpers.rs | 21 +++---------------- .../payments/operations/payment_create.rs | 2 -- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index faacb8ed1375..042356a338d2 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1486,19 +1486,6 @@ pub async fn retrieve_card_with_permanent_token( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to fetch card information from the permanent locker")?; - let payment_method = state - .store - .find_payment_method(token) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch payment method")?; - let card_network = payment_method - .scheme - .map(|scheme| scheme.parse_enum("CardNetwork")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed deserilaize card network")?; - let name_on_card = if let Some(name_on_card) = card.name_on_card.clone() { if card.name_on_card.unwrap_or_default().expose().is_empty() { card_token_data @@ -1519,12 +1506,10 @@ pub async fn retrieve_card_with_permanent_token( card_exp_month: card.card_exp_month, card_exp_year: card.card_exp_year, card_cvc: card_cvc.unwrap_or_default(), - card_issuer: card.card_brand.or(payment_method.issuer_name), + card_issuer: card.card_brand, nick_name: card.nick_name.map(masking::Secret::new), - card_network, - card_type: payment_method - .payment_method_type - .map(|payment_method_type| payment_method_type.to_string()), + card_network: None, + card_type: None, card_issuing_country: None, bank_code: None, }; diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 9923bd4424bd..6d83b946448a 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -298,8 +298,6 @@ impl payment_method_data.apply_additional_payment_data(additional_payment_data) }); - logger::debug!(payment_method_data_after_card_bin_call=?payment_method_data_after_card_bin_call); - let payment_data = PaymentData { flow: PhantomData, payment_intent, From 09adb026e242c181b7ca54754ce42b2c74313155 Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Mon, 4 Dec 2023 14:28:11 +0530 Subject: [PATCH 7/9] cargo clippy --- crates/router/src/core/payments/operations/payment_create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 6d83b946448a..3664c97be493 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -7,7 +7,7 @@ use data_models::{mandates::MandateData, payments::payment_attempt::PaymentAttem use diesel_models::ephemeral_key; use error_stack::{self, ResultExt}; use router_derive::PaymentOperation; -use router_env::{instrument, logger, tracing}; +use router_env::{instrument, tracing}; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ From e428e8e1230de9179e092f7f2d3461d185d65396 Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Mon, 4 Dec 2023 15:18:32 +0530 Subject: [PATCH 8/9] add display_total_surcharge_amount field in SurchargeDetailsResponse --- crates/api_models/src/payment_methods.rs | 2 ++ .../src/core/payment_methods/surcharge_decision_configs.rs | 1 - crates/router/src/core/payments/types.rs | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 3343becaaae6..b3c6b049d5d9 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -342,6 +342,8 @@ pub struct SurchargeDetailsResponse { pub display_surcharge_amount: f64, /// tax on surcharge amount for this payment pub display_tax_on_surcharge_amount: f64, + /// sum of display_surcharge_amount and display_tax_on_surcharge_amount + pub display_total_surcharge_amount: f64, /// sum of original amount, pub display_final_amount: f64, } diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs index 38ae71754b87..c179bdb3050d 100644 --- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs +++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs @@ -103,7 +103,6 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( Some(card_network_type.card_network.clone()); let surcharge_output = execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; - // let surcharge_details = card_network_type.surcharge_details = surcharge_output .surcharge_details .map(|surcharge_details| { diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs index 7a50254e6f05..f97cdc17d724 100644 --- a/crates/router/src/core/payments/types.rs +++ b/crates/router/src/core/payments/types.rs @@ -219,6 +219,8 @@ impl ForeignTryFrom<(&SurchargeDetails, &PaymentAttempt)> for SurchargeDetailsRe tax_on_surcharge: surcharge_details.tax_on_surcharge.clone(), display_surcharge_amount, display_tax_on_surcharge_amount, + display_total_surcharge_amount: display_surcharge_amount + + display_tax_on_surcharge_amount, display_final_amount, }) } From 69146fd73ea53bef2593f795f2c0ef9485061c35 Mon Sep 17 00:00:00 2001 From: hrithikeshvm Date: Mon, 4 Dec 2023 18:02:48 +0530 Subject: [PATCH 9/9] address clippy lints --- crates/api_models/src/payments.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 56730f6853dc..8fcb35ed07fc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -736,7 +736,7 @@ impl Card { } } -#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] +#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, Default)] #[serde(rename_all = "snake_case")] pub struct CardToken { /// The card holder's name