mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 12:15:40 +08:00
refactor(routing): Remove backwards compatibility for the routing crate (#3015)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Shanks <shashank.attarde@juspay.in>
This commit is contained in:
@ -1,42 +1,33 @@
|
||||
pub mod helpers;
|
||||
pub mod transformers;
|
||||
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery};
|
||||
use api_models::{
|
||||
enums,
|
||||
routing::{self as routing_types, RoutingAlgorithmId},
|
||||
routing::{
|
||||
self as routing_types, RoutingAlgorithmId, RoutingRetrieveLinkQuery, RoutingRetrieveQuery,
|
||||
},
|
||||
};
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
use common_utils::ext_traits::{Encode, StringExt};
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
use diesel_models::configs;
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use diesel_models::routing_algorithm::RoutingAlgorithm;
|
||||
use error_stack::ResultExt;
|
||||
use rustc_hash::FxHashSet;
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
use storage_impl::redis::cache;
|
||||
|
||||
use super::payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
use super::payouts;
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use crate::types::transformers::{ForeignInto, ForeignTryFrom};
|
||||
use crate::{
|
||||
consts,
|
||||
core::{
|
||||
errors::{RouterResponse, StorageErrorExt},
|
||||
errors::{self, RouterResponse, StorageErrorExt},
|
||||
metrics, utils as core_utils,
|
||||
},
|
||||
routes::SessionState,
|
||||
types::domain,
|
||||
services::api as service_api,
|
||||
types::{
|
||||
domain,
|
||||
transformers::{ForeignInto, ForeignTryFrom},
|
||||
},
|
||||
utils::{self, OptionExt, ValueExt},
|
||||
};
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
use crate::{core::errors, services::api as service_api, types::storage};
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use crate::{errors, services::api as service_api};
|
||||
|
||||
pub enum TransactionData<'a, F>
|
||||
where
|
||||
@ -50,47 +41,29 @@ where
|
||||
pub async fn retrieve_merchant_routing_dictionary(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveQuery,
|
||||
#[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType,
|
||||
query_params: RoutingRetrieveQuery,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingKind> {
|
||||
metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE.add(&metrics::CONTEXT, 1, &[]);
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let routing_metadata = state
|
||||
.store
|
||||
.list_routing_algorithm_metadata_by_merchant_id_transaction_type(
|
||||
&merchant_account.merchant_id,
|
||||
transaction_type,
|
||||
i64::from(query_params.limit.unwrap_or_default()),
|
||||
i64::from(query_params.offset.unwrap_or_default()),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let result = routing_metadata
|
||||
.into_iter()
|
||||
.map(ForeignInto::foreign_into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&[],
|
||||
);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_types::RoutingKind::RoutingAlgorithm(result),
|
||||
))
|
||||
}
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
let routing_metadata = state
|
||||
.store
|
||||
.list_routing_algorithm_metadata_by_merchant_id_transaction_type(
|
||||
&merchant_account.merchant_id,
|
||||
transaction_type,
|
||||
i64::from(query_params.limit.unwrap_or_default()),
|
||||
i64::from(query_params.offset.unwrap_or_default()),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let result = routing_metadata
|
||||
.into_iter()
|
||||
.map(ForeignInto::foreign_into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_types::RoutingKind::Config(
|
||||
helpers::get_merchant_routing_dictionary(
|
||||
state.store.as_ref(),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
routing_types::RoutingKind::RoutingAlgorithm(result),
|
||||
))
|
||||
}
|
||||
|
||||
@ -131,251 +104,121 @@ pub async fn create_routing_config(
|
||||
&format!("routing_{}", &merchant_account.merchant_id),
|
||||
);
|
||||
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let profile_id = request
|
||||
.profile_id
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id",
|
||||
})
|
||||
.attach_printable("Profile_id not provided")?;
|
||||
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
helpers::validate_connectors_in_routing_config(
|
||||
db,
|
||||
&key_store,
|
||||
&merchant_account.merchant_id,
|
||||
&profile_id,
|
||||
&algorithm,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let timestamp = common_utils::date_time::now();
|
||||
let algo = RoutingAlgorithm {
|
||||
algorithm_id: algorithm_id.clone(),
|
||||
profile_id,
|
||||
merchant_id: merchant_account.merchant_id,
|
||||
name: name.clone(),
|
||||
description: Some(description.clone()),
|
||||
kind: algorithm.get_kind().foreign_into(),
|
||||
algorithm_data: serde_json::json!(algorithm),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: transaction_type.to_owned(),
|
||||
};
|
||||
let record = db
|
||||
.insert_routing_algorithm(algo)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let new_record = record.foreign_into();
|
||||
|
||||
metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(new_record))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let algorithm_str = algorithm
|
||||
.encode_to_string_of_json()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to serialize routing algorithm to string")?;
|
||||
|
||||
let mut algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let mut merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
utils::when(
|
||||
merchant_dictionary.records.len() >= consts::MAX_ROUTING_CONFIGS_PER_MERCHANT,
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: format!("Reached the maximum number of routing configs ({}), please delete some to create new ones", consts::MAX_ROUTING_CONFIGS_PER_MERCHANT),
|
||||
let profile_id = request
|
||||
.profile_id
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id",
|
||||
})
|
||||
},
|
||||
)?;
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
let records_are_empty = merchant_dictionary.records.is_empty();
|
||||
.attach_printable("Profile_id not provided")?;
|
||||
|
||||
let new_record = routing_types::RoutingDictionaryRecord {
|
||||
id: algorithm_id.clone(),
|
||||
name: name.clone(),
|
||||
kind: algorithm.get_kind(),
|
||||
description: description.clone(),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: Some(*transaction_type),
|
||||
};
|
||||
merchant_dictionary.records.push(new_record.clone());
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let new_algorithm_config = configs::ConfigNew {
|
||||
key: algorithm_id.clone(),
|
||||
config: algorithm_str,
|
||||
};
|
||||
helpers::validate_connectors_in_routing_config(
|
||||
db,
|
||||
&key_store,
|
||||
&merchant_account.merchant_id,
|
||||
&profile_id,
|
||||
&algorithm,
|
||||
)
|
||||
.await?;
|
||||
|
||||
db.insert_config(new_algorithm_config)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to save new routing algorithm config to DB")?;
|
||||
let timestamp = common_utils::date_time::now();
|
||||
let algo = RoutingAlgorithm {
|
||||
algorithm_id: algorithm_id.clone(),
|
||||
profile_id,
|
||||
merchant_id: merchant_account.merchant_id,
|
||||
name: name.clone(),
|
||||
description: Some(description.clone()),
|
||||
kind: algorithm.get_kind().foreign_into(),
|
||||
algorithm_data: serde_json::json!(algorithm),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: transaction_type.to_owned(),
|
||||
};
|
||||
let record = db
|
||||
.insert_routing_algorithm(algo)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
if records_are_empty {
|
||||
merchant_dictionary.active_id = Some(algorithm_id.clone());
|
||||
algorithm_ref.update_algorithm_id(algorithm_id);
|
||||
let key =
|
||||
cache::CacheKind::Routing(format!("dsl_{}", &merchant_account.merchant_id).into());
|
||||
let new_record = record.foreign_into();
|
||||
|
||||
helpers::update_merchant_active_algorithm_ref(db, &key_store, key, algorithm_ref)
|
||||
.await?;
|
||||
}
|
||||
|
||||
helpers::update_merchant_routing_dictionary(
|
||||
db,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_dictionary,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(new_record))
|
||||
}
|
||||
metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(new_record))
|
||||
}
|
||||
|
||||
pub async fn link_routing_config(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore,
|
||||
algorithm_id: String,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let routing_algorithm = db
|
||||
.find_routing_algorithm_by_algorithm_id_merchant_id(
|
||||
&algorithm_id,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&routing_algorithm.profile_id),
|
||||
let routing_algorithm = db
|
||||
.find_routing_algorithm_by_algorithm_id_merchant_id(
|
||||
&algorithm_id,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound {
|
||||
id: routing_algorithm.profile_id.clone(),
|
||||
})?;
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
|
||||
}
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&routing_algorithm.profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound {
|
||||
id: routing_algorithm.profile_id.clone(),
|
||||
})?;
|
||||
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = business_profile
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
utils::when(routing_algorithm.algorithm_for != *transaction_type, || {
|
||||
utils::when(routing_algorithm.algorithm_for != *transaction_type, || {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: format!(
|
||||
"Cannot use {}'s routing algorithm for {} operation",
|
||||
routing_algorithm.algorithm_for, transaction_type
|
||||
),
|
||||
})
|
||||
})?;
|
||||
|
||||
utils::when(
|
||||
routing_ref.algorithm_id == Some(algorithm_id.clone()),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: format!(
|
||||
"Cannot use {}'s routing algorithm for {} operation",
|
||||
routing_algorithm.algorithm_for, transaction_type
|
||||
),
|
||||
message: "Algorithm is already active".to_string(),
|
||||
})
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
routing_ref.update_algorithm_id(algorithm_id);
|
||||
helpers::update_business_profile_active_algorithm_ref(
|
||||
db,
|
||||
business_profile,
|
||||
routing_ref,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
utils::when(
|
||||
routing_ref.algorithm_id == Some(algorithm_id.clone()),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already active".to_string(),
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
routing_ref.update_algorithm_id(algorithm_id);
|
||||
helpers::update_business_profile_active_algorithm_ref(
|
||||
db,
|
||||
business_profile,
|
||||
routing_ref,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_algorithm.foreign_into(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(),
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
utils::when(
|
||||
routing_ref.algorithm_id == Some(algorithm_id.clone()),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already active".to_string(),
|
||||
})
|
||||
},
|
||||
)?;
|
||||
let mut merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
let modified_at = common_utils::date_time::now_unix_timestamp();
|
||||
let record = merchant_dictionary
|
||||
.records
|
||||
.iter_mut()
|
||||
.find(|rec| rec.id == algorithm_id)
|
||||
.ok_or(errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
.attach_printable("Record with given ID not found for routing config activation")?;
|
||||
|
||||
record.modified_at = modified_at;
|
||||
merchant_dictionary.active_id = Some(record.id.clone());
|
||||
let response = record.clone();
|
||||
routing_ref.update_algorithm_id(algorithm_id);
|
||||
helpers::update_merchant_routing_dictionary(
|
||||
db,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_dictionary,
|
||||
)
|
||||
.await?;
|
||||
let key =
|
||||
cache::CacheKind::Routing(format!("dsl_{}", &merchant_account.merchant_id).into());
|
||||
helpers::update_merchant_active_algorithm_ref(db, &key_store, key, routing_ref).await?;
|
||||
|
||||
metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_algorithm.foreign_into(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn retrieve_routing_config(
|
||||
@ -385,256 +228,105 @@ pub async fn retrieve_routing_config(
|
||||
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
|
||||
metrics::ROUTING_RETRIEVE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let routing_algorithm = db
|
||||
.find_routing_algorithm_by_algorithm_id_merchant_id(
|
||||
&algorithm_id.0,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&routing_algorithm.profile_id),
|
||||
let routing_algorithm = db
|
||||
.find_routing_algorithm_by_algorithm_id_merchant_id(
|
||||
&algorithm_id.0,
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to parse routing algorithm")?;
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&routing_algorithm.profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to parse routing algorithm")?;
|
||||
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
let record = merchant_dictionary
|
||||
.records
|
||||
.into_iter()
|
||||
.find(|rec| rec.id == algorithm_id.0)
|
||||
.ok_or(errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
.attach_printable("Algorithm with the given ID not found in the merchant dictionary")?;
|
||||
|
||||
let algorithm_config = db
|
||||
.find_config_by_key(&algorithm_id.0)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
.attach_printable("Routing config not found in DB")?;
|
||||
|
||||
let algorithm: routing_types::RoutingAlgorithm = algorithm_config
|
||||
.config
|
||||
.parse_struct("RoutingAlgorithm")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error deserializing routing algorithm config")?;
|
||||
|
||||
let response = routing_types::MerchantRoutingAlgorithm {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
description: record.description,
|
||||
algorithm,
|
||||
created_at: record.created_at,
|
||||
modified_at: record.modified_at,
|
||||
algorithm_for: record
|
||||
.algorithm_for
|
||||
.unwrap_or(enums::TransactionType::Payment),
|
||||
};
|
||||
|
||||
metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
pub async fn unlink_routing_config(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore,
|
||||
#[cfg(feature = "business_profile_routing")] request: routing_types::RoutingConfigRequest,
|
||||
request: routing_types::RoutingConfigRequest,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_UNLINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let profile_id = request
|
||||
.profile_id
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id",
|
||||
})
|
||||
.attach_printable("Profile_id not provided")?;
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?;
|
||||
match business_profile {
|
||||
Some(business_profile) => {
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => {
|
||||
business_profile.payout_routing_algorithm.clone()
|
||||
}
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"unable to deserialize routing algorithm ref from merchant account",
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
|
||||
match routing_algo_ref.algorithm_id {
|
||||
Some(algorithm_id) => {
|
||||
let routing_algorithm: routing_types::RoutingAlgorithmRef =
|
||||
routing_types::RoutingAlgorithmRef {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
config_algo_id: routing_algo_ref.config_algo_id.clone(),
|
||||
surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id,
|
||||
};
|
||||
|
||||
let record = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
&profile_id,
|
||||
&algorithm_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let response = record.foreign_into();
|
||||
helpers::update_business_profile_active_algorithm_ref(
|
||||
db,
|
||||
business_profile,
|
||||
routing_algorithm,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&[],
|
||||
);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?,
|
||||
}
|
||||
let profile_id = request
|
||||
.profile_id
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id",
|
||||
})
|
||||
.attach_printable("Profile_id not provided")?;
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?;
|
||||
match business_profile {
|
||||
Some(business_profile) => {
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: "The business_profile is not present".to_string(),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let mut merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(),
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
|
||||
utils::when(routing_algo_ref.algorithm_id.is_none(), || {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})
|
||||
})?;
|
||||
let routing_algorithm: routing_types::RoutingAlgorithmRef =
|
||||
routing_types::RoutingAlgorithmRef {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
config_algo_id: routing_algo_ref.config_algo_id.clone(),
|
||||
surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id,
|
||||
};
|
||||
|
||||
let active_algorithm_id = merchant_dictionary
|
||||
.active_id
|
||||
.or(routing_algo_ref.algorithm_id.clone())
|
||||
.ok_or(errors::ApiErrorResponse::PreconditionFailed {
|
||||
// When the merchant_dictionary doesn't have any active algorithm and merchant_account doesn't have any routing_algorithm configured
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?;
|
||||
|
||||
let record = merchant_dictionary
|
||||
.records
|
||||
.iter_mut()
|
||||
.find(|rec| rec.id == active_algorithm_id)
|
||||
.ok_or(errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
.attach_printable("Record with the given ID not found for de-activation")?;
|
||||
|
||||
let response = record.clone();
|
||||
|
||||
merchant_dictionary.active_id = None;
|
||||
|
||||
helpers::update_merchant_routing_dictionary(
|
||||
db,
|
||||
&merchant_account.merchant_id,
|
||||
merchant_dictionary,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ref_value = routing_algorithm
|
||||
.encode_to_value()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed converting routing algorithm ref to json value")?;
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
let merchant_account_update = storage::MerchantAccountUpdate::Update {
|
||||
merchant_name: None,
|
||||
merchant_details: None,
|
||||
return_url: None,
|
||||
webhook_details: None,
|
||||
sub_merchants_enabled: None,
|
||||
parent_merchant_id: None,
|
||||
enable_payment_response_hash: None,
|
||||
payment_response_hash_key: None,
|
||||
redirect_to_merchant_with_http_post: None,
|
||||
publishable_key: None,
|
||||
locker_id: None,
|
||||
metadata: None,
|
||||
routing_algorithm: Some(ref_value),
|
||||
primary_business_details: None,
|
||||
intent_fulfillment_time: None,
|
||||
frm_routing_algorithm: None,
|
||||
payout_routing_algorithm: None,
|
||||
default_profile: None,
|
||||
payment_link_config: None,
|
||||
pm_collect_link_config: None,
|
||||
};
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
|
||||
db.update_specific_fields_in_merchant(
|
||||
&key_store.merchant_id,
|
||||
merchant_account_update,
|
||||
&key_store,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to update routing algorithm ref in merchant account")?;
|
||||
match routing_algo_ref.algorithm_id {
|
||||
Some(algorithm_id) => {
|
||||
let routing_algorithm: routing_types::RoutingAlgorithmRef =
|
||||
routing_types::RoutingAlgorithmRef {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
config_algo_id: routing_algo_ref.config_algo_id.clone(),
|
||||
surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id,
|
||||
};
|
||||
|
||||
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
let record = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
&profile_id,
|
||||
&algorithm_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let response = record.foreign_into();
|
||||
helpers::update_business_profile_active_algorithm_ref(
|
||||
db,
|
||||
business_profile,
|
||||
routing_algorithm,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?,
|
||||
}
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: "The business_profile is not present".to_string(),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,110 +402,60 @@ pub async fn retrieve_default_routing_config(
|
||||
pub async fn retrieve_linked_routing_config(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveLinkQuery,
|
||||
#[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType,
|
||||
query_params: RoutingRetrieveLinkQuery,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::LinkedRoutingConfigRetrieveResponse> {
|
||||
metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let business_profiles = if let Some(profile_id) = query_params.profile_id {
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.map(|profile| vec![profile])
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })?
|
||||
} else {
|
||||
db.list_business_profile_by_merchant_id(&merchant_account.merchant_id)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?
|
||||
};
|
||||
let business_profiles = if let Some(profile_id) = query_params.profile_id {
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
Some(&profile_id),
|
||||
&merchant_account.merchant_id,
|
||||
)
|
||||
.await?
|
||||
.map(|profile| vec![profile])
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })?
|
||||
} else {
|
||||
db.list_business_profile_by_merchant_id(&merchant_account.merchant_id)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?
|
||||
};
|
||||
|
||||
let mut active_algorithms = Vec::new();
|
||||
let mut active_algorithms = Vec::new();
|
||||
|
||||
for business_profile in business_profiles {
|
||||
let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm,
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => business_profile.payout_routing_algorithm,
|
||||
}
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(algorithm_id) = routing_ref.algorithm_id {
|
||||
let record = db
|
||||
.find_routing_algorithm_metadata_by_algorithm_id_profile_id(
|
||||
&algorithm_id,
|
||||
&business_profile.profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
active_algorithms.push(record.foreign_into());
|
||||
}
|
||||
for business_profile in business_profiles {
|
||||
let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm,
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => business_profile.payout_routing_algorithm,
|
||||
}
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms),
|
||||
))
|
||||
}
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
let algorithm = if let Some(algorithm_id) = merchant_dictionary.active_id {
|
||||
let record = merchant_dictionary
|
||||
.records
|
||||
.into_iter()
|
||||
.find(|rec| rec.id == algorithm_id)
|
||||
.ok_or(errors::ApiErrorResponse::ResourceIdNotFound)
|
||||
.attach_printable("record for active algorithm not found in merchant dictionary")?;
|
||||
|
||||
let config = db
|
||||
.find_config_by_key(&algorithm_id)
|
||||
if let Some(algorithm_id) = routing_ref.algorithm_id {
|
||||
let record = db
|
||||
.find_routing_algorithm_metadata_by_algorithm_id_profile_id(
|
||||
&algorithm_id,
|
||||
&business_profile.profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("error finding routing config in db")?;
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let the_algorithm: routing_types::RoutingAlgorithm = config
|
||||
.config
|
||||
.parse_struct("RoutingAlgorithm")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to parse routing algorithm")?;
|
||||
|
||||
Some(routing_types::MerchantRoutingAlgorithm {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
description: record.description,
|
||||
algorithm: the_algorithm,
|
||||
created_at: record.created_at,
|
||||
modified_at: record.modified_at,
|
||||
algorithm_for: record
|
||||
.algorithm_for
|
||||
.unwrap_or(enums::TransactionType::Payment),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response = routing_types::LinkedRoutingConfigRetrieveResponse::MerchantAccountBased(
|
||||
routing_types::RoutingRetrieveResponse { algorithm },
|
||||
);
|
||||
|
||||
metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
active_algorithms.push(record.foreign_into());
|
||||
}
|
||||
}
|
||||
|
||||
metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn retrieve_default_routing_config_for_profiles(
|
||||
@ -883,25 +525,17 @@ pub async fn update_default_routing_config_for_profile(
|
||||
})
|
||||
})?;
|
||||
|
||||
let existing_set = FxHashSet::from_iter(default_config.iter().map(|c| {
|
||||
(
|
||||
c.connector.to_string(),
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
c.merchant_connector_id.as_ref(),
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
c.sub_label.as_ref(),
|
||||
)
|
||||
}));
|
||||
let existing_set = FxHashSet::from_iter(
|
||||
default_config
|
||||
.iter()
|
||||
.map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())),
|
||||
);
|
||||
|
||||
let updated_set = FxHashSet::from_iter(updated_config.iter().map(|c| {
|
||||
(
|
||||
c.connector.to_string(),
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
c.merchant_connector_id.as_ref(),
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
c.sub_label.as_ref(),
|
||||
)
|
||||
}));
|
||||
let updated_set = FxHashSet::from_iter(
|
||||
updated_config
|
||||
.iter()
|
||||
.map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())),
|
||||
);
|
||||
|
||||
let symmetric_diff = existing_set
|
||||
.symmetric_difference(&updated_set)
|
||||
|
||||
Reference in New Issue
Block a user