diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 790bb0d3a6..8b27fde320 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -7646,8 +7646,9 @@ "card_brands": { "type": "array", "items": { - "type": "string" - } + "$ref": "#/components/schemas/CardNetwork" + }, + "example": "[Visa, Mastercard]" }, "acquirer_bin": { "type": "string" diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 19a05ed630..36705fba44 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -9732,8 +9732,9 @@ "card_brands": { "type": "array", "items": { - "type": "string" - } + "$ref": "#/components/schemas/CardNetwork" + }, + "example": "[Visa, Mastercard]" }, "acquirer_bin": { "type": "string" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 4414a8665e..aaa66ab2c1 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -630,4 +630,7 @@ connector_list = "cybersource" enabled = false [billing_connectors_payment_sync] -billing_connectors_which_require_payment_sync = "stripebilling, recurly" \ No newline at end of file +billing_connectors_which_require_payment_sync = "stripebilling, recurly" + +[authentication_providers] +click_to_pay = {connector_list = "adyen, cybersource"} \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index fe9a89a9c9..9d25b79935 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -631,4 +631,7 @@ connector_list = "cybersource" enabled = false [billing_connectors_payment_sync] -billing_connectors_which_require_payment_sync = "stripebilling, recurly" \ No newline at end of file +billing_connectors_which_require_payment_sync = "stripebilling, recurly" + +[authentication_providers] +click_to_pay = {connector_list = "adyen, cybersource"} \ No newline at end of file diff --git a/config/development.toml b/config/development.toml index aba8c8c21a..0df2c4d8b6 100644 --- a/config/development.toml +++ b/config/development.toml @@ -1046,6 +1046,9 @@ background_color = "#FFFFFF" [platform] enabled = true +[authentication_providers] +click_to_pay = {connector_list = "adyen, cybersource"} + [open_router] enabled = false url = "http://localhost:8080" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 967b46a62b..9614685141 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -913,3 +913,6 @@ background_color = "#FFFFFF" [platform] enabled = true + +[authentication_providers] +click_to_pay = {connector_list = "adyen, cybersource"} \ No newline at end of file diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 8e87f893cf..fe9c9767a0 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -8233,7 +8233,8 @@ pub struct ClickToPaySessionResponse { pub dpa_id: String, pub dpa_name: String, pub locale: String, - pub card_brands: Vec, + #[schema(value_type = Vec, example = "[Visa, Mastercard]")] + pub card_brands: HashSet, pub acquirer_bin: String, pub acquirer_merchant_id: String, pub merchant_category_code: String, diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 9ba3e86cac..eaf3101aa8 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -5552,14 +5552,6 @@ placeholder="Enter locale" required=true type="Text" -[ctp_mastercard.metadata.card_brands] -name="card_brands" -label="Card Brands" -placeholder="Enter Card Brands" -required=true -type="MultiSelect" -options=["visa","mastercard"] - [ctp_mastercard.metadata.acquirer_bin] name="acquirer_bin" label="Acquire Bin" @@ -5757,14 +5749,6 @@ placeholder="Enter locale" required=true type="Text" -[ctp_visa.metadata.card_brands] -name="card_brands" -label="Card Brands" -placeholder="Enter Card Brands" -required=true -type="MultiSelect" -options=["visa","mastercard"] - [ctp_visa.metadata.acquirer_bin] name="acquirer_bin" label="Acquire Bin" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index da0fd93f70..fe0cf95f24 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -4281,14 +4281,6 @@ placeholder="Enter locale" required=true type="Text" -[ctp_visa.metadata.card_brands] -name="card_brands" -label="Card Brands" -placeholder="Enter Card Brands" -required=true -type="MultiSelect" -options=["visa","mastercard"] - [ctp_visa.metadata.acquirer_bin] name="acquirer_bin" label="Acquire Bin" @@ -4354,6 +4346,60 @@ api_key = "Merchant ID" key1 = "Terminal ID" api_secret = "Secret Key" + +[ctp_mastercard] +[ctp_mastercard.connector_auth.HeaderKey] +api_key="API Key" + +[ctp_mastercard.metadata.dpa_id] +name="dpa_id" +label="DPA Id" +placeholder="Enter DPA Id" +required=true +type="Text" + +[ctp_mastercard.metadata.dpa_name] +name="dpa_name" +label="DPA Name" +placeholder="Enter DPA Name" +required=true +type="Text" + +[ctp_mastercard.metadata.locale] +name="locale" +label="Locale" +placeholder="Enter locale" +required=true +type="Text" + +[ctp_mastercard.metadata.acquirer_bin] +name="acquirer_bin" +label="Acquire Bin" +placeholder="Enter Acquirer Bin" +required=true +type="Text" + +[ctp_mastercard.metadata.acquirer_merchant_id] +name="acquirer_merchant_id" +label="Acquire Merchant Id" +placeholder="Enter Acquirer Merchant Id" +required=true +type="Text" + +[ctp_mastercard.metadata.merchant_category_code] +name="merchant_category_code" +label="Merchant Category Code" +placeholder="Enter Merchant Category Code" +required=true +type="Text" + +[ctp_mastercard.metadata.merchant_country_code] +name="merchant_country_code" +label="Merchant Country Code" +placeholder="Enter Merchant Country Code" +required=true +type="Text" + [facilitapay] [[facilitapay.bank_transfer]] payment_method_type = "pix" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index a2bdf1863b..c8d9cd5f66 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -5521,13 +5521,7 @@ placeholder="Enter locale" required=true type="Text" -[ctp_mastercard.metadata.card_brands] -name="card_brands" -label="Card Brands" -placeholder="Enter Card Brands" -required=true -type="MultiSelect" -options=["visa","mastercard"] + [ctp_mastercard.metadata.acquirer_bin] name="acquirer_bin" @@ -5728,13 +5722,6 @@ placeholder="Enter locale" required=true type="Text" -[ctp_visa.metadata.card_brands] -name="card_brands" -label="Card Brands" -placeholder="Enter Card Brands" -required=true -type="MultiSelect" -options=["visa","mastercard"] [ctp_visa.metadata.acquirer_bin] name="acquirer_bin" diff --git a/crates/diesel_models/src/query/merchant_connector_account.rs b/crates/diesel_models/src/query/merchant_connector_account.rs index 7abc0d6efd..be3a2351c2 100644 --- a/crates/diesel_models/src/query/merchant_connector_account.rs +++ b/crates/diesel_models/src/query/merchant_connector_account.rs @@ -152,6 +152,24 @@ impl MerchantConnectorAccount { .await } } + + pub async fn list_enabled_by_profile_id( + conn: &PgPooledConn, + profile_id: &common_utils::id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> StorageResult> { + generics::generic_filter::<::Table, _, _, _>( + conn, + dsl::profile_id + .eq(profile_id.to_owned()) + .and(dsl::disabled.eq(false)) + .and(dsl::connector_type.eq(connector_type)), + None, + None, + Some(dsl::created_at.asc()), + ) + .await + } } #[cfg(feature = "v2")] diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 01066a2d3c..a5aedab894 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -532,6 +532,7 @@ pub(crate) async fn fetch_raw_secrets( network_tokenization_supported_connectors: conf.network_tokenization_supported_connectors, theme: conf.theme, platform: conf.platform, + authentication_providers: conf.authentication_providers, open_router: conf.open_router, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 057a9d4531..0830270b08 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -145,6 +145,7 @@ pub struct Settings { pub network_tokenization_supported_connectors: NetworkTokenizationSupportedConnectors, pub theme: ThemeSettings, pub platform: Platform, + pub authentication_providers: AuthenticationProviders, pub open_router: OpenRouter, } @@ -508,6 +509,31 @@ pub struct CorsSettings { pub allowed_methods: HashSet, } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct AuthenticationProviders { + #[serde(deserialize_with = "deserialize_connector_list")] + pub click_to_pay: HashSet, +} + +fn deserialize_connector_list<'a, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'a>, +{ + use serde::de::Error; + + #[derive(Deserialize)] + struct Wrapper { + connector_list: String, + } + + let wrapper = Wrapper::deserialize(deserializer)?; + wrapper + .connector_list + .split(',') + .map(|s| s.trim().parse().map_err(D::Error::custom)) + .collect() +} + #[derive(Debug, Deserialize, Clone, Default)] pub struct NetworkTransactionIdSupportedConnectors { #[serde(deserialize_with = "deserialize_hashset")] diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 34a8fba476..df55dddd0c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -16,7 +16,8 @@ pub mod types; #[cfg(feature = "olap")] use std::collections::HashMap; use std::{ - collections::HashSet, fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter, + collections::HashSet, fmt::Debug, marker::PhantomData, ops::Deref, str::FromStr, time::Instant, + vec::IntoIter, }; #[cfg(feature = "v2")] @@ -4156,6 +4157,7 @@ where key_store, value, payment_data.get_payment_intent(), + business_profile.get_id(), ) .await?; payment_data.push_sessions_token(session_token); @@ -4177,6 +4179,7 @@ pub async fn get_session_token_for_click_to_pay( key_store: &domain::MerchantKeyStore, authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap, payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, + profile_id: &id_type::ProfileId, ) -> RouterResult { let click_to_pay_mca_id = authentication_product_ids .get_click_to_pay_connector_account_id() @@ -4230,12 +4233,16 @@ pub async fn get_session_token_for_click_to_pay( _ => None, }; + let card_brands = + get_card_brands_based_on_active_merchant_connector_account(state, profile_id, key_store) + .await?; + Ok(api_models::payments::SessionToken::ClickToPay(Box::new( api_models::payments::ClickToPaySessionResponse { dpa_id: click_to_pay_metadata.dpa_id, dpa_name: click_to_pay_metadata.dpa_name, locale: click_to_pay_metadata.locale, - card_brands: click_to_pay_metadata.card_brands, + card_brands, acquirer_bin: click_to_pay_metadata.acquirer_bin, acquirer_merchant_id: click_to_pay_metadata.acquirer_merchant_id, merchant_category_code: click_to_pay_metadata.merchant_category_code, @@ -4251,6 +4258,63 @@ pub async fn get_session_token_for_click_to_pay( ))) } +#[cfg(feature = "v1")] +async fn get_card_brands_based_on_active_merchant_connector_account( + state: &SessionState, + profile_id: &id_type::ProfileId, + key_store: &domain::MerchantKeyStore, +) -> RouterResult> { + let key_manager_state = &(state).into(); + let merchant_configured_payment_connectors = state + .store + .list_enabled_connector_accounts_by_profile_id( + key_manager_state, + profile_id, + key_store, + common_enums::ConnectorType::PaymentProcessor, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("error when fetching merchant connector accounts")?; + + let payment_connectors_eligible_for_click_to_pay = + state.conf.authentication_providers.click_to_pay.clone(); + + let filtered_payment_connector_accounts: Vec< + hyperswitch_domain_models::merchant_connector_account::MerchantConnectorAccount, + > = merchant_configured_payment_connectors + .into_iter() + .filter(|account| { + enums::Connector::from_str(&account.connector_name) + .ok() + .map(|connector| payment_connectors_eligible_for_click_to_pay.contains(&connector)) + .unwrap_or(false) + }) + .collect(); + + let mut card_brands = HashSet::new(); + + for account in filtered_payment_connector_accounts { + if let Some(values) = &account.payment_methods_enabled { + for val in values { + let payment_methods_enabled: api_models::admin::PaymentMethodsEnabled = + serde_json::from_value(val.peek().to_owned()).inspect_err(|err| { + logger::error!("Failed to parse Payment methods enabled data set from dashboard because {}", err) + }) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + if let Some(payment_method_types) = payment_methods_enabled.payment_method_types { + for payment_method_type in payment_method_types { + if let Some(networks) = payment_method_type.card_networks { + card_brands.extend(networks); + } + } + } + } + } + } + Ok(card_brands) +} + fn validate_customer_details_for_click_to_pay(customer_details: &CustomerData) -> RouterResult<()> { match ( customer_details.phone.as_ref(), diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 304428ca14..0e39cbf841 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1281,7 +1281,6 @@ impl MerchantConnectorAccountInterface for KafkaStore { .await } - #[cfg(all(feature = "oltp", feature = "v2"))] async fn list_enabled_connector_accounts_by_profile_id( &self, state: &KeyManagerState, diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index a9c7d99e30..b9618e2c63 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -189,7 +189,6 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; - #[cfg(all(feature = "oltp", feature = "v2"))] async fn list_enabled_connector_accounts_by_profile_id( &self, state: &KeyManagerState, @@ -502,7 +501,6 @@ impl MerchantConnectorAccountInterface for Store { .await } - #[cfg(all(feature = "oltp", feature = "v2"))] async fn list_enabled_connector_accounts_by_profile_id( &self, state: &KeyManagerState, @@ -1067,15 +1065,14 @@ impl MerchantConnectorAccountInterface for MockDb { } } - #[cfg(all(feature = "oltp", feature = "v2"))] async fn list_enabled_connector_accounts_by_profile_id( &self, - state: &KeyManagerState, - profile_id: &common_utils::id_type::ProfileId, - key_store: &domain::MerchantKeyStore, - connector_type: common_enums::ConnectorType, + _state: &KeyManagerState, + _profile_id: &common_utils::id_type::ProfileId, + _key_store: &domain::MerchantKeyStore, + _connector_type: common_enums::ConnectorType, ) -> CustomResult, errors::StorageError> { - todo!() + Err(errors::StorageError::MockDbError)? } #[cfg(feature = "v1")]