From 55029ed692f175214175a63dde576cf689ecc76e Mon Sep 17 00:00:00 2001 From: Sahkal Poddar Date: Fri, 20 Dec 2024 15:54:59 +0530 Subject: [PATCH] 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(()) }