Skip to content

Commit

Permalink
feat(router): profile specific fallback derivation while routing paym…
Browse files Browse the repository at this point in the history
…ents (#2806)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aprabhat19 <[email protected]>
Co-authored-by: Amisha Prabhat <[email protected]>
  • Loading branch information
4 people authored Nov 13, 2023
1 parent f88eee7 commit 8e538db
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 34 deletions.
16 changes: 14 additions & 2 deletions crates/api_models/src/events/routing.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use common_utils::events::{ApiEventMetric, ApiEventsType};

use crate::routing::{
LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, RoutingAlgorithmId,
RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig,
RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
RoutingPayloadWrapper,
};
#[cfg(feature = "business_profile_routing")]
use crate::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery};
Expand Down Expand Up @@ -37,6 +38,17 @@ impl ApiEventMetric for LinkedRoutingConfigRetrieveResponse {
}
}

impl ApiEventMetric for RoutingPayloadWrapper {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}
impl ApiEventMetric for ProfileDefaultRoutingConfig {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

#[cfg(feature = "business_profile_routing")]
impl ApiEventMetric for RoutingRetrieveQuery {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Expand Down
13 changes: 13 additions & 0 deletions crates/api_models/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ pub struct RoutingConfigRequest {
pub profile_id: Option<String>,
}

#[derive(Debug, serde::Serialize)]
pub struct ProfileDefaultRoutingConfig {
pub profile_id: String,
pub connectors: Vec<RoutableConnectorChoice>,
}

#[cfg(feature = "business_profile_routing")]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct RoutingRetrieveQuery {
Expand Down Expand Up @@ -389,6 +395,13 @@ pub enum RoutingAlgorithmKind {
Advanced,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]

pub struct RoutingPayloadWrapper {
pub updated_config: Vec<RoutableConnectorChoice>,
pub profile_id: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(
tag = "type",
Expand Down
5 changes: 3 additions & 2 deletions crates/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ readme = "README.md"
license.workspace = true

[features]
default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts"]
default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "profile_specific_fallback_routing"]
s3 = ["dep:aws-sdk-s3", "dep:aws-config"]
kms = ["external_services/kms", "dep:aws-config"]
email = ["external_services/email", "dep:aws-config"]
basilisk = ["kms"]
stripe = ["dep:serde_qs"]
release = ["kms", "stripe", "basilisk", "s3", "email", "business_profile_routing", "accounts_cache", "kv_store", "olap"]
release = ["kms", "stripe", "basilisk", "s3", "email", "business_profile_routing", "accounts_cache", "kv_store", "profile_specific_fallback_routing"]
olap = ["data_models/olap", "storage_impl/olap", "scheduler/olap"]
oltp = ["data_models/oltp", "storage_impl/oltp"]
kv_store = ["scheduler/kv_store"]
Expand All @@ -24,6 +24,7 @@ openapi = ["olap", "oltp", "payouts"]
vergen = ["router_env/vergen"]
backwards_compatibility = ["api_models/backwards_compatibility", "euclid/backwards_compatibility", "kgraph_utils/backwards_compatibility"]
business_profile_routing=["api_models/business_profile_routing"]
profile_specific_fallback_routing = []
dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector", "kgraph_utils/dummy_connector"]
connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connector_choice_mca_id", "kgraph_utils/connector_choice_mca_id"]
external_access_dc = ["dummy_connector"]
Expand Down
16 changes: 14 additions & 2 deletions crates/router/src/core/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,13 +916,16 @@ pub async fn create_payment_connector(
let mut default_routing_config =
routing_helpers::get_merchant_default_config(&*state.store, merchant_id).await?;

let mut default_routing_config_for_profile =
routing_helpers::get_merchant_default_config(&*state.clone().store, &profile_id).await?;

let mca = state
.store
.insert_merchant_connector_account(merchant_connector_account, &key_store)
.await
.to_duplicate_response(
errors::ApiErrorResponse::DuplicateMerchantConnectorAccount {
profile_id,
profile_id: profile_id.clone(),
connector_name: req.connector_name.to_string(),
},
)?;
Expand All @@ -939,14 +942,23 @@ pub async fn create_payment_connector(
};

if !default_routing_config.contains(&choice) {
default_routing_config.push(choice);
default_routing_config.push(choice.clone());
routing_helpers::update_merchant_default_config(
&*state.store,
merchant_id,
default_routing_config,
)
.await?;
}
if !default_routing_config_for_profile.contains(&choice.clone()) {
default_routing_config_for_profile.push(choice);
routing_helpers::update_merchant_default_config(
&*state.store,
&profile_id.clone(),
default_routing_config_for_profile,
)
.await?;
}
}

metrics::MCA_CREATE.add(
Expand Down
70 changes: 59 additions & 11 deletions crates/router/src/core/payments/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ pub struct SessionRoutingPmTypeInput<'a> {
routing_algorithm: &'a MerchantAccountRoutingAlgorithm,
backend_input: dsl_inputs::BackendInput,
allowed_connectors: FxHashMap<String, api::GetToken>,
#[cfg(feature = "business_profile_routing")]
#[cfg(any(
feature = "business_profile_routing",
feature = "profile_specific_fallback_routing"
))]
profile_id: Option<String>,
}
static ROUTING_CACHE: StaticCache<CachedAlgorithm> = StaticCache::new();
Expand Down Expand Up @@ -207,10 +210,22 @@ pub async fn perform_static_routing_v1<F: Clone>(
let algorithm_id = if let Some(id) = algorithm_ref.algorithm_id {
id
} else {
let fallback_config =
routing_helpers::get_merchant_default_config(&*state.clone().store, merchant_id)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
let fallback_config = routing_helpers::get_merchant_default_config(
&*state.clone().store,
#[cfg(not(feature = "profile_specific_fallback_routing"))]
merchant_id,
#[cfg(feature = "profile_specific_fallback_routing")]
{
payment_data
.payment_intent
.profile_id
.as_ref()
.get_required_value("profile_id")
.change_context(errors::RoutingError::ProfileIdMissing)?
},
)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;

return Ok(fallback_config);
};
Expand Down Expand Up @@ -616,10 +631,22 @@ pub async fn perform_fallback_routing<F: Clone>(
eligible_connectors: Option<&Vec<api_enums::RoutableConnectors>>,
#[cfg(feature = "business_profile_routing")] profile_id: Option<String>,
) -> RoutingResult<Vec<routing_types::RoutableConnectorChoice>> {
let fallback_config =
routing_helpers::get_merchant_default_config(&*state.store, &key_store.merchant_id)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
let fallback_config = routing_helpers::get_merchant_default_config(
&*state.store,
#[cfg(not(feature = "profile_specific_fallback_routing"))]
&key_store.merchant_id,
#[cfg(feature = "profile_specific_fallback_routing")]
{
payment_data
.payment_intent
.profile_id
.as_ref()
.get_required_value("profile_id")
.change_context(errors::RoutingError::ProfileIdMissing)?
},
)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
let backend_input = make_dsl_input(payment_data)?;

perform_kgraph_filtering(
Expand Down Expand Up @@ -819,8 +846,11 @@ pub async fn perform_session_flow_routing(
routing_algorithm: &routing_algorithm,
backend_input: backend_input.clone(),
allowed_connectors,
#[cfg(feature = "business_profile_routing")]
profile_id: session_input.payment_intent.clone().profile_id,
#[cfg(any(
feature = "business_profile_routing",
feature = "profile_specific_fallback_routing"
))]
profile_id: session_input.payment_intent.profile_id.clone(),
};
let maybe_choice = perform_session_routing_for_pm_type(session_pm_input).await?;

Expand Down Expand Up @@ -880,7 +910,16 @@ async fn perform_session_routing_for_pm_type(
} else {
routing_helpers::get_merchant_default_config(
&*session_pm_input.state.clone().store,
#[cfg(not(feature = "profile_specific_fallback_routing"))]
merchant_id,
#[cfg(feature = "profile_specific_fallback_routing")]
{
session_pm_input
.profile_id
.as_ref()
.get_required_value("profile_id")
.change_context(errors::RoutingError::ProfileIdMissing)?
},
)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?
Expand All @@ -903,7 +942,16 @@ async fn perform_session_routing_for_pm_type(
if final_selection.is_empty() {
let fallback = routing_helpers::get_merchant_default_config(
&*session_pm_input.state.clone().store,
#[cfg(not(feature = "profile_specific_fallback_routing"))]
merchant_id,
#[cfg(feature = "profile_specific_fallback_routing")]
{
session_pm_input
.profile_id
.as_ref()
.get_required_value("profile_id")
.change_context(errors::RoutingError::ProfileIdMissing)?
},
)
.await
.change_context(errors::RoutingError::FallbackConfigFetchFailed)?;
Expand Down
Loading

0 comments on commit 8e538db

Please sign in to comment.