From a6546950085cb8da61cd2009e727c858e871bbcf Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Tue, 27 May 2025 15:31:08 +0530 Subject: [PATCH] feat: list for dynamic routing (#8111) Co-authored-by: Sarthak Soni Co-authored-by: Gaurav Rawat <104276743+GauravRawat369@users.noreply.github.com> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com> --- api-reference-v2/openapi_spec.json | 534 +++++++++++++++--- api-reference/openapi_spec.json | 267 +++++---- crates/api_models/src/admin.rs | 24 +- crates/api_models/src/events/routing.rs | 12 +- crates/api_models/src/payouts.rs | 2 +- crates/api_models/src/routing.rs | 41 +- crates/openapi/src/openapi.rs | 7 +- crates/openapi/src/openapi_v2.rs | 20 +- .../stripe/payment_intents/types.rs | 2 +- .../stripe/setup_intents/types.rs | 2 +- crates/router/src/core/admin.rs | 4 +- crates/router/src/core/payments.rs | 2 +- crates/router/src/core/payments/routing.rs | 16 +- crates/router/src/core/routing.rs | 73 ++- crates/router/src/core/routing/helpers.rs | 20 +- .../router/src/core/routing/transformers.rs | 20 +- crates/router/src/types/api/routing.rs | 6 +- crates/router/src/types/transformers.rs | 2 +- 18 files changed, 790 insertions(+), 264 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 2cf101b656..039981f661 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -8438,6 +8438,54 @@ }, "additionalProperties": false }, + "ContractBasedRoutingConfig": { + "type": "object", + "properties": { + "config": { + "allOf": [ + { + "$ref": "#/components/schemas/ContractBasedRoutingConfigBody" + } + ], + "nullable": true + }, + "label_info": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LabelInformation" + }, + "nullable": true + } + } + }, + "ContractBasedRoutingConfigBody": { + "type": "object", + "properties": { + "constants": { + "type": "array", + "items": { + "type": "number", + "format": "double" + }, + "nullable": true + }, + "time_scale": { + "allOf": [ + { + "$ref": "#/components/schemas/ContractBasedTimeScale" + } + ], + "nullable": true + } + } + }, + "ContractBasedTimeScale": { + "type": "string", + "enum": [ + "day", + "month" + ] + }, "CountryAlpha2": { "type": "string", "enum": [ @@ -9260,6 +9308,23 @@ "ZWL" ] }, + "CurrentBlockThreshold": { + "type": "object", + "properties": { + "duration_in_mins": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "max_total_count": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + } + } + }, "CustomerAcceptance": { "type": "object", "description": "This \"CustomerAcceptance\" object is passed during Payments-Confirm request, it enlists the type, time, and mode of acceptance properties related to an acceptance done by the customer. The customer_acceptance sub object is usually passed by the SDK or client.", @@ -9767,6 +9832,123 @@ }, "additionalProperties": false }, + "DecisionEngineEliminationData": { + "type": "object", + "required": [ + "threshold" + ], + "properties": { + "threshold": { + "type": "number", + "format": "double" + } + } + }, + "DecisionEngineGatewayWiseExtraScore": { + "type": "object", + "required": [ + "gatewayName", + "gatewaySigmaFactor" + ], + "properties": { + "gatewayName": { + "type": "string" + }, + "gatewaySigmaFactor": { + "type": "number", + "format": "double" + } + } + }, + "DecisionEngineSRSubLevelInputConfig": { + "type": "object", + "properties": { + "paymentMethodType": { + "type": "string", + "nullable": true + }, + "paymentMethod": { + "type": "string", + "nullable": true + }, + "latencyThreshold": { + "type": "number", + "format": "double", + "nullable": true + }, + "bucketSize": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "hedgingPercent": { + "type": "number", + "format": "double", + "nullable": true + }, + "lowerResetFactor": { + "type": "number", + "format": "double", + "nullable": true + }, + "upperResetFactor": { + "type": "number", + "format": "double", + "nullable": true + }, + "gatewayExtraScore": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DecisionEngineGatewayWiseExtraScore" + }, + "nullable": true + } + } + }, + "DecisionEngineSuccessRateData": { + "type": "object", + "properties": { + "defaultLatencyThreshold": { + "type": "number", + "format": "double", + "nullable": true + }, + "defaultBucketSize": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "defaultHedgingPercent": { + "type": "number", + "format": "double", + "nullable": true + }, + "defaultLowerResetFactor": { + "type": "number", + "format": "double", + "nullable": true + }, + "defaultUpperResetFactor": { + "type": "number", + "format": "double", + "nullable": true + }, + "defaultGatewayExtraScore": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DecisionEngineGatewayWiseExtraScore" + }, + "nullable": true + }, + "subLevelInputConfig": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DecisionEngineSRSubLevelInputConfig" + }, + "nullable": true + } + } + }, "DecoupledAuthenticationType": { "type": "string", "enum": [ @@ -10032,6 +10214,31 @@ } } }, + "DynamicRoutingAlgorithm": { + "oneOf": [ + { + "$ref": "#/components/schemas/EliminationRoutingConfig" + }, + { + "$ref": "#/components/schemas/SuccessBasedRoutingConfig" + }, + { + "$ref": "#/components/schemas/ContractBasedRoutingConfig" + } + ] + }, + "DynamicRoutingConfigParams": { + "type": "string", + "enum": [ + "PaymentMethod", + "PaymentMethodType", + "AuthenticationType", + "Currency", + "Country", + "CardNetwork", + "CardBin" + ] + }, "ElementPosition": { "type": "string", "enum": [ @@ -10087,6 +10294,51 @@ } ] }, + "EliminationAnalyserConfig": { + "type": "object", + "properties": { + "bucket_size": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "bucket_leak_interval_in_secs": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + } + }, + "additionalProperties": false + }, + "EliminationRoutingConfig": { + "type": "object", + "required": [ + "decision_engine_configs" + ], + "properties": { + "params": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DynamicRoutingConfigParams" + }, + "nullable": true + }, + "elimination_analyser_config": { + "allOf": [ + { + "$ref": "#/components/schemas/EliminationAnalyserConfig" + } + ], + "nullable": true + }, + "decision_engine_configs": { + "$ref": "#/components/schemas/DecisionEngineEliminationData" + } + }, + "additionalProperties": false + }, "EnablePaymentLinkRequest": { "type": "string", "description": "Whether payment link is requested to be enabled or not for this transaction", @@ -12161,6 +12413,33 @@ } } }, + "LabelInformation": { + "type": "object", + "required": [ + "label", + "target_count", + "target_time", + "mca_id" + ], + "properties": { + "label": { + "type": "string" + }, + "target_count": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "target_time": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "mca_id": { + "type": "string" + } + } + }, "LinkedRoutingConfigRetrieveResponse": { "oneOf": [ { @@ -13344,7 +13623,7 @@ "type": "string" }, "algorithm": { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/RoutingAlgorithmWrapper" }, "created_at": { "type": "integer", @@ -18913,7 +19192,7 @@ "routing": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -21800,92 +22079,6 @@ "zsl" ] }, - "RoutingAlgorithm": { - "oneOf": [ - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "single" - ] - }, - "data": { - "$ref": "#/components/schemas/RoutableConnectorChoice" - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "priority" - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoutableConnectorChoice" - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "volume_split" - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConnectorVolumeSplit" - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "advanced" - ] - }, - "data": { - "$ref": "#/components/schemas/ProgramConnectorSelection" - } - } - } - ], - "description": "Routing Algorithm kind", - "discriminator": { - "propertyName": "type" - } - }, "RoutingAlgorithmId": { "type": "object", "required": [ @@ -21907,6 +22100,16 @@ "dynamic" ] }, + "RoutingAlgorithmWrapper": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaticRoutingAlgorithm" + }, + { + "$ref": "#/components/schemas/DynamicRoutingAlgorithm" + } + ] + }, "RoutingConfigRequest": { "type": "object", "required": [ @@ -21923,7 +22126,7 @@ "type": "string" }, "algorithm": { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" }, "profile_id": { "type": "string" @@ -22880,6 +23083,91 @@ ], "description": "Charge specific fields for controlling the revert of funds from either platform or connected account. Check sub-fields for more details." }, + "StaticRoutingAlgorithm": { + "oneOf": [ + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ] + }, + "data": { + "$ref": "#/components/schemas/RoutableConnectorChoice" + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "priority" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoutableConnectorChoice" + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "volume_split" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConnectorVolumeSplit" + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "advanced" + ] + }, + "data": { + "$ref": "#/components/schemas/ProgramConnectorSelection" + } + } + } + ], + "discriminator": { + "propertyName": "type" + } + }, "StraightThroughAlgorithm": { "oneOf": [ { @@ -23029,6 +23317,74 @@ }, "additionalProperties": false }, + "SuccessBasedRoutingConfig": { + "type": "object", + "required": [ + "decision_engine_configs" + ], + "properties": { + "params": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DynamicRoutingConfigParams" + }, + "nullable": true + }, + "config": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessBasedRoutingConfigBody" + } + ], + "nullable": true + }, + "decision_engine_configs": { + "$ref": "#/components/schemas/DecisionEngineSuccessRateData" + } + }, + "additionalProperties": false + }, + "SuccessBasedRoutingConfigBody": { + "type": "object", + "properties": { + "min_aggregates_size": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0 + }, + "default_success_rate": { + "type": "number", + "format": "double", + "nullable": true + }, + "max_aggregates_size": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0 + }, + "current_block_threshold": { + "allOf": [ + { + "$ref": "#/components/schemas/CurrentBlockThreshold" + } + ], + "nullable": true + }, + "specificity_level": { + "$ref": "#/components/schemas/SuccessRateSpecificityLevel" + } + }, + "additionalProperties": false + }, + "SuccessRateSpecificityLevel": { + "type": "string", + "enum": [ + "merchant", + "global" + ] + }, "SupportedPaymentMethod": { "allOf": [ { diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 15a5dbb972..70c8ed1e8c 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -12108,6 +12108,19 @@ } } }, + "DynamicRoutingAlgorithm": { + "oneOf": [ + { + "$ref": "#/components/schemas/EliminationRoutingConfig" + }, + { + "$ref": "#/components/schemas/SuccessBasedRoutingConfig" + }, + { + "$ref": "#/components/schemas/ContractBasedRoutingConfig" + } + ] + }, "DynamicRoutingConfigParams": { "type": "string", "enum": [ @@ -12183,6 +12196,51 @@ } ] }, + "EliminationAnalyserConfig": { + "type": "object", + "properties": { + "bucket_size": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + }, + "bucket_leak_interval_in_secs": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": 0 + } + }, + "additionalProperties": false + }, + "EliminationRoutingConfig": { + "type": "object", + "required": [ + "decision_engine_configs" + ], + "properties": { + "params": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DynamicRoutingConfigParams" + }, + "nullable": true + }, + "elimination_analyser_config": { + "allOf": [ + { + "$ref": "#/components/schemas/EliminationAnalyserConfig" + } + ], + "nullable": true + }, + "decision_engine_configs": { + "$ref": "#/components/schemas/DecisionEngineEliminationData" + } + }, + "additionalProperties": false + }, "EnabledPaymentMethod": { "type": "object", "description": "Object for EnabledPaymentMethod", @@ -14621,7 +14679,7 @@ "payout_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -14867,7 +14925,7 @@ "payout_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -14913,7 +14971,7 @@ "frm_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -15000,7 +15058,7 @@ "payout_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -15941,7 +15999,7 @@ "type": "string" }, "algorithm": { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/RoutingAlgorithmWrapper" }, "created_at": { "type": "integer", @@ -22619,7 +22677,7 @@ "routing": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -23565,7 +23623,7 @@ "routing": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -23788,7 +23846,7 @@ "routing": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -24368,7 +24426,7 @@ "payout_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -24648,7 +24706,7 @@ "payout_routing_algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -26053,92 +26111,6 @@ "zsl" ] }, - "RoutingAlgorithm": { - "oneOf": [ - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "single" - ] - }, - "data": { - "$ref": "#/components/schemas/RoutableConnectorChoice" - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "priority" - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoutableConnectorChoice" - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "volume_split" - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConnectorVolumeSplit" - } - } - } - }, - { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string", - "enum": [ - "advanced" - ] - }, - "data": { - "$ref": "#/components/schemas/ProgramConnectorSelection" - } - } - } - ], - "description": "Routing Algorithm kind", - "discriminator": { - "propertyName": "type" - } - }, "RoutingAlgorithmKind": { "type": "string", "enum": [ @@ -26149,6 +26121,16 @@ "dynamic" ] }, + "RoutingAlgorithmWrapper": { + "oneOf": [ + { + "$ref": "#/components/schemas/StaticRoutingAlgorithm" + }, + { + "$ref": "#/components/schemas/DynamicRoutingAlgorithm" + } + ] + }, "RoutingConfigRequest": { "type": "object", "properties": { @@ -26163,7 +26145,7 @@ "algorithm": { "allOf": [ { - "$ref": "#/components/schemas/RoutingAlgorithm" + "$ref": "#/components/schemas/StaticRoutingAlgorithm" } ], "nullable": true @@ -27110,6 +27092,91 @@ ], "description": "Charge specific fields for controlling the revert of funds from either platform or connected account. Check sub-fields for more details." }, + "StaticRoutingAlgorithm": { + "oneOf": [ + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "single" + ] + }, + "data": { + "$ref": "#/components/schemas/RoutableConnectorChoice" + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "priority" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RoutableConnectorChoice" + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "volume_split" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConnectorVolumeSplit" + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "advanced" + ] + }, + "data": { + "$ref": "#/components/schemas/ProgramConnectorSelection" + } + } + } + ], + "discriminator": { + "propertyName": "type" + } + }, "StraightThroughAlgorithm": { "oneOf": [ { @@ -27283,7 +27350,8 @@ "decision_engine_configs": { "$ref": "#/components/schemas/DecisionEngineSuccessRateData" } - } + }, + "additionalProperties": false }, "SuccessBasedRoutingConfigBody": { "type": "object", @@ -27316,7 +27384,8 @@ "specificity_level": { "$ref": "#/components/schemas/SuccessRateSpecificityLevel" } - } + }, + "additionalProperties": false }, "SuccessRateSpecificityLevel": { "type": "string", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 60eca62b61..1d026a6854 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -58,7 +58,7 @@ pub struct MerchantAccountCreate { /// The routing algorithm to be used for routing payouts to desired connectors #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false. @@ -165,8 +165,9 @@ impl MerchantAccountCreate { pub fn parse_routing_algorithm(&self) -> CustomResult<(), errors::ParsingError> { match self.routing_algorithm { Some(ref routing_algorithm) => { - let _: routing::RoutingAlgorithm = - routing_algorithm.clone().parse_value("RoutingAlgorithm")?; + let _: routing::StaticRoutingAlgorithm = routing_algorithm + .clone() + .parse_value("StaticRoutingAlgorithm")?; Ok(()) } None => Ok(()), @@ -325,7 +326,7 @@ pub struct MerchantAccountUpdate { /// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom' #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false. @@ -425,8 +426,9 @@ impl MerchantAccountUpdate { pub fn parse_routing_algorithm(&self) -> CustomResult<(), errors::ParsingError> { match self.routing_algorithm { Some(ref routing_algorithm) => { - let _: routing::RoutingAlgorithm = - routing_algorithm.clone().parse_value("RoutingAlgorithm")?; + let _: routing::StaticRoutingAlgorithm = routing_algorithm + .clone() + .parse_value("StaticRoutingAlgorithm")?; Ok(()) } None => Ok(()), @@ -517,7 +519,7 @@ pub struct MerchantAccountResponse { /// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom' #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// A boolean value to indicate if the merchant is a sub-merchant under a master or a parent merchant. By default, its value is false. @@ -545,7 +547,7 @@ pub struct MerchantAccountResponse { pub primary_business_details: Vec, /// The frm routing algorithm to be used to process the incoming request from merchant to outgoing payment FRM. - #[schema(value_type = Option, max_length = 255, example = r#"{"type": "single", "data": "stripe" }"#)] + #[schema(value_type = Option, max_length = 255, example = r#"{"type": "single", "data": "stripe" }"#)] pub frm_routing_algorithm: Option, /// The organization id merchant is associated with @@ -1897,7 +1899,7 @@ pub struct ProfileCreate { /// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom' #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// Verified Apple Pay domains for a particular profile @@ -2199,7 +2201,7 @@ pub struct ProfileResponse { /// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom' #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// Verified Apple Pay domains for a particular profile @@ -2504,7 +2506,7 @@ pub struct ProfileUpdate { /// The routing algorithm to be used to process the incoming request from merchant to outgoing payment processor or payment method. The default is 'Custom' #[cfg(feature = "payouts")] - #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] + #[schema(value_type = Option,example = json!({"type": "single", "data": "wise"}))] pub payout_routing_algorithm: Option, /// Verified Apple Pay domains for a particular profile diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs index c9fa118f59..750bfde271 100644 --- a/crates/api_models/src/events/routing.rs +++ b/crates/api_models/src/events/routing.rs @@ -6,9 +6,9 @@ use crate::routing::{ LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery, - RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitWrapper, - SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingQuery, - ToggleDynamicRoutingWrapper, + RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplit, + RoutingVolumeSplitWrapper, SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, + ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper, }; impl ApiEventMetric for RoutingKind { @@ -134,3 +134,9 @@ impl ApiEventMetric for RoutingVolumeSplitWrapper { Some(ApiEventsType::Routing) } } + +impl ApiEventMetric for RoutingVolumeSplit { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index 4c8a73b75a..cf07fd9e10 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -58,7 +58,7 @@ pub struct PayoutCreateRequest { pub currency: Option, /// Specifies routing algorithm for selecting a connector - #[schema(value_type = Option, example = json!({ + #[schema(value_type = Option, example = json!({ "type": "single", "data": "adyen" }))] diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 12075935cd..a15b135ac1 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -49,7 +49,7 @@ impl ConnectorSelection { pub struct RoutingConfigRequest { pub name: String, pub description: String, - pub algorithm: RoutingAlgorithm, + pub algorithm: StaticRoutingAlgorithm, #[schema(value_type = String)] pub profile_id: common_utils::id_type::ProfileId, } @@ -59,7 +59,7 @@ pub struct RoutingConfigRequest { pub struct RoutingConfigRequest { pub name: Option, pub description: Option, - pub algorithm: Option, + pub algorithm: Option, #[schema(value_type = Option)] pub profile_id: Option, } @@ -96,7 +96,7 @@ pub struct RoutingRetrieveResponse { #[derive(Debug, serde::Serialize, ToSchema)] #[serde(untagged)] pub enum LinkedRoutingConfigRetrieveResponse { - MerchantAccountBased(RoutingRetrieveResponse), + MerchantAccountBased(Box), ProfileBased(Vec), } @@ -109,7 +109,7 @@ pub struct MerchantRoutingAlgorithm { pub profile_id: common_utils::id_type::ProfileId, pub name: String, pub description: String, - pub algorithm: RoutingAlgorithm, + pub algorithm: RoutingAlgorithmWrapper, pub created_at: i64, pub modified_at: i64, pub algorithm_for: TransactionType, @@ -301,6 +301,20 @@ pub struct RoutingPayloadWrapper { pub updated_config: Vec, pub profile_id: common_utils::id_type::ProfileId, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(untagged)] +pub enum RoutingAlgorithmWrapper { + Static(StaticRoutingAlgorithm), + Dynamic(DynamicRoutingAlgorithm), +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(untagged)] +pub enum DynamicRoutingAlgorithm { + EliminationBasedAlgorithm(EliminationRoutingConfig), + SuccessBasedAlgorithm(SuccessBasedRoutingConfig), + ContractBasedAlgorithm(ContractBasedRoutingConfig), +} #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] #[serde( @@ -309,8 +323,7 @@ pub struct RoutingPayloadWrapper { rename_all = "snake_case", try_from = "RoutingAlgorithmSerde" )] -/// Routing Algorithm kind -pub enum RoutingAlgorithm { +pub enum StaticRoutingAlgorithm { Single(Box), Priority(Vec), VolumeSplit(Vec), @@ -318,7 +331,7 @@ pub enum RoutingAlgorithm { Advanced(ast::Program), } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] #[serde(tag = "type", content = "data", rename_all = "snake_case")] pub enum RoutingAlgorithmSerde { Single(Box), @@ -327,7 +340,7 @@ pub enum RoutingAlgorithmSerde { Advanced(ast::Program), } -impl TryFrom for RoutingAlgorithm { +impl TryFrom for StaticRoutingAlgorithm { type Error = error_stack::Report; fn try_from(value: RoutingAlgorithmSerde) -> Result { @@ -434,7 +447,7 @@ impl From for StraightThroughAlgorithmSerde { } } -impl From for RoutingAlgorithm { +impl From for StaticRoutingAlgorithm { fn from(value: StraightThroughAlgorithm) -> Self { match value { StraightThroughAlgorithm::Single(conn) => Self::Single(conn), @@ -444,7 +457,7 @@ impl From for RoutingAlgorithm { } } -impl RoutingAlgorithm { +impl StaticRoutingAlgorithm { pub fn get_kind(&self) -> RoutingAlgorithmKind { match self { Self::Single(_) => RoutingAlgorithmKind::Single, @@ -845,6 +858,7 @@ pub struct ToggleDynamicRoutingPath { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +#[serde(deny_unknown_fields)] pub struct EliminationRoutingConfig { pub params: Option>, pub elimination_analyser_config: Option, @@ -853,6 +867,7 @@ pub struct EliminationRoutingConfig { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, ToSchema)] +#[serde(deny_unknown_fields)] pub struct EliminationAnalyserConfig { pub bucket_size: Option, pub bucket_leak_interval_in_secs: Option, @@ -924,6 +939,7 @@ impl EliminationRoutingConfig { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +#[serde(deny_unknown_fields)] pub struct SuccessBasedRoutingConfig { pub params: Option>, pub config: Option, @@ -950,7 +966,9 @@ impl Default for SuccessBasedRoutingConfig { } } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema, strum::Display)] +#[derive( + serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema, PartialEq, strum::Display, +)] pub enum DynamicRoutingConfigParams { PaymentMethod, PaymentMethodType, @@ -962,6 +980,7 @@ pub enum DynamicRoutingConfigParams { } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] +#[serde(deny_unknown_fields)] pub struct SuccessBasedRoutingConfigBody { pub min_aggregates_size: Option, pub default_success_rate: Option, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index cec1b463a8..8612dd080b 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -659,7 +659,12 @@ Never share your secret api keys. Keep them guarded and secure. api_models::routing::MerchantRoutingAlgorithm, api_models::routing::RoutingAlgorithmKind, api_models::routing::RoutingDictionary, - api_models::routing::RoutingAlgorithm, + api_models::routing::RoutingAlgorithmWrapper, + api_models::routing::EliminationRoutingConfig, + api_models::open_router::DecisionEngineEliminationData, + api_models::routing::EliminationAnalyserConfig, + api_models::routing::DynamicRoutingAlgorithm, + api_models::routing::StaticRoutingAlgorithm, api_models::routing::StraightThroughAlgorithm, api_models::routing::ConnectorVolumeSplit, api_models::routing::ConnectorSelection, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index d9f506aab4..3091467c9d 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -648,7 +648,25 @@ Never share your secret api keys. Keep them guarded and secure. api_models::routing::MerchantRoutingAlgorithm, api_models::routing::RoutingAlgorithmKind, api_models::routing::RoutingDictionary, - api_models::routing::RoutingAlgorithm, + api_models::routing::DynamicRoutingConfigParams, + api_models::routing::SuccessBasedRoutingConfig, + api_models::routing::SuccessRateSpecificityLevel, + api_models::routing::CurrentBlockThreshold, + api_models::open_router::DecisionEngineSuccessRateData, + api_models::routing::ContractBasedTimeScale, + api_models::routing::LabelInformation, + api_models::routing::ContractBasedRoutingConfig, + api_models::routing::ContractBasedRoutingConfigBody, + api_models::open_router::DecisionEngineGatewayWiseExtraScore, + api_models::open_router::DecisionEngineSRSubLevelInputConfig, + api_models::open_router::DecisionEngineEliminationData, + api_models::routing::SuccessBasedRoutingConfigBody, + api_models::routing::RoutingAlgorithmWrapper, + api_models::routing::EliminationRoutingConfig, + api_models::open_router::DecisionEngineEliminationData, + api_models::routing::EliminationAnalyserConfig, + api_models::routing::DynamicRoutingAlgorithm, + api_models::routing::StaticRoutingAlgorithm, api_models::routing::StraightThroughAlgorithm, api_models::routing::ConnectorVolumeSplit, api_models::routing::ConnectorSelection, diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index ee5fb18425..c153c88e0e 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -287,7 +287,7 @@ impl TryFrom for payments::PaymentsRequest { let routing = routable_connector .map(|connector| { - api_models::routing::RoutingAlgorithm::Single(Box::new( + api_models::routing::StaticRoutingAlgorithm::Single(Box::new( api_models::routing::RoutableConnectorChoice { choice_kind: api_models::routing::RoutableChoiceKind::FullStruct, connector, diff --git a/crates/router/src/compatibility/stripe/setup_intents/types.rs b/crates/router/src/compatibility/stripe/setup_intents/types.rs index 77db75e218..90098f1c30 100644 --- a/crates/router/src/compatibility/stripe/setup_intents/types.rs +++ b/crates/router/src/compatibility/stripe/setup_intents/types.rs @@ -183,7 +183,7 @@ impl TryFrom for payments::PaymentsRequest { let routing = routable_connector .map(|connector| { - api_models::routing::RoutingAlgorithm::Single(Box::new( + api_models::routing::StaticRoutingAlgorithm::Single(Box::new( api_models::routing::RoutableConnectorChoice { choice_kind: api_models::routing::RoutableChoiceKind::FullStruct, connector, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index ba078fde73..c26108fa4b 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3834,7 +3834,7 @@ impl ProfileCreateBridge for api::ProfileCreate { } if let Some(ref routing_algorithm) = self.routing_algorithm { - let _: api_models::routing::RoutingAlgorithm = routing_algorithm + let _: api_models::routing::StaticRoutingAlgorithm = routing_algorithm .clone() .parse_value("RoutingAlgorithm") .change_context(errors::ApiErrorResponse::InvalidDataValue { @@ -4333,7 +4333,7 @@ impl ProfileUpdateBridge for api::ProfileUpdate { let webhook_details = self.webhook_details.map(ForeignInto::foreign_into); if let Some(ref routing_algorithm) = self.routing_algorithm { - let _: api_models::routing::RoutingAlgorithm = routing_algorithm + let _: api_models::routing::StaticRoutingAlgorithm = routing_algorithm .clone() .parse_value("RoutingAlgorithm") .change_context(errors::ApiErrorResponse::InvalidDataValue { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 4c3894d5d3..7a3727aa56 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -6240,7 +6240,7 @@ where F: Send + Clone, D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, { - let _: api_models::routing::RoutingAlgorithm = request_straight_through + let _: api_models::routing::StaticRoutingAlgorithm = request_straight_through .clone() .parse_value("RoutingAlgorithm") .attach_printable("Invalid straight through routing rules format")?; diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 2b851c192b..b4ac7d4421 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -577,15 +577,15 @@ fn execute_dsl_and_get_connector_v1( backend_input: dsl_inputs::BackendInput, interpreter: &backend::VirInterpreterBackend, ) -> RoutingResult> { - let routing_output: routing_types::RoutingAlgorithm = interpreter + let routing_output: routing_types::StaticRoutingAlgorithm = interpreter .execute(backend_input) .map(|out| out.connector_selection.foreign_into()) .change_context(errors::RoutingError::DslExecutionError)?; Ok(match routing_output { - routing_types::RoutingAlgorithm::Priority(plist) => plist, + routing_types::StaticRoutingAlgorithm::Priority(plist) => plist, - routing_types::RoutingAlgorithm::VolumeSplit(splits) => perform_volume_split(splits) + routing_types::StaticRoutingAlgorithm::VolumeSplit(splits) => perform_volume_split(splits) .change_context(errors::RoutingError::DslFinalConnectorSelectionFailed)?, _ => Err(errors::RoutingError::DslIncorrectSelectionAlgorithm) @@ -605,7 +605,7 @@ pub async fn refresh_routing_cache_v1( .find_routing_algorithm_by_profile_id_algorithm_id(profile_id, algorithm_id) .await .change_context(errors::RoutingError::DslMissingInDb)?; - let algorithm: routing_types::RoutingAlgorithm = algorithm + let algorithm: routing_types::StaticRoutingAlgorithm = algorithm .algorithm_data .parse_value("RoutingAlgorithm") .change_context(errors::RoutingError::DslParsingError)?; @@ -613,12 +613,12 @@ pub async fn refresh_routing_cache_v1( }; let cached_algorithm = match algorithm { - routing_types::RoutingAlgorithm::Single(conn) => CachedAlgorithm::Single(conn), - routing_types::RoutingAlgorithm::Priority(plist) => CachedAlgorithm::Priority(plist), - routing_types::RoutingAlgorithm::VolumeSplit(splits) => { + routing_types::StaticRoutingAlgorithm::Single(conn) => CachedAlgorithm::Single(conn), + routing_types::StaticRoutingAlgorithm::Priority(plist) => CachedAlgorithm::Priority(plist), + routing_types::StaticRoutingAlgorithm::VolumeSplit(splits) => { CachedAlgorithm::VolumeSplit(splits) } - routing_types::RoutingAlgorithm::Advanced(program) => { + routing_types::StaticRoutingAlgorithm::Advanced(program) => { let interpreter = backend::VirInterpreterBackend::with_program(program) .change_context(errors::RoutingError::DslBackendInitError) .attach_printable("Error initializing DSL interpreter backend")?; diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 893c66af24..df6189ea42 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -275,7 +275,7 @@ pub async fn create_routing_algorithm_under_profile( request: routing_types::RoutingConfigRequest, transaction_type: enums::TransactionType, ) -> RouterResponse { - use api_models::routing::RoutingAlgorithm as EuclidAlgorithm; + use api_models::routing::StaticRoutingAlgorithm as EuclidAlgorithm; use crate::services::logger; @@ -1174,16 +1174,20 @@ pub async fn retrieve_linked_routing_config( transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(1, &[]); + let db = state.store.as_ref(); let key_manager_state = &(&state).into(); + let merchant_key_store = merchant_context.get_merchant_key_store(); + let merchant_id = merchant_context.get_merchant_account().get_id(); + // Get business profiles let business_profiles = if let Some(profile_id) = query_params.profile_id { core_utils::validate_and_get_business_profile( db, key_manager_state, - merchant_context.get_merchant_key_store(), + merchant_key_store, Some(&profile_id), - merchant_context.get_merchant_account().get_id(), + merchant_id, ) .await? .map(|profile| vec![profile]) @@ -1193,28 +1197,25 @@ pub async fn retrieve_linked_routing_config( })? } else { let business_profile = db - .list_profile_by_merchant_id( - key_manager_state, - merchant_context.get_merchant_key_store(), - merchant_context.get_merchant_account().get_id(), - ) + .list_profile_by_merchant_id(key_manager_state, merchant_key_store, merchant_id) .await .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; core_utils::filter_objects_based_on_profile_id_list( authentication_profile_id.map(|profile_id| vec![profile_id]), - business_profile.clone(), + business_profile, ) }; let mut active_algorithms = Vec::new(); for business_profile in business_profiles { - let profile_id = business_profile.get_id().to_owned(); + let profile_id = business_profile.get_id(); + // Handle static routing algorithm let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => business_profile.routing_algorithm, + enums::TransactionType::Payment => &business_profile.routing_algorithm, #[cfg(feature = "payouts")] - enums::TransactionType::Payout => business_profile.payout_routing_algorithm, + enums::TransactionType::Payout => &business_profile.payout_routing_algorithm, } .clone() .map(|val| val.parse_value("RoutingAlgorithmRef")) @@ -1227,11 +1228,53 @@ pub async fn retrieve_linked_routing_config( let record = db .find_routing_algorithm_metadata_by_algorithm_id_profile_id( &algorithm_id, - &profile_id, + profile_id, ) .await .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + active_algorithms.push(record.foreign_into()); + } + // Handle dynamic routing algorithms + let dynamic_routing_ref: routing_types::DynamicRoutingAlgorithmRef = business_profile + .dynamic_routing_algorithm + .clone() + .map(|val| val.parse_value("DynamicRoutingAlgorithmRef")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "unable to deserialize dynamic routing algorithm ref from business profile", + )? + .unwrap_or_default(); + + // Collect all dynamic algorithm IDs + let mut dynamic_algorithm_ids = Vec::new(); + + if let Some(sba) = &dynamic_routing_ref.success_based_algorithm { + if let Some(id) = &sba.algorithm_id_with_timestamp.algorithm_id { + dynamic_algorithm_ids.push(id.clone()); + } + } + if let Some(era) = &dynamic_routing_ref.elimination_routing_algorithm { + if let Some(id) = &era.algorithm_id_with_timestamp.algorithm_id { + dynamic_algorithm_ids.push(id.clone()); + } + } + if let Some(cbr) = &dynamic_routing_ref.contract_based_routing { + if let Some(id) = &cbr.algorithm_id_with_timestamp.algorithm_id { + dynamic_algorithm_ids.push(id.clone()); + } + } + + // Fetch all dynamic algorithms + for algorithm_id in dynamic_algorithm_ids { + let record = db + .find_routing_algorithm_metadata_by_algorithm_id_profile_id( + &algorithm_id, + profile_id, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; active_algorithms.push(record.foreign_into()); } } @@ -1456,7 +1499,7 @@ pub async fn configure_dynamic_routing_volume_split( merchant_context: domain::MerchantContext, profile_id: common_utils::id_type::ProfileId, routing_info: routing::RoutingVolumeSplit, -) -> RouterResponse<()> { +) -> RouterResponse { metrics::ROUTING_CREATE_REQUEST_RECEIVED.add( 1, router_env::metric_attributes!(("profile_id", profile_id.clone())), @@ -1508,7 +1551,7 @@ pub async fn configure_dynamic_routing_volume_split( ) .await?; - Ok(service_api::ApplicationResponse::StatusOk) + Ok(service_api::ApplicationResponse::Json(routing_info)) } #[cfg(all(feature = "v1", feature = "dynamic_routing"))] diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 89d0cb7bfe..a29f25331e 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -312,7 +312,7 @@ pub async fn update_business_profile_active_dynamic_algorithm_ref( pub struct RoutingAlgorithmHelpers<'h> { pub name_mca_id_set: ConnectNameAndMCAIdForProfile<'h>, pub name_set: ConnectNameForProfile<'h>, - pub routing_algorithm: &'h routing_types::RoutingAlgorithm, + pub routing_algorithm: &'h routing_types::StaticRoutingAlgorithm, } #[cfg(feature = "v1")] @@ -410,23 +410,23 @@ impl RoutingAlgorithmHelpers<'_> { pub fn validate_connectors_in_routing_config(&self) -> RouterResult<()> { match self.routing_algorithm { - routing_types::RoutingAlgorithm::Single(choice) => { + routing_types::StaticRoutingAlgorithm::Single(choice) => { self.connector_choice(choice)?; } - routing_types::RoutingAlgorithm::Priority(list) => { + routing_types::StaticRoutingAlgorithm::Priority(list) => { for choice in list { self.connector_choice(choice)?; } } - routing_types::RoutingAlgorithm::VolumeSplit(splits) => { + routing_types::StaticRoutingAlgorithm::VolumeSplit(splits) => { for split in splits { self.connector_choice(&split.connector)?; } } - routing_types::RoutingAlgorithm::Advanced(program) => { + routing_types::StaticRoutingAlgorithm::Advanced(program) => { let check_connector_selection = |selection: &routing_types::ConnectorSelection| -> RouterResult<()> { match selection { @@ -464,7 +464,7 @@ pub async fn validate_connectors_in_routing_config( key_store: &domain::MerchantKeyStore, merchant_id: &id_type::MerchantId, profile_id: &id_type::ProfileId, - routing_algorithm: &routing_types::RoutingAlgorithm, + routing_algorithm: &routing_types::StaticRoutingAlgorithm, ) -> RouterResult<()> { let all_mcas = state .store @@ -518,23 +518,23 @@ pub async fn validate_connectors_in_routing_config( }; match routing_algorithm { - routing_types::RoutingAlgorithm::Single(choice) => { + routing_types::StaticRoutingAlgorithm::Single(choice) => { connector_choice(choice)?; } - routing_types::RoutingAlgorithm::Priority(list) => { + routing_types::StaticRoutingAlgorithm::Priority(list) => { for choice in list { connector_choice(choice)?; } } - routing_types::RoutingAlgorithm::VolumeSplit(splits) => { + routing_types::StaticRoutingAlgorithm::VolumeSplit(splits) => { for split in splits { connector_choice(&split.connector)?; } } - routing_types::RoutingAlgorithm::Advanced(program) => { + routing_types::StaticRoutingAlgorithm::Advanced(program) => { let check_connector_selection = |selection: &routing_types::ConnectorSelection| -> RouterResult<()> { match selection { diff --git a/crates/router/src/core/routing/transformers.rs b/crates/router/src/core/routing/transformers.rs index ebf521c460..4face88799 100644 --- a/crates/router/src/core/routing/transformers.rs +++ b/crates/router/src/core/routing/transformers.rs @@ -1,6 +1,6 @@ use api_models::routing::{ - MerchantRoutingAlgorithm, RoutingAlgorithm as Algorithm, RoutingAlgorithmKind, - RoutingDictionaryRecord, + DynamicRoutingAlgorithm, MerchantRoutingAlgorithm, RoutingAlgorithmKind, + RoutingAlgorithmWrapper, RoutingDictionaryRecord, }; #[cfg(feature = "v1")] use api_models::{ @@ -59,15 +59,23 @@ impl ForeignTryFrom for MerchantRoutingAlgorithm { type Error = error_stack::Report; fn foreign_try_from(value: RoutingAlgorithm) -> Result { + let algorithm: RoutingAlgorithmWrapper = match value.kind { + diesel_models::enums::RoutingAlgorithmKind::Dynamic => value + .algorithm_data + .parse_value::("RoutingAlgorithmDynamic") + .map(RoutingAlgorithmWrapper::Dynamic)?, + _ => value + .algorithm_data + .parse_value::("RoutingAlgorithm") + .map(RoutingAlgorithmWrapper::Static)?, + }; + Ok(Self { id: value.algorithm_id, name: value.name, - profile_id: value.profile_id, description: value.description.unwrap_or_default(), - algorithm: value - .algorithm_data - .parse_value::("RoutingAlgorithm")?, + algorithm, created_at: value.created_at.assume_utc().unix_timestamp(), modified_at: value.modified_at.assume_utc().unix_timestamp(), algorithm_for: value.algorithm_for, diff --git a/crates/router/src/types/api/routing.rs b/crates/router/src/types/api/routing.rs index d201f57eb9..ac7337d923 100644 --- a/crates/router/src/types/api/routing.rs +++ b/crates/router/src/types/api/routing.rs @@ -1,9 +1,9 @@ pub use api_models::{ enums as api_enums, routing::{ - ConnectorVolumeSplit, RoutableChoiceKind, RoutableConnectorChoice, RoutingAlgorithm, - RoutingAlgorithmKind, RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, - RoutingDictionaryRecord, StraightThroughAlgorithm, + ConnectorVolumeSplit, RoutableChoiceKind, RoutableConnectorChoice, RoutingAlgorithmKind, + RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, RoutingDictionaryRecord, + StaticRoutingAlgorithm, StraightThroughAlgorithm, }, }; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index f4421242a4..ce991c560f 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1821,7 +1821,7 @@ impl From for payments::AddressDetails { } } -impl ForeignFrom for routing_types::RoutingAlgorithm { +impl ForeignFrom for routing_types::StaticRoutingAlgorithm { fn foreign_from(value: ConnectorSelection) -> Self { match value { ConnectorSelection::Priority(connectors) => Self::Priority(connectors),