mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(router): Add Smart Routing to route payments efficiently (#2665)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: shashank_attarde <shashank.attarde@juspay.in> Co-authored-by: Aprabhat19 <amishaprabhat@gmail.com> Co-authored-by: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com>
This commit is contained in:
@ -9,8 +9,12 @@ license.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["payouts"]
|
||||
business_profile_routing = []
|
||||
connector_choice_bcompat = []
|
||||
errors = ["dep:actix-web", "dep:reqwest"]
|
||||
dummy_connector = ["common_enums/dummy_connector"]
|
||||
backwards_compatibility = ["connector_choice_bcompat"]
|
||||
connector_choice_mca_id = ["euclid/connector_choice_mca_id"]
|
||||
dummy_connector = ["common_enums/dummy_connector", "euclid/dummy_connector"]
|
||||
detailed_errors = []
|
||||
payouts = []
|
||||
|
||||
@ -32,5 +36,6 @@ thiserror = "1.0.40"
|
||||
cards = { version = "0.1.0", path = "../cards" }
|
||||
common_enums = { path = "../common_enums" }
|
||||
common_utils = { version = "0.1.0", path = "../common_utils" }
|
||||
euclid = { version = "0.1.0", path = "../euclid" }
|
||||
masking = { version = "0.1.0", path = "../masking" }
|
||||
router_derive = { version = "0.1.0", path = "../router_derive" }
|
||||
|
||||
@ -443,72 +443,6 @@ pub mod payout_routing_algorithm {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum RoutingAlgorithm {
|
||||
Single(RoutableConnectorChoice),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RoutableConnectorChoice {
|
||||
ConnectorName(api_enums::RoutableConnectors),
|
||||
ConnectorId {
|
||||
merchant_connector_id: String,
|
||||
connector: api_enums::RoutableConnectors,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(
|
||||
tag = "type",
|
||||
content = "data",
|
||||
rename_all = "snake_case",
|
||||
from = "StraightThroughAlgorithmSerde",
|
||||
into = "StraightThroughAlgorithmSerde"
|
||||
)]
|
||||
pub enum StraightThroughAlgorithm {
|
||||
Single(RoutableConnectorChoice),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum StraightThroughAlgorithmInner {
|
||||
Single(RoutableConnectorChoice),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum StraightThroughAlgorithmSerde {
|
||||
Direct(StraightThroughAlgorithmInner),
|
||||
Nested {
|
||||
algorithm: StraightThroughAlgorithmInner,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<StraightThroughAlgorithmSerde> for StraightThroughAlgorithm {
|
||||
fn from(value: StraightThroughAlgorithmSerde) -> Self {
|
||||
let inner = match value {
|
||||
StraightThroughAlgorithmSerde::Direct(algorithm) => algorithm,
|
||||
StraightThroughAlgorithmSerde::Nested { algorithm } => algorithm,
|
||||
};
|
||||
|
||||
match inner {
|
||||
StraightThroughAlgorithmInner::Single(conn) => Self::Single(conn),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StraightThroughAlgorithm> for StraightThroughAlgorithmSerde {
|
||||
fn from(value: StraightThroughAlgorithm) -> Self {
|
||||
let inner = match value {
|
||||
StraightThroughAlgorithm::Single(conn) => StraightThroughAlgorithmInner::Single(conn),
|
||||
};
|
||||
|
||||
Self::Nested { algorithm: inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PrimaryBusinessDetails {
|
||||
|
||||
@ -17,5 +17,6 @@ pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod refunds;
|
||||
pub mod routing;
|
||||
pub mod verifications;
|
||||
pub mod webhooks;
|
||||
|
||||
594
crates/api_models/src/routing.rs
Normal file
594
crates/api_models/src/routing.rs
Normal file
@ -0,0 +1,594 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use common_utils::errors::ParsingError;
|
||||
use error_stack::IntoReport;
|
||||
use euclid::{
|
||||
dssa::types::EuclidAnalysable,
|
||||
enums as euclid_enums,
|
||||
frontend::{
|
||||
ast,
|
||||
dir::{DirKeyKind, EuclidDirFilter},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::enums::{self, RoutableConnectors};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum ConnectorSelection {
|
||||
Priority(Vec<RoutableConnectorChoice>),
|
||||
VolumeSplit(Vec<ConnectorVolumeSplit>),
|
||||
}
|
||||
|
||||
impl ConnectorSelection {
|
||||
pub fn get_connector_list(&self) -> Vec<RoutableConnectorChoice> {
|
||||
match self {
|
||||
Self::Priority(list) => list.clone(),
|
||||
Self::VolumeSplit(splits) => {
|
||||
splits.iter().map(|split| split.connector.clone()).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutingConfigRequest {
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub algorithm: Option<RoutingAlgorithm>,
|
||||
pub profile_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct RoutingRetrieveQuery {
|
||||
pub limit: Option<u16>,
|
||||
pub offset: Option<u8>,
|
||||
|
||||
pub profile_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct RoutingRetrieveLinkQuery {
|
||||
pub profile_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutingRetrieveResponse {
|
||||
pub algorithm: Option<MerchantRoutingAlgorithm>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum LinkedRoutingConfigRetrieveResponse {
|
||||
MerchantAccountBased(RoutingRetrieveResponse),
|
||||
ProfileBased(Vec<RoutingDictionaryRecord>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MerchantRoutingAlgorithm {
|
||||
pub id: String,
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
pub profile_id: String,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub algorithm: RoutingAlgorithm,
|
||||
pub created_at: i64,
|
||||
pub modified_at: i64,
|
||||
}
|
||||
|
||||
impl EuclidDirFilter for ConnectorSelection {
|
||||
const ALLOWED: &'static [DirKeyKind] = &[
|
||||
DirKeyKind::PaymentMethod,
|
||||
DirKeyKind::CardBin,
|
||||
DirKeyKind::CardType,
|
||||
DirKeyKind::CardNetwork,
|
||||
DirKeyKind::PayLaterType,
|
||||
DirKeyKind::WalletType,
|
||||
DirKeyKind::UpiType,
|
||||
DirKeyKind::BankRedirectType,
|
||||
DirKeyKind::BankDebitType,
|
||||
DirKeyKind::CryptoType,
|
||||
DirKeyKind::MetaData,
|
||||
DirKeyKind::PaymentAmount,
|
||||
DirKeyKind::PaymentCurrency,
|
||||
DirKeyKind::AuthenticationType,
|
||||
DirKeyKind::MandateAcceptanceType,
|
||||
DirKeyKind::MandateType,
|
||||
DirKeyKind::PaymentType,
|
||||
DirKeyKind::SetupFutureUsage,
|
||||
DirKeyKind::CaptureMethod,
|
||||
DirKeyKind::BillingCountry,
|
||||
DirKeyKind::BusinessCountry,
|
||||
DirKeyKind::BusinessLabel,
|
||||
DirKeyKind::MetaData,
|
||||
DirKeyKind::RewardType,
|
||||
DirKeyKind::VoucherType,
|
||||
DirKeyKind::CardRedirectType,
|
||||
DirKeyKind::BankTransferType,
|
||||
];
|
||||
}
|
||||
|
||||
impl EuclidAnalysable for ConnectorSelection {
|
||||
fn get_dir_value_for_analysis(
|
||||
&self,
|
||||
rule_name: String,
|
||||
) -> Vec<(euclid::frontend::dir::DirValue, euclid::types::Metadata)> {
|
||||
self.get_connector_list()
|
||||
.into_iter()
|
||||
.map(|connector_choice| {
|
||||
let connector_name = connector_choice.connector.to_string();
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
let sub_label = connector_choice.sub_label.clone();
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
let mca_id = connector_choice.merchant_connector_id.clone();
|
||||
|
||||
(
|
||||
euclid::frontend::dir::DirValue::Connector(Box::new(connector_choice.into())),
|
||||
std::collections::HashMap::from_iter([(
|
||||
"CONNECTOR_SELECTION".to_string(),
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
serde_json::json!({
|
||||
"rule_name": rule_name,
|
||||
"connector_name": connector_name,
|
||||
"mca_id": mca_id,
|
||||
}),
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
serde_json ::json!({
|
||||
"rule_name": rule_name,
|
||||
"connector_name": connector_name,
|
||||
"sub_label": sub_label,
|
||||
}),
|
||||
)]),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ConnectorVolumeSplit {
|
||||
pub connector: RoutableConnectorChoice,
|
||||
pub split: u8,
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub enum RoutableChoiceKind {
|
||||
OnlyConnector,
|
||||
FullStruct,
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RoutableChoiceSerde {
|
||||
OnlyConnector(Box<RoutableConnectors>),
|
||||
FullStruct {
|
||||
connector: RoutableConnectors,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
merchant_connector_id: Option<String>,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[cfg_attr(
|
||||
feature = "connector_choice_bcompat",
|
||||
serde(from = "RoutableChoiceSerde"),
|
||||
serde(into = "RoutableChoiceSerde")
|
||||
)]
|
||||
#[cfg_attr(not(feature = "connector_choice_bcompat"), derive(PartialEq, Eq))]
|
||||
pub struct RoutableConnectorChoice {
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
pub choice_kind: RoutableChoiceKind,
|
||||
pub connector: RoutableConnectors,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
pub merchant_connector_id: Option<String>,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
pub sub_label: Option<String>,
|
||||
}
|
||||
|
||||
impl ToString for RoutableConnectorChoice {
|
||||
fn to_string(&self) -> String {
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
let base = self.connector.to_string();
|
||||
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
let base = {
|
||||
let mut sub_base = self.connector.to_string();
|
||||
if let Some(ref label) = self.sub_label {
|
||||
sub_base.push('_');
|
||||
sub_base.push_str(label);
|
||||
}
|
||||
|
||||
sub_base
|
||||
};
|
||||
|
||||
base
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
impl PartialEq for RoutableConnectorChoice {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
{
|
||||
self.connector.eq(&other.connector) && self.sub_label.eq(&other.sub_label)
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
{
|
||||
self.connector.eq(&other.connector)
|
||||
&& self.merchant_connector_id.eq(&other.merchant_connector_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
impl Eq for RoutableConnectorChoice {}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
impl From<RoutableChoiceSerde> for RoutableConnectorChoice {
|
||||
fn from(value: RoutableChoiceSerde) -> Self {
|
||||
match value {
|
||||
RoutableChoiceSerde::OnlyConnector(connector) => Self {
|
||||
choice_kind: RoutableChoiceKind::OnlyConnector,
|
||||
connector: *connector,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
merchant_connector_id: None,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label: None,
|
||||
},
|
||||
|
||||
RoutableChoiceSerde::FullStruct {
|
||||
connector,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
merchant_connector_id,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label,
|
||||
} => Self {
|
||||
choice_kind: RoutableChoiceKind::FullStruct,
|
||||
connector,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
merchant_connector_id,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "connector_choice_bcompat")]
|
||||
impl From<RoutableConnectorChoice> for RoutableChoiceSerde {
|
||||
fn from(value: RoutableConnectorChoice) -> Self {
|
||||
match value.choice_kind {
|
||||
RoutableChoiceKind::OnlyConnector => Self::OnlyConnector(Box::new(value.connector)),
|
||||
RoutableChoiceKind::FullStruct => Self::FullStruct {
|
||||
connector: value.connector,
|
||||
#[cfg(feature = "connector_choice_mca_id")]
|
||||
merchant_connector_id: value.merchant_connector_id,
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label: value.sub_label,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RoutableConnectorChoice> for ast::ConnectorChoice {
|
||||
fn from(value: RoutableConnectorChoice) -> Self {
|
||||
Self {
|
||||
connector: match value.connector {
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector1 => euclid_enums::Connector::DummyConnector1,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector2 => euclid_enums::Connector::DummyConnector2,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector3 => euclid_enums::Connector::DummyConnector3,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector4 => euclid_enums::Connector::DummyConnector4,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector5 => euclid_enums::Connector::DummyConnector5,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector6 => euclid_enums::Connector::DummyConnector6,
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
RoutableConnectors::DummyConnector7 => euclid_enums::Connector::DummyConnector7,
|
||||
RoutableConnectors::Aci => euclid_enums::Connector::Aci,
|
||||
RoutableConnectors::Adyen => euclid_enums::Connector::Adyen,
|
||||
RoutableConnectors::Airwallex => euclid_enums::Connector::Airwallex,
|
||||
RoutableConnectors::Authorizedotnet => euclid_enums::Connector::Authorizedotnet,
|
||||
RoutableConnectors::Bitpay => euclid_enums::Connector::Bitpay,
|
||||
RoutableConnectors::Bambora => euclid_enums::Connector::Bambora,
|
||||
RoutableConnectors::Bluesnap => euclid_enums::Connector::Bluesnap,
|
||||
RoutableConnectors::Boku => euclid_enums::Connector::Boku,
|
||||
RoutableConnectors::Braintree => euclid_enums::Connector::Braintree,
|
||||
RoutableConnectors::Cashtocode => euclid_enums::Connector::Cashtocode,
|
||||
RoutableConnectors::Checkout => euclid_enums::Connector::Checkout,
|
||||
RoutableConnectors::Coinbase => euclid_enums::Connector::Coinbase,
|
||||
RoutableConnectors::Cryptopay => euclid_enums::Connector::Cryptopay,
|
||||
RoutableConnectors::Cybersource => euclid_enums::Connector::Cybersource,
|
||||
RoutableConnectors::Dlocal => euclid_enums::Connector::Dlocal,
|
||||
RoutableConnectors::Fiserv => euclid_enums::Connector::Fiserv,
|
||||
RoutableConnectors::Forte => euclid_enums::Connector::Forte,
|
||||
RoutableConnectors::Globalpay => euclid_enums::Connector::Globalpay,
|
||||
RoutableConnectors::Globepay => euclid_enums::Connector::Globepay,
|
||||
RoutableConnectors::Gocardless => euclid_enums::Connector::Gocardless,
|
||||
RoutableConnectors::Helcim => euclid_enums::Connector::Helcim,
|
||||
RoutableConnectors::Iatapay => euclid_enums::Connector::Iatapay,
|
||||
RoutableConnectors::Klarna => euclid_enums::Connector::Klarna,
|
||||
RoutableConnectors::Mollie => euclid_enums::Connector::Mollie,
|
||||
RoutableConnectors::Multisafepay => euclid_enums::Connector::Multisafepay,
|
||||
RoutableConnectors::Nexinets => euclid_enums::Connector::Nexinets,
|
||||
RoutableConnectors::Nmi => euclid_enums::Connector::Nmi,
|
||||
RoutableConnectors::Noon => euclid_enums::Connector::Noon,
|
||||
RoutableConnectors::Nuvei => euclid_enums::Connector::Nuvei,
|
||||
RoutableConnectors::Opennode => euclid_enums::Connector::Opennode,
|
||||
RoutableConnectors::Payme => euclid_enums::Connector::Payme,
|
||||
RoutableConnectors::Paypal => euclid_enums::Connector::Paypal,
|
||||
RoutableConnectors::Payu => euclid_enums::Connector::Payu,
|
||||
RoutableConnectors::Powertranz => euclid_enums::Connector::Powertranz,
|
||||
RoutableConnectors::Rapyd => euclid_enums::Connector::Rapyd,
|
||||
RoutableConnectors::Shift4 => euclid_enums::Connector::Shift4,
|
||||
RoutableConnectors::Square => euclid_enums::Connector::Square,
|
||||
RoutableConnectors::Stax => euclid_enums::Connector::Stax,
|
||||
RoutableConnectors::Stripe => euclid_enums::Connector::Stripe,
|
||||
RoutableConnectors::Trustpay => euclid_enums::Connector::Trustpay,
|
||||
RoutableConnectors::Tsys => euclid_enums::Connector::Tsys,
|
||||
RoutableConnectors::Volt => euclid_enums::Connector::Volt,
|
||||
RoutableConnectors::Wise => euclid_enums::Connector::Wise,
|
||||
RoutableConnectors::Worldline => euclid_enums::Connector::Worldline,
|
||||
RoutableConnectors::Worldpay => euclid_enums::Connector::Worldpay,
|
||||
RoutableConnectors::Zen => euclid_enums::Connector::Zen,
|
||||
},
|
||||
|
||||
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||
sub_label: value.sub_label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DetailedConnectorChoice {
|
||||
pub connector: RoutableConnectors,
|
||||
pub business_label: Option<String>,
|
||||
pub business_country: Option<enums::CountryAlpha2>,
|
||||
pub business_sub_label: Option<String>,
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum RoutingAlgorithmKind {
|
||||
Single,
|
||||
Priority,
|
||||
VolumeSplit,
|
||||
Advanced,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(
|
||||
tag = "type",
|
||||
content = "data",
|
||||
rename_all = "snake_case",
|
||||
try_from = "RoutingAlgorithmSerde"
|
||||
)]
|
||||
pub enum RoutingAlgorithm {
|
||||
Single(Box<RoutableConnectorChoice>),
|
||||
Priority(Vec<RoutableConnectorChoice>),
|
||||
VolumeSplit(Vec<ConnectorVolumeSplit>),
|
||||
Advanced(euclid::frontend::ast::Program<ConnectorSelection>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum RoutingAlgorithmSerde {
|
||||
Single(Box<RoutableConnectorChoice>),
|
||||
Priority(Vec<RoutableConnectorChoice>),
|
||||
VolumeSplit(Vec<ConnectorVolumeSplit>),
|
||||
Advanced(euclid::frontend::ast::Program<ConnectorSelection>),
|
||||
}
|
||||
|
||||
impl TryFrom<RoutingAlgorithmSerde> for RoutingAlgorithm {
|
||||
type Error = error_stack::Report<ParsingError>;
|
||||
|
||||
fn try_from(value: RoutingAlgorithmSerde) -> Result<Self, Self::Error> {
|
||||
match &value {
|
||||
RoutingAlgorithmSerde::Priority(i) if i.is_empty() => {
|
||||
Err(ParsingError::StructParseFailure(
|
||||
"Connectors list can't be empty for Priority Algorithm",
|
||||
))
|
||||
.into_report()?
|
||||
}
|
||||
RoutingAlgorithmSerde::VolumeSplit(i) if i.is_empty() => {
|
||||
Err(ParsingError::StructParseFailure(
|
||||
"Connectors list can't be empty for Volume split Algorithm",
|
||||
))
|
||||
.into_report()?
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(match value {
|
||||
RoutingAlgorithmSerde::Single(i) => Self::Single(i),
|
||||
RoutingAlgorithmSerde::Priority(i) => Self::Priority(i),
|
||||
RoutingAlgorithmSerde::VolumeSplit(i) => Self::VolumeSplit(i),
|
||||
RoutingAlgorithmSerde::Advanced(i) => Self::Advanced(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(
|
||||
tag = "type",
|
||||
content = "data",
|
||||
rename_all = "snake_case",
|
||||
try_from = "StraightThroughAlgorithmSerde",
|
||||
into = "StraightThroughAlgorithmSerde"
|
||||
)]
|
||||
pub enum StraightThroughAlgorithm {
|
||||
Single(Box<RoutableConnectorChoice>),
|
||||
Priority(Vec<RoutableConnectorChoice>),
|
||||
VolumeSplit(Vec<ConnectorVolumeSplit>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum StraightThroughAlgorithmInner {
|
||||
Single(Box<RoutableConnectorChoice>),
|
||||
Priority(Vec<RoutableConnectorChoice>),
|
||||
VolumeSplit(Vec<ConnectorVolumeSplit>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum StraightThroughAlgorithmSerde {
|
||||
Direct(StraightThroughAlgorithmInner),
|
||||
Nested {
|
||||
algorithm: StraightThroughAlgorithmInner,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<StraightThroughAlgorithmSerde> for StraightThroughAlgorithm {
|
||||
type Error = error_stack::Report<ParsingError>;
|
||||
|
||||
fn try_from(value: StraightThroughAlgorithmSerde) -> Result<Self, Self::Error> {
|
||||
let inner = match value {
|
||||
StraightThroughAlgorithmSerde::Direct(algorithm) => algorithm,
|
||||
StraightThroughAlgorithmSerde::Nested { algorithm } => algorithm,
|
||||
};
|
||||
|
||||
match &inner {
|
||||
StraightThroughAlgorithmInner::Priority(i) if i.is_empty() => {
|
||||
Err(ParsingError::StructParseFailure(
|
||||
"Connectors list can't be empty for Priority Algorithm",
|
||||
))
|
||||
.into_report()?
|
||||
}
|
||||
StraightThroughAlgorithmInner::VolumeSplit(i) if i.is_empty() => {
|
||||
Err(ParsingError::StructParseFailure(
|
||||
"Connectors list can't be empty for Volume split Algorithm",
|
||||
))
|
||||
.into_report()?
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Ok(match inner {
|
||||
StraightThroughAlgorithmInner::Single(single) => Self::Single(single),
|
||||
StraightThroughAlgorithmInner::Priority(plist) => Self::Priority(plist),
|
||||
StraightThroughAlgorithmInner::VolumeSplit(vsplit) => Self::VolumeSplit(vsplit),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StraightThroughAlgorithm> for StraightThroughAlgorithmSerde {
|
||||
fn from(value: StraightThroughAlgorithm) -> Self {
|
||||
let inner = match value {
|
||||
StraightThroughAlgorithm::Single(conn) => StraightThroughAlgorithmInner::Single(conn),
|
||||
StraightThroughAlgorithm::Priority(plist) => {
|
||||
StraightThroughAlgorithmInner::Priority(plist)
|
||||
}
|
||||
StraightThroughAlgorithm::VolumeSplit(vsplit) => {
|
||||
StraightThroughAlgorithmInner::VolumeSplit(vsplit)
|
||||
}
|
||||
};
|
||||
|
||||
Self::Nested { algorithm: inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StraightThroughAlgorithm> for RoutingAlgorithm {
|
||||
fn from(value: StraightThroughAlgorithm) -> Self {
|
||||
match value {
|
||||
StraightThroughAlgorithm::Single(conn) => Self::Single(conn),
|
||||
StraightThroughAlgorithm::Priority(conns) => Self::Priority(conns),
|
||||
StraightThroughAlgorithm::VolumeSplit(splits) => Self::VolumeSplit(splits),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RoutingAlgorithm {
|
||||
pub fn get_kind(&self) -> RoutingAlgorithmKind {
|
||||
match self {
|
||||
Self::Single(_) => RoutingAlgorithmKind::Single,
|
||||
Self::Priority(_) => RoutingAlgorithmKind::Priority,
|
||||
Self::VolumeSplit(_) => RoutingAlgorithmKind::VolumeSplit,
|
||||
Self::Advanced(_) => RoutingAlgorithmKind::Advanced,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutingAlgorithmRef {
|
||||
pub algorithm_id: Option<String>,
|
||||
pub timestamp: i64,
|
||||
pub config_algo_id: Option<String>,
|
||||
pub surcharge_config_algo_id: Option<String>,
|
||||
}
|
||||
|
||||
impl RoutingAlgorithmRef {
|
||||
pub fn update_algorithm_id(&mut self, new_id: String) {
|
||||
self.algorithm_id = Some(new_id);
|
||||
self.timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
}
|
||||
|
||||
pub fn update_conditional_config_id(&mut self, ids: String) {
|
||||
self.config_algo_id = Some(ids);
|
||||
self.timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
}
|
||||
|
||||
pub fn update_surcharge_config_id(&mut self, ids: String) {
|
||||
self.surcharge_config_algo_id = Some(ids);
|
||||
self.timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
pub struct RoutingDictionaryRecord {
|
||||
pub id: String,
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
pub profile_id: String,
|
||||
pub name: String,
|
||||
pub kind: RoutingAlgorithmKind,
|
||||
pub description: String,
|
||||
pub created_at: i64,
|
||||
pub modified_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RoutingDictionary {
|
||||
pub merchant_id: String,
|
||||
pub active_id: Option<String>,
|
||||
pub records: Vec<RoutingDictionaryRecord>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum RoutingKind {
|
||||
Config(RoutingDictionary),
|
||||
RoutingAlgorithm(Vec<RoutingDictionaryRecord>),
|
||||
}
|
||||
Reference in New Issue
Block a user