diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index b418e99d9c2a..345f09aad749 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -16624,7 +16624,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 @@ -16656,7 +16656,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": { @@ -16829,9 +16830,13 @@ }, "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 + }, + "should_collect_cvv_during_payment": { + "type": "boolean", + "description": "Indicates if CVV should be collected during payment or not." } } }, diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 6132bc2b5937..e88b5eb25d75 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -21369,7 +21369,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", @@ -21596,7 +21596,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 c9da386a11de..96335f34710c 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1980,8 +1980,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, @@ -2095,8 +2094,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, } @@ -2217,8 +2215,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, @@ -2337,10 +2334,12 @@ 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, + + /// Indicates if CVV should be collected during payment or not. + pub should_collect_cvv_during_payment: bool, } #[cfg(feature = "v1")] @@ -2455,7 +2454,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. @@ -2564,7 +2563,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, } diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 47460e73b713..0602f69d1584 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -732,10 +732,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")] @@ -1707,10 +1703,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/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2ace2b956e4a..3e95fb9f2315 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1321,7 +1321,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, } @@ -1330,7 +1330,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 { @@ -1348,7 +1348,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 { @@ -1360,7 +1360,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/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/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 3ad58e62dcf9..9ca88267ae65 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}; @@ -65,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, @@ -76,11 +76,10 @@ 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, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, @@ -156,9 +155,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, @@ -167,11 +164,10 @@ 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, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, @@ -257,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, @@ -289,7 +281,7 @@ pub enum PaymentMethodUpdate { network_token_payment_method_data: Option, }, ConnectorMandateDetailsUpdate { - connector_mandate_details: Option, + connector_mandate_details: Option, }, } @@ -308,14 +300,13 @@ 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, 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, @@ -326,36 +317,49 @@ 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), } } } @@ -389,12 +393,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, @@ -402,23 +400,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), } } } @@ -597,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, @@ -635,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, @@ -654,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, @@ -673,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, @@ -689,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, @@ -714,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, @@ -732,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, @@ -813,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(), @@ -838,3 +842,37 @@ 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, + pub connector_mandate_request_reference_id: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::AsExpression)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] +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/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 5651bf95dd9d..617d6cf182b9 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, @@ -940,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_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/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index 75cdd40024f8..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, }, @@ -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/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/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 445ad5ab8005..4ff246d3760b 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -79,11 +79,10 @@ 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, - pub connector_mandate_details: Option, + pub connector_mandate_details: Option, pub customer_acceptance: Option, pub status: storage_enums::PaymentMethodStatus, pub network_transaction_id: Option, @@ -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/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/admin.rs b/crates/router/src/core/admin.rs index bc4752b818f4..7efb55c9f489 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3663,6 +3663,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, @@ -4226,7 +4227,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( @@ -4280,7 +4281,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/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 87078dfa5aff..a765c1feaa96 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1130,11 +1130,10 @@ 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, - connector_mandate_details: Option, + connector_mandate_details: Option, status: Option, network_transaction_id: Option, storage_scheme: enums::MerchantStorageScheme, @@ -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, @@ -1400,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, @@ -1430,7 +1428,8 @@ 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, + profile, key_store, payment_intent, &cust, @@ -1449,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", @@ -1456,7 +1456,8 @@ 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, + profile: domain::Profile, key_store: domain::MerchantKeyStore, payment_intent: Option, customer_id: &id_type::CustomerId, @@ -1465,7 +1466,6 @@ 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 customer = db .find_customer_by_merchant_reference_id_merchant_id( @@ -1482,7 +1482,8 @@ pub async fn list_customer_payment_method( .async_map(|pi| { pm_types::SavedPMLPaymentsInfo::form_payments_info( pi, - &merchant_account, + merchant_account, + profile, db, key_manager_state, &key_store, @@ -1525,13 +1526,33 @@ 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 merchant_connector_accounts = + domain::MerchantConnectorAccounts::new(merchant_connector_accounts); + let pm_list_futures = filtered_saved_payment_methods_ctx .into_iter() .map(|ctx| { generate_saved_pm_response( state, &key_store, - &merchant_account, + merchant_account, + &merchant_connector_accounts, ctx, &customer, payments_info.as_ref(), @@ -1539,13 +1560,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, @@ -1558,7 +1577,7 @@ pub async fn list_customer_payment_method( state, merchant_account, key_store, - payments_info.and_then(|pi| pi.business_profile), + payments_info.map(|pi| pi.profile), &mut response, )) .await?; @@ -1572,7 +1591,8 @@ async fn generate_saved_pm_response( state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_account: &domain::MerchantAccount, - pm_list_context: ( + merchant_connector_accounts: &domain::MerchantConnectorAccounts, + (pm_list_context, parent_payment_method_token, pm): ( PaymentMethodListContext, Option, domain::PaymentMethod, @@ -1580,7 +1600,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 { @@ -1603,24 +1622,14 @@ 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| { ( pi.is_connector_agnostic_mit_enabled, - pi.requires_cvv, + pi.collect_cvv_during_payment, pi.off_session_payment_flag, - pi.business_profile - .as_ref() - .map(|profile| profile.get_id().to_owned()), + Some(pi.profile.get_id().to_owned()), ) }) .unwrap_or((false, false, false, Default::default())); @@ -1631,10 +1640,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 @@ -1662,7 +1672,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, @@ -1670,8 +1679,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, }; @@ -1726,7 +1737,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), @@ -1902,50 +1912,25 @@ impl pm_types::SavedPMLPaymentsInfo { pub async fn form_payments_info( payment_intent: PaymentIntent, merchant_account: &domain::MerchantAccount, + profile: domain::Profile, db: &dyn StorageInterface, 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 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 profile_id = &payment_intent.profile_id; - - let business_profile = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - key_store, - Some(profile_id), - merchant_account.get_id(), - ) - .await?; - - let is_connector_agnostic_mit_enabled = business_profile - .as_ref() - .and_then(|business_profile| business_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, - business_profile, - requires_cvv, + profile, + collect_cvv_during_payment, off_session_payment_flag, is_connector_agnostic_mit_enabled, }) @@ -1966,44 +1951,14 @@ impl pm_types::SavedPMLPaymentsInfo { .get_required_value("PaymentTokenData")?; let intent_fulfillment_time = self - .business_profile - .as_ref() - .and_then(|b_profile| b_profile.get_order_fulfillment_time()) + .profile + .get_order_fulfillment_time() .unwrap_or(common_utils::consts::DEFAULT_INTENT_FULFILLMENT_TIME); pm_routes::ParentPaymentMethodToken::create_key_for_token((token, pma.payment_method)) .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 cbd535903ae2..fcf0c6ca4d1d 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2563,25 +2563,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, @@ -2606,11 +2587,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) @@ -4724,7 +4705,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) @@ -4986,7 +4969,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, @@ -4994,13 +4977,14 @@ pub async fn perform_surcharge_ops( todo!() } +#[cfg(feature = "v1")] 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, + connector_mandate_details: Option, network_transaction_id: Option<&String>, ) -> errors::RouterResult { if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { @@ -5019,25 +5003,42 @@ 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 - .into_iter() - .filter(|mca| { - mca.disabled == Some(false) && profile_id.clone() == Some(mca.profile_id.clone()) - }) - .collect::>(); + let merchant_connector_accounts = domain::MerchantConnectorAccounts::new(mcas); - 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); - } - } + return Ok(merchant_connector_accounts + .is_merchant_connector_account_id_in_connector_mandate_details( + profile_id.as_ref(), + &connector_mandate_details, + )); } 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: &domain::MerchantConnectorAccounts, +) -> bool { + if is_connector_agnostic_mit_enabled && network_transaction_id.is_some() { + return true; + } + match connector_mandate_details { + 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, + } +} + pub async fn decrypt_generic_data( state: &routes::SessionState, data: Option, 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/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 4ad00df9c247..a025b4b3cc85 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -5159,7 +5159,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 f4ec0ef9eb55..4b784ea9463b 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1586,7 +1586,7 @@ async fn payment_response_update_tracker( .connector_mandate_details .clone() .map(|val| { - val.parse_value::( + val.parse_value::( "PaymentsMandateReference", ) }) @@ -2367,7 +2367,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 553eb6724868..6f01af96bb8e 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}, }; @@ -1149,9 +1149,9 @@ 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 { +) -> Option { let mut mandate_details = HashMap::new(); if let Some((mca_id, connector_mandate_id)) = @@ -1159,7 +1159,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, @@ -1169,20 +1169,20 @@ pub fn add_connector_mandate_details_in_payment_method( connector_mandate_request_reference_id, }, ); - Some(storage::PaymentsMandateReference(mandate_details)) + Some(diesel_models::PaymentsMandateReference(mandate_details)) } else { None } } #[allow(clippy::too_many_arguments)] pub fn update_connector_mandate_details( - mandate_details: Option, + mandate_details: Option, payment_method_type: Option, authorized_amount: Option, 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 { @@ -1190,7 +1190,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, @@ -1204,7 +1204,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/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 b92e2f260fcb..764e75df02a0 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"), @@ -416,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, @@ -557,6 +557,7 @@ pub async fn list_customer_payment_method_for_payment( list_customer_payment_method_util( state, auth.merchant_account, + auth.profile, auth.key_store, Some(req), None, @@ -622,6 +623,7 @@ pub async fn list_customer_payment_method_api( list_customer_payment_method_util( state, auth.merchant_account, + auth.profile, auth.key_store, Some(req), Some(customer_id.clone()), @@ -900,6 +902,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, @@ -936,6 +939,7 @@ pub async fn default_payment_method_set_api( )) .await } + #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] @@ -983,17 +987,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) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index cbd694f3acdb..586252ff0ad7 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -2525,6 +2525,43 @@ 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, +) -> 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 = "v2")] pub fn check_client_secret_and_get_auth( headers: &HeaderMap, payload: &impl ClientSecretFetch, @@ -2557,9 +2594,11 @@ where } .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, @@ -2592,6 +2631,7 @@ where } } +#[cfg(feature = "v1")] pub fn is_ephemeral_auth( headers: &HeaderMap, ) -> RouterResult>> { @@ -2604,6 +2644,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/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/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 29290f0b7fcf..1a6b9053dcb9 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -139,8 +139,8 @@ impl PaymentMethodClientSecret { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, - pub business_profile: Option, - pub requires_cvv: bool, + pub profile: domain::Profile, + pub collect_cvv_during_payment: bool, pub off_session_payment_flag: bool, pub is_connector_agnostic_mit_enabled: bool, } diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 7739c0f0ba8f..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,37 +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, - pub connector_mandate_request_reference_id: 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, 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; 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),