Bug: feat(routing): Add new endpoints for evaluate, feedback, and rule-evaluate



Adds api refs for routing APIs
diff --git a/crates/api_models/src/open_router.rs b/crates/api_models/src/open_router.rs
index aefbd3eb211..02ded9713f6 100644
--- a/crates/api_models/src/open_router.rs
+++ b/crates/api_models/src/open_router.rs
@@ -16,17 +16,18 @@ use crate::{
     payment_methods,
 };
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct OpenRouterDecideGatewayRequest {
     pub payment_info: PaymentInfo,
+    #[schema(value_type = String)]
     pub merchant_id: id_type::ProfileId,
     pub eligible_gateway_list: Option<Vec<String>>,
     pub ranking_algorithm: Option<RankingAlgorithm>,
     pub elimination_enabled: Option<bool>,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 pub struct DecideGatewayResponse {
     pub decided_gateway: Option<String>,
     pub gateway_priority_map: Option<serde_json::Value>,
@@ -43,7 +44,7 @@ pub struct DecideGatewayResponse {
     pub gateway_mga_id_map: Option<serde_json::Value>,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct PriorityLogicOutput {
     pub is_enforcement: Option<bool>,
@@ -54,14 +55,14 @@ pub struct PriorityLogicOutput {
     pub fallback_logic: Option<PriorityLogicData>,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 pub struct PriorityLogicData {
     pub name: Option<String>,
     pub status: Option<String>,
     pub failure_reason: Option<String>,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
 #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
 pub enum RankingAlgorithm {
     SrBasedRouting,
@@ -69,9 +70,10 @@ pub enum RankingAlgorithm {
     NtwBasedRouting,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct PaymentInfo {
+    #[schema(value_type = String)]
     pub payment_id: id_type::PaymentId,
     pub amount: MinorUnit,
     pub currency: Currency,
@@ -189,21 +191,23 @@ pub struct UnifiedError {
     pub developer_message: String,
 }
 
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct UpdateScorePayload {
+    #[schema(value_type = String)]
     pub merchant_id: id_type::ProfileId,
     pub gateway: String,
     pub status: TxnStatus,
+    #[schema(value_type = String)]
     pub payment_id: id_type::PaymentId,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
 pub struct UpdateScoreResponse {
     pub message: String,
 }
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
 #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
 pub enum TxnStatus {
     Started,
diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs
index a900d841501..ef5a9422e20 100644
--- a/crates/api_models/src/routing.rs
+++ b/crates/api_models/src/routing.rs
@@ -1444,3 +1444,122 @@ pub enum RoutingResultSource {
     /// Inbuilt Hyperswitch Routing Engine
     HyperswitchRouting,
 }
+//TODO: temporary change will be refactored afterwards
+#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, ToSchema)]
+pub struct RoutingEvaluateRequest {
+    pub created_by: String,
+    #[schema(value_type = Object)]
+    ///Parameters that can be used in the routing evaluate request.
+    ///eg: {"parameters": {
+    ///    "payment_method": {"type": "enum_variant", "value": "card"},
+    ///    "payment_method_type": {"type": "enum_variant", "value": "credit"},
+    ///    "amount": {"type": "number", "value": 10},
+    ///    "currency": {"type": "str_value", "value": "INR"},
+    ///    "authentication_type": {"type": "enum_variant", "value": "three_ds"},
+    /// "card_bin": {"type": "str_value", "value": "424242"},
+    ///    "capture_method": {"type": "enum_variant", "value": "scheduled"},
+    ///    "business_country": {"type": "str_value", "value": "IN"},
+    ///    "billing_country": {"type": "str_value", "value": "IN"},
+    ///    "business_label": {"type": "str_value", "value": "business_label"},
+    ///    "setup_future_usage": {"type": "enum_variant", "value": "off_session"},
+    ///    "card_network": {"type": "enum_variant", "value": "visa"},
+    ///    "payment_type": {"type": "enum_variant", "value": "recurring_mandate"},
+    ///    "mandate_type": {"type": "enum_variant", "value": "single_use"},
+    ///    "mandate_acceptance_type": {"type": "enum_variant", "value": "online"},
+    ///    "metadata":{"type": "metadata_variant", "value": {"key": "key1", "value": "value1"}}
+    ///  }}
+    pub parameters: std::collections::HashMap<String, Option<ValueType>>,
+    pub fallback_output: Vec<DeRoutableConnectorChoice>,
+}
+impl common_utils::events::ApiEventMetric for RoutingEvaluateRequest {}
+
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)]
+pub struct RoutingEvaluateResponse {
+    pub status: String,
+    pub output: serde_json::Value,
+    #[serde(deserialize_with = "deserialize_connector_choices")]
+    pub evaluated_output: Vec<RoutableConnectorChoice>,
+    #[serde(deserialize_with = "deserialize_connector_choices")]
+    pub eligible_connectors: Vec<RoutableConnectorChoice>,
+}
+impl common_utils::events::ApiEventMetric for RoutingEvaluateResponse {}
+
+fn deserialize_connector_choices<'de, D>(
+    deserializer: D,
+) -> Result<Vec<RoutableConnectorChoice>, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let infos = Vec::<DeRoutableConnectorChoice>::deserialize(deserializer)?;
+    Ok(infos
+        .into_iter()
+        .map(RoutableConnectorChoice::from)
+        .collect())
+}
+
+impl From<DeRoutableConnectorChoice> for RoutableConnectorChoice {
+    fn from(choice: DeRoutableConnectorChoice) -> Self {
+        Self {
+            choice_kind: RoutableChoiceKind::FullStruct,
+            connector: choice.gateway_name,
+            merchant_connector_id: choice.gateway_id,
+        }
+    }
+}
+
+/// Routable Connector chosen for a payment
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct DeRoutableConnectorChoice {
+    pub gateway_name: RoutableConnectors,
+    #[schema(value_type = String)]
+    pub gateway_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
+}
+/// Represents a value in the DSL
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
+#[serde(tag = "type", content = "value", rename_all = "snake_case")]
+pub enum ValueType {
+    /// Represents a number literal
+    Number(u64),
+    /// Represents an enum variant
+    EnumVariant(String),
+    /// Represents a Metadata variant
+    MetadataVariant(MetadataValue),
+    /// Represents a arbitrary String value
+    StrValue(String),
+    /// Represents a global reference, which is a reference to a global variable
+    GlobalRef(String),
+    /// Represents an array of numbers. This is basically used for
+    /// "one of the given numbers" operations
+    /// eg: payment.method.amount = (1, 2, 3)
+    NumberArray(Vec<u64>),
+    /// Similar to NumberArray but for enum variants
+    /// eg: payment.method.cardtype = (debit, credit)
+    EnumVariantArray(Vec<String>),
+    /// Like a number array but can include comparisons. Useful for
+    /// conditions like "500 < amount < 1000"
+    /// eg: payment.amount = (> 500, < 1000)
+    NumberComparisonArray(Vec<NumberComparison>),
+}
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
+pub struct MetadataValue {
+    pub key: String,
+    pub value: String,
+}
+/// Represents a number comparison for "NumberComparisonArrayValue"
+#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct NumberComparison {
+    pub comparison_type: ComparisonType,
+    pub number: u64,
+}
+/// Conditional comparison type
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ComparisonType {
+    Equal,
+    NotEqual,
+    LessThan,
+    LessThanEqual,
+    GreaterThan,
+    GreaterThanEqual,
+}
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index a5e0a6d95c6..d9f525f39fd 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -170,6 +170,9 @@ Never share your secret api keys. Keep them guarded and secure.
         routes::routing::toggle_elimination_routing,
         routes::routing::contract_based_routing_setup_config,
         routes::routing::contract_based_routing_update_configs,
+        routes::routing::call_decide_gateway_open_router,
+        routes::routing::call_update_gateway_score_open_router,
+        routes::routing::evaluate_routing_rule,
 
         // Routes for blocklist
         routes::blocklist::remove_entry_from_blocklist,
@@ -809,6 +812,22 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::authentication::ThreeDsData,
         api_models::authentication::AuthenticationEligibilityRequest,
         api_models::authentication::AuthenticationEligibilityResponse,
+        api_models::open_router::OpenRouterDecideGatewayRequest,
+        api_models::open_router::DecideGatewayResponse,
+        api_models::open_router::UpdateScorePayload,
+        api_models::open_router::UpdateScoreResponse,
+        api_models::routing::RoutingEvaluateRequest,
+        api_models::routing::RoutingEvaluateResponse,
+        api_models::routing::ValueType,
+        api_models::routing::DeRoutableConnectorChoice,
+        api_models::routing::RoutableConnectorChoice,
+        api_models::open_router::PaymentInfo,
+        common_utils::id_type::PaymentId,
+        common_utils::id_type::ProfileId,
+        api_models::open_router::RankingAlgorithm,
+        api_models::open_router::TxnStatus,
+        api_models::open_router::PriorityLogicOutput,
+        api_models::open_router::PriorityLogicData,
         api_models::user::PlatformAccountCreateRequest,
         api_models::user::PlatformAccountCreateResponse,
     )),
diff --git a/crates/openapi/src/routes/routing.rs b/crates/openapi/src/routes/routing.rs
index 78f4c8b187c..234508de37b 100644
--- a/crates/openapi/src/routes/routing.rs
+++ b/crates/openapi/src/routes/routing.rs
@@ -386,3 +386,69 @@ pub async fn contract_based_routing_setup_config() {}
    security(("api_key" = []), ("jwt_key" = []))
 )]
 pub async fn contract_based_routing_update_configs() {}
+
+#[cfg(feature = "v1")]
+/// Routing - Evaluate
+///
+/// Evaluate routing rules
+#[utoipa::path(
+    post,
+    path = "/routing/evaluate",
+    request_body = OpenRouterDecideGatewayRequest,
+    responses(
+        (status = 200, description = "Routing rules evaluated successfully", body = DecideGatewayResponse),
+        (status = 400, description = "Request body is malformed"),
+        (status = 500, description = "Internal server error"),
+        (status = 404, description = "Resource missing"),
+        (status = 422, description = "Unprocessable request"),
+        (status = 403, description = "Forbidden"),
+    ),
+   tag = "Routing",
+   operation_id = "Evaluate routing rules",
+   security(("api_key" = []))
+)]
+pub async fn call_decide_gateway_open_router() {}
+
+#[cfg(feature = "v1")]
+/// Routing - Feedback
+///
+/// Update gateway scores for dynamic routing
+#[utoipa::path(
+    post,
+    path = "/routing/feedback",
+    request_body = UpdateScorePayload,
+    responses(
+        (status = 200, description = "Gateway score updated successfully", body = UpdateScoreResponse),
+        (status = 400, description = "Request body is malformed"),
+        (status = 500, description = "Internal server error"),
+        (status = 404, description = "Resource missing"),
+        (status = 422, description = "Unprocessable request"),
+        (status = 403, description = "Forbidden"),
+    ),
+   tag = "Routing",
+   operation_id = "Update gateway scores",
+   security(("api_key" = []))
+)]
+pub async fn call_update_gateway_score_open_router() {}
+
+#[cfg(feature = "v1")]
+/// Routing - Rule Evaluate
+///
+/// Evaluate routing rules
+#[utoipa::path(
+    post,
+    path = "/routing/rule/evaluate",
+    request_body = RoutingEvaluateRequest,
+    responses(
+        (status = 200, description = "Routing rules evaluated successfully", body = RoutingEvaluateResponse),
+        (status = 400, description = "Request body is malformed"),
+        (status = 500, description = "Internal server error"),
+        (status = 404, description = "Resource missing"),
+        (status = 422, description = "Unprocessable request"),
+        (status = 403, description = "Forbidden"),
+    ),
+   tag = "Routing",
+   operation_id = "Evaluate routing rules (alternative)",
+   security(("api_key" = []))
+)]
+pub async fn evaluate_routing_rule() {}
diff --git a/crates/router/src/core/payments/routing/utils.rs b/crates/router/src/core/payments/routing/utils.rs
index 5eb929a92d0..d9d5ed1de26 100644
--- a/crates/router/src/core/payments/routing/utils.rs
+++ b/crates/router/src/core/payments/routing/utils.rs
@@ -3,7 +3,9 @@ use std::collections::{HashMap, HashSet};
 use api_models::{
     open_router as or_types,
     routing::{
-        self as api_routing, ConnectorSelection, ConnectorVolumeSplit, RoutableConnectorChoice,
+        self as api_routing, ComparisonType, ConnectorSelection, ConnectorVolumeSplit,
+        DeRoutableConnectorChoice, MetadataValue, NumberComparison, RoutableConnectorChoice,
+        RoutingEvaluateRequest, RoutingEvaluateResponse, ValueType,
     },
 };
 use async_trait::async_trait;
@@ -683,99 +685,6 @@ impl DecisionEngineErrorsInterface for or_types::ErrorResponse {
     }
 }
 
-//TODO: temporary change will be refactored afterwards
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
-pub struct RoutingEvaluateRequest {
-    pub created_by: String,
-    pub parameters: HashMap<String, Option<ValueType>>,
-    pub fallback_output: Vec<DeRoutableConnectorChoice>,
-}
-impl common_utils::events::ApiEventMetric for RoutingEvaluateRequest {}
-
-#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
-pub struct RoutingEvaluateResponse {
-    pub status: String,
-    pub output: serde_json::Value,
-    #[serde(deserialize_with = "deserialize_connector_choices")]
-    pub evaluated_output: Vec<RoutableConnectorChoice>,
-    #[serde(deserialize_with = "deserialize_connector_choices")]
-    pub eligible_connectors: Vec<RoutableConnectorChoice>,
-}
-impl common_utils::events::ApiEventMetric for RoutingEvaluateResponse {}
-/// Routable Connector chosen for a payment
-#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
-pub struct DeRoutableConnectorChoice {
-    pub gateway_name: common_enums::RoutableConnectors,
-    pub gateway_id: Option<id_type::MerchantConnectorAccountId>,
-}
-
-fn deserialize_connector_choices<'de, D>(
-    deserializer: D,
-) -> Result<Vec<RoutableConnectorChoice>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-{
-    let infos = Vec::<DeRoutableConnectorChoice>::deserialize(deserializer)?;
-    Ok(infos
-        .into_iter()
-        .map(RoutableConnectorChoice::from)
-        .collect())
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct MetadataValue {
-    pub key: String,
-    pub value: String,
-}
-
-/// Represents a value in the DSL
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-#[serde(tag = "type", content = "value", rename_all = "snake_case")]
-pub enum ValueType {
-    /// Represents a number literal
-    Number(u64),
-    /// Represents an enum variant
-    EnumVariant(String),
-    /// Represents a Metadata variant
-    MetadataVariant(MetadataValue),
-    /// Represents a arbitrary String value
-    StrValue(String),
-    /// Represents a global reference, which is a reference to a global variable
-    GlobalRef(String),
-    /// Represents an array of numbers. This is basically used for
-    /// "one of the given numbers" operations
-    /// eg: payment.method.amount = (1, 2, 3)
-    NumberArray(Vec<u64>),
-    /// Similar to NumberArray but for enum variants
-    /// eg: payment.method.cardtype = (debit, credit)
-    EnumVariantArray(Vec<String>),
-    /// Like a number array but can include comparisons. Useful for
-    /// conditions like "500 < amount < 1000"
-    /// eg: payment.amount = (> 500, < 1000)
-    NumberComparisonArray(Vec<NumberComparison>),
-}
-
-pub type Metadata = HashMap<String, serde_json::Value>;
-/// Represents a number comparison for "NumberComparisonArrayValue"
-#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub struct NumberComparison {
-    pub comparison_type: ComparisonType,
-    pub number: u64,
-}
-
-/// Conditional comparison type
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum ComparisonType {
-    Equal,
-    NotEqual,
-    LessThan,
-    LessThanEqual,
-    GreaterThan,
-    GreaterThanEqual,
-}
-
 /// Represents a single comparison condition.
 #[derive(Clone, Debug, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
@@ -875,16 +784,6 @@ impl ConnectorInfo {
     }
 }
 
-impl From<DeRoutableConnectorChoice> for RoutableConnectorChoice {
-    fn from(choice: DeRoutableConnectorChoice) -> Self {
-        Self {
-            choice_kind: api_routing::RoutableChoiceKind::FullStruct,
-            connector: choice.gateway_name,
-            merchant_connector_id: choice.gateway_id,
-        }
-    }
-}
-
 #[derive(Clone, Debug, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum Output {
@@ -896,6 +795,7 @@ pub enum Output {
 
 pub type Globals = HashMap<String, HashSet<ValueType>>;
 
+pub type Metadata = HashMap<String, serde_json::Value>;
 /// The program, having a default connector selection and
 /// a bunch of rules. Also can hold arbitrary metadata.
 #[derive(Clone, Debug, Serialize, Deserialize)]
diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs
index ffd5c265db0..53f6a75c5c1 100644
--- a/crates/router/src/routes/routing.rs
+++ b/crates/router/src/routes/routing.rs
@@ -6,7 +6,10 @@
 use actix_web::{web, HttpRequest, Responder};
 use api_models::{
     enums,
-    routing::{self as routing_types, RoutingRetrieveQuery},
+    routing::{
+        self as routing_types, RoutingEvaluateRequest, RoutingEvaluateResponse,
+        RoutingRetrieveQuery,
+    },
 };
 use error_stack::ResultExt;
 use hyperswitch_domain_models::merchant_context::MerchantKeyStore;
@@ -19,10 +22,7 @@ use router_env::{
 use crate::{
     core::{
         api_locking, conditional_config,
-        payments::routing::utils::{
-            DecisionEngineApiHandler, EuclidApiClient, RoutingEvaluateRequest,
-            RoutingEvaluateResponse,
-        },
+        payments::routing::utils::{DecisionEngineApiHandler, EuclidApiClient},
         routing, surcharge_decision_config,
     },
     db::errors::StorageErrorExt,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This pr adds api refs for routing 
```
"v1/routing/routing--evaluate",
"v1/routing/routing--feedback",
"v1/routing/routing--rule-evaluate"
```
Moved `RoutingEvaluateRequest`, `RoutingEvaluateResponse`, `DeRoutableConnectorChoice`, `ValueType`, `MetadataValue`, `NumberComparison`, and `ComparisonType` to api_models so that we can use RoutingEvaluateRequest and RoutingEvaluateResponse in to openapi crate.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.
Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->



## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->

**for "v1/routing/routing--evaluate",**

<img width="1423" height="860" alt="image" src="https://github.com/user-attachments/assets/2f8bcd33-7b79-4e56-8e6b-c3d58717609c" />


**for "v1/routing/routing--feedback",**

<img width="1461" height="882" alt="image" src="https://github.com/user-attachments/assets/f59d3313-1e02-4776-9471-e116e522a271" />

**for "v1/routing/routing--rule-evaluate"**

<img width="1481" height="860" alt="image" src="https://github.com/user-attachments/assets/2b299094-a7d6-49d9-92d3-111d29c7cedd" />

<img width="771" height="207" alt="image" src="https://github.com/user-attachments/assets/abef2fb0-2e66-417b-b6d4-216a640d4c34" />

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
