Skip to content
Merged
6 changes: 5 additions & 1 deletion config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -969,4 +969,8 @@ primary_color = "#006DF9"
background_color = "#FFFFFF" # Email background color

[billing_connectors_payment_sync]
billing_connectors_which_require_payment_sync = "stripebilling, recurly" # List of billing connectors which has payment sync api call
billing_connectors_which_require_payment_sync = "stripebilling, recurly" # List of billing connectors which has payment sync api call

[open_router]
enabled = true # Enable or disable Open Router
url = "http://localhost:8080" # Open Router URL
4 changes: 4 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1033,3 +1033,7 @@ background_color = "#FFFFFF"

[platform]
enabled = true

[open_router]
enabled = false
url = "http://localhost:8080"
9 changes: 6 additions & 3 deletions config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -788,9 +788,9 @@ enabled = false
global_tenant = { tenant_id = "global", schema = "public", redis_key_prefix = "", clickhouse_database = "default" }

[multitenancy.tenants.public]
base_url = "http://localhost:8080"
schema = "public"
accounts_schema = "public"
base_url = "http://localhost:8080"
schema = "public"
accounts_schema = "public"
redis_key_prefix = ""
clickhouse_database = "default"

Expand Down Expand Up @@ -874,3 +874,6 @@ background_color = "#FFFFFF"

[platform]
enabled = true

[open_router]
enabled = false
2 changes: 2 additions & 0 deletions crates/api_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub mod gsm;
pub mod health_check;
pub mod locker_migration;
pub mod mandates;
#[cfg(feature = "dynamic_routing")]
pub mod open_router;
pub mod organization;
pub mod payment_methods;
pub mod payments;
Expand Down
122 changes: 122 additions & 0 deletions crates/api_models/src/open_router.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use std::{collections::HashMap, fmt::Debug};

use common_utils::{id_type, types::MinorUnit};
pub use euclid::{
dssa::types::EuclidAnalysable,
frontend::{
ast,
dir::{DirKeyKind, EuclidDirFilter},
},
};
use serde::{Deserialize, Serialize};

use crate::enums::{Currency, PaymentMethod, RoutableConnectors};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenRouterDecideGatewayRequest {
pub payment_info: PaymentInfo,
pub merchant_id: id_type::ProfileId,
pub eligible_gateway_list: Option<Vec<RoutableConnectors>>,
pub ranking_algorithm: Option<RankingAlgorithm>,
pub elimination_enabled: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RankingAlgorithm {
SrBasedRouting,
PlBasedRouting,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentInfo {
pub payment_id: id_type::PaymentId,
pub amount: MinorUnit,
pub currency: Currency,
// customerId: Option<ETCu::CustomerId>,
// preferredGateway: Option<ETG::Gateway>,
pub payment_type: String,
// metadata: Option<String>,
// internalMetadata: Option<String>,
// isEmi: Option<bool>,
// emiBank: Option<String>,
// emiTenure: Option<i32>,
pub payment_method_type: String,
pub payment_method: PaymentMethod,
// paymentSource: Option<String>,
// authType: Option<ETCa::txn_card_info::AuthType>,
// cardIssuerBankName: Option<String>,
// cardIsin: Option<String>,
// cardType: Option<ETCa::card_type::CardType>,
// cardSwitchProvider: Option<Secret<String>>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct DecidedGateway {
pub gateway_priority_map: Option<HashMap<String, f64>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub status: String,
pub error_code: String,
pub error_message: String,
pub priority_logic_tag: Option<String>,
pub filter_wise_gateways: Option<serde_json::Value>,
pub error_info: UnifiedError,
pub is_dynamic_mga_enabled: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct UnifiedError {
pub code: String,
pub user_message: String,
pub developer_message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UpdateScorePayload {
pub merchant_id: id_type::ProfileId,
pub gateway: RoutableConnectors,
pub status: TxnStatus,
pub payment_id: id_type::PaymentId,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TxnStatus {
Started,
AuthenticationFailed,
JuspayDeclined,
PendingVBV,
VBVSuccessful,
Authorized,
AuthorizationFailed,
Charged,
Authorizing,
CODInitiated,
Voided,
VoidInitiated,
Nop,
CaptureInitiated,
CaptureFailed,
VoidFailed,
AutoRefunded,
PartialCharged,
ToBeCharged,
Pending,
Failure,
Declined,
}

impl From<bool> for TxnStatus {
fn from(value: bool) -> Self {
match value {
true => Self::Charged,
_ => Self::Failure,
}
}
}
1 change: 1 addition & 0 deletions crates/router/src/configs/secrets_transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,5 +550,6 @@ pub(crate) async fn fetch_raw_secrets(
network_tokenization_supported_connectors: conf.network_tokenization_supported_connectors,
theme: conf.theme,
platform: conf.platform,
open_router: conf.open_router,
}
}
6 changes: 6 additions & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,14 @@ pub struct Settings<S: SecretState> {
pub network_tokenization_supported_connectors: NetworkTokenizationSupportedConnectors,
pub theme: ThemeSettings,
pub platform: Platform,
pub open_router: OpenRouter,
}

#[derive(Debug, Deserialize, Clone, Default)]
pub struct OpenRouter {
pub enabled: bool,
pub url: String,
}
#[derive(Debug, Deserialize, Clone, Default)]
pub struct Platform {
pub enabled: bool,
Expand Down
4 changes: 4 additions & 0 deletions crates/router/src/core/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ pub enum RoutingError {
ContractRoutingClientInitializationError,
#[error("Invalid contract based connector label received from dynamic routing service: '{0}'")]
InvalidContractBasedConnectorLabel(String),
#[error("Failed to perform {algo} in open_router")]
OpenRouterCallFailed { algo: String },
#[error("Error from open_router: {0}")]
OpenRouterError(String),
}

#[derive(Debug, Clone, thiserror::Error)]
Expand Down
66 changes: 39 additions & 27 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7268,6 +7268,9 @@ where
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;

#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
let payment_attempt = transaction_data.payment_attempt.clone();

let connectors = routing::perform_eligibility_analysis_with_fallback(
&state.clone(),
key_store,
Expand All @@ -7282,33 +7285,42 @@ where

// dynamic success based connector selection
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
let connectors = {
if let Some(algo) = business_profile.dynamic_routing_algorithm.clone() {
let dynamic_routing_config: api_models::routing::DynamicRoutingAlgorithmRef = algo
.parse_value("DynamicRoutingAlgorithmRef")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?;
let dynamic_split = api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Dynamic,
split: dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
let connectors = if let Some(algo) = business_profile.dynamic_routing_algorithm.clone() {
let dynamic_routing_config: api_models::routing::DynamicRoutingAlgorithmRef = algo
.parse_value("DynamicRoutingAlgorithmRef")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to deserialize DynamicRoutingAlgorithmRef from JSON")?;
let dynamic_split = api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Dynamic,
split: dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
};
let static_split: api_models::routing::RoutingVolumeSplit =
api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Static,
split: consts::DYNAMIC_ROUTING_MAX_VOLUME
- dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
};
let static_split: api_models::routing::RoutingVolumeSplit =
api_models::routing::RoutingVolumeSplit {
routing_type: api_models::routing::RoutingType::Static,
split: consts::DYNAMIC_ROUTING_MAX_VOLUME
- dynamic_routing_config
.dynamic_routing_volume_split
.unwrap_or_default(),
};
let volume_split_vec = vec![dynamic_split, static_split];
let routing_choice =
routing::perform_dynamic_routing_volume_split(volume_split_vec, None)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to perform volume split on routing type")?;
let volume_split_vec = vec![dynamic_split, static_split];
let routing_choice = routing::perform_dynamic_routing_volume_split(volume_split_vec, None)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to perform volume split on routing type")?;

if routing_choice.routing_type.is_dynamic_routing() {
if routing_choice.routing_type.is_dynamic_routing() {
if state.conf.open_router.enabled {
routing::perform_open_routing(
state,
connectors.clone(),
business_profile,
payment_attempt,
)
.await
.map_err(|e| logger::error!(open_routing_error=?e))
.unwrap_or(connectors)
} else {
let dynamic_routing_config_params_interpolator =
routing_helpers::DynamicRoutingConfigParamsInterpolator::new(
payment_data.get_payment_attempt().payment_method,
Expand Down Expand Up @@ -7350,12 +7362,12 @@ where
.await
.map_err(|e| logger::error!(dynamic_routing_error=?e))
.unwrap_or(connectors)
} else {
connectors
}
} else {
connectors
}
} else {
connectors
};

let connector_data = connectors
Expand Down
Loading
Loading