feat(core): Add cardbrand union fetch logic for click to pay session response (#7858)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Sahkal Poddar <sahkalpoddar@Sahkals-MacBook-Air.local>
This commit is contained in:
Sahkal Poddar
2025-04-28 16:54:15 +05:30
committed by GitHub
parent ac66cbe3da
commit d2ff66bb7e
16 changed files with 193 additions and 56 deletions

View File

@ -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,
}
}

View File

@ -145,6 +145,7 @@ pub struct Settings<S: SecretState> {
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<String>,
}
#[derive(Debug, Deserialize, Clone, Default)]
pub struct AuthenticationProviders {
#[serde(deserialize_with = "deserialize_connector_list")]
pub click_to_pay: HashSet<enums::Connector>,
}
fn deserialize_connector_list<'a, D>(deserializer: D) -> Result<HashSet<enums::Connector>, 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")]

View File

@ -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<api_models::payments::SessionToken> {
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<HashSet<enums::CardNetwork>> {
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(),

View File

@ -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,

View File

@ -189,7 +189,6 @@ where
key_store: &domain::MerchantKeyStore,
) -> CustomResult<Vec<domain::MerchantConnectorAccount>, 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<Vec<domain::MerchantConnectorAccount>, errors::StorageError> {
todo!()
Err(errors::StorageError::MockDbError)?
}
#[cfg(feature = "v1")]