diff --git a/config/Development.toml b/config/Development.toml index 996a06b775..40e5de9db5 100644 --- a/config/Development.toml +++ b/config/Development.toml @@ -2,8 +2,8 @@ enabled = false [log.console] -enabled = true -level = "DEBUG" +enabled = true +level = "DEBUG" log_format = "default" [log.telemetry] @@ -11,27 +11,27 @@ enabled = false # TODO: Update database credentials before running application [master_database] -username = "db_user" -password = "db_pass" -host = "localhost" -port = 5432 -dbname = "hyperswitch_db" +username = "db_user" +password = "db_pass" +host = "localhost" +port = 5432 +dbname = "hyperswitch_db" pool_size = 5 [replica_database] -username = "replica_user" -password = "replica_pass" -host = "localhost" -port = 5432 -dbname = "hyperswitch_db" -pool_size = 5 +username = "replica_user" +password = "replica_pass" +host = "localhost" +port = 5432 +dbname = "hyperswitch_db" +pool_size = 5 connection_timeout = 10 [proxy] [locker] -host = "" -mock_locker = true +host = "" +mock_locker = true basilisk_host = "" [jwekey] @@ -43,7 +43,7 @@ locker_decryption_key1 = "" locker_decryption_key2 = "" [connectors.supported] -wallets = ["klarna", "braintree", "applepay"] +wallets = [ "klarna", "braintree", "applepay" ] cards = [ "aci", "adyen", @@ -66,7 +66,7 @@ cards = [ [refund] max_attempts = 10 -max_age = 365 +max_age = 365 [webhooks] outgoing_enabled = true @@ -138,31 +138,35 @@ base_url = "https://sandbox.dlocal.com/" stream = "SCHEDULER_STREAM" [scheduler.consumer] -disabled = false +disabled = false consumer_group = "SCHEDULER_GROUP" +[bank_config.eps] +stripe = { banks = "arzte_und_apotheker_bank,austrian_anadi_bank_ag,bank_austriabankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank" } +adyen = { banks = "austrian_anadi_bank_ag,bank_austriabankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank" } + [pm_filters.stripe] -google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } -apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" } -klarna = { country = "AT,BE,DK,FI,FR,DE,IE,IT,NL,NO,ES,SE,GB,US", currency = "EUR,USD,GBP,DKK,SEK,NOK" } -affirm = { country = "US", currency = "USD" } +google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } +apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" } +klarna = { country = "AT,BE,DK,FI,FR,DE,IE,IT,NL,NO,ES,SE,GB,US", currency = "EUR,USD,GBP,DKK,SEK,NOK" } +affirm = { country = "US", currency = "USD" } afterpay_clearpay = { country = "US,CA,GB,AU,NZ,FR,ES", currency = "USD,CAD,GBP,AUD,NZD,EUR" } -giropay = { country = "DE", currency = "EUR" } -eps = { country = "AT", currency = "EUR" } -sofort = { country = "AT,BE,DE,IT,NL,ES", currency = "EUR" } -ideal = { country = "NL", currency = "EUR" } +giropay = { country = "DE", currency = "EUR" } +eps = { country = "AT", currency = "EUR" } +sofort = { country = "AT,BE,DE,IT,NL,ES", currency = "EUR" } +ideal = { country = "NL", currency = "EUR" } [pm_filters.adyen] -google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } -apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US", currency = "AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } -paypal = { currency = "AUD,BRL,CAD,CZK,DKK,EUR,HKD,HUF,INR,JPY,MYR,MXN,NZD,NOK,PHP,PLN,RUB,GBP,SGD,SEK,CHF,THB,USD" } -klarna = { country = "AT,BE,DK,FI,FR,DE,IE,IT,NL,NO,ES,SE,GB,US,CA", currency = "USD,GBP,EUR,CHF,DKK,SEK,NOK,AUD,PLN,CAD" } -affirm = { country = "US", currency = "USD" } +google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } +apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US", currency = "AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } +paypal = { currency = "AUD,BRL,CAD,CZK,DKK,EUR,HKD,HUF,INR,JPY,MYR,MXN,NZD,NOK,PHP,PLN,RUB,GBP,SGD,SEK,CHF,THB,USD" } +klarna = { country = "AT,BE,DK,FI,FR,DE,IE,IT,NL,NO,ES,SE,GB,US,CA", currency = "USD,GBP,EUR,CHF,DKK,SEK,NOK,AUD,PLN,CAD" } +affirm = { country = "US", currency = "USD" } afterpay_clearpay = { country = "US,CA,GB,AU,NZ,FR,ES", currency = "GBP" } -giropay = { country = "DE", currency = "EUR" } -eps = { country = "AT", currency = "EUR" } -sofort = { country = "ES,GB,SE,AT,NL,DE,CH,BE,FR,FI,IT,PL", currency = "EUR" } -ideal = { country = "NL", currency = "EUR" } +giropay = { country = "DE", currency = "EUR" } +eps = { country = "AT", currency = "EUR" } +sofort = { country = "ES,GB,SE,AT,NL,DE,CH,BE,FR,FI,IT,PL", currency = "EUR" } +ideal = { country = "NL", currency = "EUR" } [pm_filters.braintree] paypal = { currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" } @@ -172,8 +176,8 @@ klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,C [pm_filters.authorizedotnet] google_pay = { currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } -paypal = { currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } +paypal = { currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } [pm_filters.worldpay] google_pay = { country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" } -apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" } +apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" } diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 474d3e8080..131dca2f6f 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -4,7 +4,10 @@ use common_utils::pii; use serde::de; use utoipa::ToSchema; -use crate::{admin, enums as api_enums, payments}; +use crate::{ + admin, enums as api_enums, + payments::{self, BankCodeResponse}, +}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] #[serde(deny_unknown_fields)] @@ -168,6 +171,7 @@ pub struct ResponsePaymentMethodTypes { pub payment_method_type: api_enums::PaymentMethodType, pub payment_experience: Option>, pub card_networks: Option>, + pub bank_names: Option>, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 256e31a60d..b953c3210e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -18,6 +18,30 @@ pub enum PaymentOp { Confirm, } +#[derive(serde::Deserialize)] +pub struct BankData { + pub payment_method_type: api_enums::PaymentMethodType, + pub code_information: Vec, +} + +#[derive(serde::Deserialize)] +pub struct BankCodeInformation { + pub bank_name: api_enums::BankNames, + pub connector_codes: Vec, +} + +#[derive(serde::Deserialize)] +pub struct ConnectorCode { + pub connector: api_enums::Connector, + pub code: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq, Eq)] +pub struct BankCodeResponse { + pub bank_name: Vec, + pub eligible_connectors: Vec, +} + #[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] #[serde(deny_unknown_fields)] pub struct PaymentsRequest { diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 87e0e5b159..3fabbf6e19 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -56,6 +56,20 @@ pub struct Settings { pub jwekey: Jwekey, pub webhooks: WebhooksSettings, pub pm_filters: ConnectorFilters, + pub bank_config: BankRedirectConfig, +} + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct BankRedirectConfig( + pub HashMap, +); +#[derive(Debug, Deserialize, Clone)] +pub struct ConnectorBankNames(pub HashMap); + +#[derive(Debug, Deserialize, Clone)] +pub struct BanksVector { + #[serde(deserialize_with = "bank_vec_deser")] + pub banks: HashSet, } #[derive(Debug, Deserialize, Clone, Default)] @@ -115,6 +129,18 @@ where })) } +fn bank_vec_deser<'a, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'a>, +{ + let value = ::deserialize(deserializer)?; + Ok(value + .trim() + .split(',') + .flat_map(api_models::enums::BankNames::from_str) + .collect()) +} + #[derive(Debug, Deserialize, Clone)] #[serde(default)] pub struct Secrets { diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 435c641bcc..1d4847b568 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2,12 +2,13 @@ use std::collections::{HashMap, HashSet}; use api_models::{ admin::{self, PaymentMethodsEnabled}, - enums as api_enums, + enums::{self as api_enums}, payment_methods::{ CardNetworkTypes, PaymentExperienceTypes, RequestPaymentMethodTypes, ResponsePaymentMethodIntermediate, ResponsePaymentMethodTypes, ResponsePaymentMethodsEnabled, }, + payments::BankCodeResponse, }; use common_utils::{consts, ext_traits::AsyncExt, generate_id}; use error_stack::{report, ResultExt}; @@ -348,6 +349,71 @@ pub async fn delete_card<'a>( Ok(delete_card_resp) } +pub fn get_banks( + state: &routes::AppState, + pm_type: api_enums::PaymentMethodType, + connectors: Vec, +) -> Result, errors::ApiErrorResponse> { + let mut bank_names_hm: HashMap> = HashMap::new(); + + for connector in &connectors { + if let Some(connector_bank_names) = state.conf.bank_config.0.get(&pm_type) { + if let Some(connector_hash_set) = connector_bank_names.0.get(connector) { + bank_names_hm.insert(connector.clone(), connector_hash_set.banks.clone()); + } else { + logger::error!("Could not find any configured connectors for payment_method -> {pm_type} for connector -> {connector}"); + } + } else { + logger::error!("Could not find any configured banks for payment_method -> {pm_type} for connector -> {connector}"); + } + } + + let vector_of_hashsets = bank_names_hm + .values() + .map(|bank_names_hashset| bank_names_hashset.to_owned()) + .collect::>(); + + let mut common_bank_names = HashSet::new(); + if let Some(first_element) = vector_of_hashsets.first() { + common_bank_names = vector_of_hashsets + .iter() + .skip(1) + .fold(first_element.to_owned(), |acc, hs| { + acc.intersection(hs).cloned().collect() + }); + } + + let mut bank_code_responses = vec![]; + if !common_bank_names.is_empty() { + bank_code_responses.push(BankCodeResponse { + bank_name: common_bank_names.clone().into_iter().collect(), + eligible_connectors: connectors.clone(), + }); + } + + for connector in connectors { + if let Some(all_bank_codes_for_connector) = bank_names_hm.get(&connector) { + let remaining_bank_codes: HashSet<_> = all_bank_codes_for_connector + .difference(&common_bank_names) + .collect(); + + if !remaining_bank_codes.is_empty() { + bank_code_responses.push(BankCodeResponse { + bank_name: remaining_bank_codes + .into_iter() + .map(|ele| ele.to_owned()) + .collect(), + eligible_connectors: vec![connector], + }) + } + } else { + logger::error!("Could not find any configured banks for payment_method -> {pm_type} for connector -> {connector}"); + } + } + + Ok(bank_code_responses) +} + pub async fn list_payment_methods( state: &routes::AppState, merchant_account: storage::MerchantAccount, @@ -431,6 +497,9 @@ pub async fn list_payment_methods( HashMap>>, > = HashMap::new(); + let mut banks_consolidated_hm: HashMap> = + HashMap::new(); + for element in response.clone() { let payment_method = element.payment_method; let payment_method_type = element.payment_method_type; @@ -510,6 +579,17 @@ pub async fn list_payment_methods( card_networks_consolidated_hm.insert(payment_method, payment_method_type_hm); } } + + if element.payment_method == api_enums::PaymentMethod::BankRedirect { + let connector = element.connector.clone(); + if let Some(vector_of_connectors) = + banks_consolidated_hm.get_mut(&element.payment_method_type) + { + vector_of_connectors.push(connector); + } else { + banks_consolidated_hm.insert(element.payment_method_type, vec![connector]); + } + } } let mut payment_method_responses: Vec = vec![]; @@ -528,6 +608,7 @@ pub async fn list_payment_methods( payment_method_type: *payment_method_types_hm.0, payment_experience: Some(payment_experience_types), card_networks: None, + bank_names: None, }) } @@ -552,6 +633,7 @@ pub async fn list_payment_methods( payment_method_type: *payment_method_types_hm.0, card_networks: Some(card_network_types), payment_experience: None, + bank_names: None, }) } @@ -561,6 +643,27 @@ pub async fn list_payment_methods( }) } + let mut bank_payment_method_types = vec![]; + + for key in banks_consolidated_hm.iter() { + let payment_method_type = *key.0; + let connectors = key.1.clone(); + let bank_names = get_banks(state, payment_method_type, connectors)?; + bank_payment_method_types.push({ + ResponsePaymentMethodTypes { + payment_method_type, + bank_names: Some(bank_names), + payment_experience: None, + card_networks: None, + } + }) + } + + payment_method_responses.push(ResponsePaymentMethodsEnabled { + payment_method: api_enums::PaymentMethod::BankRedirect, + payment_method_types: bank_payment_method_types, + }); + response .is_empty() .then(|| Err(report!(errors::ApiErrorResponse::PaymentMethodNotFound))) diff --git a/openapi/generated.json b/openapi/generated.json index 1b1af4b182..96bbcd11f0 100644 --- a/openapi/generated.json +++ b/openapi/generated.json @@ -2225,7 +2225,6 @@ "easybank_ag", "erste_bank_und_sparkassen", "hypo_alpeadriabank_international_ag", - "hypo_noe_lb_fur_niederosterreich_u_wien", "hypo_oberosterreich_salzburg_steiermark", "hypo_tirol_bank_ag", "hypo_vorarlberg_bank_ag",