mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(router): Create a merchant config for enable processor agnostic MIT (#4025)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -11,7 +11,7 @@ pub use euclid::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::enums::{self, RoutableConnectors, TransactionType};
|
||||
use crate::enums::{RoutableConnectors, TransactionType};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
@ -299,34 +299,12 @@ impl From<RoutableConnectorChoice> for ast::ConnectorChoice {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct DetailedConnectorChoice {
|
||||
pub connector: RoutableConnectors,
|
||||
pub business_label: Option<String>,
|
||||
pub business_country: Option<enums::CountryAlpha2>,
|
||||
pub business_sub_label: Option<String>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl DetailedConnectorChoice {
|
||||
pub fn get_connector_label(&self) -> Option<String> {
|
||||
self.business_country
|
||||
.as_ref()
|
||||
.zip(self.business_label.as_ref())
|
||||
.map(|(business_country, business_label)| {
|
||||
let mut base_label = format!(
|
||||
"{}_{:?}_{}",
|
||||
self.connector, business_country, business_label
|
||||
);
|
||||
|
||||
if let Some(ref sub_label) = self.business_sub_label {
|
||||
base_label.push('_');
|
||||
base_label.push_str(sub_label);
|
||||
}
|
||||
|
||||
base_label
|
||||
})
|
||||
}
|
||||
}
|
||||
impl common_utils::events::ApiEventMetric for DetailedConnectorChoice {}
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
||||
@ -9,7 +9,6 @@ use api_models::{
|
||||
};
|
||||
#[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;
|
||||
@ -807,6 +806,37 @@ pub async fn retrieve_linked_routing_config(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upsert_connector_agnostic_mandate_config(
|
||||
state: AppState,
|
||||
business_profile_id: &str,
|
||||
mandate_config: routing_types::DetailedConnectorChoice,
|
||||
) -> RouterResponse<routing_types::DetailedConnectorChoice> {
|
||||
let key = helpers::get_pg_agnostic_mandate_config_key(business_profile_id);
|
||||
|
||||
let mandate_config_str = mandate_config.enabled.to_string();
|
||||
|
||||
let find_config = state
|
||||
.store
|
||||
.find_config_by_key_unwrap_or(&key, Some(mandate_config_str.clone()))
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("error saving pg agnostic mandate config to db")?;
|
||||
|
||||
if find_config.config != mandate_config_str {
|
||||
let config_update = configs::ConfigUpdate::Update {
|
||||
config: Some(mandate_config_str),
|
||||
};
|
||||
state
|
||||
.store
|
||||
.update_config_by_key(&key, config_update)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("error saving pg agnostic mandate config to db")?;
|
||||
}
|
||||
|
||||
Ok(service_api::ApplicationResponse::Json(mandate_config))
|
||||
}
|
||||
|
||||
pub async fn retrieve_default_routing_config_for_profiles(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
|
||||
@ -273,9 +273,9 @@ pub async fn update_business_profile_active_algorithm_ref(
|
||||
|
||||
pub async fn get_merchant_connector_agnostic_mandate_config(
|
||||
db: &dyn StorageInterface,
|
||||
merchant_id: &str,
|
||||
business_profile_id: &str,
|
||||
) -> RouterResult<Vec<routing_types::DetailedConnectorChoice>> {
|
||||
let key = get_pg_agnostic_mandate_config_key(merchant_id);
|
||||
let key = get_pg_agnostic_mandate_config_key(business_profile_id);
|
||||
let maybe_config = db.find_config_by_key(&key).await;
|
||||
|
||||
match maybe_config {
|
||||
@ -312,29 +312,6 @@ pub async fn get_merchant_connector_agnostic_mandate_config(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_merchant_connector_agnostic_mandate_config(
|
||||
db: &dyn StorageInterface,
|
||||
merchant_id: &str,
|
||||
mandate_config: Vec<routing_types::DetailedConnectorChoice>,
|
||||
) -> RouterResult<Vec<routing_types::DetailedConnectorChoice>> {
|
||||
let key = get_pg_agnostic_mandate_config_key(merchant_id);
|
||||
let mandate_config_str = mandate_config
|
||||
.encode_to_string_of_json()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to serialize pg agnostic mandate config during update")?;
|
||||
|
||||
let config_update = configs::ConfigUpdate::Update {
|
||||
config: Some(mandate_config_str),
|
||||
};
|
||||
|
||||
db.update_config_by_key(&key, config_update)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("error saving pg agnostic mandate config to db")?;
|
||||
|
||||
Ok(mandate_config)
|
||||
}
|
||||
|
||||
pub async fn validate_connectors_in_routing_config(
|
||||
db: &dyn StorageInterface,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
@ -465,8 +442,8 @@ pub fn get_routing_dictionary_key(merchant_id: &str) -> String {
|
||||
|
||||
/// Provides the identifier for the specific merchant's agnostic_mandate_config
|
||||
#[inline(always)]
|
||||
pub fn get_pg_agnostic_mandate_config_key(merchant_id: &str) -> String {
|
||||
format!("pg_agnostic_mandate_{merchant_id}")
|
||||
pub fn get_pg_agnostic_mandate_config_key(business_profile_id: &str) -> String {
|
||||
format!("pg_agnostic_mandate_{business_profile_id}")
|
||||
}
|
||||
|
||||
/// Provides the identifier for the specific merchant's default_config
|
||||
|
||||
@ -437,6 +437,10 @@ impl Routing {
|
||||
)
|
||||
})),
|
||||
)
|
||||
.service(
|
||||
web::resource("/business_profile/{business_profile_id}/configs/pg_agnostic_mit")
|
||||
.route(web::post().to(cloud_routing::upsert_connector_agnostic_mandate_config)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/default")
|
||||
.route(web::get().to(|state, req| {
|
||||
|
||||
@ -225,6 +225,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::ReconTokenRequest
|
||||
| Flow::ReconServiceRequest
|
||||
| Flow::ReconVerifyToken => Self::Recon,
|
||||
Flow::CreateConnectorAgnosticMandateConfig => Self::Routing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,6 +566,44 @@ pub async fn routing_retrieve_linked_config(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn upsert_connector_agnostic_mandate_config(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
json_payload: web::Json<routing_types::DetailedConnectorChoice>,
|
||||
path: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
use crate::services::authentication::AuthenticationData;
|
||||
|
||||
let flow = Flow::CreateConnectorAgnosticMandateConfig;
|
||||
let business_profile_id = path.into_inner();
|
||||
|
||||
Box::pin(oss_api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
|state, _auth: AuthenticationData, mandate_config| {
|
||||
Box::pin(routing::upsert_connector_agnostic_mandate_config(
|
||||
state,
|
||||
&business_profile_id,
|
||||
mandate_config,
|
||||
))
|
||||
},
|
||||
#[cfg(not(feature = "release"))]
|
||||
auth::auth_type(
|
||||
&auth::ApiKeyAuth,
|
||||
&auth::JWTAuth(Permission::RoutingWrite),
|
||||
req.headers(),
|
||||
),
|
||||
#[cfg(feature = "release")]
|
||||
&auth::JWTAuth(Permission::RoutingWrite),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn routing_retrieve_default_config_for_profiles(
|
||||
|
||||
@ -206,6 +206,8 @@ pub enum Flow {
|
||||
RoutingRetrieveConfig,
|
||||
/// Routing retrieve active config
|
||||
RoutingRetrieveActiveConfig,
|
||||
/// Update connector agnostic mandate config
|
||||
CreateConnectorAgnosticMandateConfig,
|
||||
/// Routing retrieve default config
|
||||
RoutingRetrieveDefaultConfig,
|
||||
/// Routing retrieve dictionary
|
||||
|
||||
Reference in New Issue
Block a user