mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
refactor(core): Refactor fallback routing behaviour in payments for v2 (#5642)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -7,7 +7,7 @@ use api_models::{
|
||||
use base64::Engine;
|
||||
use common_utils::{
|
||||
date_time,
|
||||
ext_traits::{AsyncExt, Encode, ValueExt},
|
||||
ext_traits::{AsyncExt, Encode, OptionExt, ValueExt},
|
||||
id_type, pii, type_name,
|
||||
types::keymanager::{self as km_types, KeyManagerState},
|
||||
};
|
||||
@ -21,12 +21,6 @@ use regex::Regex;
|
||||
use router_env::metrics::add_attributes;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
use crate::core::routing;
|
||||
#[cfg(any(feature = "v1", feature = "v2"))]
|
||||
use crate::types::transformers::ForeignFrom;
|
||||
use crate::{
|
||||
@ -37,8 +31,7 @@ use crate::{
|
||||
payment_methods::{cards, transformers},
|
||||
payments::helpers,
|
||||
pm_auth::helpers::PaymentAuthConnectorDataExt,
|
||||
routing::helpers as routing_helpers,
|
||||
utils as core_utils,
|
||||
routing, utils as core_utils,
|
||||
},
|
||||
db::StorageInterface,
|
||||
routes::{metrics, SessionState},
|
||||
@ -55,6 +48,7 @@ use crate::{
|
||||
},
|
||||
utils,
|
||||
};
|
||||
|
||||
const IBAN_MAX_LENGTH: usize = 34;
|
||||
const BACS_SORT_CODE_LENGTH: usize = 6;
|
||||
const BACS_MAX_ACCOUNT_NUMBER_LENGTH: usize = 8;
|
||||
@ -1871,23 +1865,40 @@ impl<'a> ConnectorTypeAndConnectorName<'a> {
|
||||
Ok(routable_connector)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2",))
|
||||
))]
|
||||
struct MerchantDefaultConfigUpdate<'a> {
|
||||
routable_connector: &'a Option<api_enums::RoutableConnectors>,
|
||||
merchant_connector_id: &'a String,
|
||||
store: &'a dyn StorageInterface,
|
||||
merchant_id: &'a id_type::MerchantId,
|
||||
default_routing_config: &'a Vec<api_models::routing::RoutableConnectorChoice>,
|
||||
default_routing_config_for_profile: &'a Vec<api_models::routing::RoutableConnectorChoice>,
|
||||
profile_id: &'a String,
|
||||
transaction_type: &'a api_enums::TransactionType,
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2",))
|
||||
))]
|
||||
impl<'a> MerchantDefaultConfigUpdate<'a> {
|
||||
async fn update_merchant_default_config(&self) -> RouterResult<()> {
|
||||
let mut default_routing_config = self.default_routing_config.to_owned();
|
||||
let mut default_routing_config_for_profile =
|
||||
self.default_routing_config_for_profile.to_owned();
|
||||
async fn retrieve_and_update_default_fallback_routing_algorithm_if_routable_connector_exists(
|
||||
&self,
|
||||
) -> RouterResult<()> {
|
||||
let mut default_routing_config = routing::helpers::get_merchant_default_config(
|
||||
self.store,
|
||||
self.merchant_id.get_string_repr(),
|
||||
self.transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut default_routing_config_for_profile = routing::helpers::get_merchant_default_config(
|
||||
self.store,
|
||||
self.profile_id,
|
||||
self.transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(routable_connector_val) = self.routable_connector {
|
||||
let choice = routing_types::RoutableConnectorChoice {
|
||||
choice_kind: routing_types::RoutableChoiceKind::FullStruct,
|
||||
@ -1896,7 +1907,7 @@ impl<'a> MerchantDefaultConfigUpdate<'a> {
|
||||
};
|
||||
if !default_routing_config.contains(&choice) {
|
||||
default_routing_config.push(choice.clone());
|
||||
routing_helpers::update_merchant_default_config(
|
||||
routing::helpers::update_merchant_default_config(
|
||||
self.store,
|
||||
self.merchant_id.get_string_repr(),
|
||||
default_routing_config.clone(),
|
||||
@ -1906,7 +1917,7 @@ impl<'a> MerchantDefaultConfigUpdate<'a> {
|
||||
}
|
||||
if !default_routing_config_for_profile.contains(&choice.clone()) {
|
||||
default_routing_config_for_profile.push(choice);
|
||||
routing_helpers::update_merchant_default_config(
|
||||
routing::helpers::update_merchant_default_config(
|
||||
self.store,
|
||||
self.profile_id,
|
||||
default_routing_config_for_profile.clone(),
|
||||
@ -1918,7 +1929,53 @@ impl<'a> MerchantDefaultConfigUpdate<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2",
|
||||
))]
|
||||
struct DefaultFallbackRoutingConfigUpdate<'a> {
|
||||
routable_connector: &'a Option<api_enums::RoutableConnectors>,
|
||||
merchant_connector_id: &'a String,
|
||||
store: &'a dyn StorageInterface,
|
||||
business_profile: domain::BusinessProfile,
|
||||
key_store: hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
|
||||
key_manager_state: &'a KeyManagerState,
|
||||
}
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
impl<'a> DefaultFallbackRoutingConfigUpdate<'a> {
|
||||
async fn retrieve_and_update_default_fallback_routing_algorithm_if_routable_connector_exists(
|
||||
&self,
|
||||
) -> RouterResult<()> {
|
||||
let profile_wrapper = BusinessProfileWrapper::new(self.business_profile.clone());
|
||||
let default_routing_config_for_profile =
|
||||
&mut profile_wrapper.get_default_fallback_list_of_connector_under_profile()?;
|
||||
if let Some(routable_connector_val) = self.routable_connector {
|
||||
let choice = routing_types::RoutableConnectorChoice {
|
||||
choice_kind: routing_types::RoutableChoiceKind::FullStruct,
|
||||
connector: *routable_connector_val,
|
||||
merchant_connector_id: Some(self.merchant_connector_id.clone()),
|
||||
};
|
||||
if !default_routing_config_for_profile.contains(&choice.clone()) {
|
||||
default_routing_config_for_profile.push(choice);
|
||||
|
||||
profile_wrapper
|
||||
.update_default_fallback_routing_of_connectors_under_profile(
|
||||
self.store,
|
||||
&default_routing_config_for_profile,
|
||||
self.key_manager_state,
|
||||
&self.key_store,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(any(feature = "v1", feature = "v2", feature = "olap"))]
|
||||
#[async_trait::async_trait]
|
||||
trait MerchantConnectorAccountUpdateBridge {
|
||||
@ -2219,14 +2276,13 @@ trait MerchantConnectorAccountCreateBridge {
|
||||
key_manager_state: &KeyManagerState,
|
||||
) -> RouterResult<domain::MerchantConnectorAccount>;
|
||||
|
||||
async fn validate_and_get_profile_id(
|
||||
async fn validate_and_get_business_profile(
|
||||
self,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
db: &dyn StorageInterface,
|
||||
key_manager_state: &KeyManagerState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
should_validate: bool,
|
||||
) -> RouterResult<String>;
|
||||
) -> RouterResult<domain::BusinessProfile>;
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
@ -2360,27 +2416,28 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
|
||||
})
|
||||
}
|
||||
|
||||
async fn validate_and_get_profile_id(
|
||||
async fn validate_and_get_business_profile(
|
||||
self,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
db: &dyn StorageInterface,
|
||||
key_manager_state: &KeyManagerState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
should_validate: bool,
|
||||
) -> RouterResult<String> {
|
||||
) -> RouterResult<domain::BusinessProfile> {
|
||||
let profile_id = self.profile_id;
|
||||
// Check whether this business profile belongs to the merchant
|
||||
if should_validate {
|
||||
let _ = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(profile_id.clone())
|
||||
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })?;
|
||||
|
||||
Ok(business_profile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2533,28 +2590,31 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
|
||||
/// If profile_id is not passed, use default profile if available, or
|
||||
/// If business_details (business_country and business_label) are passed, get the business_profile
|
||||
/// or return a `MissingRequiredField` error
|
||||
async fn validate_and_get_profile_id(
|
||||
async fn validate_and_get_business_profile(
|
||||
self,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
db: &dyn StorageInterface,
|
||||
key_manager_state: &KeyManagerState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
should_validate: bool,
|
||||
) -> RouterResult<String> {
|
||||
) -> RouterResult<domain::BusinessProfile> {
|
||||
match self.profile_id.or(merchant_account.default_profile.clone()) {
|
||||
Some(profile_id) => {
|
||||
// Check whether this business profile belongs to the merchant
|
||||
if should_validate {
|
||||
let _ = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(profile_id.clone())
|
||||
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(
|
||||
errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id },
|
||||
)?;
|
||||
|
||||
Ok(business_profile)
|
||||
}
|
||||
None => match self.business_country.zip(self.business_label) {
|
||||
Some((business_country, business_label)) => {
|
||||
@ -2571,7 +2631,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate {
|
||||
errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_name },
|
||||
)?;
|
||||
|
||||
Ok(business_profile.profile_id)
|
||||
Ok(business_profile)
|
||||
}
|
||||
_ => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id or business_country, business_label"
|
||||
@ -2627,15 +2687,9 @@ pub async fn create_connector(
|
||||
&merchant_account,
|
||||
)?;
|
||||
|
||||
let profile_id = req
|
||||
let business_profile = req
|
||||
.clone()
|
||||
.validate_and_get_profile_id(
|
||||
&merchant_account,
|
||||
store,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
true,
|
||||
)
|
||||
.validate_and_get_business_profile(&merchant_account, store, key_manager_state, &key_store)
|
||||
.await?;
|
||||
|
||||
let pm_auth_config_validation = PMAuthConfigValidation {
|
||||
@ -2643,20 +2697,12 @@ pub async fn create_connector(
|
||||
pm_auth_config: &req.pm_auth_config,
|
||||
db: store,
|
||||
merchant_id,
|
||||
profile_id: &profile_id.clone(),
|
||||
profile_id: &business_profile.profile_id,
|
||||
key_store: &key_store,
|
||||
key_manager_state,
|
||||
};
|
||||
pm_auth_config_validation.validate_pm_auth_config().await?;
|
||||
|
||||
let business_profile = state
|
||||
.store
|
||||
.find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound {
|
||||
id: profile_id.to_owned(),
|
||||
})?;
|
||||
|
||||
let connector_type_and_connector_enum = ConnectorTypeAndConnectorName {
|
||||
connector_type: &req.connector_type,
|
||||
connector_name: &req.connector_name,
|
||||
@ -2687,22 +2733,6 @@ pub async fn create_connector(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let transaction_type = req.get_transaction_type();
|
||||
|
||||
let mut default_routing_config = routing_helpers::get_merchant_default_config(
|
||||
&*state.store,
|
||||
merchant_id.get_string_repr(),
|
||||
&transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut default_routing_config_for_profile = routing_helpers::get_merchant_default_config(
|
||||
&*state.clone().store,
|
||||
&profile_id,
|
||||
&transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mca = state
|
||||
.store
|
||||
.insert_merchant_connector_account(
|
||||
@ -2713,27 +2743,44 @@ pub async fn create_connector(
|
||||
.await
|
||||
.to_duplicate_response(
|
||||
errors::ApiErrorResponse::DuplicateMerchantConnectorAccount {
|
||||
profile_id: profile_id.clone(),
|
||||
profile_id: business_profile.profile_id.clone(),
|
||||
connector_label: merchant_connector_account
|
||||
.connector_label
|
||||
.unwrap_or_default(),
|
||||
},
|
||||
)?;
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2",))
|
||||
))]
|
||||
//update merchant default config
|
||||
let merchant_default_config_update = MerchantDefaultConfigUpdate {
|
||||
routable_connector: &routable_connector,
|
||||
merchant_connector_id: &mca.get_id(),
|
||||
store,
|
||||
merchant_id,
|
||||
default_routing_config: &mut default_routing_config,
|
||||
default_routing_config_for_profile: &mut default_routing_config_for_profile,
|
||||
profile_id: &profile_id,
|
||||
transaction_type: &transaction_type,
|
||||
profile_id: &business_profile.profile_id,
|
||||
transaction_type: &req.get_transaction_type(),
|
||||
};
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2",
|
||||
))]
|
||||
//update merchant default config
|
||||
let merchant_default_config_update = DefaultFallbackRoutingConfigUpdate {
|
||||
routable_connector: &routable_connector,
|
||||
merchant_connector_id: &mca.get_id(),
|
||||
store,
|
||||
business_profile,
|
||||
key_store,
|
||||
key_manager_state,
|
||||
};
|
||||
|
||||
merchant_default_config_update
|
||||
.update_merchant_default_config()
|
||||
.retrieve_and_update_default_fallback_routing_algorithm_if_routable_connector_exists()
|
||||
.await?;
|
||||
|
||||
metrics::MCA_CREATE.add(
|
||||
@ -4033,31 +4080,23 @@ impl BusinessProfileWrapper {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_profile_id_and_routing_algorithm_id<F>(
|
||||
&self,
|
||||
transaction_data: &routing::TransactionData<'_, F>,
|
||||
) -> (Option<String>, Option<String>)
|
||||
pub fn get_routing_algorithm_id<'a, F>(
|
||||
&'a self,
|
||||
transaction_data: &'a routing::TransactionData<'_, F>,
|
||||
) -> Option<String>
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
match transaction_data {
|
||||
routing::TransactionData::Payment(payment_data) => (
|
||||
payment_data.payment_intent.profile_id.clone(),
|
||||
self.profile.routing_algorithm_id.clone(),
|
||||
),
|
||||
routing::TransactionData::Payment(_) => self.profile.routing_algorithm_id.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
routing::TransactionData::Payout(payout_data) => (
|
||||
Some(payout_data.payout_attempt.profile_id.clone()),
|
||||
self.profile.payout_routing_algorithm_id.clone(),
|
||||
),
|
||||
routing::TransactionData::Payout(_) => self.profile.payout_routing_algorithm_id.clone(),
|
||||
}
|
||||
}
|
||||
pub fn get_default_fallback_list_of_connector_under_profile(
|
||||
&self,
|
||||
) -> RouterResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
use common_utils::ext_traits::OptionExt;
|
||||
use masking::ExposeOptionInterface;
|
||||
|
||||
self.profile
|
||||
.default_fallback_routing
|
||||
.clone()
|
||||
@ -4080,7 +4119,7 @@ impl BusinessProfileWrapper {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn update_default_routing_for_profile(
|
||||
pub async fn update_default_fallback_routing_of_connectors_under_profile(
|
||||
self,
|
||||
db: &dyn StorageInterface,
|
||||
updated_config: &Vec<routing_types::RoutableConnectorChoice>,
|
||||
|
||||
@ -2435,10 +2435,17 @@ pub async fn list_payment_methods(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let profile_id = profile_id
|
||||
.clone()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
||||
message: "Profile id not found".to_string(),
|
||||
})?;
|
||||
|
||||
// filter out payment connectors based on profile_id
|
||||
let filtered_mcas = helpers::filter_mca_based_on_profile_and_connector_type(
|
||||
all_mcas.clone(),
|
||||
profile_id.as_ref(),
|
||||
&profile_id,
|
||||
ConnectorType::PaymentProcessor,
|
||||
);
|
||||
|
||||
@ -2447,12 +2454,6 @@ pub async fn list_payment_methods(
|
||||
let mut response: Vec<ResponsePaymentMethodIntermediate> = vec![];
|
||||
// Key creation for storing PM_FILTER_CGRAPH
|
||||
let key = {
|
||||
let profile_id = profile_id
|
||||
.clone()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
||||
message: "Profile id not found".to_string(),
|
||||
})?;
|
||||
format!(
|
||||
"pm_filters_cgraph_{}_{}",
|
||||
merchant_account.get_id().get_string_repr(),
|
||||
|
||||
@ -3523,6 +3523,7 @@ where
|
||||
.merchant_connector_id
|
||||
.clone()
|
||||
.as_ref(),
|
||||
business_profile.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -3555,15 +3556,13 @@ where
|
||||
.attach_printable("Failed execution of straight through routing")?;
|
||||
|
||||
if check_eligibility {
|
||||
let profile_id = payment_data.payment_intent.profile_id.clone();
|
||||
|
||||
connectors = routing::perform_eligibility_analysis_with_fallback(
|
||||
&state.clone(),
|
||||
key_store,
|
||||
connectors,
|
||||
&TransactionData::Payment(payment_data),
|
||||
eligible_connectors,
|
||||
profile_id,
|
||||
business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -3604,15 +3603,13 @@ where
|
||||
.attach_printable("Failed execution of straight through routing")?;
|
||||
|
||||
if check_eligibility {
|
||||
let profile_id = payment_data.payment_intent.profile_id.clone();
|
||||
|
||||
connectors = routing::perform_eligibility_analysis_with_fallback(
|
||||
&state,
|
||||
key_store,
|
||||
connectors,
|
||||
&TransactionData::Payment(payment_data),
|
||||
eligible_connectors,
|
||||
profile_id,
|
||||
business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -3978,6 +3975,7 @@ where
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn route_connector_v1<F>(
|
||||
state: &SessionState,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
@ -3991,14 +3989,14 @@ pub async fn route_connector_v1<F>(
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
let (profile_id, routing_algorithm_id) =
|
||||
super::admin::BusinessProfileWrapper::new(business_profile.clone())
|
||||
.get_profile_id_and_routing_algorithm_id(&transaction_data);
|
||||
let profile_wrapper = super::admin::BusinessProfileWrapper::new(business_profile.clone());
|
||||
let routing_algorithm_id = profile_wrapper.get_routing_algorithm_id(&transaction_data);
|
||||
|
||||
let connectors = routing::perform_static_routing_v1(
|
||||
state,
|
||||
merchant_account.get_id(),
|
||||
routing_algorithm_id,
|
||||
business_profile,
|
||||
&transaction_data,
|
||||
)
|
||||
.await
|
||||
@ -4010,7 +4008,7 @@ where
|
||||
connectors,
|
||||
&transaction_data,
|
||||
eligible_connectors,
|
||||
profile_id,
|
||||
business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -4079,17 +4077,12 @@ pub async fn route_connector_v1<F>(
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
let (profile_id, routing_algorithm_id) = {
|
||||
let (profile_id, routing_algorithm) = match &transaction_data {
|
||||
TransactionData::Payment(payment_data) => (
|
||||
payment_data.payment_intent.profile_id.clone(),
|
||||
business_profile.routing_algorithm.clone(),
|
||||
),
|
||||
let routing_algorithm_id = {
|
||||
let routing_algorithm = match &transaction_data {
|
||||
TransactionData::Payment(_) => business_profile.routing_algorithm.clone(),
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
TransactionData::Payout(payout_data) => (
|
||||
Some(payout_data.payout_attempt.profile_id.clone()),
|
||||
business_profile.payout_routing_algorithm.clone(),
|
||||
),
|
||||
TransactionData::Payout(_) => business_profile.payout_routing_algorithm.clone(),
|
||||
};
|
||||
|
||||
let algorithm_ref = routing_algorithm
|
||||
@ -4098,13 +4091,14 @@ where
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Could not decode merchant routing algorithm ref")?
|
||||
.unwrap_or_default();
|
||||
(profile_id, algorithm_ref.algorithm_id)
|
||||
algorithm_ref.algorithm_id
|
||||
};
|
||||
|
||||
let connectors = routing::perform_static_routing_v1(
|
||||
state,
|
||||
merchant_account.get_id(),
|
||||
routing_algorithm_id,
|
||||
business_profile,
|
||||
&transaction_data,
|
||||
)
|
||||
.await
|
||||
@ -4115,7 +4109,7 @@ where
|
||||
connectors,
|
||||
&transaction_data,
|
||||
eligible_connectors,
|
||||
profile_id,
|
||||
business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
|
||||
@ -45,6 +45,12 @@ use super::{
|
||||
operations::{BoxedOperation, Operation, PaymentResponse},
|
||||
CustomerDetails, PaymentData,
|
||||
};
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
use crate::core::admin as core_admin;
|
||||
use crate::{
|
||||
configs::settings::{ConnectorRequestReferenceIdConfig, TempLockerEnableConfig},
|
||||
connector,
|
||||
@ -124,15 +130,12 @@ pub fn create_certificate(
|
||||
|
||||
pub fn filter_mca_based_on_profile_and_connector_type(
|
||||
merchant_connector_accounts: Vec<domain::MerchantConnectorAccount>,
|
||||
profile_id: Option<&String>,
|
||||
profile_id: &String,
|
||||
connector_type: ConnectorType,
|
||||
) -> Vec<domain::MerchantConnectorAccount> {
|
||||
merchant_connector_accounts
|
||||
.into_iter()
|
||||
.filter(|mca| {
|
||||
profile_id.map_or(true, |id| &mca.profile_id == id)
|
||||
&& mca.connector_type == connector_type
|
||||
})
|
||||
.filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type)
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -4269,18 +4272,12 @@ pub async fn get_apple_pay_retryable_connectors<F>(
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
pre_routing_connector_data_list: &[api::ConnectorData],
|
||||
merchant_connector_id: Option<&String>,
|
||||
business_profile: domain::BusinessProfile,
|
||||
) -> CustomResult<Option<Vec<api::ConnectorData>>, errors::ApiErrorResponse>
|
||||
where
|
||||
F: Send + Clone,
|
||||
{
|
||||
let profile_id = &payment_data
|
||||
.payment_intent
|
||||
.profile_id
|
||||
.clone()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||
field_name: "profile_id",
|
||||
})?;
|
||||
let profile_id = &business_profile.profile_id;
|
||||
|
||||
let pre_decided_connector_data_first = pre_routing_connector_data_list
|
||||
.first()
|
||||
@ -4318,7 +4315,7 @@ where
|
||||
let profile_specific_merchant_connector_account_list =
|
||||
filter_mca_based_on_profile_and_connector_type(
|
||||
merchant_connector_account_list,
|
||||
Some(profile_id),
|
||||
profile_id,
|
||||
ConnectorType::PaymentProcessor,
|
||||
);
|
||||
|
||||
@ -4349,7 +4346,10 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
let fallback_connetors_list = crate::core::routing::helpers::get_merchant_default_config(
|
||||
&*state.clone().store,
|
||||
profile_id,
|
||||
@ -4359,6 +4359,16 @@ where
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to get merchant default fallback connectors config")?;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
let fallback_connetors_list = core_admin::BusinessProfileWrapper::new(business_profile)
|
||||
.get_default_fallback_list_of_connector_under_profile()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to get merchant default fallback connectors config")?;
|
||||
|
||||
let mut routing_connector_data_list = Vec::new();
|
||||
|
||||
pre_routing_connector_data_list.iter().for_each(|pre_val| {
|
||||
|
||||
@ -378,7 +378,7 @@ where
|
||||
|
||||
let filtered_connector_accounts = helpers::filter_mca_based_on_profile_and_connector_type(
|
||||
all_connector_accounts,
|
||||
Some(&profile_id),
|
||||
&profile_id,
|
||||
common_enums::ConnectorType::PaymentProcessor,
|
||||
);
|
||||
|
||||
|
||||
@ -34,12 +34,18 @@ use rand::{
|
||||
use rustc_hash::FxHashMap;
|
||||
use storage_impl::redis::cache::{CacheKey, CGRAPH_CACHE, ROUTING_CACHE};
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
use crate::core::admin;
|
||||
#[cfg(feature = "payouts")]
|
||||
use crate::core::payouts;
|
||||
use crate::{
|
||||
core::{
|
||||
errors, errors as oss_errors, payments as payments_oss,
|
||||
routing::{self, helpers as routing_helpers},
|
||||
routing::{self},
|
||||
},
|
||||
logger,
|
||||
types::{
|
||||
@ -75,7 +81,7 @@ pub struct SessionRoutingPmTypeInput<'a> {
|
||||
routing_algorithm: &'a MerchantAccountRoutingAlgorithm,
|
||||
backend_input: dsl_inputs::BackendInput,
|
||||
allowed_connectors: FxHashMap<String, api::GetToken>,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
}
|
||||
|
||||
type RoutingResult<O> = oss_errors::CustomResult<O, errors::RoutingError>;
|
||||
@ -273,28 +279,31 @@ pub async fn perform_static_routing_v1<F: Clone>(
|
||||
state: &SessionState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
algorithm_id: Option<String>,
|
||||
business_profile: &domain::BusinessProfile,
|
||||
transaction_data: &routing::TransactionData<'_, F>,
|
||||
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
let profile_id = match transaction_data {
|
||||
routing::TransactionData::Payment(payment_data) => payment_data
|
||||
.payment_intent
|
||||
.profile_id
|
||||
.as_ref()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::RoutingError::ProfileIdMissing)?,
|
||||
#[cfg(feature = "payouts")]
|
||||
routing::TransactionData::Payout(payout_data) => &payout_data.payout_attempt.profile_id,
|
||||
};
|
||||
let algorithm_id = if let Some(id) = algorithm_id {
|
||||
id
|
||||
} else {
|
||||
let fallback_config = routing_helpers::get_merchant_default_config(
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
let fallback_config = routing::helpers::get_merchant_default_config(
|
||||
&*state.clone().store,
|
||||
profile_id,
|
||||
&business_profile.profile_id,
|
||||
&api_enums::TransactionType::from(transaction_data),
|
||||
)
|
||||
.await
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
let fallback_config = admin::BusinessProfileWrapper::new(business_profile.clone())
|
||||
.get_default_fallback_list_of_connector_under_profile()
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
|
||||
|
||||
return Ok(fallback_config);
|
||||
};
|
||||
@ -302,7 +311,7 @@ pub async fn perform_static_routing_v1<F: Clone>(
|
||||
state,
|
||||
merchant_id,
|
||||
&algorithm_id,
|
||||
Some(profile_id).cloned(),
|
||||
business_profile.profile_id.clone(),
|
||||
&api_enums::TransactionType::from(transaction_data),
|
||||
)
|
||||
.await?;
|
||||
@ -333,15 +342,10 @@ async fn ensure_algorithm_cached_v1(
|
||||
state: &SessionState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
algorithm_id: &str,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
) -> RoutingResult<Arc<CachedAlgorithm>> {
|
||||
let key = {
|
||||
let profile_id = profile_id
|
||||
.clone()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::RoutingError::ProfileIdMissing)?;
|
||||
|
||||
match transaction_type {
|
||||
common_enums::TransactionType::Payment => {
|
||||
format!(
|
||||
@ -421,15 +425,12 @@ pub async fn refresh_routing_cache_v1(
|
||||
state: &SessionState,
|
||||
key: String,
|
||||
algorithm_id: &str,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
) -> RoutingResult<Arc<CachedAlgorithm>> {
|
||||
let algorithm = {
|
||||
let algorithm = state
|
||||
.store
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
&profile_id.unwrap_or_default(),
|
||||
algorithm_id,
|
||||
)
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, algorithm_id)
|
||||
.await
|
||||
.change_context(errors::RoutingError::DslMissingInDb)?;
|
||||
let algorithm: routing_types::RoutingAlgorithm = algorithm
|
||||
@ -506,16 +507,12 @@ pub fn perform_volume_split(
|
||||
pub async fn get_merchant_cgraph<'a>(
|
||||
state: &SessionState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
) -> RoutingResult<Arc<hyperswitch_constraint_graph::ConstraintGraph<euclid_dir::DirValue>>> {
|
||||
let merchant_id = &key_store.merchant_id;
|
||||
|
||||
let key = {
|
||||
let profile_id = profile_id
|
||||
.clone()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::RoutingError::ProfileIdMissing)?;
|
||||
match transaction_type {
|
||||
api_enums::TransactionType::Payment => {
|
||||
format!("cgraph_{}_{}", merchant_id.get_string_repr(), profile_id)
|
||||
@ -549,7 +546,7 @@ pub async fn refresh_cgraph_cache<'a>(
|
||||
state: &SessionState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
key: String,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
) -> RoutingResult<Arc<hyperswitch_constraint_graph::ConstraintGraph<euclid_dir::DirValue>>> {
|
||||
let mut merchant_connector_accounts = state
|
||||
@ -588,7 +585,7 @@ pub async fn refresh_cgraph_cache<'a>(
|
||||
let merchant_connector_accounts =
|
||||
payments_oss::helpers::filter_mca_based_on_profile_and_connector_type(
|
||||
merchant_connector_accounts,
|
||||
profile_id.as_ref(),
|
||||
&profile_id,
|
||||
connector_type,
|
||||
);
|
||||
|
||||
@ -648,7 +645,7 @@ async fn perform_cgraph_filtering(
|
||||
chosen: Vec<routing_types::RoutableConnectorChoice>,
|
||||
backend_input: dsl_inputs::BackendInput,
|
||||
eligible_connectors: Option<&Vec<api_enums::RoutableConnectors>>,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
let context = euclid_graph::AnalysisContext::from_dir_values(
|
||||
@ -692,7 +689,7 @@ pub async fn perform_eligibility_analysis<F: Clone>(
|
||||
chosen: Vec<routing_types::RoutableConnectorChoice>,
|
||||
transaction_data: &routing::TransactionData<'_, F>,
|
||||
eligible_connectors: Option<&Vec<api_enums::RoutableConnectors>>,
|
||||
profile_id: Option<String>,
|
||||
profile_id: String,
|
||||
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
let backend_input = match transaction_data {
|
||||
routing::TransactionData::Payment(payment_data) => make_dsl_input(payment_data)?,
|
||||
@ -717,9 +714,13 @@ pub async fn perform_fallback_routing<F: Clone>(
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
transaction_data: &routing::TransactionData<'_, F>,
|
||||
eligible_connectors: Option<&Vec<api_enums::RoutableConnectors>>,
|
||||
profile_id: Option<String>,
|
||||
business_profile: &domain::BusinessProfile,
|
||||
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
let fallback_config = routing_helpers::get_merchant_default_config(
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
let fallback_config = routing::helpers::get_merchant_default_config(
|
||||
&*state.store,
|
||||
match transaction_data {
|
||||
routing::TransactionData::Payment(payment_data) => payment_data
|
||||
@ -735,7 +736,14 @@ pub async fn perform_fallback_routing<F: Clone>(
|
||||
)
|
||||
.await
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
let fallback_config = admin::BusinessProfileWrapper::new(business_profile.clone())
|
||||
.get_default_fallback_list_of_connector_under_profile()
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
|
||||
let backend_input = match transaction_data {
|
||||
routing::TransactionData::Payment(payment_data) => make_dsl_input(payment_data)?,
|
||||
#[cfg(feature = "payouts")]
|
||||
@ -748,7 +756,7 @@ pub async fn perform_fallback_routing<F: Clone>(
|
||||
fallback_config,
|
||||
backend_input,
|
||||
eligible_connectors,
|
||||
profile_id,
|
||||
business_profile.profile_id.clone(),
|
||||
&api_enums::TransactionType::from(transaction_data),
|
||||
)
|
||||
.await
|
||||
@ -760,7 +768,7 @@ pub async fn perform_eligibility_analysis_with_fallback<F: Clone>(
|
||||
chosen: Vec<routing_types::RoutableConnectorChoice>,
|
||||
transaction_data: &routing::TransactionData<'_, F>,
|
||||
eligible_connectors: Option<Vec<api_enums::RoutableConnectors>>,
|
||||
profile_id: Option<String>,
|
||||
business_profile: &domain::BusinessProfile,
|
||||
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
let mut final_selection = perform_eligibility_analysis(
|
||||
state,
|
||||
@ -768,7 +776,7 @@ pub async fn perform_eligibility_analysis_with_fallback<F: Clone>(
|
||||
chosen,
|
||||
transaction_data,
|
||||
eligible_connectors.as_ref(),
|
||||
profile_id.clone(),
|
||||
business_profile.profile_id.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -777,7 +785,7 @@ pub async fn perform_eligibility_analysis_with_fallback<F: Clone>(
|
||||
key_store,
|
||||
transaction_data,
|
||||
eligible_connectors.as_ref(),
|
||||
profile_id,
|
||||
business_profile,
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -831,7 +839,7 @@ pub async fn perform_session_flow_routing(
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
let routing_algorithm =
|
||||
MerchantAccountRoutingAlgorithm::V1(business_profile.routing_algorithm_id);
|
||||
MerchantAccountRoutingAlgorithm::V1(business_profile.routing_algorithm_id.clone());
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
@ -929,11 +937,14 @@ pub async fn perform_session_flow_routing(
|
||||
routing_algorithm: &routing_algorithm,
|
||||
backend_input: backend_input.clone(),
|
||||
allowed_connectors,
|
||||
|
||||
profile_id: session_input.payment_intent.profile_id.clone(),
|
||||
profile_id: profile_id.clone(),
|
||||
};
|
||||
let routable_connector_choice_option =
|
||||
perform_session_routing_for_pm_type(&session_pm_input, transaction_type).await?;
|
||||
let routable_connector_choice_option = perform_session_routing_for_pm_type(
|
||||
&session_pm_input,
|
||||
transaction_type,
|
||||
&business_profile,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(routable_connector_choice) = routable_connector_choice_option {
|
||||
let mut session_routing_choice: Vec<routing_types::SessionRoutingChoice> = Vec::new();
|
||||
@ -964,26 +975,20 @@ pub async fn perform_session_flow_routing(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
async fn perform_session_routing_for_pm_type(
|
||||
session_pm_input: &SessionRoutingPmTypeInput<'_>,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
_business_profile: &domain::BusinessProfile,
|
||||
) -> RoutingResult<Option<Vec<api_models::routing::RoutableConnectorChoice>>> {
|
||||
let merchant_id = &session_pm_input.key_store.merchant_id;
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
|
||||
let algorithm_id = match session_pm_input.routing_algorithm {
|
||||
MerchantAccountRoutingAlgorithm::V1(algorithm_ref) => &algorithm_ref.algorithm_id,
|
||||
};
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
let algorithm_id = match session_pm_input.routing_algorithm {
|
||||
MerchantAccountRoutingAlgorithm::V1(algorithm_id) => algorithm_id,
|
||||
};
|
||||
|
||||
let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id {
|
||||
let cached_algorithm = ensure_algorithm_cached_v1(
|
||||
@ -1008,13 +1013,9 @@ async fn perform_session_routing_for_pm_type(
|
||||
)?,
|
||||
}
|
||||
} else {
|
||||
routing_helpers::get_merchant_default_config(
|
||||
routing::helpers::get_merchant_default_config(
|
||||
&*session_pm_input.state.clone().store,
|
||||
session_pm_input
|
||||
.profile_id
|
||||
.as_ref()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::RoutingError::ProfileIdMissing)?,
|
||||
&session_pm_input.profile_id,
|
||||
transaction_type,
|
||||
)
|
||||
.await
|
||||
@ -1033,13 +1034,9 @@ async fn perform_session_routing_for_pm_type(
|
||||
.await?;
|
||||
|
||||
if final_selection.is_empty() {
|
||||
let fallback = routing_helpers::get_merchant_default_config(
|
||||
let fallback = routing::helpers::get_merchant_default_config(
|
||||
&*session_pm_input.state.clone().store,
|
||||
session_pm_input
|
||||
.profile_id
|
||||
.as_ref()
|
||||
.get_required_value("profile_id")
|
||||
.change_context(errors::RoutingError::ProfileIdMissing)?,
|
||||
&session_pm_input.profile_id,
|
||||
transaction_type,
|
||||
)
|
||||
.await
|
||||
@ -1064,6 +1061,83 @@ async fn perform_session_routing_for_pm_type(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
async fn perform_session_routing_for_pm_type(
|
||||
session_pm_input: &SessionRoutingPmTypeInput<'_>,
|
||||
transaction_type: &api_enums::TransactionType,
|
||||
business_profile: &domain::BusinessProfile,
|
||||
) -> RoutingResult<Option<Vec<api_models::routing::RoutableConnectorChoice>>> {
|
||||
let merchant_id = &session_pm_input.key_store.merchant_id;
|
||||
|
||||
let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm;
|
||||
|
||||
let profile_wrapper = admin::BusinessProfileWrapper::new(business_profile.clone());
|
||||
let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id {
|
||||
let cached_algorithm = ensure_algorithm_cached_v1(
|
||||
&session_pm_input.state.clone(),
|
||||
merchant_id,
|
||||
algorithm_id,
|
||||
session_pm_input.profile_id.clone(),
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match cached_algorithm.as_ref() {
|
||||
CachedAlgorithm::Single(conn) => vec![(**conn).clone()],
|
||||
CachedAlgorithm::Priority(plist) => plist.clone(),
|
||||
CachedAlgorithm::VolumeSplit(splits) => {
|
||||
perform_volume_split(splits.to_vec(), Some(session_pm_input.attempt_id))
|
||||
.change_context(errors::RoutingError::ConnectorSelectionFailed)?
|
||||
}
|
||||
CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1(
|
||||
session_pm_input.backend_input.clone(),
|
||||
interpreter,
|
||||
)?,
|
||||
}
|
||||
} else {
|
||||
profile_wrapper
|
||||
.get_default_fallback_list_of_connector_under_profile()
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?
|
||||
};
|
||||
|
||||
let mut final_selection = perform_cgraph_filtering(
|
||||
&session_pm_input.state.clone(),
|
||||
session_pm_input.key_store,
|
||||
chosen_connectors,
|
||||
session_pm_input.backend_input.clone(),
|
||||
None,
|
||||
session_pm_input.profile_id.clone(),
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if final_selection.is_empty() {
|
||||
let fallback = profile_wrapper
|
||||
.get_default_fallback_list_of_connector_under_profile()
|
||||
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
|
||||
|
||||
final_selection = perform_cgraph_filtering(
|
||||
&session_pm_input.state.clone(),
|
||||
session_pm_input.key_store,
|
||||
fallback,
|
||||
session_pm_input.backend_input.clone(),
|
||||
None,
|
||||
session_pm_input.profile_id.clone(),
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
if final_selection.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(final_selection))
|
||||
}
|
||||
}
|
||||
pub fn make_dsl_input_for_surcharge(
|
||||
payment_attempt: &oss_storage::PaymentAttempt,
|
||||
payment_intent: &oss_storage::PaymentIntent,
|
||||
|
||||
@ -300,7 +300,7 @@ pub async fn filter_payout_methods(
|
||||
// Filter MCAs based on profile_id and connector_type
|
||||
let filtered_mcas = helpers::filter_mca_based_on_profile_and_connector_type(
|
||||
all_mcas,
|
||||
Some(&payout.profile_id),
|
||||
&payout.profile_id,
|
||||
common_enums::ConnectorType::PayoutProcessor,
|
||||
);
|
||||
let address = payout
|
||||
|
||||
@ -33,6 +33,7 @@ use crate::{
|
||||
route_connector_v1, routing, CustomerDetails,
|
||||
},
|
||||
routing::TransactionData,
|
||||
utils as core_utils,
|
||||
},
|
||||
db::StorageInterface,
|
||||
routes::{metrics, SessionState},
|
||||
@ -745,6 +746,17 @@ pub async fn decide_payout_connector(
|
||||
return Ok(api::ConnectorCallType::PreDetermined(connector_data));
|
||||
}
|
||||
|
||||
// Validate and get the business_profile from payout_attempt
|
||||
let business_profile = core_utils::validate_and_get_business_profile(
|
||||
state.store.as_ref(),
|
||||
&(state).into(),
|
||||
key_store,
|
||||
Some(&payout_attempt.profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")?;
|
||||
|
||||
// 2. Check routing algorithm passed in the request
|
||||
if let Some(routing_algorithm) = request_straight_through {
|
||||
let (mut connectors, check_eligibility) =
|
||||
@ -759,7 +771,7 @@ pub async fn decide_payout_connector(
|
||||
connectors,
|
||||
&TransactionData::<()>::Payout(payout_data),
|
||||
eligible_connectors,
|
||||
Some(payout_attempt.profile_id.clone()),
|
||||
&business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -807,7 +819,7 @@ pub async fn decide_payout_connector(
|
||||
connectors,
|
||||
&TransactionData::<()>::Payout(payout_data),
|
||||
eligible_connectors,
|
||||
Some(payout_attempt.profile_id.clone()),
|
||||
&business_profile,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
|
||||
@ -706,7 +706,7 @@ pub async fn update_default_fallback_routing(
|
||||
},
|
||||
)?;
|
||||
profile_wrapper
|
||||
.update_default_routing_for_profile(
|
||||
.update_default_fallback_routing_of_connectors_under_profile(
|
||||
db,
|
||||
&updated_list_of_connectors,
|
||||
key_manager_state,
|
||||
@ -949,7 +949,7 @@ pub async fn retrieve_linked_routing_config(
|
||||
routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms),
|
||||
))
|
||||
}
|
||||
|
||||
// List all the default fallback algorithms under all the profile under a merchant
|
||||
pub async fn retrieve_default_routing_config_for_profiles(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
|
||||
Reference in New Issue
Block a user