From 87d9fced07e5cc1366eb6d16d2584bd920ad16fe Mon Sep 17 00:00:00 2001 From: Chethan Rao <70657455+Chethan-rao@users.noreply.github.com> Date: Thu, 25 Apr 2024 18:10:00 +0530 Subject: [PATCH] feat: add an api for toggling extended card info feature (#4444) --- crates/api_models/src/admin.rs | 7 ++ crates/diesel_models/src/business_profile.rs | 89 ++++++++++++++++++- .../src/query/business_profile.rs | 8 +- crates/diesel_models/src/schema.rs | 1 + crates/router/src/core/admin.rs | 35 +++++++- crates/router/src/core/routing/helpers.rs | 4 +- crates/router/src/db/business_profile.rs | 6 +- crates/router/src/db/kafka_store.rs | 2 +- crates/router/src/routes/admin.rs | 22 +++++ crates/router/src/routes/app.rs | 15 +++- crates/router/src/routes/lock_utils.rs | 3 +- crates/router/src/types/api/admin.rs | 1 + .../src/types/storage/business_profile.rs | 2 +- crates/router_env/src/logger/types.rs | 2 + .../down.sql | 3 + .../up.sql | 3 + 16 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/down.sql create mode 100644 migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/up.sql diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 88d584b6a7..cfc9694dda 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1088,3 +1088,10 @@ pub struct PaymentLinkConfig { /// Enable saved payment method option for payment link pub enabled_saved_payment_method: bool, } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub struct ExtendedCardInfoChoice { + pub enabled: bool, +} + +impl common_utils::events::ApiEventMetric for ExtendedCardInfoChoice {} diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index d5d4f5d805..7911aa403d 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -35,6 +35,7 @@ pub struct BusinessProfile { pub payment_link_config: Option, pub session_expiry: Option, pub authentication_connector_details: Option, + pub is_extended_card_info_enabled: Option, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -61,6 +62,7 @@ pub struct BusinessProfileNew { pub payment_link_config: Option, pub session_expiry: Option, pub authentication_connector_details: Option, + pub is_extended_card_info_enabled: Option, } #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] @@ -84,6 +86,84 @@ pub struct BusinessProfileUpdateInternal { pub payment_link_config: Option, pub session_expiry: Option, pub authentication_connector_details: Option, + pub is_extended_card_info_enabled: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum BusinessProfileUpdate { + Update { + profile_name: Option, + modified_at: Option, + return_url: Option, + enable_payment_response_hash: Option, + payment_response_hash_key: Option, + redirect_to_merchant_with_http_post: Option, + webhook_details: Option, + metadata: Option, + routing_algorithm: Option, + intent_fulfillment_time: Option, + frm_routing_algorithm: Option, + payout_routing_algorithm: Option, + is_recon_enabled: Option, + applepay_verified_domains: Option>, + payment_link_config: Option, + session_expiry: Option, + authentication_connector_details: Option, + }, + ExtendedCardInfoUpdate { + is_extended_card_info_enabled: Option, + }, +} + +impl From for BusinessProfileUpdateInternal { + fn from(business_profile_update: BusinessProfileUpdate) -> Self { + match business_profile_update { + BusinessProfileUpdate::Update { + profile_name, + modified_at, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + routing_algorithm, + intent_fulfillment_time, + frm_routing_algorithm, + payout_routing_algorithm, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + } => Self { + profile_name, + modified_at, + return_url, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + webhook_details, + metadata, + routing_algorithm, + intent_fulfillment_time, + frm_routing_algorithm, + payout_routing_algorithm, + is_recon_enabled, + applepay_verified_domains, + payment_link_config, + session_expiry, + authentication_connector_details, + ..Default::default() + }, + BusinessProfileUpdate::ExtendedCardInfoUpdate { + is_extended_card_info_enabled, + } => Self { + is_extended_card_info_enabled, + ..Default::default() + }, + } + } } impl From for BusinessProfile { @@ -109,13 +189,14 @@ impl From for BusinessProfile { payment_link_config: new.payment_link_config, session_expiry: new.session_expiry, authentication_connector_details: new.authentication_connector_details, + is_extended_card_info_enabled: new.is_extended_card_info_enabled, } } } -impl BusinessProfileUpdateInternal { +impl BusinessProfileUpdate { pub fn apply_changeset(self, source: BusinessProfile) -> BusinessProfile { - let Self { + let BusinessProfileUpdateInternal { profile_name, modified_at: _, return_url, @@ -133,7 +214,8 @@ impl BusinessProfileUpdateInternal { payment_link_config, session_expiry, authentication_connector_details, - } = self; + is_extended_card_info_enabled, + } = self.into(); BusinessProfile { profile_name: profile_name.unwrap_or(source.profile_name), modified_at: common_utils::date_time::now(), @@ -154,6 +236,7 @@ impl BusinessProfileUpdateInternal { payment_link_config, session_expiry, authentication_connector_details, + is_extended_card_info_enabled, ..source } } diff --git a/crates/diesel_models/src/query/business_profile.rs b/crates/diesel_models/src/query/business_profile.rs index effe2219a6..609a5c55f2 100644 --- a/crates/diesel_models/src/query/business_profile.rs +++ b/crates/diesel_models/src/query/business_profile.rs @@ -2,7 +2,9 @@ use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, T use super::generics; use crate::{ - business_profile::{BusinessProfile, BusinessProfileNew, BusinessProfileUpdateInternal}, + business_profile::{ + BusinessProfile, BusinessProfileNew, BusinessProfileUpdate, BusinessProfileUpdateInternal, + }, errors, schema::business_profile::dsl, PgPooledConn, StorageResult, @@ -18,12 +20,12 @@ impl BusinessProfile { pub async fn update_by_profile_id( self, conn: &PgPooledConn, - business_profile: BusinessProfileUpdateInternal, + business_profile: BusinessProfileUpdate, ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( conn, self.profile_id.clone(), - business_profile, + BusinessProfileUpdateInternal::from(business_profile), ) .await { diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index bbe8a8060f..44429cc4ca 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -193,6 +193,7 @@ diesel::table! { payment_link_config -> Nullable, session_expiry -> Nullable, authentication_connector_details -> Nullable, + is_extended_card_info_enabled -> Nullable, } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index dcb5dc14fe..3c6ab1f31d 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1648,7 +1648,7 @@ pub async fn update_business_profile( }) .transpose()?; - let business_profile_update = storage::business_profile::BusinessProfileUpdateInternal { + let business_profile_update = storage::business_profile::BusinessProfileUpdate::Update { profile_name: request.profile_name, modified_at: Some(date_time::now()), return_url: request.return_url.map(|return_url| return_url.to_string()), @@ -1691,6 +1691,39 @@ pub async fn update_business_profile( )) } +pub async fn extended_card_info_toggle( + state: AppState, + profile_id: &str, + ext_card_info_choice: admin_types::ExtendedCardInfoChoice, +) -> RouterResponse { + let db = state.store.as_ref(); + let business_profile = db + .find_business_profile_by_profile_id(profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + id: profile_id.to_string(), + })?; + + if business_profile.is_extended_card_info_enabled.is_none() + || business_profile + .is_extended_card_info_enabled + .is_some_and(|existing_config| existing_config != ext_card_info_choice.enabled) + { + let business_profile_update = + storage::business_profile::BusinessProfileUpdate::ExtendedCardInfoUpdate { + is_extended_card_info_enabled: Some(ext_card_info_choice.enabled), + }; + + db.update_business_profile_by_profile_id(business_profile, business_profile_update) + .await + .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { + id: profile_id.to_owned(), + })?; + } + + Ok(service_api::ApplicationResponse::Json(ext_card_info_choice)) +} + pub(crate) fn validate_auth_and_metadata_type( connector_name: api_models::enums::Connector, val: &types::ConnectorAuthType, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index a725cc714e..62925dcc2a 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -5,7 +5,7 @@ use api_models::routing as routing_types; use common_utils::ext_traits::Encode; use diesel_models::{ - business_profile::{BusinessProfile, BusinessProfileUpdateInternal}, + business_profile::{BusinessProfile, BusinessProfileUpdate}, configs, }; use error_stack::ResultExt; @@ -245,7 +245,7 @@ pub async fn update_business_profile_active_algorithm_ref( storage::enums::TransactionType::Payout => (None, Some(ref_val)), }; - let business_profile_update = BusinessProfileUpdateInternal { + let business_profile_update = BusinessProfileUpdate::Update { profile_name: None, return_url: None, enable_payment_response_hash: None, diff --git a/crates/router/src/db/business_profile.rs b/crates/router/src/db/business_profile.rs index 4d5a94b370..7b5ade8c96 100644 --- a/crates/router/src/db/business_profile.rs +++ b/crates/router/src/db/business_profile.rs @@ -30,7 +30,7 @@ pub trait BusinessProfileInterface { async fn update_business_profile_by_profile_id( &self, current_state: business_profile::BusinessProfile, - business_profile_update: business_profile::BusinessProfileUpdateInternal, + business_profile_update: business_profile::BusinessProfileUpdate, ) -> CustomResult; async fn delete_business_profile_by_profile_id_merchant_id( @@ -90,7 +90,7 @@ impl BusinessProfileInterface for Store { async fn update_business_profile_by_profile_id( &self, current_state: business_profile::BusinessProfile, - business_profile_update: business_profile::BusinessProfileUpdateInternal, + business_profile_update: business_profile::BusinessProfileUpdate, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; storage::business_profile::BusinessProfile::update_by_profile_id( @@ -169,7 +169,7 @@ impl BusinessProfileInterface for MockDb { async fn update_business_profile_by_profile_id( &self, current_state: business_profile::BusinessProfile, - business_profile_update: business_profile::BusinessProfileUpdateInternal, + business_profile_update: business_profile::BusinessProfileUpdate, ) -> CustomResult { self.business_profiles .lock() diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 9787e366e7..cc9c98be83 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -2039,7 +2039,7 @@ impl BusinessProfileInterface for KafkaStore { async fn update_business_profile_by_profile_id( &self, current_state: business_profile::BusinessProfile, - business_profile_update: business_profile::BusinessProfileUpdateInternal, + business_profile_update: business_profile::BusinessProfileUpdate, ) -> CustomResult { self.diesel_store .update_business_profile_by_profile_id(current_state, business_profile_update) diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index bc534dde39..63d9f840a0 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -612,3 +612,25 @@ pub async fn merchant_account_kv_status( ) .await } + +#[instrument(skip_all, fields(flow = ?Flow::ToggleExtendedCardInfo))] +pub async fn toggle_extended_card_info( + state: web::Data, + req: HttpRequest, + path: web::Path<(String, String)>, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::ToggleExtendedCardInfo; + let (_, profile_id) = path.into_inner(); + + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| extended_card_info_toggle(state, &profile_id, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index ff660dbf7b..a7c6f1486d 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1104,10 +1104,17 @@ impl BusinessProfile { .route(web::get().to(business_profiles_list)), ) .service( - web::resource("/{profile_id}") - .route(web::get().to(business_profile_retrieve)) - .route(web::post().to(business_profile_update)) - .route(web::delete().to(business_profile_delete)), + web::scope("/{profile_id}") + .service( + web::resource("") + .route(web::get().to(business_profile_retrieve)) + .route(web::post().to(business_profile_update)) + .route(web::delete().to(business_profile_delete)), + ) + .service( + web::resource("/toggle_extended_card_info") + .route(web::post().to(toggle_extended_card_info)), + ), ) } } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index aec3a5f2c1..c7b1b28f2e 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -166,7 +166,8 @@ impl From for ApiIdentifier { | Flow::BusinessProfileUpdate | Flow::BusinessProfileRetrieve | Flow::BusinessProfileDelete - | Flow::BusinessProfileList => Self::Business, + | Flow::BusinessProfileList + | Flow::ToggleExtendedCardInfo => Self::Business, Flow::PaymentLinkRetrieve | Flow::PaymentLinkInitiate diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 2cc0e21d64..06935a29fb 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -175,6 +175,7 @@ impl ForeignTryFrom<(domain::MerchantAccount, BusinessProfileCreate)> .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "authentication_connector_details", })?, + is_extended_card_info_enabled: None, }) } } diff --git a/crates/router/src/types/storage/business_profile.rs b/crates/router/src/types/storage/business_profile.rs index 2ab7597bcd..d7c7d66b99 100644 --- a/crates/router/src/types/storage/business_profile.rs +++ b/crates/router/src/types/storage/business_profile.rs @@ -1,3 +1,3 @@ pub use diesel_models::business_profile::{ - BusinessProfile, BusinessProfileNew, BusinessProfileUpdateInternal, + BusinessProfile, BusinessProfileNew, BusinessProfileUpdate, BusinessProfileUpdateInternal, }; diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 0ec6894deb..705596abe8 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -406,6 +406,8 @@ pub enum Flow { WebhookEventDeliveryRetry, /// Retrieve status of the Poll RetrievePollStatus, + /// Toggles the extended card info feature in profile level + ToggleExtendedCardInfo, } /// diff --git a/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/down.sql b/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/down.sql new file mode 100644 index 0000000000..e503c2dfbe --- /dev/null +++ b/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +ALTER TABLE business_profile DROP COLUMN IF EXISTS is_extended_card_info_enabled; \ No newline at end of file diff --git a/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/up.sql b/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/up.sql new file mode 100644 index 0000000000..cd5e8851ad --- /dev/null +++ b/migrations/2024-04-23-132120_add-extended-card-info-to-business-profile/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here + +ALTER TABLE business_profile ADD COLUMN IF NOT EXISTS is_extended_card_info_enabled BOOLEAN DEFAULT FALSE; \ No newline at end of file