Skip to content
24 changes: 24 additions & 0 deletions crates/api_models/src/events/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,27 @@ impl ApiEventMetric for RuleMigrationError {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for crate::open_router::DecideGatewayResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for crate::open_router::OpenRouterDecideGatewayRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for crate::open_router::UpdateScorePayload {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for crate::open_router::UpdateScoreResponse {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}
35 changes: 35 additions & 0 deletions crates/api_models/src/open_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,41 @@ pub struct OpenRouterDecideGatewayRequest {
pub elimination_enabled: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecideGatewayResponse {
pub decided_gateway: Option<String>,
pub gateway_priority_map: Option<serde_json::Value>,
pub filter_wise_gateways: Option<serde_json::Value>,
pub priority_logic_tag: Option<String>,
pub routing_approach: Option<String>,
pub gateway_before_evaluation: Option<String>,
pub priority_logic_output: Option<PriorityLogicOutput>,
pub reset_approach: Option<String>,
pub routing_dimension: Option<String>,
pub routing_dimension_level: Option<String>,
pub is_scheduled_outage: Option<bool>,
pub is_dynamic_mga_enabled: Option<bool>,
pub gateway_mga_id_map: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PriorityLogicOutput {
pub is_enforcement: Option<bool>,
pub gws: Option<Vec<String>>,
pub priority_logic_tag: Option<String>,
pub gateway_reference_ids: Option<HashMap<String, String>>,
pub primary_logic: Option<PriorityLogicData>,
pub fallback_logic: Option<PriorityLogicData>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriorityLogicData {
pub name: Option<String>,
pub status: Option<String>,
pub failure_reason: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RankingAlgorithm {
Expand Down
2 changes: 1 addition & 1 deletion crates/router/src/core/payments/routing/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ where
String(String),
}

#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct RoutingEventsResponse<Res>
where
Res: Serialize + serde::de::DeserializeOwned + Clone,
Expand Down
55 changes: 54 additions & 1 deletion crates/router/src/core/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ use std::collections::HashSet;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use api_models::routing::DynamicRoutingAlgoAccessor;
use api_models::{
enums, mandates as mandates_api, routing,
enums, mandates as mandates_api,
open_router::{
DecideGatewayResponse, OpenRouterDecideGatewayRequest, UpdateScorePayload,
UpdateScoreResponse,
},
routing,
routing::{
self as routing_types, RoutingRetrieveQuery, RuleMigrationError, RuleMigrationResponse,
},
};
use async_trait::async_trait;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use common_utils::ext_traits::AsyncExt;
use common_utils::request::Method;
use diesel_models::routing_algorithm::RoutingAlgorithm;
use error_stack::ResultExt;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
Expand Down Expand Up @@ -2621,3 +2627,50 @@ pub async fn migrate_rules_for_profile(
errors: error_list,
})
}

pub async fn decide_gateway_open_router(
state: SessionState,
// merchant_context: domain::MerchantContext,
req_body: OpenRouterDecideGatewayRequest,
) -> RouterResponse<DecideGatewayResponse> {
let response = SRApiClient::send_decision_engine_request(
&state,
Method::Post,
"decide-gateway",
Some(req_body),
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?
.response
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to perform decide gateway call with open router")?;

Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
response,
))
}

pub async fn update_gateway_score_open_router(
state: SessionState,
req_body: UpdateScorePayload,
) -> RouterResponse<UpdateScoreResponse> {
let response: UpdateScoreResponse = SRApiClient::send_decision_engine_request(
&state,
Method::Post,
"update-gateway-score",
Some(req_body),
None,
None,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?
.response
.ok_or(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to perform update gateway score call with open router")?;

Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
response,
))
}
13 changes: 13 additions & 0 deletions crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,19 @@ impl Routing {
})),
);

#[cfg(feature = "dynamic_routing")]
{
route = route
.service(
web::resource("/evaluate")
.route(web::post().to(routing::call_decide_gateway_open_router)),
)
.service(
web::resource("/feedback")
.route(web::post().to(routing::call_update_gateway_score_open_router)),
)
}

#[cfg(feature = "payouts")]
{
route = route
Expand Down
4 changes: 3 additions & 1 deletion crates/router/src/routes/lock_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ impl From<Flow> for ApiIdentifier {
| Flow::UpdateDynamicRoutingConfigs
| Flow::DecisionManagerUpsertConfig
| Flow::DecisionEngineRuleMigration
| Flow::VolumeSplitOnRoutingType => Self::Routing,
| Flow::VolumeSplitOnRoutingType
| Flow::DecisionEngineDecideGatewayCall
| Flow::DecisionEngineGatewayFeedbackCall => Self::Routing,

Flow::RetrieveForexFlow => Self::Forex,

Expand Down
48 changes: 48 additions & 0 deletions crates/router/src/routes/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1622,3 +1622,51 @@ async fn get_merchant_account(
.to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?;
Ok((key_store, merchant_account))
}

#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn call_decide_gateway_open_router(
state: web::Data<AppState>,
req: HttpRequest,
payload: web::Json<api_models::open_router::OpenRouterDecideGatewayRequest>,
) -> impl Responder {
let flow = Flow::DecisionEngineDecideGatewayCall;
Box::pin(oss_api::server_wrap(
flow,
state,
&req,
payload.clone(),
|state, _auth, payload, _| routing::decide_gateway_open_router(state.clone(), payload),
&auth::ApiKeyAuth {
is_connected_allowed: false,
is_platform_allowed: false,
},
api_locking::LockAction::NotApplicable,
))
.await
}

#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
#[instrument(skip_all)]
pub async fn call_update_gateway_score_open_router(
state: web::Data<AppState>,
req: HttpRequest,
payload: web::Json<api_models::open_router::UpdateScorePayload>,
) -> impl Responder {
let flow = Flow::DecisionEngineGatewayFeedbackCall;
Box::pin(oss_api::server_wrap(
flow,
state,
&req,
payload.clone(),
|state, _auth, payload, _| {
routing::update_gateway_score_open_router(state.clone(), payload)
},
&auth::ApiKeyAuth {
is_connected_allowed: false,
is_platform_allowed: false,
},
api_locking::LockAction::NotApplicable,
))
.await
}
4 changes: 4 additions & 0 deletions crates/router_env/src/logger/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,10 @@ pub enum Flow {
ThreeDsDecisionRuleExecute,
/// Incoming Network Token Webhook Receive
IncomingNetworkTokenWebhookReceive,
/// Decision Engine Decide Gateway Call
DecisionEngineDecideGatewayCall,
/// Decision Engine Gateway Feedback Call
DecisionEngineGatewayFeedbackCall,
}

/// Trait for providing generic behaviour to flow metric
Expand Down
Loading