From 4470f48f42337632595a1ceac09a7d6a1037a657 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 20 Jan 2025 17:32:05 +0530 Subject: [PATCH] refactor(router): refactor ctp flow to fetch mca_id and get the connector creds instead of connector_name (#6859) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> --- crates/api_models/src/admin.rs | 30 ++++--- crates/common_enums/src/enums.rs | 21 +++++ crates/common_types/src/payments.rs | 29 ++++++- crates/diesel_models/src/business_profile.rs | 18 ++-- .../src/business_profile.rs | 18 ++-- .../src/merchant_connector_account.rs | 11 ++- crates/router/src/consts.rs | 4 + crates/router/src/core/admin.rs | 36 +------- crates/router/src/core/payments.rs | 86 ++++++++++--------- crates/router/src/core/payments/helpers.rs | 32 ++++++- crates/router/src/core/payments/operations.rs | 1 + .../payments/operations/payment_confirm.rs | 66 ++++++++------ .../core/unified_authentication_service.rs | 9 +- .../unified_authentication_service/types.rs | 12 ++- .../unified_authentication_service/utils.rs | 12 +-- crates/router/src/types/api/admin.rs | 9 +- 16 files changed, 242 insertions(+), 152 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index cbd44c15699..321ee5305f1 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1864,8 +1864,9 @@ pub struct ProfileCreate { pub is_click_to_pay_enabled: bool, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[nutype::nutype( @@ -1981,8 +1982,9 @@ pub struct ProfileCreate { pub is_click_to_pay_enabled: bool, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -2117,8 +2119,9 @@ pub struct ProfileResponse { pub is_click_to_pay_enabled: bool, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -2240,8 +2243,9 @@ pub struct ProfileResponse { pub is_click_to_pay_enabled: bool, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -2370,8 +2374,9 @@ pub struct ProfileUpdate { pub is_click_to_pay_enabled: Option, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -2481,8 +2486,9 @@ pub struct ProfileUpdate { pub is_click_to_pay_enabled: Option, /// Product authentication ids - #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + #[schema(value_type = Option, example = r#"{ "click_to_pay": "mca_ushduqwhdohwd", "netcetera": "mca_kwqhudqwd" }"#)] + pub authentication_product_ids: + Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index f31b6725d9a..562b4fdf269 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3591,12 +3591,33 @@ pub enum StripeChargeType { Destination, } +/// Authentication Products +#[derive( + Clone, + Copy, + Debug, + Eq, + Hash, + PartialEq, + serde::Serialize, + serde::Deserialize, + strum::Display, + strum::EnumString, + ToSchema, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum AuthenticationProduct { + ClickToPay, +} + /// Connector Access Method #[derive( Clone, Copy, Debug, Eq, + Hash, PartialEq, serde::Deserialize, serde::Serialize, diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index 0eef7ecaf2b..08075605628 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -1,7 +1,9 @@ //! Payment related types +use std::collections::HashMap; + use common_enums::enums; -use common_utils::{impl_to_sql_from_sql_json, types::MinorUnit}; +use common_utils::{errors, impl_to_sql_from_sql_json, types::MinorUnit}; use diesel::{sql_types::Jsonb, AsExpression, FromSqlRow}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -38,3 +40,28 @@ pub struct StripeSplitPaymentRequest { pub transfer_account_id: String, } impl_to_sql_from_sql_json!(StripeSplitPaymentRequest); + +#[derive( + Serialize, Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, ToSchema, +)] +#[diesel(sql_type = Jsonb)] +#[serde(deny_unknown_fields)] +/// Hashmap to store mca_id's with product names +pub struct AuthenticationConnectorAccountMap( + HashMap, +); +impl_to_sql_from_sql_json!(AuthenticationConnectorAccountMap); + +impl AuthenticationConnectorAccountMap { + /// fn to get click to pay connector_account_id + pub fn get_click_to_pay_connector_account_id( + &self, + ) -> Result { + self.0 + .get(&enums::AuthenticationProduct::ClickToPay) + .ok_or(errors::ValidationError::MissingRequiredField { + field_name: "authentication_product_id.click_to_pay".to_string(), + }) + .cloned() + } +} diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index dff3f174fc5..06aa21fe9d3 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -58,7 +58,8 @@ pub struct Profile { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -103,7 +104,8 @@ pub struct ProfileNew { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -145,7 +147,8 @@ pub struct ProfileUpdateInternal { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: Option, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -305,7 +308,8 @@ pub struct Profile { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } impl Profile { @@ -365,7 +369,8 @@ pub struct ProfileNew { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -409,7 +414,8 @@ pub struct ProfileUpdateInternal { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: Option, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index 1df474f18d5..47c505e8ca2 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -59,7 +59,8 @@ pub struct Profile { pub is_auto_retries_enabled: bool, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -101,7 +102,8 @@ pub struct ProfileSetter { pub is_auto_retries_enabled: bool, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -201,7 +203,8 @@ pub struct ProfileGeneralUpdate { pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, pub is_click_to_pay_enabled: Option, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -729,7 +732,8 @@ pub struct Profile { pub version: common_enums::ApiVersion, pub is_network_tokenization_enabled: bool, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -771,7 +775,8 @@ pub struct ProfileSetter { pub is_tax_connector_enabled: bool, pub is_network_tokenization_enabled: bool, pub is_click_to_pay_enabled: bool, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -872,7 +877,8 @@ pub struct ProfileGeneralUpdate { pub order_fulfillment_time_origin: Option, pub is_network_tokenization_enabled: Option, pub is_click_to_pay_enabled: Option, - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index a25b9979962..ed52325c231 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -56,7 +56,6 @@ impl MerchantConnectorAccount { pub fn get_id(&self) -> id_type::MerchantConnectorAccountId { self.merchant_connector_id.clone() } - pub fn get_connector_account_details( &self, ) -> error_stack::Result @@ -66,6 +65,11 @@ impl MerchantConnectorAccount { .clone() .parse_value("ConnectorAuthType") } + + pub fn get_connector_wallets_details(&self) -> Option> { + self.connector_wallets_details.as_deref().cloned() + } + pub fn get_connector_test_mode(&self) -> Option { self.test_mode } @@ -124,6 +128,11 @@ impl MerchantConnectorAccount { .clone() .parse_value("ConnectorAuthType") } + + pub fn get_connector_wallets_details(&self) -> Option> { + self.connector_wallets_details.as_deref().cloned() + } + pub fn get_connector_test_mode(&self) -> Option { todo!() } diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index bcc9a591863..038da0be41c 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -211,5 +211,9 @@ pub const DYNAMIC_ROUTING_MAX_VOLUME: u8 = 100; /// Click To Pay pub const CLICK_TO_PAY: &str = "click_to_pay"; +/// Merchant eligible for authentication service config +pub const AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG: &str = + "merchants_eligible_for_authentication_service"; + /// Refund flow identifier used for performing GSM operations pub const REFUND_FLOW_STR: &str = "refund_flow"; diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index c7ac726cdbb..356b4a31cc6 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3618,13 +3618,6 @@ impl ProfileCreateBridge for api::ProfileCreate { }) .transpose()?; - let authentication_product_ids = self - .authentication_product_ids - .map(serde_json::to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to parse product authentication id's to value")?; - Ok(domain::Profile::from(domain::ProfileSetter { profile_id, merchant_id: merchant_account.get_id().clone(), @@ -3695,7 +3688,7 @@ impl ProfileCreateBridge for api::ProfileCreate { is_auto_retries_enabled: self.is_auto_retries_enabled.unwrap_or_default(), max_auto_retries_enabled: self.max_auto_retries_enabled.map(i16::from), is_click_to_pay_enabled: self.is_click_to_pay_enabled, - authentication_product_ids, + authentication_product_ids: self.authentication_product_ids, })) } @@ -3747,13 +3740,6 @@ impl ProfileCreateBridge for api::ProfileCreate { }) .transpose()?; - let authentication_product_ids = self - .authentication_product_ids - .map(serde_json::to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to parse product authentication id's to value")?; - Ok(domain::Profile::from(domain::ProfileSetter { id: profile_id, merchant_id: merchant_id.clone(), @@ -3811,7 +3797,7 @@ impl ProfileCreateBridge for api::ProfileCreate { is_tax_connector_enabled: self.is_tax_connector_enabled, is_network_tokenization_enabled: self.is_network_tokenization_enabled, is_click_to_pay_enabled: self.is_click_to_pay_enabled, - authentication_product_ids, + authentication_product_ids: self.authentication_product_ids, })) } } @@ -4019,13 +4005,6 @@ impl ProfileUpdateBridge for api::ProfileUpdate { }) .transpose()?; - let authentication_product_ids = self - .authentication_product_ids - .map(serde_json::to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to parse product authentication id's to value")?; - Ok(domain::ProfileUpdate::Update(Box::new( domain::ProfileGeneralUpdate { profile_name: self.profile_name, @@ -4069,7 +4048,7 @@ impl ProfileUpdateBridge for api::ProfileUpdate { is_auto_retries_enabled: self.is_auto_retries_enabled, max_auto_retries_enabled: self.max_auto_retries_enabled.map(i16::from), is_click_to_pay_enabled: self.is_click_to_pay_enabled, - authentication_product_ids, + authentication_product_ids: self.authentication_product_ids, }, ))) } @@ -4132,13 +4111,6 @@ impl ProfileUpdateBridge for api::ProfileUpdate { }) .transpose()?; - let authentication_product_ids = self - .authentication_product_ids - .map(serde_json::to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to parse product authentication id's to value")?; - Ok(domain::ProfileUpdate::Update(Box::new( domain::ProfileGeneralUpdate { profile_name: self.profile_name, @@ -4174,7 +4146,7 @@ impl ProfileUpdateBridge for api::ProfileUpdate { .always_collect_shipping_details_from_wallet_connector, is_network_tokenization_enabled: self.is_network_tokenization_enabled, is_click_to_pay_enabled: self.is_click_to_pay_enabled, - authentication_product_ids, + authentication_product_ids: self.authentication_product_ids, }, ))) } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 1ba6209cf52..ceeb384ed21 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -5,6 +5,7 @@ pub mod customers; pub mod flows; pub mod helpers; pub mod operations; + #[cfg(feature = "retry")] pub mod retry; pub mod routing; @@ -33,14 +34,14 @@ pub use common_enums::enums::CallConnectorAction; use common_utils::{ ext_traits::{AsyncExt, StringExt}, id_type, pii, - types::{MinorUnit, Surcharge}, + types::{AmountConvertor, MinorUnit, Surcharge}, }; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use error_stack::{report, ResultExt}; use events::EventInfo; use futures::future::join_all; use helpers::{decrypt_paze_token, ApplePayData}; -use hyperswitch_domain_models::payments::payment_intent::CustomerData; +use hyperswitch_domain_models::payments::{payment_intent::CustomerData, ClickToPayMetaData}; #[cfg(feature = "v2")] use hyperswitch_domain_models::payments::{ PaymentCaptureData, PaymentConfirmData, PaymentIntentData, PaymentStatusData, @@ -380,29 +381,38 @@ where should_continue_capture, ); - operation - .to_domain()? - .call_external_three_ds_authentication_if_eligible( - state, - &mut payment_data, - &mut should_continue_transaction, - &connector_details, - &business_profile, - &key_store, - mandate_type, - ) - .await?; - operation - .to_domain()? - .call_unified_authentication_service_if_eligible( - state, - &mut payment_data, - &mut should_continue_transaction, - &connector_details, - &business_profile, - &key_store, - ) - .await?; + if helpers::is_merchant_eligible_authentication_service(merchant_account.get_id(), state) + .await? + { + operation + .to_domain()? + .call_unified_authentication_service_if_eligible( + state, + &mut payment_data, + &mut should_continue_transaction, + &connector_details, + &business_profile, + &key_store, + ) + .await?; + } else { + logger::info!( + "skipping authentication service call since the merchant is not eligible." + ); + + operation + .to_domain()? + .call_external_three_ds_authentication_if_eligible( + state, + &mut payment_data, + &mut should_continue_transaction, + &connector_details, + &business_profile, + &key_store, + mandate_type, + ) + .await?; + }; operation .to_domain()? @@ -3432,29 +3442,21 @@ pub async fn get_session_token_for_click_to_pay( state: &SessionState, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, - authentication_product_ids: serde_json::Value, + authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap, payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, ) -> RouterResult { - use common_utils::{id_type::MerchantConnectorAccountId, types::AmountConvertor}; - use hyperswitch_domain_models::payments::{payment_intent::CustomerData, ClickToPayMetaData}; - - use crate::consts::CLICK_TO_PAY; - - let mca_ids: HashMap = authentication_product_ids - .parse_value("HashMap") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while parsing authentication product ids")?; - let click_to_pay_mca_id = mca_ids - .get(CLICK_TO_PAY) - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while getting click_to_pay mca_id from business profile")?; + let click_to_pay_mca_id = authentication_product_ids + .get_click_to_pay_connector_account_id() + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "authentication_product_ids", + })?; let key_manager_state = &(state).into(); let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( key_manager_state, merchant_id, - click_to_pay_mca_id, + &click_to_pay_mca_id, key_store, ) .await @@ -3473,8 +3475,8 @@ pub async fn get_session_token_for_click_to_pay( let required_amount_type = common_utils::types::StringMajorUnitForConnector; let transaction_amount = required_amount_type .convert(payment_intent.amount, transaction_currency) - .change_context(errors::ApiErrorResponse::PreconditionFailed { - message: "Failed to convert amount to string major unit for clickToPay".to_string(), + .change_context(errors::ApiErrorResponse::AmountConversionFailed { + amount_type: "string major unit", })?; let customer_details_value = payment_intent diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 0b44278359b..1b31fdf46c0 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -13,7 +13,8 @@ use common_utils::id_type::GenerateId; use common_utils::{ crypto::Encryptable, ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, - fp_utils, generate_id, id_type, + fp_utils, generate_id, + id_type::{self}, new_type::{MaskedIban, MaskedSortCode}, pii, type_name, types::{ @@ -6271,3 +6272,32 @@ pub fn validate_platform_fees_for_marketplace( } Ok(()) } + +pub async fn is_merchant_eligible_authentication_service( + merchant_id: &id_type::MerchantId, + state: &SessionState, +) -> RouterResult { + let merchants_eligible_for_authentication_service = state + .store + .as_ref() + .find_config_by_key_unwrap_or( + consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG, + Some("[]".to_string()), + ) + .await; + + let auth_eligible_array: Vec = match merchants_eligible_for_authentication_service { + Ok(config) => serde_json::from_str(&config.config) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse authentication service config")?, + Err(err) => { + logger::error!( + "Error fetching authentication service enabled merchant config {:?}", + err + ); + Vec::new() + } + }; + + Ok(auth_eligible_array.contains(&merchant_id.get_string_repr().to_owned())) +} diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 4adb9403418..0a264f24e93 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -300,6 +300,7 @@ pub trait Domain: Send + Sync { Ok(()) } + #[allow(clippy::too_many_arguments)] async fn call_unified_authentication_service_if_eligible<'a>( &'a self, _state: &SessionState, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index c309685a6a2..a87fab9b17a 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -41,7 +41,7 @@ use crate::{ }, unified_authentication_service::{ self as uas_utils, - types::{ClickToPay, UnifiedAuthenticationService, CTP_MASTERCARD}, + types::{ClickToPay, UnifiedAuthenticationService}, }, utils as core_utils, }, @@ -1047,21 +1047,42 @@ impl Domain> for business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, ) -> CustomResult<(), errors::ApiErrorResponse> { + let authentication_product_ids = business_profile + .authentication_product_ids + .clone() + .ok_or(errors::ApiErrorResponse::PreconditionFailed { + message: "authentication_product_ids is not configured in business profile" + .to_string(), + })?; + if let Some(payment_method) = payment_data.payment_attempt.payment_method { if payment_method == storage_enums::PaymentMethod::Card && business_profile.is_click_to_pay_enabled + && payment_data.service_details.is_some() { - let connector_name = CTP_MASTERCARD; // since the above checks satisfies the connector should be click to pay hence hardcoded the connector name - let connector_mca = helpers::get_merchant_connector_account( - state, - &business_profile.merchant_id, - None, - key_store, - business_profile.get_id(), - connector_name, - None, - ) - .await?; + let click_to_pay_mca_id = authentication_product_ids + .get_click_to_pay_connector_account_id() + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "authentication_product_ids", + })?; + + let key_manager_state = &(state).into(); + let merchant_id = &business_profile.merchant_id; + + let connector_mca = state + .store + .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, + merchant_id, + &click_to_pay_mca_id, + key_store, + ) + .await + .to_not_found_response( + errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: click_to_pay_mca_id.get_string_repr().to_string(), + }, + )?; let authentication_id = common_utils::generate_id_with_default_len(consts::AUTHENTICATION_ID_PREFIX); @@ -1072,21 +1093,13 @@ impl Domain> for }, )?; - let connector_transaction_id = connector_mca - .clone() - .get_mca_id() - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Error while finding mca_id from merchant_connector_account", - )?; - ClickToPay::pre_authentication( state, key_store, business_profile, payment_data, &connector_mca, - connector_name, + &connector_mca.connector_name, &authentication_id, payment_method, ) @@ -1100,7 +1113,7 @@ impl Domain> for business_profile, payment_data, &connector_mca, - connector_name, + &connector_mca.connector_name, payment_method, ) .await?; @@ -1144,10 +1157,10 @@ impl Domain> for uas_utils::create_new_authentication( state, payment_data.payment_attempt.merchant_id.clone(), - connector_name.to_string(), + connector_mca.connector_name.to_string(), business_profile.get_id().clone(), Some(payment_data.payment_intent.get_id().clone()), - connector_transaction_id, + click_to_pay_mca_id.to_owned(), &authentication_id, payment_data.service_details.clone(), authentication_status, @@ -1155,6 +1168,11 @@ impl Domain> for .await?; } } + logger::info!( + payment_method=?payment_data.payment_attempt.payment_method, + click_to_pay_enabled=?business_profile.is_click_to_pay_enabled, + "skipping unified authentication service call since payment conditions are not satisfied" + ); Ok(()) } diff --git a/crates/router/src/core/unified_authentication_service.rs b/crates/router/src/core/unified_authentication_service.rs index 2afd8477f1f..6e47dcac032 100644 --- a/crates/router/src/core/unified_authentication_service.rs +++ b/crates/router/src/core/unified_authentication_service.rs @@ -12,7 +12,7 @@ use hyperswitch_domain_models::{ }, }; -use super::{errors::RouterResult, payments::helpers::MerchantConnectorAccountType}; +use super::errors::RouterResult; use crate::{ core::{ errors::utils::StorageErrorExt, @@ -23,6 +23,7 @@ use crate::{ }, db::domain, routes::SessionState, + types::domain::MerchantConnectorAccount, }; #[cfg(feature = "v1")] @@ -33,7 +34,7 @@ impl UnifiedAuthenticationService for ClickToPay { _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, payment_data: &PaymentData, - merchant_connector_account: &MerchantConnectorAccountType, + merchant_connector_account: &MerchantConnectorAccount, connector_name: &str, authentication_id: &str, payment_method: common_enums::PaymentMethod, @@ -68,7 +69,7 @@ impl UnifiedAuthenticationService for ClickToPay { _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, payment_data: &PaymentData, - merchant_connector_account: &MerchantConnectorAccountType, + merchant_connector_account: &MerchantConnectorAccount, connector_name: &str, payment_method: common_enums::PaymentMethod, ) -> RouterResult { @@ -106,7 +107,7 @@ impl UnifiedAuthenticationService for ClickToPay { _state: &SessionState, _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, - _merchant_connector_account: &MerchantConnectorAccountType, + _merchant_connector_account: &MerchantConnectorAccount, ) -> RouterResult<()> { Ok(()) } diff --git a/crates/router/src/core/unified_authentication_service/types.rs b/crates/router/src/core/unified_authentication_service/types.rs index db0251768cf..a400f62ffc3 100644 --- a/crates/router/src/core/unified_authentication_service/types.rs +++ b/crates/router/src/core/unified_authentication_service/types.rs @@ -1,10 +1,8 @@ use crate::{ - core::{ - errors::RouterResult, - payments::{helpers::MerchantConnectorAccountType, PaymentData}, - }, + core::{errors::RouterResult, payments::PaymentData}, db::domain, routes::SessionState, + types::domain::MerchantConnectorAccount, }; pub const CTP_MASTERCARD: &str = "ctp_mastercard"; @@ -27,7 +25,7 @@ pub trait UnifiedAuthenticationService { _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, _payment_data: &PaymentData, - _merchant_connector_account: &MerchantConnectorAccountType, + _merchant_connector_account: &MerchantConnectorAccount, _connector_name: &str, _authentication_id: &str, _payment_method: common_enums::PaymentMethod, @@ -38,7 +36,7 @@ pub trait UnifiedAuthenticationService { _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, _payment_data: &PaymentData, - _merchant_connector_account: &MerchantConnectorAccountType, + _merchant_connector_account: &MerchantConnectorAccount, _connector_name: &str, _payment_method: common_enums::PaymentMethod, ) -> RouterResult; @@ -47,6 +45,6 @@ pub trait UnifiedAuthenticationService { _state: &SessionState, _key_store: &domain::MerchantKeyStore, _business_profile: &domain::Profile, - _merchant_connector_account: &MerchantConnectorAccountType, + _merchant_connector_account: &MerchantConnectorAccount, ) -> RouterResult<()>; } diff --git a/crates/router/src/core/unified_authentication_service/utils.rs b/crates/router/src/core/unified_authentication_service/utils.rs index dfa56628c87..c74baa3a0b3 100644 --- a/crates/router/src/core/unified_authentication_service/utils.rs +++ b/crates/router/src/core/unified_authentication_service/utils.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData; use common_enums::enums::PaymentMethod; -use common_utils::ext_traits::ValueExt; use diesel_models::authentication::{Authentication, AuthenticationUpdate}; use error_stack::ResultExt; use hyperswitch_domain_models::{ @@ -21,10 +20,9 @@ use crate::{ core::{ errors::{utils::ConnectorErrorExt, RouterResult}, payments, - unified_authentication_service::MerchantConnectorAccountType, }, services::{self, execute_connector_processing_step}, - types::api, + types::{api, domain::MerchantConnectorAccount}, SessionState, }; @@ -110,13 +108,11 @@ pub fn construct_uas_router_data( merchant_id: common_utils::id_type::MerchantId, address: Option, request_data: Req, - merchant_connector_account: &MerchantConnectorAccountType, + merchant_connector_account: &MerchantConnectorAccount, authentication_id: Option, ) -> RouterResult> { - let test_mode: Option = merchant_connector_account.is_test_mode_on(); let auth_type: ConnectorAuthType = merchant_connector_account .get_connector_account_details() - .parse_value("ConnectorAuthType") .change_context(ApiErrorResponse::InternalServerError)?; Ok(RouterData { flow: PhantomData, @@ -135,7 +131,7 @@ pub fn construct_uas_router_data( description: None, address: address.unwrap_or_default(), auth_type: common_enums::AuthenticationType::default(), - connector_meta_data: merchant_connector_account.get_metadata(), + connector_meta_data: merchant_connector_account.metadata.clone(), connector_wallets_details: merchant_connector_account.get_connector_wallets_details(), amount_captured: None, minor_amount_captured: None, @@ -155,7 +151,7 @@ pub fn construct_uas_router_data( payout_method_data: None, #[cfg(feature = "payouts")] quote_id: None, - test_mode, + test_mode: None, connector_http_status_code: None, external_latency: None, apple_pay_flow: None, diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index ff72af48401..28533e33e17 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -305,13 +305,6 @@ pub async fn create_profile_from_merchant_account( }) .transpose()?; - let authentication_product_ids = request - .authentication_product_ids - .map(serde_json::to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to parse product authentication id's to value")?; - Ok(domain::Profile::from(domain::ProfileSetter { profile_id, merchant_id, @@ -383,6 +376,6 @@ pub async fn create_profile_from_merchant_account( is_auto_retries_enabled: request.is_auto_retries_enabled.unwrap_or_default(), max_auto_retries_enabled: request.max_auto_retries_enabled.map(i16::from), is_click_to_pay_enabled: request.is_click_to_pay_enabled, - authentication_product_ids, + authentication_product_ids: request.authentication_product_ids, })) }