feat(routing): Contract based routing integration (#6761)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Sarthak Soni
2025-02-06 19:16:00 +05:30
committed by GitHub
parent a6367d92f6
commit 60ddddf24a
23 changed files with 1972 additions and 214 deletions

View File

@ -2,6 +2,7 @@
//!
//! Functions that are used to perform the retrieval of merchant's
//! routing dict, configs, defaults
use std::fmt::Debug;
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
use std::str::FromStr;
#[cfg(any(feature = "dynamic_routing", feature = "v1"))]
@ -18,7 +19,10 @@ use diesel_models::dynamic_routing_stats::DynamicRoutingStatsNew;
use diesel_models::routing_algorithm;
use error_stack::ResultExt;
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
use external_services::grpc_client::dynamic_routing::success_rate_client::SuccessBasedDynamicRouting;
use external_services::grpc_client::dynamic_routing::{
contract_routing_client::ContractBasedDynamicRouting,
success_rate_client::SuccessBasedDynamicRouting,
};
#[cfg(feature = "v1")]
use hyperswitch_domain_models::api::ApplicationResponse;
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
@ -26,7 +30,7 @@ use router_env::logger;
#[cfg(any(feature = "dynamic_routing", feature = "v1"))]
use router_env::{instrument, tracing};
use rustc_hash::FxHashSet;
use storage_impl::redis::cache;
use storage_impl::redis::cache::{self, Cacheable};
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
use crate::db::errors::StorageErrorExt;
@ -45,6 +49,8 @@ pub const SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
"Success rate based dynamic routing algorithm";
pub const ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
"Elimination based dynamic routing algorithm";
pub const CONTRACT_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
"Contract based dynamic routing algorithm";
/// Provides us with all the configured configs of the Merchant in the ascending time configured
/// manner and chooses the first of them
@ -562,78 +568,146 @@ pub fn get_default_config_key(
}
}
/// Retrieves cached success_based routing configs specific to tenant and profile
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
pub async fn get_cached_success_based_routing_config_for_profile(
state: &SessionState,
key: &str,
) -> Option<Arc<routing_types::SuccessBasedRoutingConfig>> {
cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
.get_val::<Arc<routing_types::SuccessBasedRoutingConfig>>(cache::CacheKey {
key: key.to_string(),
prefix: state.tenant.redis_key_prefix.clone(),
})
.await
#[async_trait::async_trait]
pub trait DynamicRoutingCache {
async fn get_cached_dynamic_routing_config_for_profile(
state: &SessionState,
key: &str,
) -> Option<Arc<Self>>;
async fn refresh_dynamic_routing_cache<T, F, Fut>(
state: &SessionState,
key: &str,
func: F,
) -> RouterResult<T>
where
F: FnOnce() -> Fut + Send,
T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send;
}
/// Refreshes the cached success_based routing configs specific to tenant and profile
#[cfg(feature = "v1")]
pub async fn refresh_success_based_routing_cache(
state: &SessionState,
key: &str,
success_based_routing_config: routing_types::SuccessBasedRoutingConfig,
) -> Arc<routing_types::SuccessBasedRoutingConfig> {
let config = Arc::new(success_based_routing_config);
cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
.push(
cache::CacheKey {
#[async_trait::async_trait]
impl DynamicRoutingCache for routing_types::SuccessBasedRoutingConfig {
async fn get_cached_dynamic_routing_config_for_profile(
state: &SessionState,
key: &str,
) -> Option<Arc<Self>> {
cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
.get_val::<Arc<Self>>(cache::CacheKey {
key: key.to_string(),
prefix: state.tenant.redis_key_prefix.clone(),
},
config.clone(),
})
.await
}
async fn refresh_dynamic_routing_cache<T, F, Fut>(
state: &SessionState,
key: &str,
func: F,
) -> RouterResult<T>
where
F: FnOnce() -> Fut + Send,
T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send,
{
cache::get_or_populate_in_memory(
state.store.get_cache_store().as_ref(),
key,
func,
&cache::SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE,
)
.await;
config
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to populate SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE")
}
}
/// Checked fetch of success based routing configs
#[async_trait::async_trait]
impl DynamicRoutingCache for routing_types::ContractBasedRoutingConfig {
async fn get_cached_dynamic_routing_config_for_profile(
state: &SessionState,
key: &str,
) -> Option<Arc<Self>> {
cache::CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE
.get_val::<Arc<Self>>(cache::CacheKey {
key: key.to_string(),
prefix: state.tenant.redis_key_prefix.clone(),
})
.await
}
async fn refresh_dynamic_routing_cache<T, F, Fut>(
state: &SessionState,
key: &str,
func: F,
) -> RouterResult<T>
where
F: FnOnce() -> Fut + Send,
T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone,
Fut: futures::Future<Output = errors::CustomResult<T, errors::StorageError>> + Send,
{
cache::get_or_populate_in_memory(
state.store.get_cache_store().as_ref(),
key,
func,
&cache::CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to populate CONTRACT_BASED_DYNAMIC_ALGORITHM_CACHE")
}
}
/// Cfetch dynamic routing configs
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn fetch_success_based_routing_configs(
pub async fn fetch_dynamic_routing_configs<T>(
state: &SessionState,
business_profile: &domain::Profile,
success_based_routing_id: id_type::RoutingId,
) -> RouterResult<routing_types::SuccessBasedRoutingConfig> {
profile_id: &id_type::ProfileId,
routing_id: id_type::RoutingId,
) -> RouterResult<T>
where
T: serde::de::DeserializeOwned
+ Clone
+ DynamicRoutingCache
+ Cacheable
+ serde::Serialize
+ Debug,
{
let key = format!(
"{}_{}",
business_profile.get_id().get_string_repr(),
success_based_routing_id.get_string_repr()
profile_id.get_string_repr(),
routing_id.get_string_repr()
);
if let Some(config) =
get_cached_success_based_routing_config_for_profile(state, key.as_str()).await
T::get_cached_dynamic_routing_config_for_profile(state, key.as_str()).await
{
Ok(config.as_ref().clone())
} else {
let success_rate_algorithm = state
.store
.find_routing_algorithm_by_profile_id_algorithm_id(
business_profile.get_id(),
&success_based_routing_id,
)
.await
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)
.attach_printable("unable to retrieve success_rate_algorithm for profile from db")?;
let func = || async {
let routing_algorithm = state
.store
.find_routing_algorithm_by_profile_id_algorithm_id(profile_id, &routing_id)
.await
.change_context(errors::StorageError::ValueNotFound(
"RoutingAlgorithm".to_string(),
))
.attach_printable("unable to retrieve routing_algorithm for profile from db")?;
let success_rate_config = success_rate_algorithm
.algorithm_data
.parse_value::<routing_types::SuccessBasedRoutingConfig>("SuccessBasedRoutingConfig")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to parse success_based_routing_config struct")?;
let dynamic_routing_config = routing_algorithm
.algorithm_data
.parse_value::<T>("dynamic_routing_config")
.change_context(errors::StorageError::DeserializationFailed)
.attach_printable("unable to parse dynamic_routing_config")?;
refresh_success_based_routing_cache(state, key.as_str(), success_rate_config.clone()).await;
Ok(dynamic_routing_config)
};
Ok(success_rate_config)
let dynamic_routing_config =
T::refresh_dynamic_routing_cache(state, key.as_str(), func).await?;
Ok(dynamic_routing_config)
}
}
@ -644,20 +718,11 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
state: &SessionState,
payment_attempt: &storage::PaymentAttempt,
routable_connectors: Vec<routing_types::RoutableConnectorChoice>,
business_profile: &domain::Profile,
success_based_routing_config_params_interpolator: SuccessBasedRoutingConfigParamsInterpolator,
profile_id: &id_type::ProfileId,
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
dynamic_routing_config_params_interpolator: DynamicRoutingConfigParamsInterpolator,
) -> RouterResult<()> {
let success_based_dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef =
business_profile
.dynamic_routing_algorithm
.clone()
.map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
.transpose()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to deserialize DynamicRoutingAlgorithmRef from JSON")?
.unwrap_or_default();
let success_based_algo_ref = success_based_dynamic_routing_algo_ref
let success_based_algo_ref = dynamic_routing_algo_ref
.success_based_algorithm
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("success_based_algorithm not found in dynamic_routing_algorithm from business_profile table")?;
@ -678,22 +743,23 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
},
)?;
let success_based_routing_configs = fetch_success_based_routing_configs(
state,
business_profile,
success_based_algo_ref
.algorithm_id_with_timestamp
.algorithm_id
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"success_based_routing_algorithm_id not found in business_profile",
)?,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to retrieve success_rate based dynamic routing configs")?;
let success_based_routing_configs =
fetch_dynamic_routing_configs::<routing_types::SuccessBasedRoutingConfig>(
state,
profile_id,
success_based_algo_ref
.algorithm_id_with_timestamp
.algorithm_id
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"success_based_routing_algorithm_id not found in business_profile",
)?,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to retrieve success_rate based dynamic routing configs")?;
let success_based_routing_config_params = success_based_routing_config_params_interpolator
let success_based_routing_config_params = dynamic_routing_config_params_interpolator
.get_string_val(
success_based_routing_configs
.params
@ -704,7 +770,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
let success_based_connectors = client
.calculate_entity_and_global_success_rate(
business_profile.get_id().get_string_repr().into(),
profile_id.get_string_repr().into(),
success_based_routing_configs.clone(),
success_based_routing_config_params.clone(),
routable_connectors.clone(),
@ -717,7 +783,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
)?;
let payment_status_attribute =
get_desired_payment_status_for_success_routing_metrics(payment_attempt.status);
get_desired_payment_status_for_dynamic_routing_metrics(payment_attempt.status);
let first_merchant_success_based_connector = &success_based_connectors
.entity_scores_with_labels
@ -743,7 +809,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
"unable to fetch the first global connector from list of connectors obtained from dynamic routing service",
)?;
let outcome = get_success_based_metrics_outcome_for_payment(
let outcome = get_dynamic_routing_based_metrics_outcome_for_payment(
payment_status_attribute,
payment_connector.to_string(),
first_merchant_success_based_connector_label.to_string(),
@ -852,7 +918,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
client
.update_success_rate(
business_profile.get_id().get_string_repr().into(),
profile_id.get_string_repr().into(),
success_based_routing_configs,
success_based_routing_config_params,
vec![routing_types::RoutableConnectorChoiceWithStatus::new(
@ -880,8 +946,212 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
}
}
/// metrics for contract based dynamic routing
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
fn get_desired_payment_status_for_success_routing_metrics(
#[instrument(skip_all)]
pub async fn push_metrics_with_update_window_for_contract_based_routing(
state: &SessionState,
payment_attempt: &storage::PaymentAttempt,
routable_connectors: Vec<routing_types::RoutableConnectorChoice>,
profile_id: &id_type::ProfileId,
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
_dynamic_routing_config_params_interpolator: DynamicRoutingConfigParamsInterpolator,
) -> RouterResult<()> {
let contract_routing_algo_ref = dynamic_routing_algo_ref
.contract_based_routing
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("contract_routing_algorithm not found in dynamic_routing_algorithm from business_profile table")?;
if contract_routing_algo_ref.enabled_feature != routing_types::DynamicRoutingFeatures::None {
let client = state
.grpc_client
.dynamic_routing
.contract_based_client
.clone()
.ok_or(errors::ApiErrorResponse::GenericNotFoundError {
message: "contract_routing gRPC client not found".to_string(),
})?;
let payment_connector = &payment_attempt.connector.clone().ok_or(
errors::ApiErrorResponse::GenericNotFoundError {
message: "unable to derive payment connector from payment attempt".to_string(),
},
)?;
let contract_based_routing_config =
fetch_dynamic_routing_configs::<routing_types::ContractBasedRoutingConfig>(
state,
profile_id,
contract_routing_algo_ref
.algorithm_id_with_timestamp
.algorithm_id
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"contract_based_routing_algorithm_id not found in business_profile",
)?,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to retrieve contract based dynamic routing configs")?;
let mut existing_label_info = None;
contract_based_routing_config
.label_info
.as_ref()
.map(|label_info_vec| {
for label_info in label_info_vec {
if Some(&label_info.mca_id) == payment_attempt.merchant_connector_id.as_ref() {
existing_label_info = Some(label_info.clone());
}
}
});
let final_label_info = existing_label_info
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to get LabelInformation from ContractBasedRoutingConfig")?;
logger::debug!(
"contract based routing: matched LabelInformation - {:?}",
final_label_info
);
let request_label_info = routing_types::LabelInformation {
label: format!(
"{}:{}",
final_label_info.label.clone(),
final_label_info.mca_id.get_string_repr()
),
target_count: final_label_info.target_count,
target_time: final_label_info.target_time,
mca_id: final_label_info.mca_id.to_owned(),
};
let payment_status_attribute =
get_desired_payment_status_for_dynamic_routing_metrics(payment_attempt.status);
if payment_status_attribute == common_enums::AttemptStatus::Charged {
client
.update_contracts(
profile_id.get_string_repr().into(),
vec![request_label_info],
"".to_string(),
vec![],
state.get_grpc_headers(),
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"unable to update contract based routing window in dynamic routing service",
)?;
}
let contract_scores = client
.calculate_contract_score(
profile_id.get_string_repr().into(),
contract_based_routing_config.clone(),
"".to_string(),
routable_connectors.clone(),
state.get_grpc_headers(),
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"unable to calculate/fetch contract scores from dynamic routing service",
)?;
let first_contract_based_connector = &contract_scores
.labels_with_score
.first()
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable(
"unable to fetch the first connector from list of connectors obtained from dynamic routing service",
)?;
let (first_contract_based_connector, connector_score, current_payment_cnt) = (first_contract_based_connector.label
.split_once(':')
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable(format!(
"unable to split connector_name and mca_id from the first connector {:?} obtained from dynamic routing service",
first_contract_based_connector
))?
.0, first_contract_based_connector.score, first_contract_based_connector.current_count );
core_metrics::DYNAMIC_CONTRACT_BASED_ROUTING.add(
1,
router_env::metric_attributes!(
(
"tenant",
state.tenant.tenant_id.get_string_repr().to_owned(),
),
(
"merchant_profile_id",
format!(
"{}:{}",
payment_attempt.merchant_id.get_string_repr(),
payment_attempt.profile_id.get_string_repr()
),
),
(
"contract_based_routing_connector",
first_contract_based_connector.to_string(),
),
(
"contract_based_routing_connector_score",
connector_score.to_string(),
),
(
"current_payment_count_contract_based_routing_connector",
current_payment_cnt.to_string(),
),
("payment_connector", payment_connector.to_string()),
(
"currency",
payment_attempt
.currency
.map_or_else(|| "None".to_string(), |currency| currency.to_string()),
),
(
"payment_method",
payment_attempt.payment_method.map_or_else(
|| "None".to_string(),
|payment_method| payment_method.to_string(),
),
),
(
"payment_method_type",
payment_attempt.payment_method_type.map_or_else(
|| "None".to_string(),
|payment_method_type| payment_method_type.to_string(),
),
),
(
"capture_method",
payment_attempt.capture_method.map_or_else(
|| "None".to_string(),
|capture_method| capture_method.to_string(),
),
),
(
"authentication_type",
payment_attempt.authentication_type.map_or_else(
|| "None".to_string(),
|authentication_type| authentication_type.to_string(),
),
),
("payment_status", payment_attempt.status.to_string()),
),
);
logger::debug!("successfully pushed contract_based_routing metrics");
Ok(())
} else {
Ok(())
}
}
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
fn get_desired_payment_status_for_dynamic_routing_metrics(
attempt_status: common_enums::AttemptStatus,
) -> common_enums::AttemptStatus {
match attempt_status {
@ -917,7 +1187,7 @@ fn get_desired_payment_status_for_success_routing_metrics(
}
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
fn get_success_based_metrics_outcome_for_payment(
fn get_dynamic_routing_based_metrics_outcome_for_payment(
payment_status_attribute: common_enums::AttemptStatus,
payment_connector: String,
first_success_based_connector: String,
@ -957,7 +1227,6 @@ pub async fn disable_dynamic_routing_algorithm(
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
let db = state.store.as_ref();
let key_manager_state = &state.into();
let timestamp = common_utils::date_time::now_unix_timestamp();
let profile_id = business_profile.get_id().clone();
let (algorithm_id, dynamic_routing_algorithm, cache_entries_to_redact) =
match dynamic_routing_type {
@ -988,14 +1257,12 @@ pub async fn disable_dynamic_routing_algorithm(
routing_types::DynamicRoutingAlgorithmRef {
success_based_algorithm: Some(routing_types::SuccessBasedAlgorithm {
algorithm_id_with_timestamp:
routing_types::DynamicAlgorithmWithTimestamp {
algorithm_id: None,
timestamp,
},
routing_types::DynamicAlgorithmWithTimestamp::new(None),
enabled_feature: routing_types::DynamicRoutingFeatures::None,
}),
elimination_routing_algorithm: dynamic_routing_algo_ref
.elimination_routing_algorithm,
contract_based_routing: dynamic_routing_algo_ref.contract_based_routing,
dynamic_routing_volume_split: dynamic_routing_algo_ref
.dynamic_routing_volume_split,
},
@ -1033,13 +1300,49 @@ pub async fn disable_dynamic_routing_algorithm(
elimination_routing_algorithm: Some(
routing_types::EliminationRoutingAlgorithm {
algorithm_id_with_timestamp:
routing_types::DynamicAlgorithmWithTimestamp {
algorithm_id: None,
timestamp,
},
routing_types::DynamicAlgorithmWithTimestamp::new(None),
enabled_feature: routing_types::DynamicRoutingFeatures::None,
},
),
contract_based_routing: dynamic_routing_algo_ref.contract_based_routing,
},
cache_entries_to_redact,
)
}
routing_types::DynamicRoutingType::ContractBasedRouting => {
let Some(algorithm_ref) = dynamic_routing_algo_ref.contract_based_routing else {
Err(errors::ApiErrorResponse::PreconditionFailed {
message: "Contract routing is already disabled".to_string(),
})?
};
let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id
else {
Err(errors::ApiErrorResponse::PreconditionFailed {
message: "Algorithm is already inactive".to_string(),
})?
};
let cache_key = format!(
"{}_{}",
business_profile.get_id().get_string_repr(),
algorithm_id.get_string_repr()
);
let cache_entries_to_redact =
vec![cache::CacheKind::ContractBasedDynamicRoutingCache(
cache_key.into(),
)];
(
algorithm_id,
routing_types::DynamicRoutingAlgorithmRef {
success_based_algorithm: dynamic_routing_algo_ref.success_based_algorithm,
elimination_routing_algorithm: dynamic_routing_algo_ref
.elimination_routing_algorithm,
dynamic_routing_volume_split: dynamic_routing_algo_ref
.dynamic_routing_volume_split,
contract_based_routing: Some(routing_types::ContractRoutingAlgorithm {
algorithm_id_with_timestamp:
routing_types::DynamicAlgorithmWithTimestamp::new(None),
enabled_feature: routing_types::DynamicRoutingFeatures::None,
}),
},
cache_entries_to_redact,
)
@ -1088,15 +1391,18 @@ pub async fn enable_dynamic_routing_algorithm(
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
dynamic_routing_type: routing_types::DynamicRoutingType,
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
let dynamic_routing = dynamic_routing_algo_ref.clone();
let mut dynamic_routing = dynamic_routing_algo_ref.clone();
match dynamic_routing_type {
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
dynamic_routing
.disable_algorithm_id(routing_types::DynamicRoutingType::ContractBasedRouting);
enable_specific_routing_algorithm(
state,
key_store,
business_profile,
feature_to_enable,
dynamic_routing_algo_ref,
dynamic_routing.clone(),
dynamic_routing_type,
dynamic_routing.success_based_algorithm,
)
@ -1108,12 +1414,18 @@ pub async fn enable_dynamic_routing_algorithm(
key_store,
business_profile,
feature_to_enable,
dynamic_routing_algo_ref,
dynamic_routing.clone(),
dynamic_routing_type,
dynamic_routing.elimination_routing_algorithm,
)
.await
}
routing_types::DynamicRoutingType::ContractBasedRouting => {
Err((errors::ApiErrorResponse::InvalidRequestData {
message: "Contract routing cannot be set as default".to_string(),
})
.into())
}
}
}
@ -1128,7 +1440,7 @@ pub async fn enable_specific_routing_algorithm<A>(
algo_type: Option<A>,
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>>
where
A: routing_types::DynamicRoutingAlgoAccessor + Clone + std::fmt::Debug,
A: routing_types::DynamicRoutingAlgoAccessor + Clone + Debug,
{
// Algorithm wasn't created yet
let Some(mut algo_type) = algo_type else {
@ -1169,9 +1481,8 @@ where
}
.into());
};
*algo_type_enabled_features = feature_to_enable.clone();
dynamic_routing_algo_ref
.update_specific_ref(dynamic_routing_type.clone(), feature_to_enable.clone());
*algo_type_enabled_features = feature_to_enable;
dynamic_routing_algo_ref.update_enabled_features(dynamic_routing_type, feature_to_enable);
update_business_profile_active_dynamic_algorithm_ref(
db,
&state.into(),
@ -1242,6 +1553,13 @@ pub async fn default_specific_dynamic_routing_setup(
algorithm_for: common_enums::TransactionType::Payment,
}
}
routing_types::DynamicRoutingType::ContractBasedRouting => {
return Err((errors::ApiErrorResponse::InvalidRequestData {
message: "Contract routing cannot be set as default".to_string(),
})
.into())
}
};
let record = db
@ -1273,7 +1591,8 @@ pub async fn default_specific_dynamic_routing_setup(
Ok(ApplicationResponse::Json(new_record))
}
pub struct SuccessBasedRoutingConfigParamsInterpolator {
#[derive(Debug, Clone)]
pub struct DynamicRoutingConfigParamsInterpolator {
pub payment_method: Option<common_enums::PaymentMethod>,
pub payment_method_type: Option<common_enums::PaymentMethodType>,
pub authentication_type: Option<common_enums::AuthenticationType>,
@ -1283,7 +1602,7 @@ pub struct SuccessBasedRoutingConfigParamsInterpolator {
pub card_bin: Option<String>,
}
impl SuccessBasedRoutingConfigParamsInterpolator {
impl DynamicRoutingConfigParamsInterpolator {
pub fn new(
payment_method: Option<common_enums::PaymentMethod>,
payment_method_type: Option<common_enums::PaymentMethodType>,