From 1ea832301a67d1d7c9e3e82bd8321c61e7e4cbcf Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Mon, 7 Oct 2024 19:59:23 +0530 Subject: [PATCH 01/20] refactor(profile): add `should_collect_cvv_during_payment` field --- api-reference-v2/openapi_spec.json | 7 +- crates/api_models/src/admin.rs | 3 + crates/diesel_models/src/business_profile.rs | 6 ++ crates/diesel_models/src/schema_v2.rs | 1 + .../src/business_profile.rs | 66 +++++++++++++++++-- crates/router/src/core/admin.rs | 5 +- crates/router/src/types/api/admin.rs | 1 + .../2024-08-28-081721_add_v2_columns/down.sql | 3 +- .../2024-08-28-081721_add_v2_columns/up.sql | 9 ++- 9 files changed, 90 insertions(+), 11 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index bd9de3a2962a..0f1f62a92702 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -16596,7 +16596,8 @@ "enable_payment_response_hash", "redirect_to_merchant_with_http_post", "is_tax_connector_enabled", - "is_network_tokenization_enabled" + "is_network_tokenization_enabled", + "should_collect_cvv_during_payment" ], "properties": { "merchant_id": { @@ -16772,6 +16773,10 @@ "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", "default": false, "example": false + }, + "should_collect_cvv_during_payment": { + "type": "boolean", + "description": "Indicates if CVV should be collected during payment or not." } } }, diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 8f24bdf65bc5..d41a596b0fd9 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2332,6 +2332,9 @@ pub struct ProfileResponse { /// If set to `true` is_network_tokenization_enabled will be checked. #[schema(default = false, example = false)] pub is_network_tokenization_enabled: bool, + + /// Indicates if CVV should be collected during payment or not. + pub should_collect_cvv_during_payment: bool, } #[cfg(feature = "v1")] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index fd0abc16614d..ecb536206400 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -285,6 +285,7 @@ pub struct Profile { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub should_collect_cvv_during_payment: bool, pub id: common_utils::id_type::ProfileId, pub version: common_enums::ApiVersion, pub dynamic_routing_algorithm: Option, @@ -343,6 +344,7 @@ pub struct ProfileNew { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub should_collect_cvv_during_payment: bool, pub id: common_utils::id_type::ProfileId, pub version: common_enums::ApiVersion, pub is_network_tokenization_enabled: bool, @@ -386,6 +388,7 @@ pub struct ProfileUpdateInternal { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub should_collect_cvv_during_payment: Option, pub is_network_tokenization_enabled: Option, pub is_auto_retries_enabled: Option, pub max_auto_retries_enabled: Option, @@ -426,6 +429,7 @@ impl ProfileUpdateInternal { frm_routing_algorithm_id, payout_routing_algorithm_id, default_fallback_routing, + should_collect_cvv_during_payment, is_network_tokenization_enabled, is_auto_retries_enabled, max_auto_retries_enabled, @@ -485,6 +489,8 @@ impl ProfileUpdateInternal { payout_routing_algorithm_id: payout_routing_algorithm_id .or(source.payout_routing_algorithm_id), default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), + should_collect_cvv_during_payment: should_collect_cvv_during_payment + .unwrap_or(source.should_collect_cvv_during_payment), version: source.version, dynamic_routing_algorithm: None, is_network_tokenization_enabled: is_network_tokenization_enabled diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index b5e895cde790..aa6f8e074b69 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -213,6 +213,7 @@ diesel::table! { #[max_length = 64] payout_routing_algorithm_id -> Nullable, default_fallback_routing -> Nullable, + should_collect_cvv_during_payment -> Bool, #[max_length = 64] id -> Varchar, version -> ApiVersion, diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index 75cdd40024f8..e2629f06ed83 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -695,6 +695,7 @@ pub struct Profile { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub should_collect_cvv_during_payment: bool, pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, pub version: common_enums::ApiVersion, @@ -735,6 +736,7 @@ pub struct ProfileSetter { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub should_collect_cvv_during_payment: bool, pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, pub is_network_tokenization_enabled: bool, @@ -780,6 +782,7 @@ impl From for Profile { frm_routing_algorithm_id: value.frm_routing_algorithm_id, payout_routing_algorithm_id: value.payout_routing_algorithm_id, default_fallback_routing: value.default_fallback_routing, + should_collect_cvv_during_payment: value.should_collect_cvv_during_payment, tax_connector_id: value.tax_connector_id, is_tax_connector_enabled: value.is_tax_connector_enabled, version: consts::API_VERSION, @@ -848,13 +851,16 @@ pub enum ProfileUpdate { default_fallback_routing: Option, }, ExtendedCardInfoUpdate { - is_extended_card_info_enabled: Option, + is_extended_card_info_enabled: bool, }, ConnectorAgnosticMitUpdate { - is_connector_agnostic_mit_enabled: Option, + is_connector_agnostic_mit_enabled: bool, }, NetworkTokenizationUpdate { - is_network_tokenization_enabled: Option, + is_network_tokenization_enabled: bool, + }, + CollectCvvDuringPaymentUpdate { + should_collect_cvv_during_payment: bool, }, } @@ -921,6 +927,7 @@ impl From for ProfileUpdateInternal { frm_routing_algorithm_id: None, payout_routing_algorithm_id: None, default_fallback_routing: None, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, is_network_tokenization_enabled, @@ -961,6 +968,7 @@ impl From for ProfileUpdateInternal { frm_routing_algorithm_id: None, payout_routing_algorithm_id, default_fallback_routing: None, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, is_network_tokenization_enabled: None, @@ -984,7 +992,7 @@ impl From for ProfileUpdateInternal { session_expiry: None, authentication_connector_details: None, payout_link_config: None, - is_extended_card_info_enabled, + is_extended_card_info_enabled: Some(is_extended_card_info_enabled), extended_card_info_config: None, is_connector_agnostic_mit_enabled: None, use_billing_as_payment_method_billing: None, @@ -999,6 +1007,7 @@ impl From for ProfileUpdateInternal { order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, default_fallback_routing: None, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, is_network_tokenization_enabled: None, @@ -1024,7 +1033,7 @@ impl From for ProfileUpdateInternal { payout_link_config: None, is_extended_card_info_enabled: None, extended_card_info_config: None, - is_connector_agnostic_mit_enabled, + is_connector_agnostic_mit_enabled: Some(is_connector_agnostic_mit_enabled), use_billing_as_payment_method_billing: None, collect_shipping_details_from_wallet_connector: None, collect_billing_details_from_wallet_connector: None, @@ -1037,6 +1046,7 @@ impl From for ProfileUpdateInternal { order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, default_fallback_routing: None, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, is_network_tokenization_enabled: None, @@ -1075,6 +1085,7 @@ impl From for ProfileUpdateInternal { order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, default_fallback_routing, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, is_network_tokenization_enabled: None, @@ -1113,9 +1124,49 @@ impl From for ProfileUpdateInternal { order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, default_fallback_routing: None, + should_collect_cvv_during_payment: None, tax_connector_id: None, is_tax_connector_enabled: None, - is_network_tokenization_enabled, + is_network_tokenization_enabled: Some(is_network_tokenization_enabled), + is_auto_retries_enabled: None, + max_auto_retries_enabled: None, + }, + ProfileUpdate::CollectCvvDuringPaymentUpdate { + should_collect_cvv_during_payment, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + always_collect_billing_details_from_wallet_connector: None, + always_collect_shipping_details_from_wallet_connector: None, + routing_algorithm_id: None, + payout_routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + default_fallback_routing: None, + should_collect_cvv_during_payment: Some(should_collect_cvv_during_payment), + tax_connector_id: None, + is_tax_connector_enabled: None, + is_network_tokenization_enabled: None, is_auto_retries_enabled: None, max_auto_retries_enabled: None, }, @@ -1169,6 +1220,7 @@ impl super::behaviour::Conversion for Profile { order_fulfillment_time_origin: self.order_fulfillment_time_origin, frm_routing_algorithm_id: self.frm_routing_algorithm_id, default_fallback_routing: self.default_fallback_routing, + should_collect_cvv_during_payment: self.should_collect_cvv_during_payment, tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), version: self.version, @@ -1239,6 +1291,7 @@ impl super::behaviour::Conversion for Profile { frm_routing_algorithm_id: item.frm_routing_algorithm_id, payout_routing_algorithm_id: item.payout_routing_algorithm_id, default_fallback_routing: item.default_fallback_routing, + should_collect_cvv_during_payment: item.should_collect_cvv_during_payment, tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled.unwrap_or(false), version: item.version, @@ -1291,6 +1344,7 @@ impl super::behaviour::Conversion for Profile { frm_routing_algorithm_id: self.frm_routing_algorithm_id, payout_routing_algorithm_id: self.payout_routing_algorithm_id, default_fallback_routing: self.default_fallback_routing, + should_collect_cvv_during_payment: self.should_collect_cvv_during_payment, tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), version: self.version, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index f49f4096340b..b394f087cf18 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3647,6 +3647,7 @@ impl ProfileCreateBridge for api::ProfileCreate { .or(Some(common_utils::consts::DEFAULT_ORDER_FULFILLMENT_TIME)), order_fulfillment_time_origin: self.order_fulfillment_time_origin, default_fallback_routing: None, + should_collect_cvv_during_payment: false, tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: self.is_tax_connector_enabled, is_network_tokenization_enabled: self.is_network_tokenization_enabled, @@ -4210,7 +4211,7 @@ pub async fn extended_card_info_toggle( .is_some_and(|existing_config| existing_config != ext_card_info_choice.enabled) { let profile_update = domain::ProfileUpdate::ExtendedCardInfoUpdate { - is_extended_card_info_enabled: Some(ext_card_info_choice.enabled), + is_extended_card_info_enabled: ext_card_info_choice.enabled, }; db.update_profile_by_profile_id( @@ -4264,7 +4265,7 @@ pub async fn connector_agnostic_mit_toggle( != Some(connector_agnostic_mit_choice.enabled) { let profile_update = domain::ProfileUpdate::ConnectorAgnosticMitUpdate { - is_connector_agnostic_mit_enabled: Some(connector_agnostic_mit_choice.enabled), + is_connector_agnostic_mit_enabled: connector_agnostic_mit_choice.enabled, }; db.update_profile_by_profile_id( diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 02951d1a1880..c81fc7ceb484 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -233,6 +233,7 @@ impl ForeignTryFrom for ProfileResponse { outgoing_webhook_custom_http_headers, order_fulfillment_time, order_fulfillment_time_origin: item.order_fulfillment_time_origin, + should_collect_cvv_during_payment: item.should_collect_cvv_during_payment, tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled, is_network_tokenization_enabled: item.is_network_tokenization_enabled, diff --git a/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql b/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql index 4687c57fae31..70aa43951050 100644 --- a/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql +++ b/v2_migrations/2024-08-28-081721_add_v2_columns/down.sql @@ -10,7 +10,8 @@ ALTER TABLE business_profile DROP COLUMN routing_algorithm_id, DROP COLUMN order_fulfillment_time_origin, DROP COLUMN frm_routing_algorithm_id, DROP COLUMN payout_routing_algorithm_id, - DROP COLUMN default_fallback_routing; + DROP COLUMN default_fallback_routing, + DROP COLUMN should_collect_cvv_during_payment; DROP TYPE "OrderFulfillmentTimeOrigin"; diff --git a/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql b/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql index 3852c9c2ece3..64decb1a0a87 100644 --- a/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql +++ b/v2_migrations/2024-08-28-081721_add_v2_columns/up.sql @@ -14,7 +14,14 @@ ADD COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, ADD COLUMN order_fulfillment_time_origin "OrderFulfillmentTimeOrigin" DEFAULT NULL, ADD COLUMN frm_routing_algorithm_id VARCHAR(64) DEFAULT NULL, ADD COLUMN payout_routing_algorithm_id VARCHAR(64) DEFAULT NULL, - ADD COLUMN default_fallback_routing JSONB DEFAULT NULL; + ADD COLUMN default_fallback_routing JSONB DEFAULT NULL, + -- Intentionally not adding a default value here since we would have to + -- check if any merchants have enabled this from configs table, + -- before filling data for this column. + -- If no merchants have enabled this, then we can use `false` as the default value + -- when adding the column, later we can drop the default added for the column + -- so that we ensure new records inserted always have a value for the column. + ADD COLUMN should_collect_cvv_during_payment BOOLEAN NOT NULL; ALTER TABLE payment_intent ADD COLUMN merchant_reference_id VARCHAR(64), From 01a9445e7f29b53349e4679fe0a5c8f2c6a7e023 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Wed, 9 Oct 2024 03:27:19 +0530 Subject: [PATCH 02/20] refactor(api_models): improve doc comments for `is_network_tokenization_enabled` field in profile types --- api-reference-v2/openapi_spec.json | 4 ++-- api-reference/openapi_spec.json | 4 ++-- crates/api_models/src/admin.rs | 16 ++++++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 0f1f62a92702..a9eb01e05aac 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -16564,7 +16564,7 @@ }, "is_network_tokenization_enabled": { "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + "description": "Indicates if network tokenization is enabled or not." } }, "additionalProperties": false @@ -16770,7 +16770,7 @@ }, "is_network_tokenization_enabled": { "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "description": "Indicates if network tokenization is enabled or not.", "default": false, "example": false }, diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 9374acb024d1..a441f7921804 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -21451,7 +21451,7 @@ }, "is_network_tokenization_enabled": { "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." + "description": "Indicates if network tokenization is enabled or not." }, "is_auto_retries_enabled": { "type": "boolean", @@ -21678,7 +21678,7 @@ }, "is_network_tokenization_enabled": { "type": "boolean", - "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "description": "Indicates if network tokenization is enabled or not.", "default": false, "example": false }, diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index d41a596b0fd9..dde0016a3080 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1971,8 +1971,7 @@ pub struct ProfileCreate { #[serde(default)] pub is_tax_connector_enabled: bool, - /// Indicates if is_network_tokenization_enabled is enabled or not. - /// If set to `true` is_network_tokenization_enabled will be checked. + /// Indicates if network tokenization is enabled or not. #[serde(default)] pub is_network_tokenization_enabled: bool, @@ -2086,8 +2085,7 @@ pub struct ProfileCreate { #[serde(default)] pub is_tax_connector_enabled: bool, - /// Indicates if is_network_tokenization_enabled is enabled or not. - /// If set to `true` is_network_tokenization_enabled will be checked. + /// Indicates if network tokenization is enabled or not. #[serde(default)] pub is_network_tokenization_enabled: bool, } @@ -2208,8 +2206,7 @@ pub struct ProfileResponse { /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: bool, - /// Indicates if is_network_tokenization_enabled is enabled or not. - /// If set to `true` is_network_tokenization_enabled will be checked. + /// Indicates if network tokenization is enabled or not. #[schema(default = false, example = false)] pub is_network_tokenization_enabled: bool, @@ -2328,8 +2325,7 @@ pub struct ProfileResponse { /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: bool, - /// Indicates if is_network_tokenization_enabled is enabled or not. - /// If set to `true` is_network_tokenization_enabled will be checked. + /// Indicates if network tokenization is enabled or not. #[schema(default = false, example = false)] pub is_network_tokenization_enabled: bool, @@ -2449,7 +2445,7 @@ pub struct ProfileUpdate { #[serde(default)] pub dynamic_routing_algorithm: Option, - /// Indicates if is_network_tokenization_enabled is enabled or not. + /// Indicates if network tokenization is enabled or not. pub is_network_tokenization_enabled: Option, /// Indicates if is_auto_retries_enabled is enabled or not. @@ -2558,7 +2554,7 @@ pub struct ProfileUpdate { /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: Option, - /// Indicates if is_network_tokenization_enabled is enabled or not. + /// Indicates if network tokenization is enabled or not. pub is_network_tokenization_enabled: Option, } From 763cfa99f5914a4f2176d51b5a0f336c2b518d8c Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Wed, 9 Oct 2024 17:30:45 +0530 Subject: [PATCH 03/20] refactor(payment_methods): rename `business_profile` to `profile` in `SavedPMLPaymentsInfo` --- crates/router/src/core/payment_methods.rs | 14 +++++++------- crates/router/src/types/payment_methods.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 87078dfa5aff..6eb75677fa76 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1558,7 +1558,7 @@ pub async fn list_customer_payment_method( state, merchant_account, key_store, - payments_info.and_then(|pi| pi.business_profile), + payments_info.and_then(|pi| pi.profile), &mut response, )) .await?; @@ -1618,7 +1618,7 @@ async fn generate_saved_pm_response( pi.is_connector_agnostic_mit_enabled, pi.requires_cvv, pi.off_session_payment_flag, - pi.business_profile + pi.profile .as_ref() .map(|profile| profile.get_id().to_owned()), ) @@ -1928,7 +1928,7 @@ impl pm_types::SavedPMLPaymentsInfo { let profile_id = &payment_intent.profile_id; - let business_profile = core_utils::validate_and_get_business_profile( + let profile = core_utils::validate_and_get_business_profile( db, key_manager_state, key_store, @@ -1937,14 +1937,14 @@ impl pm_types::SavedPMLPaymentsInfo { ) .await?; - let is_connector_agnostic_mit_enabled = business_profile + let is_connector_agnostic_mit_enabled = profile .as_ref() - .and_then(|business_profile| business_profile.is_connector_agnostic_mit_enabled) + .and_then(|profile| profile.is_connector_agnostic_mit_enabled) .unwrap_or(false); Ok(Self { payment_intent, - business_profile, + profile, requires_cvv, off_session_payment_flag, is_connector_agnostic_mit_enabled, @@ -1966,7 +1966,7 @@ impl pm_types::SavedPMLPaymentsInfo { .get_required_value("PaymentTokenData")?; let intent_fulfillment_time = self - .business_profile + .profile .as_ref() .and_then(|b_profile| b_profile.get_order_fulfillment_time()) .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME); diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 29290f0b7fcf..87ea14ef6e79 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -139,7 +139,7 @@ impl PaymentMethodClientSecret { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, - pub business_profile: Option, + pub profile: Option, pub requires_cvv: bool, pub off_session_payment_flag: bool, pub is_connector_agnostic_mit_enabled: bool, From bf04f1920100a71d875b56d1de5d8a50a4b7900f Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Wed, 9 Oct 2024 18:30:14 +0530 Subject: [PATCH 04/20] refactor(payment_methods): rename `requires_cvv` to `collect_cvv_during_payment` in `SavedPMLPaymentsInfo` and populate it from profile --- crates/router/src/core/payment_methods.rs | 34 ++++++++-------------- crates/router/src/types/payment_methods.rs | 2 +- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 6eb75677fa76..c8042e74f902 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1616,7 +1616,7 @@ async fn generate_saved_pm_response( .map(|pi| { ( pi.is_connector_agnostic_mit_enabled, - pi.requires_cvv, + pi.collect_cvv_during_payment, pi.off_session_payment_flag, pi.profile .as_ref() @@ -1906,26 +1906,6 @@ impl pm_types::SavedPMLPaymentsInfo { key_manager_state: &util_types::keymanager::KeyManagerState, key_store: &domain::MerchantKeyStore, ) -> RouterResult { - let requires_cvv = db - .find_config_by_key_unwrap_or( - format!( - "{}_requires_cvv", - merchant_account.get_id().get_string_repr() - ) - .as_str(), - Some("true".to_string()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch requires_cvv config")? - .config - != "false"; - - let off_session_payment_flag = matches!( - payment_intent.setup_future_usage, - common_enums::FutureUsage::OffSession - ); - let profile_id = &payment_intent.profile_id; let profile = core_utils::validate_and_get_business_profile( @@ -1937,6 +1917,16 @@ impl pm_types::SavedPMLPaymentsInfo { ) .await?; + let collect_cvv_during_payment = profile + .as_ref() + .map(|profile| profile.should_collect_cvv_during_payment) + .unwrap_or(true); + + let off_session_payment_flag = matches!( + payment_intent.setup_future_usage, + common_enums::FutureUsage::OffSession + ); + let is_connector_agnostic_mit_enabled = profile .as_ref() .and_then(|profile| profile.is_connector_agnostic_mit_enabled) @@ -1945,7 +1935,7 @@ impl pm_types::SavedPMLPaymentsInfo { Ok(Self { payment_intent, profile, - requires_cvv, + collect_cvv_during_payment, off_session_payment_flag, is_connector_agnostic_mit_enabled, }) diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 87ea14ef6e79..0c586c94d3f6 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -140,7 +140,7 @@ impl PaymentMethodClientSecret { pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, pub profile: Option, - pub requires_cvv: bool, + pub collect_cvv_during_payment: bool, pub off_session_payment_flag: bool, pub is_connector_agnostic_mit_enabled: bool, } From a24617f70c4bb0e2e55ca6818d16f4883a6ec761 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Mon, 14 Oct 2024 17:14:32 +0530 Subject: [PATCH 05/20] refactor(payment_methods): accept merchant account by reference instead of value --- crates/router/src/core/payment_methods.rs | 8 ++++---- crates/router/src/core/payment_methods/cards.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index c8042e74f902..2092f13c5c38 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1430,7 +1430,7 @@ pub async fn list_customer_payment_method_util( let resp = if let Some(cust) = customer_id { Box::pin(list_customer_payment_method( &state, - merchant_account, + &merchant_account, key_store, payment_intent, &cust, @@ -1456,7 +1456,7 @@ pub async fn list_customer_payment_method_util( ))] pub async fn list_customer_payment_method( state: &SessionState, - merchant_account: domain::MerchantAccount, + merchant_account: &domain::MerchantAccount, key_store: domain::MerchantKeyStore, payment_intent: Option, customer_id: &id_type::CustomerId, @@ -1482,7 +1482,7 @@ pub async fn list_customer_payment_method( .async_map(|pi| { pm_types::SavedPMLPaymentsInfo::form_payments_info( pi, - &merchant_account, + merchant_account, db, key_manager_state, &key_store, @@ -1531,7 +1531,7 @@ pub async fn list_customer_payment_method( generate_saved_pm_response( state, &key_store, - &merchant_account, + merchant_account, ctx, &customer, payments_info.as_ref(), diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 067b35e1e5ea..89202ee16fec 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4636,7 +4636,7 @@ async fn perform_surcharge_ops( pub async fn perform_surcharge_ops( _payment_intent: Option, _state: &routes::SessionState, - _merchant_account: domain::MerchantAccount, + _merchant_account: &domain::MerchantAccount, _key_store: domain::MerchantKeyStore, _business_profile: Option, _response: &mut api::CustomerPaymentMethodsListResponse, From 9c02f4dc239991a8ac286a99ac9da1d42755eba9 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Sun, 20 Oct 2024 19:57:19 +0530 Subject: [PATCH 06/20] refactor(hyperswitch_domain_models): update variants toggling a field during update to be required fields --- .../src/business_profile.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index e2629f06ed83..fb846e288626 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -208,13 +208,13 @@ pub enum ProfileUpdate { dynamic_routing_algorithm: Option, }, ExtendedCardInfoUpdate { - is_extended_card_info_enabled: Option, + is_extended_card_info_enabled: bool, }, ConnectorAgnosticMitUpdate { - is_connector_agnostic_mit_enabled: Option, + is_connector_agnostic_mit_enabled: bool, }, NetworkTokenizationUpdate { - is_network_tokenization_enabled: Option, + is_network_tokenization_enabled: bool, }, } @@ -391,7 +391,7 @@ impl From for ProfileUpdateInternal { session_expiry: None, authentication_connector_details: None, payout_link_config: None, - is_extended_card_info_enabled, + is_extended_card_info_enabled: Some(is_extended_card_info_enabled), extended_card_info_config: None, is_connector_agnostic_mit_enabled: None, use_billing_as_payment_method_billing: None, @@ -430,7 +430,7 @@ impl From for ProfileUpdateInternal { payout_link_config: None, is_extended_card_info_enabled: None, extended_card_info_config: None, - is_connector_agnostic_mit_enabled, + is_connector_agnostic_mit_enabled: Some(is_connector_agnostic_mit_enabled), use_billing_as_payment_method_billing: None, collect_shipping_details_from_wallet_connector: None, collect_billing_details_from_wallet_connector: None, @@ -477,7 +477,7 @@ impl From for ProfileUpdateInternal { tax_connector_id: None, is_tax_connector_enabled: None, dynamic_routing_algorithm: None, - is_network_tokenization_enabled, + is_network_tokenization_enabled: Some(is_network_tokenization_enabled), is_auto_retries_enabled: None, max_auto_retries_enabled: None, }, From 40459cc837f7b7083b2f703489a6d8da92490591 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Mon, 21 Oct 2024 15:42:25 +0530 Subject: [PATCH 07/20] refactor: pass profile to `form_payments_info()` instead of fetching it within `form_payments_info()` --- crates/router/src/core/payment_methods.rs | 27 +++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 2092f13c5c38..62377be7574e 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1465,7 +1465,19 @@ pub async fn list_customer_payment_method( ) -> RouterResponse { let db = &*state.store; let key_manager_state = &(state).into(); - // let key = key_store.key.get_inner().peek(); + + let profile_id = payment_intent + .as_ref() + .map(|payment_intent| &payment_intent.profile_id); + + let profile = core_utils::validate_and_get_business_profile( + db, + key_manager_state, + &key_store, + profile_id, + merchant_account.get_id(), + ) + .await?; let customer = db .find_customer_by_merchant_reference_id_merchant_id( @@ -1483,6 +1495,7 @@ pub async fn list_customer_payment_method( pm_types::SavedPMLPaymentsInfo::form_payments_info( pi, merchant_account, + profile, db, key_manager_state, &key_store, @@ -1902,21 +1915,11 @@ impl pm_types::SavedPMLPaymentsInfo { pub async fn form_payments_info( payment_intent: PaymentIntent, merchant_account: &domain::MerchantAccount, + profile: Option, db: &dyn StorageInterface, key_manager_state: &util_types::keymanager::KeyManagerState, key_store: &domain::MerchantKeyStore, ) -> RouterResult { - let profile_id = &payment_intent.profile_id; - - let profile = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - key_store, - Some(profile_id), - merchant_account.get_id(), - ) - .await?; - let collect_cvv_during_payment = profile .as_ref() .map(|profile| profile.should_collect_cvv_during_payment) From 6ffd4ff12b7844399f1790d932d9817aad7fa043 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Wed, 23 Oct 2024 23:58:48 +0530 Subject: [PATCH 08/20] refactor(payment_methods): move `PaymentsMandateReference` from `router` crate to `diesel_models` crate --- crates/diesel_models/src/payment_method.rs | 34 ++++++++++++++++++ crates/router/src/core/payment_methods.rs | 4 ++- .../router/src/core/payment_methods/cards.rs | 6 ++-- crates/router/src/core/payments.rs | 3 +- .../payments/operations/payment_response.rs | 2 +- .../router/src/core/payments/tokenization.rs | 12 +++---- .../src/types/storage/payment_method.rs | 35 ------------------- 7 files changed, 50 insertions(+), 46 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 3ad58e62dcf9..d78e59a50298 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use common_enums::MerchantStorageScheme; use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; @@ -838,3 +840,35 @@ impl From<&PaymentMethodNew> for PaymentMethod { } } } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PaymentsMandateReferenceRecord { + pub connector_mandate_id: String, + pub payment_method_type: Option, + pub original_payment_authorized_amount: Option, + pub original_payment_authorized_currency: Option, + pub mandate_metadata: Option, + pub connector_mandate_status: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PaymentsMandateReference( + pub HashMap, +); + +impl std::ops::Deref for PaymentsMandateReference { + type Target = + HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for PaymentsMandateReference { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +common_utils::impl_to_sql_from_sql_json!(PaymentsMandateReference); diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 62377be7574e..9c950f2e9235 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1619,7 +1619,9 @@ async fn generate_saved_pm_response( let connector_mandate_details = pm .connector_mandate_details .clone() - .map(|val| val.parse_value::("PaymentsMandateReference")) + .map(|val| { + val.parse_value::("PaymentsMandateReference") + }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to deserialize to Payment Mandate Reference ")?; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 89202ee16fec..63f89a9f7824 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4374,7 +4374,9 @@ pub async fn list_customer_payment_method( .connector_mandate_details .clone() .map(|val| { - val.parse_value::("PaymentsMandateReference") + val.parse_value::( + "PaymentsMandateReference", + ) }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4650,7 +4652,7 @@ pub async fn get_mca_status( profile_id: Option, merchant_id: &id_type::MerchantId, is_connector_agnostic_mit_enabled: bool, - connector_mandate_details: Option, + connector_mandate_details: Option, network_transaction_id: Option<&String>, ) -> errors::RouterResult { if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index a76e24af0c26..913cf947cf48 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -5157,7 +5157,8 @@ where .connector_mandate_details .clone() .map(|details| { - details.parse_value::("connector_mandate_details") + details + .parse_value::("connector_mandate_details") }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 7aa67a9b50f6..1fdc5bfa10d3 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1541,7 +1541,7 @@ async fn payment_response_update_tracker( .connector_mandate_details .clone() .map(|val| { - val.parse_value::( + val.parse_value::( "PaymentsMandateReference", ) }) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index c14542de5476..2b3ac5cb11cf 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -1134,7 +1134,7 @@ pub fn add_connector_mandate_details_in_payment_method( merchant_connector_id: Option, connector_mandate_id: Option, mandate_metadata: Option, -) -> Option { +) -> Option { let mut mandate_details = HashMap::new(); if let Some((mca_id, connector_mandate_id)) = @@ -1142,7 +1142,7 @@ pub fn add_connector_mandate_details_in_payment_method( { mandate_details.insert( mca_id, - storage::PaymentsMandateReferenceRecord { + diesel_models::PaymentsMandateReferenceRecord { connector_mandate_id, payment_method_type, original_payment_authorized_amount: authorized_amount, @@ -1151,14 +1151,14 @@ pub fn add_connector_mandate_details_in_payment_method( connector_mandate_status: Some(ConnectorMandateStatus::Active), }, ); - Some(storage::PaymentsMandateReference(mandate_details)) + Some(diesel_models::PaymentsMandateReference(mandate_details)) } else { None } } pub fn update_connector_mandate_details( - mandate_details: Option, + mandate_details: Option, payment_method_type: Option, authorized_amount: Option, authorized_currency: Option, @@ -1171,7 +1171,7 @@ pub fn update_connector_mandate_details( if let Some((mca_id, connector_mandate_id)) = merchant_connector_id.clone().zip(connector_mandate_id) { - let updated_record = storage::PaymentsMandateReferenceRecord { + let updated_record = diesel_models::PaymentsMandateReferenceRecord { connector_mandate_id: connector_mandate_id.clone(), payment_method_type, original_payment_authorized_amount: authorized_amount, @@ -1183,7 +1183,7 @@ pub fn update_connector_mandate_details( payment_mandate_reference .entry(mca_id) .and_modify(|pm| *pm = updated_record) - .or_insert(storage::PaymentsMandateReferenceRecord { + .or_insert(diesel_models::PaymentsMandateReferenceRecord { connector_mandate_id, payment_method_type, original_payment_authorized_amount: authorized_amount, diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 20d0ee74b011..3214f911f567 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -1,8 +1,3 @@ -use std::{ - collections::HashMap, - ops::{Deref, DerefMut}, -}; - use api_models::payment_methods; use diesel_models::enums; pub use diesel_models::payment_method::{ @@ -118,36 +113,6 @@ pub struct PaymentMethodListContext { pub bank_transfer_details: Option, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct PaymentsMandateReferenceRecord { - pub connector_mandate_id: String, - pub payment_method_type: Option, - pub original_payment_authorized_amount: Option, - pub original_payment_authorized_currency: Option, - pub mandate_metadata: Option, - pub connector_mandate_status: Option, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct PaymentsMandateReference( - pub HashMap, -); - -impl Deref for PaymentsMandateReference { - type Target = - HashMap; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for PaymentsMandateReference { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct PaymentMethodStatusTrackingData { pub payment_method_id: String, From 5aa96511195f66381558ae803b0e81077c713f5d Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Thu, 24 Oct 2024 00:02:41 +0530 Subject: [PATCH 09/20] refactor(payment_methods): use concrete type instead of `serde_json::Value` for `connector_mandate_details` field --- crates/diesel_models/src/payment_method.rs | 19 ++++++++----------- .../src/payment_methods.rs | 2 +- crates/router/src/core/payment_methods.rs | 2 +- .../router/src/core/payment_methods/cards.rs | 4 ++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index d78e59a50298..be15462ed503 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -67,9 +67,7 @@ pub struct PaymentMethod { } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive( - Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize, -)] +#[derive(Clone, Debug, Identifiable, Queryable, Selectable, Serialize, Deserialize)] #[diesel(table_name = payment_methods, primary_key(id), check_for_backend(diesel::pg::Pg))] pub struct PaymentMethod { pub customer_id: common_utils::id_type::CustomerId, @@ -82,7 +80,7 @@ pub struct PaymentMethod { pub payment_method_data: Option, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, @@ -158,9 +156,7 @@ pub struct PaymentMethodNew { } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive( - Clone, Debug, Eq, PartialEq, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize, -)] +#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = payment_methods)] pub struct PaymentMethodNew { pub customer_id: common_utils::id_type::CustomerId, @@ -173,7 +169,7 @@ pub struct PaymentMethodNew { pub payment_method_data: Option, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, @@ -291,7 +287,7 @@ pub enum PaymentMethodUpdate { network_token_payment_method_data: Option, }, ConnectorMandateDetailsUpdate { - connector_mandate_details: Option, + connector_mandate_details: Option, }, } @@ -317,7 +313,7 @@ pub struct PaymentMethodUpdateInternal { status: Option, locker_id: Option, payment_method: Option, - connector_mandate_details: Option, + connector_mandate_details: Option, updated_by: Option, payment_method_type: Option, last_modified: PrimitiveDateTime, @@ -851,7 +847,8 @@ pub struct PaymentsMandateReferenceRecord { pub connector_mandate_status: Option, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] pub struct PaymentsMandateReference( pub HashMap, ); diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 445ad5ab8005..8d9247e6e3c1 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -83,7 +83,7 @@ pub struct PaymentMethod { pub payment_method_data: OptionalEncryptableValue, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 9c950f2e9235..76a48fcac65d 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1134,7 +1134,7 @@ pub async fn create_payment_method_in_db( customer_acceptance: Option, payment_method_data: crypto::OptionalEncryptableValue, key_store: &domain::MerchantKeyStore, - connector_mandate_details: Option, + connector_mandate_details: Option, status: Option, network_transaction_id: Option, storage_scheme: enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 63f89a9f7824..144ebb9cf752 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2256,11 +2256,11 @@ pub async fn update_payment_method_connector_mandate_details( key_store: &domain::MerchantKeyStore, db: &dyn db::StorageInterface, pm: domain::PaymentMethod, - connector_mandate_details: Option, + connector_mandate_details: Option, storage_scheme: MerchantStorageScheme, ) -> errors::CustomResult<(), errors::VaultError> { let pm_update = payment_method::PaymentMethodUpdate::ConnectorMandateDetailsUpdate { - connector_mandate_details: connector_mandate_details.map(Secret::new), + connector_mandate_details, }; db.update_payment_method(&(state.into()), key_store, pm, pm_update, storage_scheme) From c86426cad1e60fd8a59ccfe806ff93a0d1e83f23 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Thu, 24 Oct 2024 01:33:26 +0530 Subject: [PATCH 10/20] refactor(payment_methods): refactor `get_mca_status()` function to remove database call happening in a loop --- crates/router/src/core/payment_methods.rs | 43 ++++++++----- .../router/src/core/payment_methods/cards.rs | 62 +++++++++++++++---- .../router/src/core/payments/tokenization.rs | 2 +- 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 76a48fcac65d..a1a59cfdf1a8 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1538,6 +1538,23 @@ pub async fn list_customer_payment_method( } } + let merchant_connector_accounts = if filtered_saved_payment_methods_ctx.iter().any( + |(_pm_list_context, _parent_payment_method_token, pm)| { + pm.connector_mandate_details.is_some() + }, + ) { + db.find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, + merchant_account.get_id(), + true, + &key_store, + ) + .await + .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)? + } else { + Vec::new() + }; + let pm_list_futures = filtered_saved_payment_methods_ctx .into_iter() .map(|ctx| { @@ -1545,6 +1562,7 @@ pub async fn list_customer_payment_method( state, &key_store, merchant_account, + merchant_connector_accounts.clone(), ctx, &customer, payments_info.as_ref(), @@ -1585,7 +1603,8 @@ async fn generate_saved_pm_response( state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, - pm_list_context: ( + merchant_connector_accounts: Vec, + (pm_list_context, parent_payment_method_token, pm): ( PaymentMethodListContext, Option, domain::PaymentMethod, @@ -1593,7 +1612,6 @@ async fn generate_saved_pm_response( customer: &domain::Customer, payment_info: Option<&pm_types::SavedPMLPaymentsInfo>, ) -> Result> { - let (pm_list_context, parent_payment_method_token, pm) = pm_list_context; let payment_method = pm.payment_method.get_required_value("payment_method")?; let bank_details = if payment_method == enums::PaymentMethod::BankDebit { @@ -1616,16 +1634,6 @@ async fn generate_saved_pm_response( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("unable to parse payment method billing address details")?; - let connector_mandate_details = pm - .connector_mandate_details - .clone() - .map(|val| { - val.parse_value::("PaymentsMandateReference") - }) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize to Payment Mandate Reference ")?; - let (is_connector_agnostic_mit_enabled, requires_cvv, off_session_payment_flag, profile_id) = payment_info .map(|pi| { @@ -1646,10 +1654,11 @@ async fn generate_saved_pm_response( profile_id, merchant_account.get_id(), is_connector_agnostic_mit_enabled, - connector_mandate_details, + pm.connector_mandate_details.as_ref(), pm.network_transaction_id.as_ref(), + merchant_connector_accounts, ) - .await?; + .await; let requires_cvv = if is_connector_agnostic_mit_enabled { requires_cvv @@ -1685,8 +1694,10 @@ async fn generate_saved_pm_response( requires_cvv: requires_cvv && !(off_session_payment_flag && pm.connector_mandate_details.is_some()), last_used_at: Some(pm.last_used_at), - is_default: customer.default_payment_method_id.is_some() - && customer.default_payment_method_id.as_deref() == Some(pm.get_id().get_string_repr()), + is_default: customer + .default_payment_method_id + .as_ref() + .is_some_and(|payment_method_id| payment_method_id == pm.get_id().get_string_repr()), billing: payment_method_billing, }; diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 144ebb9cf752..0ce1dc8f49a7 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4646,6 +4646,7 @@ pub async fn perform_surcharge_ops( todo!() } +#[cfg(feature = "v1")] pub async fn get_mca_status( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, @@ -4671,25 +4672,64 @@ pub async fn get_mca_status( .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { id: merchant_id.get_string_repr().to_owned(), })?; - let mut mca_ids = HashSet::new(); - let mcas = mcas + let mca_ids = mcas .into_iter() .filter(|mca| { - mca.disabled == Some(false) && profile_id.clone() == Some(mca.profile_id.clone()) + mca.disabled.is_some_and(|disabled| !disabled) + && profile_id + .as_ref() + .is_some_and(|profile_id| *profile_id == mca.profile_id) }) - .collect::>(); + .map(|mca| mca.get_id()) + .collect::>(); - for mca in mcas { - mca_ids.insert(mca.get_id()); - } - for mca_id in connector_mandate_details.keys() { - if mca_ids.contains(mca_id) { - return Ok(true); - } + if connector_mandate_details + .keys() + .any(|mca_id| mca_ids.contains(mca_id)) + { + return Ok(true); } } Ok(false) } + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[allow(clippy::too_many_arguments)] +pub async fn get_mca_status( + state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, + profile_id: Option, + merchant_id: &id_type::MerchantId, + is_connector_agnostic_mit_enabled: bool, + connector_mandate_details: Option<&payment_method::PaymentsMandateReference>, + network_transaction_id: Option<&String>, + merchant_connector_accounts: Vec, +) -> bool { + if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { + return true; + } + if let Some(connector_mandate_details) = connector_mandate_details { + let mca_ids = merchant_connector_accounts + .into_iter() + .filter(|mca| { + mca.disabled.is_some_and(|disabled| !disabled) + && profile_id + .as_ref() + .is_some_and(|profile_id| *profile_id == mca.profile_id) + }) + .map(|mca| mca.get_id()) + .collect::>(); + + if connector_mandate_details + .keys() + .any(|mca_id| mca_ids.contains(mca_id)) + { + return true; + } + } + false +} + pub async fn decrypt_generic_data( state: &routes::SessionState, data: Option, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 2b3ac5cb11cf..8a073599bc1c 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -32,7 +32,7 @@ use crate::{ self, api::{self, CardDetailFromLocker, CardDetailsPaymentMethod, PaymentMethodCreateExt}, domain, - storage::{self, enums as storage_enums}, + storage::enums as storage_enums, }, utils::{generate_id, OptionExt}, }; From bce98195addd8e996f8e86ad2d653fa8489b6682 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Thu, 24 Oct 2024 01:42:23 +0530 Subject: [PATCH 11/20] refactor(payment_methods): use `serialize_and_set_key_with_expiry()` when storing payment method token in Redis --- crates/router/src/routes/payment_methods.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 0dbddbef77bf..d94228f070f6 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -976,17 +976,13 @@ impl ParentPaymentMethodToken { token: PaymentTokenData, state: &SessionState, ) -> CustomResult<(), errors::ApiErrorResponse> { - let token_json_str = token - .encode_to_string_of_json() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to serialize hyperswitch token to json")?; let redis_conn = state .store .get_redis_conn() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get redis connection")?; redis_conn - .set_key_with_expiry(&self.key_for_token, token_json_str, fulfillment_time) + .serialize_and_set_key_with_expiry(&self.key_for_token, token, fulfillment_time) .await .change_context(errors::StorageError::KVError) .change_context(errors::ApiErrorResponse::InternalServerError) From 1735421598f51e5e87cbf5166c938a3a4f20a92b Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Thu, 24 Oct 2024 16:51:20 +0530 Subject: [PATCH 12/20] refactor(payment_methods): replace usages of `create_payment_method()` with a correct `apply_changeset()` implementation --- crates/diesel_models/src/payment_method.rs | 103 ++++++++++++++------ crates/router/src/db/payment_method.rs | 86 ++++++++-------- crates/router/src/routes/payment_methods.rs | 1 - 3 files changed, 119 insertions(+), 71 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index be15462ed503..4c15d01b3cf8 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -324,36 +324,50 @@ pub struct PaymentMethodUpdateInternal { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl PaymentMethodUpdateInternal { - pub fn create_payment_method(self, source: PaymentMethod) -> PaymentMethod { - let metadata = self.metadata; - - PaymentMethod { metadata, ..source } - } - pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod { let Self { - metadata, payment_method_data, last_used_at, network_transaction_id, status, + locker_id, + payment_method, connector_mandate_details, updated_by, - .. + payment_method_type, + last_modified, + network_token_requestor_reference_id, + network_token_locker_id, + network_token_payment_method_data, } = self; PaymentMethod { metadata: metadata.map_or(source.metadata, Some), - payment_method_data: payment_method_data.map_or(source.payment_method_data, Some), + customer_id: source.customer_id, + merchant_id: source.merchant_id, + created_at: source.created_at, + last_modified, + payment_method: payment_method.or(source.payment_method), + payment_method_type: payment_method_type.or(source.payment_method_type), + payment_method_data: payment_method_data.or(source.payment_method_data), + locker_id: locker_id.or(source.locker_id), last_used_at: last_used_at.unwrap_or(source.last_used_at), - network_transaction_id: network_transaction_id - .map_or(source.network_transaction_id, Some), - status: status.unwrap_or(source.status), connector_mandate_details: connector_mandate_details - .map_or(source.connector_mandate_details, Some), - updated_by: updated_by.map_or(source.updated_by, Some), - last_modified: common_utils::date_time::now(), - ..source + .or(source.connector_mandate_details), + customer_acceptance: source.customer_acceptance, + status: status.unwrap_or(source.status), + network_transaction_id: network_transaction_id.or(source.network_transaction_id), + client_secret: source.client_secret, + payment_method_billing_address: source.payment_method_billing_address, + updated_by: updated_by.or(source.updated_by), + locker_fingerprint_id: source.locker_fingerprint_id, + id: source.id, + version: source.version, + network_token_requestor_reference_id: network_token_requestor_reference_id + .or(source.network_token_requestor_reference_id), + network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id), + network_token_payment_method_data: network_token_payment_method_data + .or(source.network_token_payment_method_data), } } } @@ -387,12 +401,6 @@ pub struct PaymentMethodUpdateInternal { not(feature = "payment_methods_v2") ))] impl PaymentMethodUpdateInternal { - pub fn create_payment_method(self, source: PaymentMethod) -> PaymentMethod { - let metadata = self.metadata.map(Secret::new); - - PaymentMethod { metadata, ..source } - } - pub fn apply_changeset(self, source: PaymentMethod) -> PaymentMethod { let Self { metadata, @@ -400,23 +408,56 @@ impl PaymentMethodUpdateInternal { last_used_at, network_transaction_id, status, + locker_id, + network_token_requestor_reference_id, + payment_method, connector_mandate_details, updated_by, - .. + payment_method_type, + payment_method_issuer, + last_modified, + network_token_locker_id, + network_token_payment_method_data, } = self; PaymentMethod { + customer_id: source.customer_id, + merchant_id: source.merchant_id, + payment_method_id: source.payment_method_id, + accepted_currency: source.accepted_currency, + scheme: source.scheme, + token: source.token, + cardholder_name: source.cardholder_name, + issuer_name: source.issuer_name, + issuer_country: source.issuer_country, + payer_country: source.payer_country, + is_stored: source.is_stored, + swift_code: source.swift_code, + direct_debit_token: source.direct_debit_token, + created_at: source.created_at, + last_modified, + payment_method: payment_method.or(source.payment_method), + payment_method_type: payment_method_type.or(source.payment_method_type), + payment_method_issuer: payment_method_issuer.or(source.payment_method_issuer), + payment_method_issuer_code: source.payment_method_issuer_code, metadata: metadata.map_or(source.metadata, |v| Some(v.into())), - payment_method_data: payment_method_data.map_or(source.payment_method_data, Some), + payment_method_data: payment_method_data.or(source.payment_method_data), + locker_id: locker_id.or(source.locker_id), last_used_at: last_used_at.unwrap_or(source.last_used_at), - network_transaction_id: network_transaction_id - .map_or(source.network_transaction_id, Some), - status: status.unwrap_or(source.status), connector_mandate_details: connector_mandate_details - .map_or(source.connector_mandate_details, Some), - updated_by: updated_by.map_or(source.updated_by, Some), - last_modified: common_utils::date_time::now(), - ..source + .or(source.connector_mandate_details), + customer_acceptance: source.customer_acceptance, + status: status.unwrap_or(source.status), + network_transaction_id: network_transaction_id.or(source.network_transaction_id), + client_secret: source.client_secret, + payment_method_billing_address: source.payment_method_billing_address, + updated_by: updated_by.or(source.updated_by), + version: source.version, + network_token_requestor_reference_id: network_token_requestor_reference_id + .or(source.network_token_requestor_reference_id), + network_token_locker_id: network_token_locker_id.or(source.network_token_locker_id), + network_token_payment_method_data: network_token_payment_method_data + .or(source.network_token_payment_method_data), } } } diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index ac66ed707f13..28a3ae7ec095 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -1,4 +1,4 @@ -use common_utils::{id_type, types::keymanager::KeyManagerState}; +use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::payment_method::PaymentMethodUpdateInternal; use error_stack::ResultExt; use hyperswitch_domain_models::behaviour::{Conversion, ReverseConversion}; @@ -1524,34 +1524,38 @@ impl PaymentMethodInterface for MockDb { payment_method_update: storage_types::PaymentMethodUpdate, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { - let pm_update_res = self - .payment_methods + self.payment_methods .lock() .await .iter_mut() .find(|pm| pm.get_id() == payment_method.get_id()) - .map(|pm| { + .async_map(|pm| async { let payment_method_updated = - PaymentMethodUpdateInternal::from(payment_method_update) - .create_payment_method(pm.clone()); + PaymentMethodUpdateInternal::from(payment_method_update).apply_changeset( + Conversion::convert(payment_method) + .await + .change_context(errors::StorageError::EncryptionError)?, + ); + *pm = payment_method_updated.clone(); - payment_method_updated - }); - match pm_update_res { - Some(result) => Ok(result - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), + payment_method_updated + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? + .ok_or( + errors::StorageError::ValueNotFound( + "cannot find payment method to update".to_string(), ) - .await - .change_context(errors::StorageError::DecryptionError)?), - None => Err(errors::StorageError::ValueNotFound( - "cannot find payment method to update".to_string(), + .into(), ) - .into()), - } } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -1565,34 +1569,38 @@ impl PaymentMethodInterface for MockDb { status: Some(common_enums::PaymentMethodStatus::Inactive), }; - let pm_update_res = self - .payment_methods + self.payment_methods .lock() .await .iter_mut() .find(|pm| pm.get_id() == payment_method.get_id()) - .map(|pm| { + .async_map(|pm| async { let payment_method_updated = - PaymentMethodUpdateInternal::from(payment_method_update) - .create_payment_method(pm.clone()); + PaymentMethodUpdateInternal::from(payment_method_update).apply_changeset( + Conversion::convert(payment_method) + .await + .change_context(errors::StorageError::EncryptionError)?, + ); + *pm = payment_method_updated.clone(); - payment_method_updated - }); - match pm_update_res { - Some(result) => Ok(result - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), + payment_method_updated + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? + .ok_or( + errors::StorageError::ValueNotFound( + "cannot find payment method to update".to_string(), ) - .await - .change_context(errors::StorageError::DecryptionError)?), - None => Err(errors::StorageError::ValueNotFound( - "cannot find payment method to update".to_string(), + .into(), ) - .into()), - } } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index d94228f070f6..8e7ba15f97ce 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -29,7 +29,6 @@ use crate::{ domain, storage::payment_method::PaymentTokenData, }, - utils::Encode, }; #[cfg(all( any(feature = "v1", feature = "v2", feature = "olap", feature = "oltp"), From a22240092b426da6e9c715bc79b214eefe565a23 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Thu, 24 Oct 2024 17:09:14 +0530 Subject: [PATCH 13/20] refactor(payment_methods): drop column `metadata` in v2 code since it not actively being used in v1 There does not seem to be any useful information in this column in any of our hosted environments either. --- crates/api_models/src/payment_methods.rs | 8 ---- crates/diesel_models/src/payment_method.rs | 35 ----------------- crates/diesel_models/src/schema_v2.rs | 1 - .../src/payment_methods.rs | 4 -- crates/router/src/core/payment_methods.rs | 34 ---------------- .../router/src/core/payment_methods/cards.rs | 19 --------- .../src/core/payment_methods/transformers.rs | 1 - .../down.sql | 33 +++++++--------- .../up.sql | 39 +++++++------------ 9 files changed, 28 insertions(+), 146 deletions(-) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index d5255b5e8c5d..6dc79a6b1bf8 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -730,10 +730,6 @@ pub struct PaymentMethodResponse { #[schema(example = true)] pub recurring_enabled: bool, - /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object. - #[schema(value_type = Option, example = json!({ "city": "NY", "unit": "245" }))] - pub metadata: Option, - /// A timestamp (ISO 8601 code) that determines when the customer was created #[schema(value_type = Option, example = "2023-01-18T11:04:09.922Z")] #[serde(default, with = "common_utils::custom_serde::iso8601::option")] @@ -1705,10 +1701,6 @@ pub struct CustomerPaymentMethod { #[schema(example = json!({"mask": "0000"}))] pub bank: Option, - /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object. - #[schema(value_type = Option,example = json!({ "city": "NY", "unit": "245" }))] - pub metadata: Option, - /// A timestamp (ISO 8601 code) that determines when the customer was created #[schema(value_type = Option,example = "2023-01-18T11:04:09.922Z")] #[serde(default, with = "common_utils::custom_serde::iso8601::option")] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 4c15d01b3cf8..7ccb200a7f2b 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -76,7 +76,6 @@ pub struct PaymentMethod { pub last_modified: PrimitiveDateTime, pub payment_method: Option, pub payment_method_type: Option, - pub metadata: Option, pub payment_method_data: Option, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, @@ -165,7 +164,6 @@ pub struct PaymentMethodNew { pub payment_method_type: Option, pub created_at: PrimitiveDateTime, pub last_modified: PrimitiveDateTime, - pub metadata: Option, pub payment_method_data: Option, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, @@ -255,10 +253,6 @@ pub enum PaymentMethodUpdate { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, Serialize, Deserialize)] pub enum PaymentMethodUpdate { - MetadataUpdateAndLastUsed { - metadata: Option, - last_used_at: PrimitiveDateTime, - }, UpdatePaymentMethodDataAndLastUsed { payment_method_data: Option, last_used_at: PrimitiveDateTime, @@ -306,7 +300,6 @@ impl PaymentMethodUpdate { #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = payment_methods)] pub struct PaymentMethodUpdateInternal { - metadata: Option, payment_method_data: Option, last_used_at: Option, network_transaction_id: Option, @@ -342,7 +335,6 @@ impl PaymentMethodUpdateInternal { } = self; PaymentMethod { - metadata: metadata.map_or(source.metadata, Some), customer_id: source.customer_id, merchant_id: source.merchant_id, created_at: source.created_at, @@ -636,29 +628,9 @@ impl From for PaymentMethodUpdateInternal { impl From for PaymentMethodUpdateInternal { fn from(payment_method_update: PaymentMethodUpdate) -> Self { match payment_method_update { - PaymentMethodUpdate::MetadataUpdateAndLastUsed { - metadata, - last_used_at, - } => Self { - metadata, - payment_method_data: None, - last_used_at: Some(last_used_at), - network_transaction_id: None, - status: None, - locker_id: None, - payment_method: None, - connector_mandate_details: None, - updated_by: None, - payment_method_type: None, - last_modified: common_utils::date_time::now(), - network_token_locker_id: None, - network_token_requestor_reference_id: None, - network_token_payment_method_data: None, - }, PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data, } => Self { - metadata: None, payment_method_data, last_used_at: None, network_transaction_id: None, @@ -674,7 +646,6 @@ impl From for PaymentMethodUpdateInternal { network_token_payment_method_data: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { - metadata: None, payment_method_data: None, last_used_at: Some(last_used_at), network_transaction_id: None, @@ -693,7 +664,6 @@ impl From for PaymentMethodUpdateInternal { payment_method_data, last_used_at, } => Self { - metadata: None, payment_method_data, last_used_at: Some(last_used_at), network_transaction_id: None, @@ -712,7 +682,6 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id, status, } => Self { - metadata: None, payment_method_data: None, last_used_at: None, network_transaction_id, @@ -728,7 +697,6 @@ impl From for PaymentMethodUpdateInternal { network_token_payment_method_data: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { - metadata: None, payment_method_data: None, last_used_at: None, network_transaction_id: None, @@ -753,7 +721,6 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id, network_token_payment_method_data, } => Self { - metadata: None, payment_method_data, last_used_at: None, network_transaction_id: None, @@ -771,7 +738,6 @@ impl From for PaymentMethodUpdateInternal { PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, } => Self { - metadata: None, payment_method_data: None, last_used_at: None, status: None, @@ -852,7 +818,6 @@ impl From<&PaymentMethodNew> for PaymentMethod { last_modified: payment_method_new.last_modified, payment_method: payment_method_new.payment_method, payment_method_type: payment_method_new.payment_method_type, - metadata: payment_method_new.metadata.clone(), payment_method_data: payment_method_new.payment_method_data.clone(), last_used_at: payment_method_new.last_used_at, connector_mandate_details: payment_method_new.connector_mandate_details.clone(), diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index aa6f8e074b69..f7250412ef6c 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -941,7 +941,6 @@ diesel::table! { payment_method -> Nullable, #[max_length = 64] payment_method_type -> Nullable, - metadata -> Nullable, payment_method_data -> Nullable, #[max_length = 64] locker_id -> Nullable, diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 8d9247e6e3c1..4ff246d3760b 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -79,7 +79,6 @@ pub struct PaymentMethod { pub last_modified: PrimitiveDateTime, pub payment_method: Option, pub payment_method_type: Option, - pub metadata: Option, pub payment_method_data: OptionalEncryptableValue, pub locker_id: Option, pub last_used_at: PrimitiveDateTime, @@ -314,7 +313,6 @@ impl super::behaviour::Conversion for PaymentMethod { last_modified: self.last_modified, payment_method: self.payment_method, payment_method_type: self.payment_method_type, - metadata: self.metadata, payment_method_data: self.payment_method_data.map(|val| val.into()), locker_id: self.locker_id.map(|id| id.get_string_repr().clone()), last_used_at: self.last_used_at, @@ -355,7 +353,6 @@ impl super::behaviour::Conversion for PaymentMethod { last_modified: item.last_modified, payment_method: item.payment_method, payment_method_type: item.payment_method_type, - metadata: item.metadata, payment_method_data: item .payment_method_data .async_lift(|inner| async { @@ -427,7 +424,6 @@ impl super::behaviour::Conversion for PaymentMethod { last_modified: self.last_modified, payment_method: self.payment_method, payment_method_type: self.payment_method_type, - metadata: self.metadata, payment_method_data: self.payment_method_data.map(|val| val.into()), locker_id: self.locker_id.map(|id| id.get_string_repr().clone()), last_used_at: self.last_used_at, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index a1a59cfdf1a8..9bb387eda278 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1130,7 +1130,6 @@ pub async fn create_payment_method_in_db( payment_method_id: id_type::GlobalPaymentMethodId, locker_id: Option, merchant_id: &id_type::MerchantId, - pm_metadata: Option, customer_acceptance: Option, payment_method_data: crypto::OptionalEncryptableValue, key_store: &domain::MerchantKeyStore, @@ -1156,7 +1155,6 @@ pub async fn create_payment_method_in_db( locker_id, payment_method: Some(req.payment_method), payment_method_type: Some(req.payment_method_type), - metadata: pm_metadata, payment_method_data, connector_mandate_details, customer_acceptance, @@ -1211,7 +1209,6 @@ pub async fn create_payment_method_for_intent( locker_id: None, payment_method: None, payment_method_type: None, - metadata, payment_method_data: None, connector_mandate_details: None, customer_acceptance: None, @@ -1686,7 +1683,6 @@ async fn generate_saved_pm_response( payment_method, payment_method_type: pm.payment_method_type, payment_method_data: pmd, - metadata: pm.metadata.clone(), recurring_enabled: mca_enabled, created: Some(pm.created_at), bank: bank_details, @@ -1752,7 +1748,6 @@ pub async fn retrieve_payment_method( payment_method_id: payment_method.id.get_string_repr().to_string(), payment_method: payment_method.payment_method, payment_method_type: payment_method.payment_method_type, - metadata: payment_method.metadata.clone(), created: Some(payment_method.created_at), recurring_enabled: false, last_used_at: Some(payment_method.last_used_at), @@ -1981,35 +1976,6 @@ impl pm_types::SavedPMLPaymentsInfo { .insert(intent_fulfillment_time, hyperswitch_token_data, state) .await?; - if let Some(metadata) = pma.metadata.clone() { - let pm_metadata_vec: pm_transforms::PaymentMethodMetadata = metadata - .parse_value("PaymentMethodMetadata") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Failed to deserialize metadata to PaymentmethodMetadata struct", - )?; - - let redis_conn = state - .store - .get_redis_conn() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get redis connection")?; - - for pm_metadata in pm_metadata_vec.payment_method_tokenization { - let key = format!( - "pm_token_{}_{}_{}", - token, pma.payment_method, pm_metadata.0 - ); - - redis_conn - .set_key_with_expiry(&key, pm_metadata.1, intent_fulfillment_time) - .await - .change_context(errors::StorageError::KVError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to add data in redis")?; - } - } - Ok(()) } } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 0ce1dc8f49a7..ab723589046e 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2213,25 +2213,6 @@ pub async fn update_payment_method_metadata_and_last_used( Ok(()) } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub async fn update_payment_method_metadata_and_last_used( - state: &routes::SessionState, - key_store: &domain::MerchantKeyStore, - db: &dyn db::StorageInterface, - pm: domain::PaymentMethod, - pm_metadata: Option, - storage_scheme: MerchantStorageScheme, -) -> errors::CustomResult<(), errors::VaultError> { - let pm_update = payment_method::PaymentMethodUpdate::MetadataUpdateAndLastUsed { - metadata: pm_metadata.map(Secret::new), - last_used_at: common_utils::date_time::now(), - }; - db.update_payment_method(&(state.into()), key_store, pm, pm_update, storage_scheme) - .await - .change_context(errors::VaultError::UpdateInPaymentMethodDataTableFailed)?; - Ok(()) -} - pub async fn update_payment_method_and_last_used( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 78770c34579c..51a109e8a909 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -570,7 +570,6 @@ pub fn generate_payment_method_response( payment_method_id: pm.id.get_string_repr().to_owned(), payment_method: pm.payment_method, payment_method_type: pm.payment_method_type, - metadata: pm.metadata.clone(), created: Some(pm.created_at), recurring_enabled: false, last_used_at: Some(pm.last_used_at), diff --git a/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/down.sql b/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/down.sql index cde77d754f88..ce485048f4ef 100644 --- a/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/down.sql +++ b/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/down.sql @@ -1,23 +1,16 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS accepted_currency "Currency"[]; - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS scheme VARCHAR(32); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS token VARCHAR(128); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS cardholder_name VARCHAR(255); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS issuer_name VARCHAR(64); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS issuer_country VARCHAR(64); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS is_stored BOOLEAN; - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS direct_debit_token VARCHAR(128); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS swift_code VARCHAR(32); - -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS payment_method_issuer VARCHAR(128); +ALTER TABLE payment_methods + ADD COLUMN IF NOT EXISTS accepted_currency "Currency" [ ], + ADD COLUMN IF NOT EXISTS scheme VARCHAR(32), + ADD COLUMN IF NOT EXISTS token VARCHAR(128), + ADD COLUMN IF NOT EXISTS cardholder_name VARCHAR(255), + ADD COLUMN IF NOT EXISTS issuer_name VARCHAR(64), + ADD COLUMN IF NOT EXISTS issuer_country VARCHAR(64), + ADD COLUMN IF NOT EXISTS is_stored BOOLEAN, + ADD COLUMN IF NOT EXISTS direct_debit_token VARCHAR(128), + ADD COLUMN IF NOT EXISTS swift_code VARCHAR(32), + ADD COLUMN IF NOT EXISTS payment_method_issuer VARCHAR(128), + ADD COLUMN IF NOT EXISTS metadata JSON; CREATE TYPE "PaymentMethodIssuerCode" AS ENUM ( 'jp_hdfc', @@ -41,4 +34,4 @@ UPDATE payment_methods SET payment_method_id = id; ALTER TABLE payment_methods DROP CONSTRAINT IF EXISTS payment_methods_pkey; ALTER TABLE payment_methods ADD CONSTRAINT payment_methods_pkey PRIMARY KEY (payment_method_id); ALTER TABLE payment_methods DROP COLUMN IF EXISTS id; -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS id SERIAL; \ No newline at end of file +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS id SERIAL; diff --git a/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/up.sql b/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/up.sql index 151cf068cff2..23a274b68444 100644 --- a/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/up.sql +++ b/v2_migrations/2024-08-23-112510_payment_methods_v2_db_changes/up.sql @@ -1,27 +1,18 @@ -- Your SQL goes here -ALTER TABLE payment_methods DROP COLUMN IF EXISTS accepted_currency; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS scheme; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS token; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS cardholder_name; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS issuer_name; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS issuer_country; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS payer_country; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS is_stored; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS direct_debit_token; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS swift_code; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS payment_method_issuer; - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS payment_method_issuer_code; +ALTER TABLE payment_methods + DROP COLUMN IF EXISTS accepted_currency, + DROP COLUMN IF EXISTS scheme, + DROP COLUMN IF EXISTS token, + DROP COLUMN IF EXISTS cardholder_name, + DROP COLUMN IF EXISTS issuer_name, + DROP COLUMN IF EXISTS issuer_country, + DROP COLUMN IF EXISTS payer_country, + DROP COLUMN IF EXISTS is_stored, + DROP COLUMN IF EXISTS direct_debit_token, + DROP COLUMN IF EXISTS swift_code, + DROP COLUMN IF EXISTS payment_method_issuer, + DROP COLUMN IF EXISTS payment_method_issuer_code, + DROP COLUMN IF EXISTS metadata; DROP TYPE IF EXISTS "PaymentMethodIssuerCode"; @@ -32,4 +23,4 @@ ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS id VARCHAR(64); UPDATE payment_methods SET id = payment_method_id; ALTER TABLE payment_methods DROP CONSTRAINT IF EXISTS payment_methods_pkey; ALTER TABLE payment_methods ADD CONSTRAINT payment_methods_pkey PRIMARY KEY (id); -ALTER TABLE payment_methods DROP COLUMN IF EXISTS payment_method_id; \ No newline at end of file +ALTER TABLE payment_methods DROP COLUMN IF EXISTS payment_method_id; From c08643028ebc83b3fec88cbb9daafe81ddc29e80 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Fri, 25 Oct 2024 00:03:54 +0530 Subject: [PATCH 14/20] refactor(payment_methods): use `collect()` to collect `Vec` to `Result` --- crates/router/src/core/payment_methods.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 9bb387eda278..baf31fd982ee 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1567,13 +1567,11 @@ pub async fn list_customer_payment_method( }) .collect::>(); - let final_result = futures::future::join_all(pm_list_futures).await; - - let mut customer_pms = Vec::new(); - for result in final_result.into_iter() { - let pma = result.attach_printable("saved pm list failed")?; - customer_pms.push(pma); - } + let customer_pms = futures::future::join_all(pm_list_futures) + .await + .into_iter() + .collect::, _>>() + .attach_printable("Failed to obtain customer payment methods")?; let mut response = api::CustomerPaymentMethodsListResponse { customer_payment_methods: customer_pms, From cc4d1161341da63d9eee010554ec0c1802bc9e50 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Sun, 3 Nov 2024 16:01:58 +0530 Subject: [PATCH 15/20] refactor(payment_methods): use `match` instead of `if let Some` to match on `Option` --- .../router/src/core/payment_methods/cards.rs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index ab723589046e..9d6601cf570c 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4689,26 +4689,25 @@ pub async fn get_mca_status( if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { return true; } - if let Some(connector_mandate_details) = connector_mandate_details { - let mca_ids = merchant_connector_accounts - .into_iter() - .filter(|mca| { - mca.disabled.is_some_and(|disabled| !disabled) - && profile_id - .as_ref() - .is_some_and(|profile_id| *profile_id == mca.profile_id) - }) - .map(|mca| mca.get_id()) - .collect::>(); + match connector_mandate_details { + Some(connector_mandate_details) => { + let mca_ids = merchant_connector_accounts + .into_iter() + .filter(|mca| { + mca.disabled.is_some_and(|disabled| !disabled) + && profile_id + .as_ref() + .is_some_and(|profile_id| *profile_id == mca.profile_id) + }) + .map(|mca| mca.get_id()) + .collect::>(); - if connector_mandate_details - .keys() - .any(|mca_id| mca_ids.contains(mca_id)) - { - return true; + connector_mandate_details + .keys() + .any(|mca_id| mca_ids.contains(mca_id)) } + None => false, } - false } pub async fn decrypt_generic_data( From dd5fbc386a81835a4e69f4105488a23d5762ba94 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Mon, 4 Nov 2024 19:31:07 +0530 Subject: [PATCH 16/20] refactor(payment_methods): pass profile from middleware for `list_customer_payment_method()` --- crates/router/src/core/payment_methods.rs | 39 +++++----------- crates/router/src/routes/customers.rs | 2 +- crates/router/src/routes/payment_methods.rs | 11 +++-- crates/router/src/services/authentication.rs | 47 ++++++++++++++++++++ crates/router/src/types/payment_methods.rs | 2 +- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index baf31fd982ee..f8e622570cb1 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1397,6 +1397,7 @@ async fn get_pm_list_context( pub async fn list_customer_payment_method_util( state: SessionState, merchant_account: domain::MerchantAccount, + profile: domain::Profile, key_store: domain::MerchantKeyStore, req: Option, customer_id: Option, @@ -1428,6 +1429,7 @@ pub async fn list_customer_payment_method_util( Box::pin(list_customer_payment_method( &state, &merchant_account, + profile, key_store, payment_intent, &cust, @@ -1446,6 +1448,7 @@ pub async fn list_customer_payment_method_util( Ok(resp) } +#[allow(clippy::too_many_arguments)] #[cfg(all( feature = "v2", feature = "payment_methods_v2", @@ -1454,6 +1457,7 @@ pub async fn list_customer_payment_method_util( pub async fn list_customer_payment_method( state: &SessionState, merchant_account: &domain::MerchantAccount, + profile: domain::Profile, key_store: domain::MerchantKeyStore, payment_intent: Option, customer_id: &id_type::CustomerId, @@ -1463,19 +1467,6 @@ pub async fn list_customer_payment_method( let db = &*state.store; let key_manager_state = &(state).into(); - let profile_id = payment_intent - .as_ref() - .map(|payment_intent| &payment_intent.profile_id); - - let profile = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - &key_store, - profile_id, - merchant_account.get_id(), - ) - .await?; - let customer = db .find_customer_by_merchant_reference_id_merchant_id( key_manager_state, @@ -1584,7 +1575,7 @@ pub async fn list_customer_payment_method( state, merchant_account, key_store, - payments_info.and_then(|pi| pi.profile), + payments_info.map(|pi| pi.profile), &mut response, )) .await?; @@ -1636,9 +1627,7 @@ async fn generate_saved_pm_response( pi.is_connector_agnostic_mit_enabled, pi.collect_cvv_during_payment, pi.off_session_payment_flag, - pi.profile - .as_ref() - .map(|profile| profile.get_id().to_owned()), + Some(pi.profile.get_id().to_owned()), ) }) .unwrap_or((false, false, false, Default::default())); @@ -1921,25 +1910,20 @@ impl pm_types::SavedPMLPaymentsInfo { pub async fn form_payments_info( payment_intent: PaymentIntent, merchant_account: &domain::MerchantAccount, - profile: Option, + profile: domain::Profile, db: &dyn StorageInterface, key_manager_state: &util_types::keymanager::KeyManagerState, key_store: &domain::MerchantKeyStore, ) -> RouterResult { - let collect_cvv_during_payment = profile - .as_ref() - .map(|profile| profile.should_collect_cvv_during_payment) - .unwrap_or(true); + let collect_cvv_during_payment = profile.should_collect_cvv_during_payment; let off_session_payment_flag = matches!( payment_intent.setup_future_usage, common_enums::FutureUsage::OffSession ); - let is_connector_agnostic_mit_enabled = profile - .as_ref() - .and_then(|profile| profile.is_connector_agnostic_mit_enabled) - .unwrap_or(false); + let is_connector_agnostic_mit_enabled = + profile.is_connector_agnostic_mit_enabled.unwrap_or(false); Ok(Self { payment_intent, @@ -1966,8 +1950,7 @@ impl pm_types::SavedPMLPaymentsInfo { let intent_fulfillment_time = self .profile - .as_ref() - .and_then(|b_profile| b_profile.get_order_fulfillment_time()) + .get_order_fulfillment_time() .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME); pm_routes::ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method)) diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index 5ff155966a0f..004d4cf417aa 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -109,7 +109,7 @@ pub async fn customers_retrieve( state, &req, payload, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationDataV2, req, _| { retrieve_customer(state, auth.merchant_account, auth.key_store, req) }, &*auth, diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 28748bfb43a3..4df4a1ada087 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -160,7 +160,7 @@ pub async fn confirm_payment_method_intent_api( state, &req, inner_payload, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationDataV2, req, _| { let pm_id = pm_id.clone(); async move { Box::pin(payment_method_intent_confirm( @@ -415,6 +415,7 @@ pub async fn save_payment_method_api( .await } +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsList))] pub async fn list_payment_method_api( state: web::Data, @@ -552,10 +553,11 @@ pub async fn list_customer_payment_method_for_payment( state, &req, payload, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationDataV2, req, _| { list_customer_payment_method_util( state, auth.merchant_account, + auth.profile, auth.key_store, Some(req), None, @@ -617,10 +619,11 @@ pub async fn list_customer_payment_method_api( state, &req, payload, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationDataV2, req, _| { list_customer_payment_method_util( state, auth.merchant_account, + auth.profile, auth.key_store, Some(req), Some(customer_id.clone()), @@ -897,6 +900,7 @@ pub async fn list_countries_currencies_for_connector_payment_method( .await } +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::DefaultPaymentMethodsSet))] pub async fn default_payment_method_set_api( state: web::Data, @@ -933,6 +937,7 @@ pub async fn default_payment_method_set_api( )) .await } + #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index a3cc54de34f9..3e5d92926a5e 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -2156,6 +2156,7 @@ pub fn get_auth_type_and_flow( Ok((Box::new(HeaderAuth(ApiKeyAuth)), api::AuthFlow::Merchant)) } +#[cfg(feature = "v1")] pub fn check_client_secret_and_get_auth( headers: &HeaderMap, payload: &impl ClientSecretFetch, @@ -2191,6 +2192,44 @@ where Ok((Box::new(HeaderAuth(ApiKeyAuth)), api::AuthFlow::Merchant)) } +#[cfg(feature = "v2")] +pub fn check_client_secret_and_get_auth( + headers: &HeaderMap, + payload: &impl ClientSecretFetch, +) -> RouterResult<( + Box>, + api::AuthFlow, +)> +where + T: SessionStateInfo + Sync + Send, + ApiKeyAuth: AuthenticateAndFetch, + PublishableKeyAuth: AuthenticateAndFetch, +{ + let api_key = get_api_key(headers)?; + if api_key.starts_with("pk_") { + payload + .get_client_secret() + .check_value_present("client_secret") + .map_err(|_| errors::ApiErrorResponse::MissingRequiredField { + field_name: "client_secret", + })?; + return Ok(( + Box::new(HeaderAuth(PublishableKeyAuth)), + api::AuthFlow::Client, + )); + } + + if payload.get_client_secret().is_some() { + return Err(errors::ApiErrorResponse::InvalidRequestData { + message: "client_secret is not a valid parameter".to_owned(), + } + .into()); + } + + Ok((Box::new(HeaderAuth(ApiKeyAuth)), api::AuthFlow::Merchant)) +} + +#[cfg(feature = "v1")] pub async fn get_ephemeral_or_other_auth( headers: &HeaderMap, is_merchant_flow: bool, @@ -2223,6 +2262,7 @@ where } } +#[cfg(feature = "v1")] pub fn is_ephemeral_auth( headers: &HeaderMap, ) -> RouterResult>> { @@ -2235,6 +2275,13 @@ pub fn is_ephemeral_auth( } } +#[cfg(feature = "v2")] +pub fn is_ephemeral_auth( + headers: &HeaderMap, +) -> RouterResult>> { + todo!() +} + pub fn is_jwt_auth(headers: &HeaderMap) -> bool { headers.get(headers::AUTHORIZATION).is_some() || get_cookie_from_header(headers) diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 0c586c94d3f6..1a6b9053dcb9 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -139,7 +139,7 @@ impl PaymentMethodClientSecret { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, - pub profile: Option, + pub profile: domain::Profile, pub collect_cvv_during_payment: bool, pub off_session_payment_flag: bool, pub is_connector_agnostic_mit_enabled: bool, From 48dbafa14162eabf920b58e904982ef23ce2074c Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Mon, 4 Nov 2024 23:03:43 +0530 Subject: [PATCH 17/20] refactor(payment_methods): extract logic to check if merchant connector ID is in `connector_mandate_details` to a domain model method --- .../src/merchant_connector_account.rs | 29 ++++++++++++ crates/router/src/core/payment_methods.rs | 6 ++- .../router/src/core/payment_methods/cards.rs | 45 +++++-------------- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 0d730be203b1..ed2388488e27 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -511,3 +511,32 @@ impl From for MerchantConnectorAccountUpdateInte } } } + +#[derive(Debug)] +pub struct MerchantConnectorAccounts(Vec); + +impl MerchantConnectorAccounts { + pub fn new(merchant_connector_accounts: Vec) -> Self { + Self(merchant_connector_accounts) + } + + pub fn is_merchant_connector_account_id_in_connector_mandate_details( + &self, + profile_id: Option<&id_type::ProfileId>, + connector_mandate_details: &diesel_models::PaymentsMandateReference, + ) -> bool { + let mca_ids = self + .0 + .iter() + .filter(|mca| { + mca.disabled.is_some_and(|disabled| !disabled) + && profile_id.is_some_and(|profile_id| *profile_id == mca.profile_id) + }) + .map(|mca| mca.get_id()) + .collect::>(); + + connector_mandate_details + .keys() + .any(|mca_id| mca_ids.contains(mca_id)) + } +} diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index f8e622570cb1..a765c1feaa96 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1542,6 +1542,8 @@ pub async fn list_customer_payment_method( } else { Vec::new() }; + let merchant_connector_accounts = + domain::MerchantConnectorAccounts::new(merchant_connector_accounts); let pm_list_futures = filtered_saved_payment_methods_ctx .into_iter() @@ -1550,7 +1552,7 @@ pub async fn list_customer_payment_method( state, &key_store, merchant_account, - merchant_connector_accounts.clone(), + &merchant_connector_accounts, ctx, &customer, payments_info.as_ref(), @@ -1589,7 +1591,7 @@ async fn generate_saved_pm_response( state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, - merchant_connector_accounts: Vec, + merchant_connector_accounts: &domain::MerchantConnectorAccounts, (pm_list_context, parent_payment_method_token, pm): ( PaymentMethodListContext, Option, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 7b024923b256..a798a2deaf97 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4653,23 +4653,13 @@ pub async fn get_mca_status( .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { id: merchant_id.get_string_repr().to_owned(), })?; - let mca_ids = mcas - .into_iter() - .filter(|mca| { - mca.disabled.is_some_and(|disabled| !disabled) - && profile_id - .as_ref() - .is_some_and(|profile_id| *profile_id == mca.profile_id) - }) - .map(|mca| mca.get_id()) - .collect::>(); + let merchant_connector_accounts = domain::MerchantConnectorAccounts::new(mcas); - if connector_mandate_details - .keys() - .any(|mca_id| mca_ids.contains(mca_id)) - { - return Ok(true); - } + return Ok(merchant_connector_accounts + .is_merchant_connector_account_id_in_connector_mandate_details( + profile_id.as_ref(), + &connector_mandate_details, + )); } Ok(false) } @@ -4684,28 +4674,17 @@ pub async fn get_mca_status( is_connector_agnostic_mit_enabled: bool, connector_mandate_details: Option<&payment_method::PaymentsMandateReference>, network_transaction_id: Option<&String>, - merchant_connector_accounts: Vec, + merchant_connector_accounts: &domain::MerchantConnectorAccounts, ) -> bool { if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { return true; } match connector_mandate_details { - Some(connector_mandate_details) => { - let mca_ids = merchant_connector_accounts - .into_iter() - .filter(|mca| { - mca.disabled.is_some_and(|disabled| !disabled) - && profile_id - .as_ref() - .is_some_and(|profile_id| *profile_id == mca.profile_id) - }) - .map(|mca| mca.get_id()) - .collect::>(); - - connector_mandate_details - .keys() - .any(|mca_id| mca_ids.contains(mca_id)) - } + Some(connector_mandate_details) => merchant_connector_accounts + .is_merchant_connector_account_id_in_connector_mandate_details( + profile_id.as_ref(), + connector_mandate_details, + ), None => false, } } From 9178c6ff2f613963baa3e69993555ce64152a24e Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Tue, 5 Nov 2024 16:43:26 +0530 Subject: [PATCH 18/20] refactor: wrap the `mandate_metadata` field within a `Secret` --- crates/api_models/src/payments.rs | 8 ++++---- crates/diesel_models/src/payment_attempt.rs | 2 +- crates/diesel_models/src/payment_method.rs | 2 +- .../src/connectors/deutschebank/transformers.rs | 6 ++++-- crates/hyperswitch_domain_models/src/router_data.rs | 2 +- .../src/router_response_types.rs | 2 +- .../src/core/payments/operations/payment_response.rs | 2 +- crates/router/src/core/payments/tokenization.rs | 4 ++-- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 8fd3b5d25dc6..0ab86c8e35ce 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1318,7 +1318,7 @@ pub struct ConnectorMandateReferenceId { connector_mandate_id: Option, payment_method_id: Option, update_history: Option>, - mandate_metadata: Option, + mandate_metadata: Option, connector_mandate_request_reference_id: Option, } @@ -1327,7 +1327,7 @@ impl ConnectorMandateReferenceId { connector_mandate_id: Option, payment_method_id: Option, update_history: Option>, - mandate_metadata: Option, + mandate_metadata: Option, connector_mandate_request_reference_id: Option, ) -> Self { Self { @@ -1345,7 +1345,7 @@ impl ConnectorMandateReferenceId { pub fn get_payment_method_id(&self) -> Option { self.payment_method_id.clone() } - pub fn get_mandate_metadata(&self) -> Option { + pub fn get_mandate_metadata(&self) -> Option { self.mandate_metadata.clone() } pub fn get_connector_mandate_request_reference_id(&self) -> Option { @@ -1357,7 +1357,7 @@ impl ConnectorMandateReferenceId { connector_mandate_id: Option, payment_method_id: Option, update_history: Option>, - mandate_metadata: Option, + mandate_metadata: Option, connector_mandate_request_reference_id: Option, ) { self.connector_mandate_id = connector_mandate_id.or(self.connector_mandate_id.clone()); diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index f8654d3cd963..f08847651e76 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -20,7 +20,7 @@ common_utils::impl_to_sql_from_sql_json!(ConnectorMandateReferenceId); pub struct ConnectorMandateReferenceId { pub connector_mandate_id: Option, pub payment_method_id: Option, - pub mandate_metadata: Option, + pub mandate_metadata: Option, pub connector_mandate_request_reference_id: Option, } diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 330fd1d26bf9..9ca88267ae65 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -849,7 +849,7 @@ pub struct PaymentsMandateReferenceRecord { pub payment_method_type: Option, pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, - pub mandate_metadata: Option, + pub mandate_metadata: Option, pub connector_mandate_status: Option, pub connector_mandate_request_reference_id: Option, } diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs index 9dd842768f19..328940d83aee 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs @@ -310,7 +310,8 @@ impl Box::new(Some(MandateReference { connector_mandate_id: item.response.mandate_id, payment_method_id: None, - mandate_metadata: Some(serde_json::json!(DeutschebankMandateMetadata { + mandate_metadata: Some(Secret::new( + serde_json::json!(DeutschebankMandateMetadata { account_holder: item.data.get_billing_address()?.get_full_name()?, iban: match item.data.request.payment_method_data.clone() { PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { @@ -324,7 +325,8 @@ impl }?, reference: Secret::from(reference.clone()), signed_on, - })), + }), + )), connector_mandate_request_reference_id: None, })) } else { diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index 186a4f012629..96a2a57a4bfd 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -312,7 +312,7 @@ pub struct RecurringMandatePaymentData { pub payment_method_type: Option, //required for making recurring payment using saved payment method through stripe pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, - pub mandate_metadata: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone)] diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs index 6356301c7264..e6af5c5eb3bc 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -82,7 +82,7 @@ pub struct TaxCalculationResponseData { pub struct MandateReference { pub connector_mandate_id: Option, pub payment_method_id: Option, - pub mandate_metadata: Option, + pub mandate_metadata: Option, pub connector_mandate_request_reference_id: Option, } diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index bae760253693..412b4ecd983d 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2335,7 +2335,7 @@ impl PostUpdateTracker, types::PaymentsAuthor #[cfg(feature = "v1")] fn update_connector_mandate_details_for_the_flow( connector_mandate_id: Option, - mandate_metadata: Option, + mandate_metadata: Option>, connector_mandate_request_reference_id: Option, payment_data: &mut PaymentData, ) -> RouterResult<()> { diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 47f68083e872..6f01af96bb8e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -1149,7 +1149,7 @@ pub fn add_connector_mandate_details_in_payment_method( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, - mandate_metadata: Option, + mandate_metadata: Option>, connector_mandate_request_reference_id: Option, ) -> Option { let mut mandate_details = HashMap::new(); @@ -1182,7 +1182,7 @@ pub fn update_connector_mandate_details( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, - mandate_metadata: Option, + mandate_metadata: Option>, connector_mandate_request_reference_id: Option, ) -> RouterResult> { let mandate_reference = match mandate_details { From 19998e491596e3a180c25eee983bab5a4aededbf Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Tue, 5 Nov 2024 16:59:59 +0530 Subject: [PATCH 19/20] chore: update `Cargo.lock` --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 760d2865ec2d..b13fbbf148db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3961,9 +3961,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes 1.7.1", "futures-channel", From f2a07259938619d3bc8896e8d8ea3303853c9003 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde Date: Fri, 8 Nov 2024 17:11:05 +0530 Subject: [PATCH 20/20] fix: fix failing v2 builds --- crates/router/src/routes/customers.rs | 2 +- crates/router/src/routes/payment_methods.rs | 6 +++--- crates/router/src/services/authentication.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index 004d4cf417aa..5ff155966a0f 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -109,7 +109,7 @@ pub async fn customers_retrieve( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, req, _| { + |state, auth: auth::AuthenticationData, req, _| { retrieve_customer(state, auth.merchant_account, auth.key_store, req) }, &*auth, diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 2fcfdbdc937f..764e75df02a0 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -160,7 +160,7 @@ pub async fn confirm_payment_method_intent_api( state, &req, inner_payload, - |state, auth: auth::AuthenticationDataV2, req, _| { + |state, auth: auth::AuthenticationData, req, _| { let pm_id = pm_id.clone(); async move { Box::pin(payment_method_intent_confirm( @@ -553,7 +553,7 @@ pub async fn list_customer_payment_method_for_payment( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, req, _| { + |state, auth: auth::AuthenticationData, req, _| { list_customer_payment_method_util( state, auth.merchant_account, @@ -619,7 +619,7 @@ pub async fn list_customer_payment_method_api( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, req, _| { + |state, auth: auth::AuthenticationData, req, _| { list_customer_payment_method_util( state, auth.merchant_account, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 857f96eca5de..586252ff0ad7 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -2566,13 +2566,13 @@ pub fn check_client_secret_and_get_auth( headers: &HeaderMap, payload: &impl ClientSecretFetch, ) -> RouterResult<( - Box>, + Box>, api::AuthFlow, )> where T: SessionStateInfo + Sync + Send, - ApiKeyAuth: AuthenticateAndFetch, - PublishableKeyAuth: AuthenticateAndFetch, + ApiKeyAuth: AuthenticateAndFetch, + PublishableKeyAuth: AuthenticateAndFetch, { let api_key = get_api_key(headers)?; if api_key.starts_with("pk_") { @@ -2647,7 +2647,7 @@ pub fn is_ephemeral_auth( #[cfg(feature = "v2")] pub fn is_ephemeral_auth( headers: &HeaderMap, -) -> RouterResult>> { +) -> RouterResult>> { todo!() }