fix(core): return surcharge in payment method list response if passed in create request (#3363)

Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
This commit is contained in:
Hrithikesh
2024-01-25 12:38:50 +05:30
committed by GitHub
parent 777771048a
commit 3507ad60b2
4 changed files with 249 additions and 197 deletions

View File

@ -2015,9 +2015,9 @@ impl<F>
resource_id: types::ResponseId::NoResponseId, resource_id: types::ResponseId::NoResponseId,
redirection_data, redirection_data,
mandate_reference: None, mandate_reference: None,
connector_metadata: Some( connector_metadata: Some(serde_json::json!({
serde_json::json!({"three_ds_data":three_ds_data}), "three_ds_data": three_ds_data
), })),
network_txn_id: None, network_txn_id: None,
connector_response_reference_id, connector_response_reference_id,
incremental_authorization_allowed: None, incremental_authorization_allowed: None,

View File

@ -1864,9 +1864,6 @@ pub async fn call_surcharge_decision_management(
billing_address: Option<domain::Address>, billing_address: Option<domain::Address>,
response_payment_method_types: &mut [ResponsePaymentMethodsEnabled], response_payment_method_types: &mut [ResponsePaymentMethodsEnabled],
) -> errors::RouterResult<api_surcharge_decision_configs::MerchantSurchargeConfigs> { ) -> errors::RouterResult<api_surcharge_decision_configs::MerchantSurchargeConfigs> {
if payment_attempt.surcharge_amount.is_some() {
Ok(api_surcharge_decision_configs::MerchantSurchargeConfigs::default())
} else {
let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
.routing_algorithm .routing_algorithm
.clone() .clone()
@ -1907,7 +1904,6 @@ pub async fn call_surcharge_decision_management(
} }
Ok(merchant_sucharge_configs) Ok(merchant_sucharge_configs)
} }
}
pub async fn call_surcharge_decision_management_for_saved_card( pub async fn call_surcharge_decision_management_for_saved_card(
state: &routes::AppState, state: &routes::AppState,
@ -1917,9 +1913,6 @@ pub async fn call_surcharge_decision_management_for_saved_card(
payment_intent: storage::PaymentIntent, payment_intent: storage::PaymentIntent,
customer_payment_method_response: &mut api::CustomerPaymentMethodsListResponse, customer_payment_method_response: &mut api::CustomerPaymentMethodsListResponse,
) -> errors::RouterResult<()> { ) -> errors::RouterResult<()> {
if payment_attempt.surcharge_amount.is_some() {
Ok(())
} else {
let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
.routing_algorithm .routing_algorithm
.clone() .clone()
@ -1958,7 +1951,6 @@ pub async fn call_surcharge_decision_management_for_saved_card(
} }
Ok(()) Ok(())
} }
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn filter_payment_methods( pub async fn filter_payment_methods(

View File

@ -1,7 +1,8 @@
use std::sync::Arc;
use api_models::{ use api_models::{
payment_methods::SurchargeDetailsResponse, payment_methods::SurchargeDetailsResponse,
payments::Address, payments, routing,
routing,
surcharge_decision_configs::{self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord}, surcharge_decision_configs::{self, SurchargeDecisionConfigs, SurchargeDecisionManagerRecord},
}; };
use common_utils::{ext_traits::StringExt, static_cache::StaticCache, types as common_utils_types}; use common_utils::{ext_traits::StringExt, static_cache::StaticCache, types as common_utils_types};
@ -15,7 +16,10 @@ use router_env::{instrument, tracing};
use crate::{ use crate::{
core::payments::{types, PaymentData}, core::payments::{types, PaymentData},
db::StorageInterface, db::StorageInterface,
types::{storage as oss_storage, transformers::ForeignTryFrom}, types::{
storage::{self as oss_storage, payment_attempt::PaymentAttemptExt},
transformers::ForeignTryFrom,
},
}; };
static CONF_CACHE: StaticCache<VirInterpreterBackendCacheWrapper> = StaticCache::new(); static CONF_CACHE: StaticCache<VirInterpreterBackendCacheWrapper> = StaticCache::new();
use crate::{ use crate::{
@ -49,27 +53,71 @@ impl TryFrom<SurchargeDecisionManagerRecord> for VirInterpreterBackendCacheWrapp
} }
} }
enum SurchargeSource {
/// Surcharge will be generated through the surcharge rules
Generate(Arc<VirInterpreterBackendCacheWrapper>),
/// Surcharge is predefined by the merchant through payment create request
Predetermined(payments::RequestSurchargeDetails),
}
impl SurchargeSource {
pub fn generate_surcharge_details_and_populate_surcharge_metadata(
&self,
backend_input: &backend::BackendInput,
payment_attempt: &oss_storage::PaymentAttempt,
surcharge_metadata_and_key: (&mut types::SurchargeMetadata, types::SurchargeKey),
) -> ConditionalConfigResult<Option<types::SurchargeDetails>> {
match self {
Self::Generate(interpreter) => {
let surcharge_output = execute_dsl_and_get_conditional_config(
backend_input.clone(),
&interpreter.cached_alogorith,
)?;
Ok(surcharge_output
.surcharge_details
.map(|surcharge_details| {
get_surcharge_details_from_surcharge_output(
surcharge_details,
payment_attempt,
)
})
.transpose()?
.map(|surcharge_details| {
let (surcharge_metadata, surcharge_key) = surcharge_metadata_and_key;
surcharge_metadata
.insert_surcharge_details(surcharge_key, surcharge_details.clone());
surcharge_details
}))
}
Self::Predetermined(request_surcharge_details) => Ok(Some(
types::SurchargeDetails::from((request_surcharge_details, payment_attempt)),
)),
}
}
}
pub async fn perform_surcharge_decision_management_for_payment_method_list( pub async fn perform_surcharge_decision_management_for_payment_method_list(
state: &AppState, state: &AppState,
algorithm_ref: routing::RoutingAlgorithmRef, algorithm_ref: routing::RoutingAlgorithmRef,
payment_attempt: &oss_storage::PaymentAttempt, payment_attempt: &oss_storage::PaymentAttempt,
payment_intent: &oss_storage::PaymentIntent, payment_intent: &oss_storage::PaymentIntent,
billing_address: Option<Address>, billing_address: Option<payments::Address>,
response_payment_method_types: &mut [api_models::payment_methods::ResponsePaymentMethodsEnabled], response_payment_method_types: &mut [api_models::payment_methods::ResponsePaymentMethodsEnabled],
) -> ConditionalConfigResult<( ) -> ConditionalConfigResult<(
types::SurchargeMetadata, types::SurchargeMetadata,
surcharge_decision_configs::MerchantSurchargeConfigs, surcharge_decision_configs::MerchantSurchargeConfigs,
)> { )> {
let mut surcharge_metadata = types::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 {
return Ok((
surcharge_metadata,
surcharge_decision_configs::MerchantSurchargeConfigs::default(),
));
};
let (surcharge_source, merchant_surcharge_configs) = match (
payment_attempt.get_surcharge_details(),
algorithm_ref.surcharge_config_algo_id,
) {
(Some(request_surcharge_details), _) => (
SurchargeSource::Predetermined(request_surcharge_details),
surcharge_decision_configs::MerchantSurchargeConfigs::default(),
),
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached( let key = ensure_algorithm_cached(
&*state.store, &*state.store,
&payment_attempt.merchant_id, &payment_attempt.merchant_id,
@ -81,12 +129,26 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list(
.retrieve(&key) .retrieve(&key)
.into_report() .into_report()
.change_context(ConfigError::CacheMiss) .change_context(ConfigError::CacheMiss)
.attach_printable("Unable to retrieve cached routing algorithm even after refresh")?; .attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;
let merchant_surcharge_config = cached_algo.merchant_surcharge_configs.clone();
(
SurchargeSource::Generate(cached_algo),
merchant_surcharge_config,
)
}
(None, None) => {
return Ok((
surcharge_metadata,
surcharge_decision_configs::MerchantSurchargeConfigs::default(),
))
}
};
let mut backend_input = let mut backend_input =
make_dsl_input_for_surcharge(payment_attempt, payment_intent, billing_address) make_dsl_input_for_surcharge(payment_attempt, payment_intent, billing_address)
.change_context(ConfigError::InputConstructionError)?; .change_context(ConfigError::InputConstructionError)?;
let interpreter = &cached_algo.cached_alogorith;
let merchant_surcharge_configs = cached_algo.merchant_surcharge_configs.clone();
for payment_methods_enabled in response_payment_method_types.iter_mut() { for payment_methods_enabled in response_payment_method_types.iter_mut() {
for payment_method_type_response in for payment_method_type_response in
@ -101,24 +163,21 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list(
for card_network_type in card_network_list.iter_mut() { for card_network_type in card_network_list.iter_mut() {
backend_input.payment_method.card_network = backend_input.payment_method.card_network =
Some(card_network_type.card_network.clone()); Some(card_network_type.card_network.clone());
let surcharge_output = let surcharge_details = surcharge_source
execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; .generate_surcharge_details_and_populate_surcharge_metadata(
// let surcharge_details = &backend_input,
card_network_type.surcharge_details = surcharge_output
.surcharge_details
.map(|surcharge_details| {
let surcharge_details = get_surcharge_details_from_surcharge_output(
surcharge_details,
payment_attempt, payment_attempt,
)?; (
surcharge_metadata.insert_surcharge_details( &mut surcharge_metadata,
types::SurchargeKey::PaymentMethodData( types::SurchargeKey::PaymentMethodData(
payment_methods_enabled.payment_method, payment_methods_enabled.payment_method,
payment_method_type_response.payment_method_type, payment_method_type_response.payment_method_type,
Some(card_network_type.card_network.clone()), Some(card_network_type.card_network.clone()),
), ),
surcharge_details.clone(), ),
); )?;
card_network_type.surcharge_details = surcharge_details
.map(|surcharge_details| {
SurchargeDetailsResponse::foreign_try_from(( SurchargeDetailsResponse::foreign_try_from((
&surcharge_details, &surcharge_details,
payment_attempt, payment_attempt,
@ -130,23 +189,21 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list(
.transpose()?; .transpose()?;
} }
} else { } else {
let surcharge_output = let surcharge_details = surcharge_source
execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; .generate_surcharge_details_and_populate_surcharge_metadata(
payment_method_type_response.surcharge_details = surcharge_output &backend_input,
.surcharge_details
.map(|surcharge_details| {
let surcharge_details = get_surcharge_details_from_surcharge_output(
surcharge_details,
payment_attempt, payment_attempt,
)?; (
surcharge_metadata.insert_surcharge_details( &mut surcharge_metadata,
types::SurchargeKey::PaymentMethodData( types::SurchargeKey::PaymentMethodData(
payment_methods_enabled.payment_method, payment_methods_enabled.payment_method,
payment_method_type_response.payment_method_type, payment_method_type_response.payment_method_type,
None, None,
), ),
surcharge_details.clone(), ),
); )?;
payment_method_type_response.surcharge_details = surcharge_details
.map(|surcharge_details| {
SurchargeDetailsResponse::foreign_try_from(( SurchargeDetailsResponse::foreign_try_from((
&surcharge_details, &surcharge_details,
payment_attempt, payment_attempt,
@ -173,12 +230,14 @@ where
{ {
let mut surcharge_metadata = let mut surcharge_metadata =
types::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 { let surcharge_source = match (
id payment_data.payment_attempt.get_surcharge_details(),
} else { algorithm_ref.surcharge_config_algo_id,
return Ok(surcharge_metadata); ) {
}; (Some(request_surcharge_details), _) => {
SurchargeSource::Predetermined(request_surcharge_details)
}
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached( let key = ensure_algorithm_cached(
&*state.store, &*state.store,
&payment_data.payment_attempt.merchant_id, &payment_data.payment_attempt.merchant_id,
@ -190,34 +249,35 @@ where
.retrieve(&key) .retrieve(&key)
.into_report() .into_report()
.change_context(ConfigError::CacheMiss) .change_context(ConfigError::CacheMiss)
.attach_printable("Unable to retrieve cached routing algorithm even after refresh")?; .attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;
SurchargeSource::Generate(cached_algo)
}
(None, None) => return Ok(surcharge_metadata),
};
let mut backend_input = make_dsl_input_for_surcharge( let mut backend_input = make_dsl_input_for_surcharge(
&payment_data.payment_attempt, &payment_data.payment_attempt,
&payment_data.payment_intent, &payment_data.payment_intent,
payment_data.address.billing.clone(), payment_data.address.billing.clone(),
) )
.change_context(ConfigError::InputConstructionError)?; .change_context(ConfigError::InputConstructionError)?;
let interpreter = &cached_algo.cached_alogorith;
for payment_method_type in payment_method_type_list { for payment_method_type in payment_method_type_list {
backend_input.payment_method.payment_method_type = Some(*payment_method_type); backend_input.payment_method.payment_method_type = Some(*payment_method_type);
// in case of session flow, payment_method will always be wallet // in case of session flow, payment_method will always be wallet
backend_input.payment_method.payment_method = Some(payment_method_type.to_owned().into()); backend_input.payment_method.payment_method = Some(payment_method_type.to_owned().into());
let surcharge_output = surcharge_source.generate_surcharge_details_and_populate_surcharge_metadata(
execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; &backend_input,
if let Some(surcharge_details) = surcharge_output.surcharge_details {
let surcharge_details = get_surcharge_details_from_surcharge_output(
surcharge_details,
&payment_data.payment_attempt, &payment_data.payment_attempt,
)?; (
surcharge_metadata.insert_surcharge_details( &mut surcharge_metadata,
types::SurchargeKey::PaymentMethodData( types::SurchargeKey::PaymentMethodData(
payment_method_type.to_owned().into(), payment_method_type.to_owned().into(),
*payment_method_type, *payment_method_type,
None, None,
), ),
surcharge_details, ),
); )?;
}
} }
Ok(surcharge_metadata) Ok(surcharge_metadata)
} }
@ -229,12 +289,14 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
customer_payment_method_list: &mut [api_models::payment_methods::CustomerPaymentMethod], customer_payment_method_list: &mut [api_models::payment_methods::CustomerPaymentMethod],
) -> ConditionalConfigResult<types::SurchargeMetadata> { ) -> ConditionalConfigResult<types::SurchargeMetadata> {
let mut surcharge_metadata = types::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 { let surcharge_source = match (
id payment_attempt.get_surcharge_details(),
} else { algorithm_ref.surcharge_config_algo_id,
return Ok(surcharge_metadata); ) {
}; (Some(request_surcharge_details), _) => {
SurchargeSource::Predetermined(request_surcharge_details)
}
(None, Some(algorithm_id)) => {
let key = ensure_algorithm_cached( let key = ensure_algorithm_cached(
&*state.store, &*state.store,
&payment_attempt.merchant_id, &payment_attempt.merchant_id,
@ -246,10 +308,15 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
.retrieve(&key) .retrieve(&key)
.into_report() .into_report()
.change_context(ConfigError::CacheMiss) .change_context(ConfigError::CacheMiss)
.attach_printable("Unable to retrieve cached routing algorithm even after refresh")?; .attach_printable(
"Unable to retrieve cached routing algorithm even after refresh",
)?;
SurchargeSource::Generate(cached_algo)
}
(None, None) => return Ok(surcharge_metadata),
};
let mut backend_input = make_dsl_input_for_surcharge(payment_attempt, payment_intent, None) let mut backend_input = make_dsl_input_for_surcharge(payment_attempt, payment_intent, None)
.change_context(ConfigError::InputConstructionError)?; .change_context(ConfigError::InputConstructionError)?;
let interpreter = &cached_algo.cached_alogorith;
for customer_payment_method in customer_payment_method_list.iter_mut() { for customer_payment_method in customer_payment_method_list.iter_mut() {
backend_input.payment_method.payment_method = Some(customer_payment_method.payment_method); backend_input.payment_method.payment_method = Some(customer_payment_method.payment_method);
@ -266,23 +333,22 @@ pub async fn perform_surcharge_decision_management_for_saved_cards(
.change_context(ConfigError::DslExecutionError) .change_context(ConfigError::DslExecutionError)
}) })
.transpose()?; .transpose()?;
let surcharge_output = let surcharge_details = surcharge_source
execute_dsl_and_get_conditional_config(backend_input.clone(), interpreter)?; .generate_surcharge_details_and_populate_surcharge_metadata(
if let Some(surcharge_details_output) = surcharge_output.surcharge_details { &backend_input,
let surcharge_details = get_surcharge_details_from_surcharge_output(
surcharge_details_output,
payment_attempt, payment_attempt,
)?; (
surcharge_metadata.insert_surcharge_details( &mut surcharge_metadata,
types::SurchargeKey::Token(customer_payment_method.payment_token.clone()), types::SurchargeKey::Token(customer_payment_method.payment_token.clone()),
surcharge_details.clone(), ),
); )?;
customer_payment_method.surcharge_details = Some( customer_payment_method.surcharge_details = surcharge_details
.map(|surcharge_details| {
SurchargeDetailsResponse::foreign_try_from((&surcharge_details, payment_attempt)) SurchargeDetailsResponse::foreign_try_from((&surcharge_details, payment_attempt))
.into_report() .into_report()
.change_context(ConfigError::DslParsingError)?, .change_context(ConfigError::DslParsingError)
); })
} .transpose()?;
} }
Ok(surcharge_metadata) Ok(surcharge_metadata)
} }

View File

@ -366,7 +366,6 @@ where
call_surcharge_decision_management_for_session_flow( call_surcharge_decision_management_for_session_flow(
state, state,
&merchant_account, &merchant_account,
&business_profile,
&mut payment_data, &mut payment_data,
&connectors, &connectors,
) )
@ -599,7 +598,6 @@ pub fn get_connector_data(
pub async fn call_surcharge_decision_management_for_session_flow<O>( pub async fn call_surcharge_decision_management_for_session_flow<O>(
state: &AppState, state: &AppState,
merchant_account: &domain::MerchantAccount, merchant_account: &domain::MerchantAccount,
business_profile: &diesel_models::business_profile::BusinessProfile,
payment_data: &mut PaymentData<O>, payment_data: &mut PaymentData<O>,
session_connector_data: &[api::SessionConnectorData], session_connector_data: &[api::SessionConnectorData],
) -> RouterResult<Option<api::SessionSurchargeDetails>> ) -> RouterResult<Option<api::SessionSurchargeDetails>>
@ -644,10 +642,6 @@ where
.change_context(errors::ApiErrorResponse::InternalServerError) .change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error performing surcharge decision operation")?; .attach_printable("error performing surcharge decision operation")?;
surcharge_results
.persist_individual_surcharge_details_in_redis(state, business_profile)
.await?;
Ok(if surcharge_results.is_empty_result() { Ok(if surcharge_results.is_empty_result() {
None None
} else { } else {