From 5f694cfa173a22cb339d955d916f966c4b4f9cd2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Tue, 17 Dec 2024 16:21:00 +0530 Subject: [PATCH 01/10] refactor(router): fixed mca logic fetch --- .../src/merchant_connector_account.rs | 6 ++ crates/router/src/core/payments.rs | 28 +++++--- crates/router/src/core/payments/operations.rs | 2 + .../payments/operations/payment_confirm.rs | 67 ++++++++++++------- .../core/unified_authentication_service.rs | 9 +-- .../unified_authentication_service/types.rs | 12 ++-- .../unified_authentication_service/utils.rs | 9 ++- 7 files changed, 80 insertions(+), 53 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 51c75113dcda..9249ce18d002 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -62,6 +62,12 @@ impl MerchantConnectorAccount { pub fn get_id(&self) -> id_type::MerchantConnectorAccountId { self.merchant_connector_id.clone() } + pub fn get_connector_account_details(&self) -> Value { + self.connector_account_details.peek().to_owned() + } + pub fn get_connector_wallets_details(&self) -> Option> { + self.connector_wallets_details.as_deref().cloned() + } } #[cfg(feature = "v2")] diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 58490271079f..14d5af5adca3 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -386,17 +386,23 @@ where 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 let Some(ref authentication_product_ids) = business_profile.authentication_product_ids { + operation + .to_domain()? + .call_unified_authentication_service_if_eligible( + state, + &mut payment_data, + &mut should_continue_transaction, + &connector_details, + &business_profile, + &key_store, + authentication_product_ids, + ) + .await? + } else { + tracing::info!("skipping unified authentication service call since no product authentication id's are present in business profile") + } operation .to_domain()? diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index a7e60274d135..73ad0e766102 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -295,6 +295,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, @@ -303,6 +304,7 @@ pub trait Domain: Send + Sync { _connector_call_type: &ConnectorCallType, _merchant_account: &domain::Profile, _key_store: &domain::MerchantKeyStore, + _authentication_product_ids: &serde_json::Value, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index aa551ce6fff6..1b471f1da423 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{collections::HashMap, marker::PhantomData}; // use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] @@ -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, }, @@ -1045,22 +1045,45 @@ impl Domain> for _connector_call_type: &ConnectorCallType, business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, + authentication_product_ids: &serde_json::Value, ) -> CustomResult<(), errors::ApiErrorResponse> { 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 { - 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 authentication_product_ids: HashMap< + String, + common_utils::id_type::MerchantConnectorAccountId, + > = authentication_product_ids + .to_owned() + .parse_value("HashMap") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while parsing authentication product ids")?; + + let click_to_pay_mca_id = authentication_product_ids + .get(consts::CLICK_TO_PAY) + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Error while getting click_to_pay mca_id from business profile", + )?; + + 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); @@ -1071,21 +1094,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, ) @@ -1099,7 +1114,7 @@ impl Domain> for business_profile, payment_data, &connector_mca, - connector_name, + &connector_mca.connector_name, payment_method, ) .await?; @@ -1143,10 +1158,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, @@ -1154,7 +1169,7 @@ impl Domain> for .await?; } } - + tracing::debug!("skipping unified authentication service call since payment conditions {:?}, {:?} are not satisfied", payment_data.payment_attempt.payment_method, business_profile.is_click_to_pay_enabled); Ok(()) } diff --git a/crates/router/src/core/unified_authentication_service.rs b/crates/router/src/core/unified_authentication_service.rs index 5ed1b63d721f..cff8c642de72 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, @@ -67,7 +68,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 { @@ -104,7 +105,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 db0251768cf0..a400f62ffc3f 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 e0d10251049a..14b2435ac2d4 100644 --- a/crates/router/src/core/unified_authentication_service/utils.rs +++ b/crates/router/src/core/unified_authentication_service/utils.rs @@ -21,10 +21,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, }; @@ -108,10 +107,10 @@ 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 test_mode: Option = merchant_connector_account.test_mode; let auth_type: ConnectorAuthType = merchant_connector_account .get_connector_account_details() .parse_value("ConnectorAuthType") @@ -133,7 +132,7 @@ pub fn construct_uas_router_data( return_url: 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, From 5d0d9d178fd01752c501e41b195e3181d18423f2 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 18 Dec 2024 11:51:15 +0530 Subject: [PATCH 02/10] dummy commit --- .../src/merchant_connector_account.rs | 4 ++++ .../router/src/core/unified_authentication_service/utils.rs | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 9249ce18d002..f9a71992d5f0 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -140,6 +140,10 @@ impl MerchantConnectorAccount { .clone() .parse_value("ConnectorAuthType") } + + pub fn get_connector_wallets_details(&self) -> Option> { + self.connector_wallets_details.as_deref().cloned() + } } #[cfg(feature = "v1")] diff --git a/crates/router/src/core/unified_authentication_service/utils.rs b/crates/router/src/core/unified_authentication_service/utils.rs index 14b2435ac2d4..7076a7369709 100644 --- a/crates/router/src/core/unified_authentication_service/utils.rs +++ b/crates/router/src/core/unified_authentication_service/utils.rs @@ -110,7 +110,6 @@ pub fn construct_uas_router_data( merchant_connector_account: &MerchantConnectorAccount, authentication_id: Option, ) -> RouterResult> { - let test_mode: Option = merchant_connector_account.test_mode; let auth_type: ConnectorAuthType = merchant_connector_account .get_connector_account_details() .parse_value("ConnectorAuthType") @@ -152,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, From 6e4bc873d876cc83b2760a388ad62f854eb869ed Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 06:22:53 +0000 Subject: [PATCH 03/10] chore: run formatter --- .../hyperswitch_domain_models/src/merchant_connector_account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index f9a71992d5f0..df63c264a1f5 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -140,7 +140,7 @@ impl MerchantConnectorAccount { .clone() .parse_value("ConnectorAuthType") } - + pub fn get_connector_wallets_details(&self) -> Option> { self.connector_wallets_details.as_deref().cloned() } From 3c2e034c673de2a740644aaf1c534faf8092fd69 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 18 Dec 2024 18:11:20 +0530 Subject: [PATCH 04/10] addressed pr comments --- crates/api_models/src/admin.rs | 12 +++---- crates/common_types/src/payments.rs | 20 +++++++++++ crates/diesel_models/src/business_profile.rs | 12 +++---- .../src/business_profile.rs | 12 +++---- .../src/merchant_connector_account.rs | 14 ++++++-- crates/router/src/core/admin.rs | 36 +++---------------- crates/router/src/core/payments.rs | 11 +++--- crates/router/src/core/payments/operations.rs | 2 +- .../payments/operations/payment_confirm.rs | 14 ++------ .../unified_authentication_service/utils.rs | 2 -- crates/router/src/types/api/admin.rs | 9 +---- 11 files changed, 63 insertions(+), 81 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index ce6025f493ef..d5e875672b88 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1973,7 +1973,7 @@ pub struct ProfileCreate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + pub authentication_product_ids: Option, } #[nutype::nutype( @@ -2089,7 +2089,7 @@ pub struct ProfileCreate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + pub authentication_product_ids: Option, } #[cfg(feature = "v1")] @@ -2225,7 +2225,7 @@ pub struct ProfileResponse { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: Option, } #[cfg(feature = "v2")] @@ -2348,7 +2348,7 @@ pub struct ProfileResponse { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: Option, } #[cfg(feature = "v1")] @@ -2478,7 +2478,7 @@ pub struct ProfileUpdate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + pub authentication_product_ids: Option, } #[cfg(feature = "v2")] @@ -2589,7 +2589,7 @@ pub struct ProfileUpdate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option>, + pub authentication_product_ids: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index 0eef7ecaf2b4..2f1e86f44ee9 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -1,5 +1,7 @@ //! Payment related types +use std::collections::HashMap; + use common_enums::enums; use common_utils::{impl_to_sql_from_sql_json, types::MinorUnit}; use diesel::{sql_types::Jsonb, AsExpression, FromSqlRow}; @@ -38,3 +40,21 @@ 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 MerchantConnectorAccountMap( + HashMap, +); +impl_to_sql_from_sql_json!(MerchantConnectorAccountMap); + +impl MerchantConnectorAccountMap { + /// get inner hashmap + pub fn inner(&self) -> &HashMap { + &self.0 + } +} diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index bafd1897200c..fc78ffc8c718 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -58,7 +58,7 @@ 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 +103,7 @@ 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 +145,7 @@ 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 +305,7 @@ 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 +365,7 @@ 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 +409,7 @@ 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 1df474f18d5f..8771ab9eda84 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -59,7 +59,7 @@ 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 +101,7 @@ 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 +201,7 @@ 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 +729,7 @@ 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 +771,7 @@ 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 +872,7 @@ 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 df63c264a1f5..e9c0ae235d10 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -62,8 +62,18 @@ impl MerchantConnectorAccount { pub fn get_id(&self) -> id_type::MerchantConnectorAccountId { self.merchant_connector_id.clone() } - pub fn get_connector_account_details(&self) -> Value { - self.connector_account_details.peek().to_owned() + pub fn get_connector_account_details( + &self, + ) -> error_stack::Result< + crate::router_data::ConnectorAuthType, + common_utils::errors::ParsingError, + > { + use common_utils::ext_traits::ValueExt; + + self.connector_account_details + .get_inner() + .clone() + .parse_value("ConnectorAuthType") } pub fn get_connector_wallets_details(&self) -> Option> { self.connector_wallets_details.as_deref().cloned() diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 8e6d74c83753..cfc10b637f7c 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3623,13 +3623,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(), @@ -3700,7 +3693,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, })) } @@ -3752,13 +3745,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(), @@ -3816,7 +3802,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, })) } } @@ -4024,13 +4010,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, @@ -4074,7 +4053,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, }, ))) } @@ -4137,13 +4116,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, @@ -4179,7 +4151,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 a139d134435a..7d6a1d803601 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3401,19 +3401,16 @@ 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::MerchantConnectorAccountMap, payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, ) -> RouterResult { - use common_utils::{id_type::MerchantConnectorAccountId, types::AmountConvertor}; + use common_utils::types::AmountConvertor; use hyperswitch_domain_models::payments::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 + let click_to_pay_mca_id = authentication_product_ids + .inner() .get(CLICK_TO_PAY) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("Error while getting click_to_pay mca_id from business profile")?; diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 73ad0e766102..3e8e540e133b 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -304,7 +304,7 @@ pub trait Domain: Send + Sync { _connector_call_type: &ConnectorCallType, _merchant_account: &domain::Profile, _key_store: &domain::MerchantKeyStore, - _authentication_product_ids: &serde_json::Value, + _authentication_product_ids: &common_types::payments::MerchantConnectorAccountMap, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 1b471f1da423..cb0a606d2fc5 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, marker::PhantomData}; +use std::marker::PhantomData; // use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] @@ -1045,22 +1045,14 @@ impl Domain> for _connector_call_type: &ConnectorCallType, business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, - authentication_product_ids: &serde_json::Value, + authentication_product_ids: &common_types::payments::MerchantConnectorAccountMap, ) -> CustomResult<(), errors::ApiErrorResponse> { 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 { - let authentication_product_ids: HashMap< - String, - common_utils::id_type::MerchantConnectorAccountId, - > = authentication_product_ids - .to_owned() - .parse_value("HashMap") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while parsing authentication product ids")?; - let click_to_pay_mca_id = authentication_product_ids + .inner() .get(consts::CLICK_TO_PAY) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable( diff --git a/crates/router/src/core/unified_authentication_service/utils.rs b/crates/router/src/core/unified_authentication_service/utils.rs index 7076a7369709..6cbba47c495e 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::{ @@ -112,7 +111,6 @@ pub fn construct_uas_router_data( ) -> RouterResult> { let auth_type: ConnectorAuthType = merchant_connector_account .get_connector_account_details() - .parse_value("ConnectorAuthType") .change_context(ApiErrorResponse::InternalServerError)?; Ok(RouterData { flow: PhantomData, diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index ff72af484016..28533e33e178 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, })) } From 60a60e1d916e45a8acdd8c55345c7a51d4d0f504 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Wed, 18 Dec 2024 18:42:19 +0530 Subject: [PATCH 05/10] fixed path url for uas --- .../src/connectors/unified_authentication_service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs b/crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs index e820a07b350c..00d2d31cd1a5 100644 --- a/crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs +++ b/crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs @@ -189,7 +189,7 @@ impl connectors: &Connectors, ) -> CustomResult { Ok(format!( - "{}pre_authetication_processing", + "{}pre_authentication_processing", self.base_url(connectors) )) } From 55029ed692f175214175a63dde576cf689ecc76e Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 20 Dec 2024 15:54:59 +0530 Subject: [PATCH 06/10] addressed pr comments --- crates/api_models/src/admin.rs | 18 ++++--- crates/common_enums/src/enums.rs | 19 +++++++ crates/common_types/Cargo.toml | 1 + crates/common_types/src/payments.rs | 13 +++-- crates/diesel_models/src/business_profile.rs | 18 ++++--- .../src/business_profile.rs | 18 ++++--- .../src/merchant_connector_account.rs | 18 +++---- crates/router/src/consts.rs | 4 ++ crates/router/src/core/payments.rs | 49 +++++++++++++------ crates/router/src/core/payments/operations.rs | 2 +- .../payments/operations/payment_confirm.rs | 6 +-- 11 files changed, 112 insertions(+), 54 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 22674f29a92f..e517d1a0c7c9 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1973,7 +1973,8 @@ pub struct ProfileCreate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[nutype::nutype( @@ -2089,7 +2090,8 @@ pub struct ProfileCreate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -2225,7 +2227,8 @@ pub struct ProfileResponse { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -2348,7 +2351,8 @@ pub struct ProfileResponse { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v1")] @@ -2478,7 +2482,8 @@ pub struct ProfileUpdate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + pub authentication_product_ids: + Option, } #[cfg(feature = "v2")] @@ -2589,7 +2594,8 @@ pub struct ProfileUpdate { /// Product authentication ids #[schema(value_type = Option, example = r#"{ "key1": "value-1", "key2": "value-2" }"#)] - pub authentication_product_ids: Option, + 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 5aac1a07457b..9f67be92783d 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -3507,3 +3507,22 @@ pub enum StripeChargeType { Direct, Destination, } + +#[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, +} diff --git a/crates/common_types/Cargo.toml b/crates/common_types/Cargo.toml index b21d7211a344..072d20b0ad6b 100644 --- a/crates/common_types/Cargo.toml +++ b/crates/common_types/Cargo.toml @@ -15,5 +15,6 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils"} + [lints] workspace = true diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index 2f1e86f44ee9..ce4fb4c2dd9b 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -47,14 +47,17 @@ impl_to_sql_from_sql_json!(StripeSplitPaymentRequest); #[diesel(sql_type = Jsonb)] #[serde(deny_unknown_fields)] /// Hashmap to store mca_id's with product names -pub struct MerchantConnectorAccountMap( - HashMap, +pub struct AuthenticationConnectorAccountMap( + HashMap, ); -impl_to_sql_from_sql_json!(MerchantConnectorAccountMap); +impl_to_sql_from_sql_json!(AuthenticationConnectorAccountMap); -impl MerchantConnectorAccountMap { +impl AuthenticationConnectorAccountMap { /// get inner hashmap - pub fn inner(&self) -> &HashMap { + pub fn inner( + &self, + ) -> &HashMap + { &self.0 } } diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 2d2f3f576940..06aa21fe9d3f 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 8771ab9eda84..47c505e8ca24 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 e9c0ae235d10..2a0aa55e8b93 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -1,12 +1,11 @@ #[cfg(feature = "v2")] use api_models::admin; -#[cfg(feature = "v2")] -use common_utils::ext_traits::ValueExt; use common_utils::{ crypto::Encryptable, date_time, encryption::Encryption, errors::{CustomResult, ValidationError}, + ext_traits::ValueExt, id_type, pii, type_name, types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; @@ -21,9 +20,10 @@ use serde_json::Value; use super::behaviour; #[cfg(feature = "v2")] use crate::errors::api_error_response::ApiErrorResponse; -#[cfg(feature = "v2")] -use crate::router_data; -use crate::type_encryption::{crypto_operation, CryptoOperation}; +use crate::{ + router_data, + type_encryption::{crypto_operation, CryptoOperation}, +}; #[cfg(feature = "v1")] #[derive(Clone, Debug, router_derive::ToEncryption)] @@ -64,12 +64,8 @@ impl MerchantConnectorAccount { } pub fn get_connector_account_details( &self, - ) -> error_stack::Result< - crate::router_data::ConnectorAuthType, - common_utils::errors::ParsingError, - > { - use common_utils::ext_traits::ValueExt; - + ) -> error_stack::Result + { self.connector_account_details .get_inner() .clone() diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 823f0a4bf04f..85e5563f7daa 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -210,3 +210,7 @@ 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"; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 384bc09f1c4c..8f403785982d 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; @@ -30,14 +31,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::{ PaymentConfirmData, PaymentIntentData, PaymentStatusData, @@ -388,7 +389,26 @@ where ) .await?; - if let Some(ref authentication_product_ids) = business_profile.authentication_product_ids { + let merchants_eligible_for_authentication_service = state + .store + .as_ref() + .find_config_by_key(crate::consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG) + .await + .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?; + let auth_eligible_array: Vec = + serde_json::from_str(&merchants_eligible_for_authentication_service.config) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse authentication service config")?; + let merchant_id = merchant_account.get_id(); + + if auth_eligible_array.contains(&merchant_id.get_string_repr().to_owned()) { + 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(), + })?; operation .to_domain()? .call_unified_authentication_service_if_eligible( @@ -398,12 +418,14 @@ where &connector_details, &business_profile, &key_store, - authentication_product_ids, + &authentication_product_ids, ) - .await? + .await?; } else { - tracing::info!("skipping unified authentication service call since no product authentication id's are present in business profile") - } + logger::info!( + "skipping authentication service call since the merchant is not eligible." + ) + }; operation .to_domain()? @@ -3404,17 +3426,12 @@ pub async fn get_session_token_for_click_to_pay( state: &SessionState, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, - authentication_product_ids: common_types::payments::MerchantConnectorAccountMap, + authentication_product_ids: common_types::payments::AuthenticationConnectorAccountMap, payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, ) -> RouterResult { - use common_utils::types::AmountConvertor; - use hyperswitch_domain_models::payments::{payment_intent::CustomerData, ClickToPayMetaData}; - - use crate::consts::CLICK_TO_PAY; - let click_to_pay_mca_id = authentication_product_ids .inner() - .get(CLICK_TO_PAY) + .get(&common_enums::AuthenticationProduct::ClickToPay) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("Error while getting click_to_pay mca_id from business profile")?; let key_manager_state = &(state).into(); @@ -3442,8 +3459,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/operations.rs b/crates/router/src/core/payments/operations.rs index 3e8e540e133b..01cc37b4d3da 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -304,7 +304,7 @@ pub trait Domain: Send + Sync { _connector_call_type: &ConnectorCallType, _merchant_account: &domain::Profile, _key_store: &domain::MerchantKeyStore, - _authentication_product_ids: &common_types::payments::MerchantConnectorAccountMap, + _authentication_product_ids: &common_types::payments::AuthenticationConnectorAccountMap, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 729b5115225f..1c3fb77c38ee 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1045,7 +1045,7 @@ impl Domain> for _connector_call_type: &ConnectorCallType, business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, - authentication_product_ids: &common_types::payments::MerchantConnectorAccountMap, + authentication_product_ids: &common_types::payments::AuthenticationConnectorAccountMap, ) -> CustomResult<(), errors::ApiErrorResponse> { if let Some(payment_method) = payment_data.payment_attempt.payment_method { if payment_method == storage_enums::PaymentMethod::Card @@ -1053,7 +1053,7 @@ impl Domain> for { let click_to_pay_mca_id = authentication_product_ids .inner() - .get(consts::CLICK_TO_PAY) + .get(&common_enums::enums::AuthenticationProduct::ClickToPay) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable( "Error while getting click_to_pay mca_id from business profile", @@ -1161,7 +1161,7 @@ impl Domain> for .await?; } } - tracing::debug!("skipping unified authentication service call since payment conditions {:?}, {:?} are not satisfied", payment_data.payment_attempt.payment_method, business_profile.is_click_to_pay_enabled); + logger::info!("skipping unified authentication service call since payment conditions {:?}, {:?} are not satisfied", payment_data.payment_attempt.payment_method, business_profile.is_click_to_pay_enabled); Ok(()) } From 98b5af4ea8c7c5b79a37b61a88eae1463397e6ff Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 20 Dec 2024 16:36:41 +0530 Subject: [PATCH 07/10] moved external 3ds under else condition --- crates/router/src/core/payments.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 8f403785982d..aac567ecdd63 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -376,19 +376,6 @@ 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?; - let merchants_eligible_for_authentication_service = state .store .as_ref() @@ -424,7 +411,20 @@ where } 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 From be0ffa9308d642add8f7f3efba2fff879a1d5437 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Sat, 21 Dec 2024 00:17:39 +0530 Subject: [PATCH 08/10] addressed pr comments --- crates/common_types/Cargo.toml | 1 - crates/common_types/src/payments.rs | 13 +++++++++- crates/router/src/core/payments.rs | 26 ++++++------------- crates/router/src/core/payments/helpers.rs | 22 +++++++++++++++- .../payments/operations/payment_confirm.rs | 12 ++++----- 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/crates/common_types/Cargo.toml b/crates/common_types/Cargo.toml index 072d20b0ad6b..b21d7211a344 100644 --- a/crates/common_types/Cargo.toml +++ b/crates/common_types/Cargo.toml @@ -15,6 +15,5 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils"} - [lints] workspace = true diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index ce4fb4c2dd9b..e97dc48c014f 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -3,7 +3,7 @@ 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; @@ -60,4 +60,15 @@ impl AuthenticationConnectorAccountMap { { &self.0 } + /// fn to get click to pay connector_account_id + pub fn get_click_to_pay_connector_account_id( + &self, + ) -> Result { + self.inner() + .get(&enums::AuthenticationProduct::ClickToPay) + .ok_or(errors::ValidationError::MissingRequiredField { + field_name: "authentication_product_id".to_string(), + }) + .cloned() + } } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index aac567ecdd63..c358c26535e2 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -376,19 +376,9 @@ where should_continue_capture, ); - let merchants_eligible_for_authentication_service = state - .store - .as_ref() - .find_config_by_key(crate::consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG) - .await - .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?; - let auth_eligible_array: Vec = - serde_json::from_str(&merchants_eligible_for_authentication_service.config) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to parse authentication service config")?; - let merchant_id = merchant_account.get_id(); - - if auth_eligible_array.contains(&merchant_id.get_string_repr().to_owned()) { + if helpers::is_merchant_eligible_authenthention_service(merchant_account.get_id(), state) + .await? + { let authentication_product_ids = business_profile .authentication_product_ids .clone() @@ -3430,17 +3420,17 @@ pub async fn get_session_token_for_click_to_pay( payment_intent: &hyperswitch_domain_models::payments::PaymentIntent, ) -> RouterResult { let click_to_pay_mca_id = authentication_product_ids - .inner() - .get(&common_enums::AuthenticationProduct::ClickToPay) - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error while getting click_to_pay mca_id from business profile")?; + .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 diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 78cfdda33b49..5eaabd7d0d4b 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -9,7 +9,8 @@ use common_enums::ConnectorType; 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::{ @@ -6162,3 +6163,22 @@ pub fn validate_platform_fees_for_marketplace( } Ok(()) } + +pub async fn is_merchant_eligible_authenthention_service( + merchant_id: &id_type::MerchantId, + state: &SessionState, +) -> RouterResult { + let merchants_eligible_for_authentication_service = state + .store + .as_ref() + .find_config_by_key(consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG) + .await + .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?; + + let auth_eligible_array: Vec = + serde_json::from_str(&merchants_eligible_for_authentication_service.config) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse authentication service config")?; + + Ok(auth_eligible_array.contains(&merchant_id.get_string_repr().to_owned())) +} diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 1c3fb77c38ee..82bd398df231 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1052,12 +1052,10 @@ impl Domain> for && business_profile.is_click_to_pay_enabled { let click_to_pay_mca_id = authentication_product_ids - .inner() - .get(&common_enums::enums::AuthenticationProduct::ClickToPay) - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Error while getting click_to_pay mca_id from business profile", - )?; + .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; @@ -1067,7 +1065,7 @@ impl Domain> for .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 From 2e791d5289a5eaf011722af0413bc64967acca19 Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Mon, 23 Dec 2024 14:42:53 +0530 Subject: [PATCH 09/10] addressed pr comments --- crates/common_types/src/payments.rs | 11 ++------- crates/router/src/core/payments.rs | 8 ------- crates/router/src/core/payments/helpers.rs | 24 ++++++++++++++----- crates/router/src/core/payments/operations.rs | 1 - .../payments/operations/payment_confirm.rs | 9 ++++++- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/crates/common_types/src/payments.rs b/crates/common_types/src/payments.rs index e97dc48c014f..080756056288 100644 --- a/crates/common_types/src/payments.rs +++ b/crates/common_types/src/payments.rs @@ -53,21 +53,14 @@ pub struct AuthenticationConnectorAccountMap( impl_to_sql_from_sql_json!(AuthenticationConnectorAccountMap); impl AuthenticationConnectorAccountMap { - /// get inner hashmap - pub fn inner( - &self, - ) -> &HashMap - { - &self.0 - } /// fn to get click to pay connector_account_id pub fn get_click_to_pay_connector_account_id( &self, ) -> Result { - self.inner() + self.0 .get(&enums::AuthenticationProduct::ClickToPay) .ok_or(errors::ValidationError::MissingRequiredField { - field_name: "authentication_product_id".to_string(), + field_name: "authentication_product_id.click_to_pay".to_string(), }) .cloned() } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3ba60cf0fe36..171eb5dd1574 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -379,13 +379,6 @@ where if helpers::is_merchant_eligible_authenthention_service(merchant_account.get_id(), state) .await? { - 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(), - })?; operation .to_domain()? .call_unified_authentication_service_if_eligible( @@ -395,7 +388,6 @@ where &connector_details, &business_profile, &key_store, - &authentication_product_ids, ) .await?; } else { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 534bf1fd54a4..cb059307d87f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -6275,14 +6275,26 @@ pub async fn is_merchant_eligible_authenthention_service( let merchants_eligible_for_authentication_service = state .store .as_ref() - .find_config_by_key(consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG) - .await - .to_not_found_response(errors::ApiErrorResponse::ConfigNotFound)?; + .find_config_by_key_unwrap_or( + consts::AUTHENTICATION_SERVICE_ELIGIBLE_CONFIG, + Some("[]".to_string()), + ) + .await; - let auth_eligible_array: Vec = - serde_json::from_str(&merchants_eligible_for_authentication_service.config) + 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")?; + .attach_printable("unable to parse authentication service config")?, + Err(err) => { + if !err.current_context().is_db_not_found() { + 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 fd00a845a204..29aab634353c 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -307,7 +307,6 @@ pub trait Domain: Send + Sync { _connector_call_type: &ConnectorCallType, _merchant_account: &domain::Profile, _key_store: &domain::MerchantKeyStore, - _authentication_product_ids: &common_types::payments::AuthenticationConnectorAccountMap, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 82bd398df231..a424ae232cee 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1045,8 +1045,15 @@ impl Domain> for _connector_call_type: &ConnectorCallType, business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, - authentication_product_ids: &common_types::payments::AuthenticationConnectorAccountMap, ) -> 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 From a0ea8e7c10c000b3a6177e50baf4f130c9b4ba91 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:22:29 +0000 Subject: [PATCH 10/10] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 1 - api-reference/openapi_spec.json | 1 - 2 files changed, 2 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index f1caede89ccc..ab5417d4f4d2 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -12825,7 +12825,6 @@ }, "PaymentConnectorCategory": { "type": "string", - "description": "Connector Access Method", "enum": [ "payment_gateway", "alternative_payment_method", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index df7e96431dbc..c0e37ad56d64 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -15863,7 +15863,6 @@ }, "PaymentConnectorCategory": { "type": "string", - "description": "Connector Access Method", "enum": [ "payment_gateway", "alternative_payment_method",