mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
fix(list): adding configuation changes for filtering pm based on countries & currencies (#669)
This commit is contained in:
@ -8,10 +8,11 @@ PaymentVas = "PaymentVas"
|
|||||||
HypoNoeLbFurNiederosterreichUWien = "HypoNoeLbFurNiederosterreichUWien"
|
HypoNoeLbFurNiederosterreichUWien = "HypoNoeLbFurNiederosterreichUWien"
|
||||||
|
|
||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
aci = "aci" # Name of a connector
|
aci = "aci" # Name of a connector
|
||||||
encrypter = "encrypter" # Used by the `ring` crate
|
encrypter = "encrypter" # Used by the `ring` crate
|
||||||
nin = "nin" # National identification number, a field used by PayU connector
|
nin = "nin" # National identification number, a field used by PayU connector
|
||||||
substituters = "substituters" # Present in `flake.nix`
|
substituters = "substituters" # Present in `flake.nix`
|
||||||
|
FO = "FO" # Faroe Islands (the) country code
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = [
|
extend-exclude = [
|
||||||
|
|||||||
@ -140,3 +140,40 @@ stream = "SCHEDULER_STREAM"
|
|||||||
[scheduler.consumer]
|
[scheduler.consumer]
|
||||||
disabled = false
|
disabled = false
|
||||||
consumer_group = "SCHEDULER_GROUP"
|
consumer_group = "SCHEDULER_GROUP"
|
||||||
|
|
||||||
|
[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" }
|
||||||
|
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" }
|
||||||
|
|
||||||
|
[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" }
|
||||||
|
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" }
|
||||||
|
|
||||||
|
[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" }
|
||||||
|
|
||||||
|
[pm_filters.klarna]
|
||||||
|
klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,CH,GB,US", currency = "AUD,EUR,EUR,CAD,CZK,DKK,EUR,EUR,EUR,EUR,EUR,EUR,EUR,NZD,NOK,PLN,EUR,EUR,SEK,CHF,GBP,USD" }
|
||||||
|
|
||||||
|
[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" }
|
||||||
|
|
||||||
|
[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" }
|
||||||
|
|||||||
@ -204,3 +204,11 @@ num_partitions = 64 # Specifies the number of partitions the stream w
|
|||||||
max_read_count = 100 # Specifies the maximum number of entries that would be read from redis stream in one call
|
max_read_count = 100 # Specifies the maximum number of entries that would be read from redis stream in one call
|
||||||
shutdown_interval = 1000 # Specifies how much time to wait, while waiting for threads to complete execution (in milliseconds)
|
shutdown_interval = 1000 # Specifies how much time to wait, while waiting for threads to complete execution (in milliseconds)
|
||||||
loop_interval = 500 # Specifies how much time to wait after checking all the possible streams in completed (in milliseconds)
|
loop_interval = 500 # Specifies how much time to wait after checking all the possible streams in completed (in milliseconds)
|
||||||
|
|
||||||
|
# Filteration logic for list payment method, allowing use to limit payment methods based on the requirement country and currency
|
||||||
|
[pm_filters.stripe]
|
||||||
|
# ^--- This can be any connector (can be multiple)
|
||||||
|
paypal = { currency = "USD,INR", country = "US" }
|
||||||
|
# ^ ^------- comma-separated values
|
||||||
|
# ^------------------------------- any valid payment method type (can be multiple)
|
||||||
|
# If either currency or country isn't provided then, all possible values are accepted
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
use std::path::PathBuf;
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use common_utils::ext_traits::ConfigExt;
|
use common_utils::ext_traits::ConfigExt;
|
||||||
use config::{Environment, File};
|
use config::{Environment, File};
|
||||||
use redis_interface::RedisSettings;
|
use redis_interface::RedisSettings;
|
||||||
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};
|
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{ApplicationError, ApplicationResult},
|
core::errors::{ApplicationError, ApplicationResult},
|
||||||
@ -51,6 +55,64 @@ pub struct Settings {
|
|||||||
pub drainer: DrainerSettings,
|
pub drainer: DrainerSettings,
|
||||||
pub jwekey: Jwekey,
|
pub jwekey: Jwekey,
|
||||||
pub webhooks: WebhooksSettings,
|
pub webhooks: WebhooksSettings,
|
||||||
|
pub pm_filters: ConnectorFilters,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct ConnectorFilters(pub HashMap<String, PaymentMethodFilters>);
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct PaymentMethodFilters(
|
||||||
|
pub HashMap<api_models::enums::PaymentMethodType, CurrencyCountryFilter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct CurrencyCountryFilter {
|
||||||
|
#[serde(deserialize_with = "currency_set_deser")]
|
||||||
|
pub currency: Option<HashSet<api_models::enums::Currency>>,
|
||||||
|
#[serde(deserialize_with = "string_set_deser")]
|
||||||
|
pub country: Option<HashSet<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_set_deser<'a, D>(deserializer: D) -> Result<Option<HashSet<String>>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'a>,
|
||||||
|
{
|
||||||
|
let value = <Option<String>>::deserialize(deserializer)?;
|
||||||
|
Ok(value.and_then(|inner| {
|
||||||
|
let list = inner
|
||||||
|
.trim()
|
||||||
|
.split(',')
|
||||||
|
.map(|value| value.to_string())
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
match list.len() {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(list),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn currency_set_deser<'a, D>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<Option<HashSet<api_models::enums::Currency>>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'a>,
|
||||||
|
{
|
||||||
|
let value = <Option<String>>::deserialize(deserializer)?;
|
||||||
|
Ok(value.and_then(|inner| {
|
||||||
|
let list = inner
|
||||||
|
.trim()
|
||||||
|
.split(',')
|
||||||
|
.flat_map(api_models::enums::Currency::from_str)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
match list.len() {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(list),
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use error_stack::{report, ResultExt};
|
|||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
configs::settings,
|
||||||
core::{
|
core::{
|
||||||
errors::{self, StorageErrorExt},
|
errors::{self, StorageErrorExt},
|
||||||
payment_methods::{transformers as payment_methods, vault},
|
payment_methods::{transformers as payment_methods, vault},
|
||||||
@ -348,10 +349,13 @@ pub async fn delete_card<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_payment_methods(
|
pub async fn list_payment_methods(
|
||||||
db: &dyn db::StorageInterface,
|
state: &routes::AppState,
|
||||||
merchant_account: storage::MerchantAccount,
|
merchant_account: storage::MerchantAccount,
|
||||||
mut req: api::ListPaymentMethodRequest,
|
mut req: api::ListPaymentMethodRequest,
|
||||||
) -> errors::RouterResponse<api::ListPaymentMethodResponse> {
|
) -> errors::RouterResponse<api::ListPaymentMethodResponse> {
|
||||||
|
let db = &*state.store;
|
||||||
|
let pm_config_mapping = &state.conf.pm_filters;
|
||||||
|
|
||||||
let payment_intent = helpers::verify_client_secret(
|
let payment_intent = helpers::verify_client_secret(
|
||||||
db,
|
db,
|
||||||
merchant_account.storage_scheme,
|
merchant_account.storage_scheme,
|
||||||
@ -410,6 +414,7 @@ pub async fn list_payment_methods(
|
|||||||
payment_attempt.as_ref(),
|
payment_attempt.as_ref(),
|
||||||
address.as_ref(),
|
address.as_ref(),
|
||||||
mca.connector_name,
|
mca.connector_name,
|
||||||
|
pm_config_mapping,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
@ -567,6 +572,7 @@ pub async fn list_payment_methods(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn filter_payment_methods(
|
async fn filter_payment_methods(
|
||||||
payment_methods: Vec<serde_json::Value>,
|
payment_methods: Vec<serde_json::Value>,
|
||||||
req: &mut api::ListPaymentMethodRequest,
|
req: &mut api::ListPaymentMethodRequest,
|
||||||
@ -575,6 +581,7 @@ async fn filter_payment_methods(
|
|||||||
payment_attempt: Option<&storage::PaymentAttempt>,
|
payment_attempt: Option<&storage::PaymentAttempt>,
|
||||||
address: Option<&storage::Address>,
|
address: Option<&storage::Address>,
|
||||||
connector: String,
|
connector: String,
|
||||||
|
config: &settings::ConnectorFilters,
|
||||||
) -> errors::CustomResult<(), errors::ApiErrorResponse> {
|
) -> errors::CustomResult<(), errors::ApiErrorResponse> {
|
||||||
for payment_method in payment_methods.into_iter() {
|
for payment_method in payment_methods.into_iter() {
|
||||||
let parse_result = serde_json::from_value::<PaymentMethodsEnabled>(payment_method);
|
let parse_result = serde_json::from_value::<PaymentMethodsEnabled>(payment_method);
|
||||||
@ -627,6 +634,16 @@ async fn filter_payment_methods(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let filter5 = filter_pm_based_on_config(
|
||||||
|
config,
|
||||||
|
&connector,
|
||||||
|
&payment_method_object.payment_method_type,
|
||||||
|
address.and_then(|inner| inner.country.clone()),
|
||||||
|
payment_attempt
|
||||||
|
.and_then(|value| value.currency)
|
||||||
|
.map(|value| value.foreign_into()),
|
||||||
|
);
|
||||||
|
|
||||||
let connector = connector.clone();
|
let connector = connector.clone();
|
||||||
|
|
||||||
let response_pm_type = ResponsePaymentMethodIntermediate::new(
|
let response_pm_type = ResponsePaymentMethodIntermediate::new(
|
||||||
@ -635,7 +652,7 @@ async fn filter_payment_methods(
|
|||||||
payment_method,
|
payment_method,
|
||||||
);
|
);
|
||||||
|
|
||||||
if filter && filter2 && filter3 && filter4 {
|
if filter && filter2 && filter3 && filter4 && filter5 {
|
||||||
resp.push(response_pm_type);
|
resp.push(response_pm_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -645,6 +662,33 @@ async fn filter_payment_methods(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter_pm_based_on_config<'a>(
|
||||||
|
config: &'a crate::configs::settings::ConnectorFilters,
|
||||||
|
connector: &'a str,
|
||||||
|
payment_method_type: &'a api_enums::PaymentMethodType,
|
||||||
|
country: Option<String>,
|
||||||
|
currency: Option<api_enums::Currency>,
|
||||||
|
) -> bool {
|
||||||
|
config
|
||||||
|
.0
|
||||||
|
.get(connector)
|
||||||
|
.and_then(|inner| inner.0.get(payment_method_type))
|
||||||
|
.map(|value| {
|
||||||
|
let condition1 = value
|
||||||
|
.country
|
||||||
|
.as_ref()
|
||||||
|
.zip(country)
|
||||||
|
.map(|(lhs, rhs)| lhs.contains(&rhs));
|
||||||
|
let condition2 = value
|
||||||
|
.currency
|
||||||
|
.as_ref()
|
||||||
|
.zip(currency)
|
||||||
|
.map(|(lhs, rhs)| lhs.contains(&rhs));
|
||||||
|
condition1.unwrap_or(true) && condition2.unwrap_or(true)
|
||||||
|
})
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_pm_card_network_based(
|
fn filter_pm_card_network_based(
|
||||||
pm_card_networks: Option<&Vec<api_enums::CardNetwork>>,
|
pm_card_networks: Option<&Vec<api_enums::CardNetwork>>,
|
||||||
request_card_networks: Option<&Vec<api_enums::CardNetwork>>,
|
request_card_networks: Option<&Vec<api_enums::CardNetwork>>,
|
||||||
|
|||||||
@ -82,9 +82,7 @@ pub async fn list_payment_method_api(
|
|||||||
state.get_ref(),
|
state.get_ref(),
|
||||||
&req,
|
&req,
|
||||||
payload,
|
payload,
|
||||||
|state, merchant_account, req| {
|
cards::list_payment_methods,
|
||||||
cards::list_payment_methods(&*state.store, merchant_account, req)
|
|
||||||
},
|
|
||||||
&*auth,
|
&*auth,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user