diff --git a/config/config.example.toml b/config/config.example.toml index e4a15e7fb1..37f28e6994 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -578,8 +578,13 @@ logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] card = "credit,debit" -bank_transfer = "ach,bacs,sepa" -wallet = "paypal,pix,venmo" + +#Payout Method Filters Based on Country and Currency +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } [payment_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index 67f32396b7..1d723596b4 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -179,8 +179,6 @@ logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] card = "credit,debit" -bank_transfer = "ach,bacs,sepa" -wallet = "paypal,pix,venmo" [payment_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 962f885c08..b8945db5f2 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -308,6 +308,12 @@ red_pagos = { country = "UY", currency = "UYU" } [pm_filters.zsl] local_bank_transfer = { country = "CN", currency = "CNY" } +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } + [temp_locker_enable_config] bluesnap.payment_method = "card" nuvei.payment_method = "card" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 45a7c093ab..c79297a246 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -327,6 +327,12 @@ red_pagos = { country = "UY", currency = "UYU" } [pm_filters.zsl] local_bank_transfer = { country = "CN", currency = "CNY" } +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } + [temp_locker_enable_config] bluesnap.payment_method = "card" nuvei.payment_method = "card" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 05e3c7e90c..d776483ebc 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -331,6 +331,12 @@ red_pagos = { country = "UY", currency = "UYU" } [pm_filters.zsl] local_bank_transfer = { country = "CN", currency = "CNY" } +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } + [temp_locker_enable_config] bluesnap.payment_method = "card" nuvei.payment_method = "card" diff --git a/config/development.toml b/config/development.toml index b3548b35d0..b3d2757797 100644 --- a/config/development.toml +++ b/config/development.toml @@ -591,8 +591,12 @@ logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] card = "credit,debit" -bank_transfer = "ach,bacs,sepa" -wallet = "paypal,pix,venmo" + +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOK,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } [payment_link] sdk_url = "http://localhost:9050/HyperLoader.js" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index b11ca00574..820d89f1f0 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -543,5 +543,9 @@ logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] card = "credit,debit" -bank_transfer = "ach,bacs,sepa" -wallet = "paypal,pix,venmo" + +[payout_method_filters.adyenplatform] +sepa = { country = "ES,SK,AT,NL,DE,BE,FR,FI,PT,IE,EE,LT,LV,IT,CZ,DE,HU,NO,PL,SE,GB,CH" , currency = "EUR,CZK,DKK,HUF,NOR,PLN,SEK,GBP,CHF" } + +[payout_method_filters.stripe] +ach = { country = "US", currency = "USD" } diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index e62e8d8ea4..d6cdefd79f 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -2705,10 +2705,6 @@ api_key="Api Key" payment_method_type = "CartesBancaires" [[adyen_payout.debit]] payment_method_type = "UnionPay" -[[adyen_payout.bank_transfer]] - payment_method_type = "ach" -[[adyen_payout.bank_transfer]] - payment_method_type = "bacs" [[adyen_payout.bank_transfer]] payment_method_type = "sepa" [[adyen_payout.wallet]] diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 5b44dc1551..664309c992 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -2706,10 +2706,6 @@ api_key="Api Key" payment_method_type = "CartesBancaires" [[adyen_payout.debit]] payment_method_type = "UnionPay" -[[adyen_payout.bank_transfer]] - payment_method_type = "ach" -[[adyen_payout.bank_transfer]] - payment_method_type = "bacs" [[adyen_payout.bank_transfer]] payment_method_type = "sepa" [[adyen_payout.wallet]] diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 1ab2e519b7..e546de9102 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -349,6 +349,7 @@ pub(crate) async fn fetch_raw_secrets( jwekey, webhooks: conf.webhooks, pm_filters: conf.pm_filters, + payout_method_filters: conf.payout_method_filters, bank_config: conf.bank_config, api_keys, file_storage: conf.file_storage, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index a29d7e1502..b943c5334d 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -101,6 +101,7 @@ pub struct Settings { pub connector_request_reference_id_config: ConnectorRequestReferenceIdConfig, #[cfg(feature = "payouts")] pub payouts: Payouts, + pub payout_method_filters: ConnectorFilters, pub applepay_decrypt_keys: SecretStateContainer, pub multiple_api_version_supported_connectors: MultipleApiVersionSupportedConnectors, pub applepay_merchant_configs: SecretStateContainer, diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index d500667877..0d4c525f10 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -11,6 +11,7 @@ use error_stack::ResultExt; use super::errors::{RouterResponse, StorageErrorExt}; use crate::{ + configs::settings::{PaymentMethodFilterKey, PaymentMethodFilters}, core::payments::helpers, errors, routes::{app::StorageInterface, SessionState}, @@ -62,7 +63,7 @@ pub async fn initiate_payout_link( let has_expired = common_utils::date_time::now() > payout_link.expiry; let status = payout_link.link_status.clone(); let link_data = payout_link.link_data.clone(); - let default_config = &state.conf.generic_link.payout_link; + let default_config = &state.conf.generic_link.payout_link.clone(); let default_ui_config = default_config.ui_config.clone(); let ui_config_data = link_utils::GenericLinkUiConfigFormData { merchant_name: link_data @@ -126,7 +127,7 @@ pub async fn initiate_payout_link( format!("customer [{}] not found", payout_link.primary_reference) })?; let enabled_payout_methods = - filter_payout_methods(db, &merchant_account, &key_store, &payout).await?; + filter_payout_methods(&state, &merchant_account, &key_store, &payout).await?; // Fetch default enabled_payout_methods let mut default_enabled_payout_methods: Vec = vec![]; for (payment_method, payment_method_types) in @@ -148,7 +149,6 @@ pub async fn initiate_payout_link( let enabled_payment_methods = link_data .enabled_payment_methods .unwrap_or(fallback_enabled_payout_methods.to_vec()); - let js_data = payouts::PayoutLinkDetails { publishable_key: masking::Secret::new(merchant_account.publishable_key), client_secret: link_data.client_secret.clone(), @@ -233,11 +233,12 @@ pub async fn initiate_payout_link( #[cfg(feature = "payouts")] pub async fn filter_payout_methods( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payout: &hyperswitch_domain_models::payouts::payouts::Payouts, ) -> errors::RouterResult> { + let db: &dyn StorageInterface = &*state.store; //Fetch all merchant connector accounts let all_mcas = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( @@ -255,40 +256,81 @@ pub async fn filter_payout_methods( filtered_mca_on_profile.clone(), common_enums::ConnectorType::PayoutProcessor, ); + let address = db + .find_address_by_address_id(&payout.address_id.clone(), key_store) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| { + format!( + "Failed while fetching address with address id {}", + payout.address_id.clone() + ) + })?; let mut response: Vec = vec![]; let mut payment_method_list_hm: HashMap< common_enums::PaymentMethod, HashSet, > = HashMap::new(); - let mut bank_transfer_hs: HashSet = HashSet::new(); - let mut card_hs: HashSet = HashSet::new(); - let mut wallet_hs: HashSet = HashSet::new(); + let mut bank_transfer_hash_set: HashSet = HashSet::new(); + let mut card_hash_set: HashSet = HashSet::new(); + let mut wallet_hash_set: HashSet = HashSet::new(); + let payout_filter_config = &state.conf.payout_method_filters.clone(); for mca in &filtered_mca { - let payment_methods = match &mca.payment_methods_enabled { + let payout_methods = match &mca.payment_methods_enabled { Some(pm) => pm, None => continue, }; - for payment_method in payment_methods.iter() { + for payout_method in payout_methods.iter() { let parse_result = serde_json::from_value::( - payment_method.clone(), + payout_method.clone(), ); if let Ok(payment_methods_enabled) = parse_result { let payment_method = payment_methods_enabled.payment_method; let payment_method_types = match payment_methods_enabled.payment_method_types { - Some(pmt) => pmt, + Some(payment_method_types) => payment_method_types, None => continue, }; - for pmts in &payment_method_types { - if payment_method == common_enums::PaymentMethod::Card { - card_hs.insert(pmts.payment_method_type); - payment_method_list_hm.insert(payment_method, card_hs.clone()); - } else if payment_method == common_enums::PaymentMethod::Wallet { - wallet_hs.insert(pmts.payment_method_type); - payment_method_list_hm.insert(payment_method, wallet_hs.clone()); - } else if payment_method == common_enums::PaymentMethod::BankTransfer { - bank_transfer_hs.insert(pmts.payment_method_type); - payment_method_list_hm.insert(payment_method, bank_transfer_hs.clone()); + let connector = mca.connector_name.clone(); + let payout_filter = payout_filter_config.0.get(&connector); + for request_payout_method_type in &payment_method_types { + let currency_country_filter = check_currency_country_filters( + payout_filter, + request_payout_method_type, + &payout.destination_currency, + &address.country, + )?; + if currency_country_filter.unwrap_or(true) { + match payment_method { + common_enums::PaymentMethod::Card => { + card_hash_set + .insert(request_payout_method_type.payment_method_type); + payment_method_list_hm + .insert(payment_method, card_hash_set.clone()); + } + common_enums::PaymentMethod::Wallet => { + wallet_hash_set + .insert(request_payout_method_type.payment_method_type); + payment_method_list_hm + .insert(payment_method, wallet_hash_set.clone()); + } + common_enums::PaymentMethod::BankTransfer => { + bank_transfer_hash_set + .insert(request_payout_method_type.payment_method_type); + payment_method_list_hm + .insert(payment_method, bank_transfer_hash_set.clone()); + } + common_enums::PaymentMethod::CardRedirect + | common_enums::PaymentMethod::PayLater + | common_enums::PaymentMethod::BankRedirect + | common_enums::PaymentMethod::Crypto + | common_enums::PaymentMethod::BankDebit + | common_enums::PaymentMethod::Reward + | common_enums::PaymentMethod::RealTimePayment + | common_enums::PaymentMethod::Upi + | common_enums::PaymentMethod::Voucher + | common_enums::PaymentMethod::GiftCard => continue, + } } } } @@ -305,3 +347,41 @@ pub async fn filter_payout_methods( } Ok(response) } + +pub fn check_currency_country_filters( + payout_method_filter: Option<&PaymentMethodFilters>, + request_payout_method_type: &api_models::payment_methods::RequestPaymentMethodTypes, + currency: &common_enums::Currency, + country: &Option, +) -> errors::RouterResult> { + if matches!( + request_payout_method_type.payment_method_type, + common_enums::PaymentMethodType::Credit | common_enums::PaymentMethodType::Debit + ) { + Ok(Some(true)) + } else { + let payout_method_type_filter = + payout_method_filter.and_then(|payout_method_filter: &PaymentMethodFilters| { + payout_method_filter + .0 + .get(&PaymentMethodFilterKey::PaymentMethodType( + request_payout_method_type.payment_method_type, + )) + }); + let country_filter = country.as_ref().and_then(|country| { + payout_method_type_filter.and_then(|currency_country_filter| { + currency_country_filter + .country + .as_ref() + .map(|country_hash_set| country_hash_set.contains(country)) + }) + }); + let currency_filter = payout_method_type_filter.and_then(|currency_country_filter| { + currency_country_filter + .currency + .as_ref() + .map(|currency_hash_set| currency_hash_set.contains(currency)) + }); + Ok(currency_filter.or(country_filter)) + } +}