diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 97eb655121..a1156366d3 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -2057,12 +2057,15 @@ pub enum PayoutStatus { Debug, Default, Eq, + Hash, PartialEq, - ToSchema, serde::Deserialize, serde::Serialize, strum::Display, + strum::EnumVariantNames, + strum::EnumIter, strum::EnumString, + ToSchema, )] #[router_derive::diesel_enum(storage_type = "db_enum")] #[serde(rename_all = "snake_case")] diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index 415398105a..d30d63d094 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -23,10 +23,12 @@ common_enums = { version = "0.1.0", path = "../common_enums" } euclid_macros = { version = "0.1.0", path = "../euclid_macros" } [features] +default = [] ast_parser = ["dep:nom"] valued_jit = [] connector_choice_mca_id = [] dummy_connector = [] +payouts = [] [dev-dependencies] criterion = "0.5" diff --git a/crates/euclid/src/enums.rs b/crates/euclid/src/enums.rs index 68e081c7aa..7bd32d3363 100644 --- a/crates/euclid/src/enums.rs +++ b/crates/euclid/src/enums.rs @@ -34,6 +34,12 @@ collect_variants!(CaptureMethod); collect_variants!(Currency); collect_variants!(Country); collect_variants!(SetupFutureUsage); +#[cfg(feature = "payouts")] +collect_variants!(PayoutType); +#[cfg(feature = "payouts")] +collect_variants!(PayoutBankTransferType); +#[cfg(feature = "payouts")] +collect_variants!(PayoutWalletType); #[derive( Clone, @@ -94,3 +100,67 @@ pub enum MandateType { SingleUse, MultiUse, } + +#[cfg(feature = "payouts")] +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + strum::Display, + strum::EnumVariantNames, + strum::EnumIter, + strum::EnumString, + serde::Serialize, + serde::Deserialize, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum PayoutBankTransferType { + Ach, + Bacs, + Sepa, +} + +#[cfg(feature = "payouts")] +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + strum::Display, + strum::EnumVariantNames, + strum::EnumIter, + strum::EnumString, + serde::Serialize, + serde::Deserialize, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum PayoutWalletType { + Paypal, +} + +#[cfg(feature = "payouts")] +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + strum::Display, + strum::EnumVariantNames, + strum::EnumIter, + strum::EnumString, + serde::Serialize, + serde::Deserialize, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum PayoutType { + Card, + BankTransfer, + Wallet, +} diff --git a/crates/euclid/src/frontend/dir.rs b/crates/euclid/src/frontend/dir.rs index dc81359c51..7fb12d96c3 100644 --- a/crates/euclid/src/frontend/dir.rs +++ b/crates/euclid/src/frontend/dir.rs @@ -667,6 +667,101 @@ impl DirValue { } } +#[cfg(feature = "payouts")] +#[derive( + Debug, + Clone, + Hash, + PartialEq, + Eq, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumVariantNames, + strum::EnumString, + strum::EnumMessage, + strum::EnumProperty, +)] +pub enum PayoutDirKeyKind { + #[strum( + serialize = "country", + serialize = "business_country", + detailed_message = "Country of the business unit", + props(Category = "Merchant") + )] + #[serde(rename = "business_country", alias = "country")] + BusinessCountry, + + #[strum( + serialize = "billing_country", + detailed_message = "Country of the billing address of the customer", + props(Category = "Customer") + )] + #[serde(rename = "billing_country")] + BillingCountry, + + #[strum( + serialize = "business_label", + detailed_message = "Identifier for business unit", + props(Category = "Merchant") + )] + #[serde(rename = "business_label")] + BusinessLabel, + + #[strum( + serialize = "amount", + detailed_message = "Value of the transaction", + props(Category = "Order details") + )] + #[serde(rename = "amount")] + PayoutAmount, + + #[strum( + serialize = "payment_method", + detailed_message = "Different modes of payout - eg. cards, wallets, banks", + props(Category = "Payout Methods") + )] + #[serde(rename = "payment_method")] + PayoutType, + + #[strum( + serialize = "wallet", + detailed_message = "Supported types of Wallets for payouts", + props(Category = "Payout Methods Type") + )] + #[serde(rename = "wallet")] + WalletType, + + #[strum( + serialize = "bank_transfer", + detailed_message = "Supported types of Bank transfer types for payouts", + props(Category = "Payout Methods Type") + )] + #[serde(rename = "bank_transfer")] + BankTransferType, +} + +#[cfg(feature = "payouts")] +#[derive( + Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, strum::Display, strum::EnumVariantNames, +)] +pub enum PayoutDirValue { + #[serde(rename = "business_country", alias = "country")] + BusinessCountry(enums::Country), + #[serde(rename = "billing_country")] + BillingCountry(enums::Country), + #[serde(rename = "business_label")] + BusinessLabel(types::StrValue), + #[serde(rename = "amount")] + PayoutAmount(types::NumValue), + #[serde(rename = "payment_method")] + PayoutType(common_enums::PayoutType), + #[serde(rename = "wallet")] + WalletType(enums::PayoutWalletType), + #[serde(rename = "bank_transfer")] + BankTransferType(enums::PayoutBankTransferType), +} + #[derive(Debug, Clone)] pub enum DirComparisonLogic { NegativeConjunction, diff --git a/crates/euclid/src/frontend/dir/enums.rs b/crates/euclid/src/frontend/dir/enums.rs index 0b71f916d0..ab33f16fb0 100644 --- a/crates/euclid/src/frontend/dir/enums.rs +++ b/crates/euclid/src/frontend/dir/enums.rs @@ -6,6 +6,8 @@ pub use crate::enums::{ Country as BillingCountry, Currency as PaymentCurrency, MandateAcceptanceType, MandateType, PaymentMethod, PaymentType, RoutableConnectors, SetupFutureUsage, }; +#[cfg(feature = "payouts")] +pub use crate::enums::{PayoutBankTransferType, PayoutType, PayoutWalletType}; #[derive( Clone, diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml index 1a7c007265..d33ed2688a 100644 --- a/crates/euclid_wasm/Cargo.toml +++ b/crates/euclid_wasm/Cargo.toml @@ -11,14 +11,14 @@ crate-type = ["cdylib"] [features] default = ["connector_choice_bcompat", "payouts", "connector_choice_mca_id"] -release = ["connector_choice_bcompat", "connector_choice_mca_id"] +release = ["connector_choice_bcompat", "connector_choice_mca_id", "payouts"] connector_choice_bcompat = ["api_models/connector_choice_bcompat"] connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connector_choice_mca_id", "kgraph_utils/connector_choice_mca_id"] dummy_connector = ["kgraph_utils/dummy_connector", "connector_configs/dummy_connector"] production = ["connector_configs/production"] development = ["connector_configs/development"] sandbox = ["connector_configs/sandbox"] -payouts = ["api_models/payouts"] +payouts = ["api_models/payouts", "euclid/payouts"] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index c2ff3efc71..36af0dc2d2 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -256,12 +256,13 @@ pub fn get_variant_values(key: &str) -> Result { dir::DirKeyKind::CardRedirectType => dir_enums::CardRedirectType::VARIANTS, dir::DirKeyKind::GiftCardType => dir_enums::GiftCardType::VARIANTS, dir::DirKeyKind::VoucherType => dir_enums::VoucherType::VARIANTS, + dir::DirKeyKind::BankDebitType => dir_enums::BankDebitType::VARIANTS, + dir::DirKeyKind::PaymentAmount | dir::DirKeyKind::Connector | dir::DirKeyKind::CardBin | dir::DirKeyKind::BusinessLabel | dir::DirKeyKind::MetaData => Err("Key does not have variants".to_string())?, - dir::DirKeyKind::BankDebitType => dir_enums::BankDebitType::VARIANTS, }; Ok(serde_wasm_bindgen::to_value(variants)?) @@ -335,3 +336,52 @@ pub fn get_response_payload(input: JsValue) -> JsResult { let result = ConnectorApiIntegrationPayload::get_transformed_response_payload(input); Ok(serde_wasm_bindgen::to_value(&result)?) } + +#[cfg(feature = "payouts")] +#[wasm_bindgen(js_name = getAllPayoutKeys)] +pub fn get_all_payout_keys() -> JsResult { + let keys: Vec<&'static str> = dir::PayoutDirKeyKind::VARIANTS.to_vec(); + Ok(serde_wasm_bindgen::to_value(&keys)?) +} + +#[cfg(feature = "payouts")] +#[wasm_bindgen(js_name = getPayoutVariantValues)] +pub fn get_payout_variant_values(key: &str) -> Result { + let key = + dir::PayoutDirKeyKind::from_str(key).map_err(|_| "Invalid key received".to_string())?; + + let variants: &[&str] = match key { + dir::PayoutDirKeyKind::BusinessCountry => dir_enums::BusinessCountry::VARIANTS, + dir::PayoutDirKeyKind::BillingCountry => dir_enums::BillingCountry::VARIANTS, + dir::PayoutDirKeyKind::PayoutType => dir_enums::PayoutType::VARIANTS, + dir::PayoutDirKeyKind::WalletType => dir_enums::PayoutWalletType::VARIANTS, + dir::PayoutDirKeyKind::BankTransferType => dir_enums::PayoutBankTransferType::VARIANTS, + + dir::PayoutDirKeyKind::PayoutAmount | dir::PayoutDirKeyKind::BusinessLabel => { + Err("Key does not have variants".to_string())? + } + }; + + Ok(serde_wasm_bindgen::to_value(variants)?) +} + +#[cfg(feature = "payouts")] +#[wasm_bindgen(js_name = getPayoutDescriptionCategory)] +pub fn get_payout_description_category() -> JsResult { + let keys = dir::PayoutDirKeyKind::VARIANTS.to_vec(); + let mut category: HashMap, Vec>> = HashMap::new(); + for key in keys { + let dir_key = + dir::PayoutDirKeyKind::from_str(key).map_err(|_| "Invalid key received".to_string())?; + let details = types::PayoutDetails { + description: dir_key.get_detailed_message(), + kind: dir_key.clone(), + }; + category + .entry(dir_key.get_str("Category")) + .and_modify(|val| val.push(details.clone())) + .or_insert(vec![details]); + } + + Ok(serde_wasm_bindgen::to_value(&category)?) +} diff --git a/crates/euclid_wasm/src/types.rs b/crates/euclid_wasm/src/types.rs index 6353d9009c..aac0fed1f8 100644 --- a/crates/euclid_wasm/src/types.rs +++ b/crates/euclid_wasm/src/types.rs @@ -1,4 +1,6 @@ use euclid::frontend::dir::DirKeyKind; +#[cfg(feature = "payouts")] +use euclid::frontend::dir::PayoutDirKeyKind; use serde::Serialize; #[derive(Serialize, Clone)] @@ -6,3 +8,10 @@ pub struct Details<'a> { pub description: Option<&'a str>, pub kind: DirKeyKind, } + +#[cfg(feature = "payouts")] +#[derive(Serialize, Clone)] +pub struct PayoutDetails<'a> { + pub description: Option<&'a str>, + pub kind: PayoutDirKeyKind, +} diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 0d9c7f4f62..d4e8530de1 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -714,6 +714,7 @@ pub async fn retrieve_linked_routing_config( state: AppState, merchant_account: domain::MerchantAccount, #[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveLinkQuery, + #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); @@ -739,16 +740,17 @@ pub async fn retrieve_linked_routing_config( let mut active_algorithms = Vec::new(); for business_profile in business_profiles { - let routing_ref: routing_types::RoutingAlgorithmRef = business_profile - .routing_algorithm - .clone() - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "unable to deserialize routing algorithm ref from merchant account", - )? - .unwrap_or_default(); + let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { + enums::TransactionType::Payment => business_profile.routing_algorithm, + #[cfg(feature = "payouts")] + enums::TransactionType::Payout => business_profile.payout_routing_algorithm, + } + .clone() + .map(|val| val.parse_value("RoutingAlgorithmRef")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to deserialize routing algorithm ref from merchant account")? + .unwrap_or_default(); if let Some(algorithm_id) = routing_ref.algorithm_id { let record = db diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index c08aae5dc6..7d6b6c8ad2 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -401,10 +401,18 @@ impl Routing { #[allow(unused_mut)] let mut route = web::scope("/routing") .app_data(web::Data::new(state.clone())) - .service( - web::resource("/active") - .route(web::get().to(cloud_routing::routing_retrieve_linked_config)), - ) + .service(web::resource("/active").route(web::get().to( + |state, req, #[cfg(feature = "business_profile_routing")] query_params| { + cloud_routing::routing_retrieve_linked_config( + state, + req, + #[cfg(feature = "business_profile_routing")] + query_params, + #[cfg(feature = "business_profile_routing")] + &TransactionType::Payment, + ) + }, + ))) .service( web::resource("") .route( @@ -524,6 +532,18 @@ impl Routing { ) })), ) + .service(web::resource("/payouts/active").route(web::get().to( + |state, req, #[cfg(feature = "business_profile_routing")] query_params| { + cloud_routing::routing_retrieve_linked_config( + state, + req, + #[cfg(feature = "business_profile_routing")] + query_params, + #[cfg(feature = "business_profile_routing")] + &TransactionType::Payout, + ) + }, + ))) .service( web::resource("/payouts/default") .route(web::get().to(|state, req| { diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 80ff1590d5..1476fc7b3d 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -509,6 +509,7 @@ pub async fn routing_retrieve_linked_config( state: web::Data, req: HttpRequest, #[cfg(feature = "business_profile_routing")] query: web::Query, + #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, ) -> impl Responder { #[cfg(feature = "business_profile_routing")] { @@ -520,7 +521,12 @@ pub async fn routing_retrieve_linked_config( &req, query.into_inner(), |state, auth: AuthenticationData, query_params| { - routing::retrieve_linked_routing_config(state, auth.merchant_account, query_params) + routing::retrieve_linked_routing_config( + state, + auth.merchant_account, + query_params, + transaction_type, + ) }, #[cfg(not(feature = "release"))] auth::auth_type(