feat: list for dynamic routing (#8111)

Co-authored-by: Sarthak Soni <sarthakasoni@gmail.com>
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>
This commit is contained in:
Prajjwal Kumar
2025-05-27 15:31:08 +05:30
committed by GitHub
parent 3bf6ef8bd5
commit a654695008
18 changed files with 790 additions and 264 deletions

View File

@ -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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// 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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// 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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// 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<PrimaryBusinessDetails>,
/// The frm routing algorithm to be used to process the incoming request from merchant to outgoing payment FRM.
#[schema(value_type = Option<RoutingAlgorithm>, max_length = 255, example = r#"{"type": "single", "data": "stripe" }"#)]
#[schema(value_type = Option<StaticRoutingAlgorithm>, max_length = 255, example = r#"{"type": "single", "data": "stripe" }"#)]
pub frm_routing_algorithm: Option<serde_json::Value>,
/// 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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// 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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// 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<RoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
#[schema(value_type = Option<StaticRoutingAlgorithm>,example = json!({"type": "single", "data": "wise"}))]
pub payout_routing_algorithm: Option<serde_json::Value>,
/// Verified Apple Pay domains for a particular profile

View File

@ -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<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

View File

@ -58,7 +58,7 @@ pub struct PayoutCreateRequest {
pub currency: Option<api_enums::Currency>,
/// Specifies routing algorithm for selecting a connector
#[schema(value_type = Option<RoutingAlgorithm>, example = json!({
#[schema(value_type = Option<StaticRoutingAlgorithm>, example = json!({
"type": "single",
"data": "adyen"
}))]

View File

@ -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<String>,
pub description: Option<String>,
pub algorithm: Option<RoutingAlgorithm>,
pub algorithm: Option<StaticRoutingAlgorithm>,
#[schema(value_type = Option<String>)]
pub profile_id: Option<common_utils::id_type::ProfileId>,
}
@ -96,7 +96,7 @@ pub struct RoutingRetrieveResponse {
#[derive(Debug, serde::Serialize, ToSchema)]
#[serde(untagged)]
pub enum LinkedRoutingConfigRetrieveResponse {
MerchantAccountBased(RoutingRetrieveResponse),
MerchantAccountBased(Box<RoutingRetrieveResponse>),
ProfileBased(Vec<RoutingDictionaryRecord>),
}
@ -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<RoutableConnectorChoice>,
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<RoutableConnectorChoice>),
Priority(Vec<RoutableConnectorChoice>),
VolumeSplit(Vec<ConnectorVolumeSplit>),
@ -318,7 +331,7 @@ pub enum RoutingAlgorithm {
Advanced(ast::Program<ConnectorSelection>),
}
#[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<RoutableConnectorChoice>),
@ -327,7 +340,7 @@ pub enum RoutingAlgorithmSerde {
Advanced(ast::Program<ConnectorSelection>),
}
impl TryFrom<RoutingAlgorithmSerde> for RoutingAlgorithm {
impl TryFrom<RoutingAlgorithmSerde> for StaticRoutingAlgorithm {
type Error = error_stack::Report<ParsingError>;
fn try_from(value: RoutingAlgorithmSerde) -> Result<Self, Self::Error> {
@ -434,7 +447,7 @@ impl From<StraightThroughAlgorithm> for StraightThroughAlgorithmSerde {
}
}
impl From<StraightThroughAlgorithm> for RoutingAlgorithm {
impl From<StraightThroughAlgorithm> for StaticRoutingAlgorithm {
fn from(value: StraightThroughAlgorithm) -> Self {
match value {
StraightThroughAlgorithm::Single(conn) => Self::Single(conn),
@ -444,7 +457,7 @@ impl From<StraightThroughAlgorithm> 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<Vec<DynamicRoutingConfigParams>>,
pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
@ -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<u64>,
pub bucket_leak_interval_in_secs: Option<u64>,
@ -924,6 +939,7 @@ impl EliminationRoutingConfig {
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct SuccessBasedRoutingConfig {
pub params: Option<Vec<DynamicRoutingConfigParams>>,
pub config: Option<SuccessBasedRoutingConfigBody>,
@ -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<u32>,
pub default_success_rate: Option<f64>,