From 301bd59b00c0778979936059a3fe0b81126d1f32 Mon Sep 17 00:00:00 2001 From: Narayan Bhat Date: Sat, 9 Nov 2024 12:21:20 +0530 Subject: [PATCH] refactor: rename authentication data to redirect form also instead of using serdejson value, use a type --- crates/common_utils/src/types.rs | 10 ++ crates/diesel_models/src/payment_attempt.rs | 55 +++++- crates/diesel_models/src/schema_v2.rs | 2 +- .../src/payments/payment_attempt.rs | 27 +-- .../src/router_data.rs | 1 + .../src/router_response_types.rs | 156 ++++++++++++++++++ crates/router/src/core/payments.rs | 14 +- .../router/src/core/payments/transformers.rs | 3 +- .../2024-08-28-081721_add_v2_columns/down.sql | 1 + .../2024-08-28-081721_add_v2_columns/up.sql | 3 +- .../down.sql | 1 + .../2024-10-08-081847_drop_v1_columns/up.sql | 1 + 12 files changed, 247 insertions(+), 27 deletions(-) diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index ce2be525989e..ef7a4b847c45 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -627,6 +627,16 @@ impl Url { pub fn get_string_repr(&self) -> &str { self.0.as_str() } + + /// wrap the url::Url in Url type + pub fn wrap(url: url::Url) -> Self { + Self(url) + } + + /// Get the inner url + pub fn into_inner(self) -> url::Url { + self.0 + } } impl ToSql for Url diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index e233cfe390bb..32ffcb545fb7 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -65,7 +65,6 @@ pub struct PaymentAttempt { pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, - pub authentication_data: Option, pub encoded_data: Option>, pub unified_code: Option, pub unified_message: Option, @@ -89,6 +88,7 @@ pub struct PaymentAttempt { pub external_reference_id: Option, pub tax_on_surcharge: Option, pub payment_method_billing_address: Option, + pub redirection_data: Option, pub connector_payment_data: Option, pub id: id_type::GlobalAttemptId, pub shipping_cost: Option, @@ -255,7 +255,7 @@ pub struct PaymentAttemptNew { pub amount_capturable: MinorUnit, pub updated_by: String, pub merchant_connector_id: Option, - pub authentication_data: Option, + pub redirection_data: Option, pub encoded_data: Option>, pub unified_code: Option, pub unified_message: Option, @@ -770,7 +770,7 @@ pub struct PaymentAttemptUpdateInternal { pub updated_by: String, pub merchant_connector_id: Option, pub connector: Option, - pub authentication_data: Option, + pub redirection_data: Option, // encoded_data: Option, pub unified_code: Option>, pub unified_message: Option>, @@ -3283,6 +3283,55 @@ impl From for PaymentAttemptUpdateInternal { } } +#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, diesel::AsExpression)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] +pub enum RedirectForm { + Form { + endpoint: String, + method: common_utils::request::Method, + form_fields: std::collections::HashMap, + }, + Html { + html_data: String, + }, + BlueSnap { + payment_fields_token: String, + }, + CybersourceAuthSetup { + access_token: String, + ddc_url: String, + reference_id: String, + }, + CybersourceConsumerAuth { + access_token: String, + step_up_url: String, + }, + Payme, + Braintree { + client_token: String, + card_token: String, + bin: String, + }, + Nmi { + amount: String, + currency: common_enums::Currency, + public_key: masking::Secret, + customer_vault_id: String, + order_id: String, + }, + Mifinity { + initialization_token: String, + }, + WorldpayDDCForm { + endpoint: common_utils::types::Url, + method: common_utils::request::Method, + form_fields: std::collections::HashMap, + collection_id: Option, + }, +} + +common_utils::impl_to_sql_from_sql_json!(RedirectForm); + mod tests { #[test] diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 617d6cf182b9..3bd31f5cbf3f 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -776,7 +776,6 @@ diesel::table! { updated_by -> Varchar, #[max_length = 32] merchant_connector_id -> Nullable, - authentication_data -> Nullable, encoded_data -> Nullable, #[max_length = 255] unified_code -> Nullable, @@ -814,6 +813,7 @@ diesel::table! { external_reference_id -> Nullable, tax_on_surcharge -> Nullable, payment_method_billing_address -> Nullable, + redirection_data -> Nullable, #[max_length = 512] connector_payment_data -> Nullable, #[max_length = 64] diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index c1bc0a472836..2a8b52c2c87c 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -31,6 +31,9 @@ use crate::{ router_request_types, ForeignIDRef, }; +#[cfg(feature = "v2")] +use crate::router_response_types; + #[async_trait::async_trait] pub trait PaymentAttemptInterface { #[cfg(feature = "v1")] @@ -261,7 +264,7 @@ pub struct PaymentAttempt { /// Whether the payment was updated by postgres or redis pub updated_by: String, /// The authentication data which is used for external authentication - pub authentication_data: Option, + pub redirection_data: Option, pub encoded_data: Option>, pub merchant_connector_id: Option, /// Whether external 3DS authentication was attempted for this payment. @@ -383,7 +386,7 @@ impl PaymentAttempt { multiple_capture_count: None, connector_response_reference_id: None, updated_by: storage_scheme.to_string(), - authentication_data: None, + redirection_data: None, encoded_data: None, merchant_connector_id: None, external_three_ds_authentication_attempted: None, @@ -1295,7 +1298,7 @@ pub enum PaymentAttemptUpdate { status: storage_enums::AttemptStatus, connector_payment_id: Option, updated_by: String, - authentication_data: Option, + redirection_data: Option, }, /// Update the payment attempt after force syncing with the connector SyncUpdate { @@ -1619,7 +1622,7 @@ impl behaviour::Conversion for PaymentAttempt { multiple_capture_count, connector_response_reference_id, updated_by, - authentication_data, + redirection_data, encoded_data, merchant_connector_id, external_three_ds_authentication_attempted, @@ -1688,7 +1691,7 @@ impl behaviour::Conversion for PaymentAttempt { amount_capturable, updated_by, merchant_connector_id, - authentication_data, + redirection_data: redirection_data.map(From::from), encoded_data, unified_code: error .as_ref() @@ -1796,7 +1799,7 @@ impl behaviour::Conversion for PaymentAttempt { multiple_capture_count: storage_model.multiple_capture_count, connector_response_reference_id: storage_model.connector_response_reference_id, updated_by: storage_model.updated_by, - authentication_data: storage_model.authentication_data, + redirection_data: storage_model.redirection_data.map(From::from), encoded_data: storage_model.encoded_data, merchant_connector_id: storage_model.merchant_connector_id, external_three_ds_authentication_attempted: storage_model @@ -1872,7 +1875,7 @@ impl behaviour::Conversion for PaymentAttempt { amount_capturable: self.amount_details.amount_capturable, updated_by: self.updated_by, merchant_connector_id: self.merchant_connector_id, - authentication_data: self.authentication_data, + redirection_data: self.redirection_data.map(From::from), encoded_data: self.encoded_data, unified_code: error_details .as_ref() @@ -1929,7 +1932,7 @@ impl From for diesel_models::PaymentAttemptUpdateInternal unified_message: None, connector_payment_id: None, connector: Some(connector), - authentication_data: None, + redirection_data: None, }, PaymentAttemptUpdate::ErrorUpdate { status, @@ -1949,13 +1952,13 @@ impl From for diesel_models::PaymentAttemptUpdateInternal unified_message: None, connector_payment_id, connector: None, - authentication_data: None, + redirection_data: None, }, PaymentAttemptUpdate::ConfirmIntentResponse { status, connector_payment_id, updated_by, - authentication_data, + redirection_data, } => Self { status: Some(status), error_message: None, @@ -1969,7 +1972,8 @@ impl From for diesel_models::PaymentAttemptUpdateInternal unified_message: None, connector_payment_id, connector: None, - authentication_data, + redirection_data: redirection_data + .map(diesel_models::payment_attempt::RedirectForm::from), }, PaymentAttemptUpdate::SyncUpdate { status, updated_by } => Self { status: Some(status), @@ -1984,6 +1988,7 @@ impl From for diesel_models::PaymentAttemptUpdateInternal unified_message: None, connector_payment_id: None, connector: None, + redirection_data: None, }, } } diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index b9920bed08ce..f995c9a1e57e 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -470,6 +470,7 @@ impl status: attempt_status, connector_payment_id, updated_by: storage_scheme.to_string(), + redirection_data: *redirection_data.clone(), } } router_response_types::PaymentsResponseData::MultipleCaptureResponse { .. } => { diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs index e6af5c5eb3bc..4501582d0038 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -191,6 +191,162 @@ impl From<(url::Url, Method)> for RedirectForm { } } +impl From for diesel_models::payment_attempt::RedirectForm { + fn from(redirect_form: RedirectForm) -> Self { + match redirect_form { + RedirectForm::Form { + endpoint, + method, + form_fields, + } => Self::Form { + endpoint, + method, + form_fields, + }, + RedirectForm::Html { html_data } => Self::Html { html_data }, + RedirectForm::BlueSnap { + payment_fields_token, + } => Self::BlueSnap { + payment_fields_token, + }, + RedirectForm::CybersourceAuthSetup { + access_token, + ddc_url, + reference_id, + } => Self::CybersourceAuthSetup { + access_token, + ddc_url, + reference_id, + }, + RedirectForm::CybersourceConsumerAuth { + access_token, + step_up_url, + } => Self::CybersourceConsumerAuth { + access_token, + step_up_url, + }, + RedirectForm::Payme => Self::Payme, + RedirectForm::Braintree { + client_token, + card_token, + bin, + } => Self::Braintree { + client_token, + card_token, + bin, + }, + RedirectForm::Nmi { + amount, + currency, + public_key, + customer_vault_id, + order_id, + } => Self::Nmi { + amount, + currency, + public_key, + customer_vault_id, + order_id, + }, + RedirectForm::Mifinity { + initialization_token, + } => Self::Mifinity { + initialization_token, + }, + RedirectForm::WorldpayDDCForm { + endpoint, + method, + form_fields, + collection_id, + } => Self::WorldpayDDCForm { + endpoint: common_utils::types::Url::wrap(endpoint), + method, + form_fields, + collection_id, + }, + } + } +} + +impl From for RedirectForm { + fn from(redirect_form: diesel_models::payment_attempt::RedirectForm) -> Self { + match redirect_form { + diesel_models::payment_attempt::RedirectForm::Form { + endpoint, + method, + form_fields, + } => Self::Form { + endpoint, + method, + form_fields, + }, + diesel_models::payment_attempt::RedirectForm::Html { html_data } => { + Self::Html { html_data } + } + diesel_models::payment_attempt::RedirectForm::BlueSnap { + payment_fields_token, + } => Self::BlueSnap { + payment_fields_token, + }, + diesel_models::payment_attempt::RedirectForm::CybersourceAuthSetup { + access_token, + ddc_url, + reference_id, + } => Self::CybersourceAuthSetup { + access_token, + ddc_url, + reference_id, + }, + diesel_models::payment_attempt::RedirectForm::CybersourceConsumerAuth { + access_token, + step_up_url, + } => Self::CybersourceConsumerAuth { + access_token, + step_up_url, + }, + diesel_models::payment_attempt::RedirectForm::Payme => Self::Payme, + diesel_models::payment_attempt::RedirectForm::Braintree { + client_token, + card_token, + bin, + } => Self::Braintree { + client_token, + card_token, + bin, + }, + diesel_models::payment_attempt::RedirectForm::Nmi { + amount, + currency, + public_key, + customer_vault_id, + order_id, + } => Self::Nmi { + amount, + currency, + public_key, + customer_vault_id, + order_id, + }, + diesel_models::payment_attempt::RedirectForm::Mifinity { + initialization_token, + } => Self::Mifinity { + initialization_token, + }, + diesel_models::payment_attempt::RedirectForm::WorldpayDDCForm { + endpoint, + method, + form_fields, + collection_id, + } => Self::WorldpayDDCForm { + endpoint: endpoint.into_inner(), + method, + form_fields, + collection_id, + }, + } + } +} + #[derive(Default, Clone, Debug)] pub struct UploadFileResponse { pub provider_file_id: String, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 8fc462ea30a5..7892880ae50d 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -6111,29 +6111,23 @@ pub async fn payment_start_redirection( &key_store, payment_intent .active_attempt_id - .clone() + .as_ref() .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("missing active attempt in payment_intent")? - .get_string_repr(), + .attach_printable("missing active attempt in payment_intent")?, storage_scheme, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Error while fetching payment_attempt")?; let redirection_data = payment_attempt - .authentication_data + .redirection_data .clone() .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("missing authentication_data in payment_attempt")?; - let form: RedirectForm = serde_json::from_value(redirection_data.expose()).map_err(|err| { - logger::error!(error = ?err, "Failed to deserialize redirection data"); - errors::ApiErrorResponse::InternalServerError - })?; - Ok(services::ApplicationResponse::Form(Box::new( services::RedirectionFormData { - redirect_form: form, + redirect_form: redirection_data, payment_method_data: None, amount: payment_attempt.amount_details.net_amount.to_string(), currency: payment_intent.amount_details.currency.to_string(), diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 5476a225bc59..52c0f69be166 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1035,7 +1035,7 @@ where let redirect_to_url = payment_intent .create_start_redirection_url(base_url, merchant_account.publishable_key.clone())?; let next_action = payment_attempt - .authentication_data + .redirection_data .as_ref() .map(|_| api_models::payments::NextActionData::RedirectToUrl { redirect_to_url }); @@ -1081,6 +1081,7 @@ where _connector_http_status_code: Option, _external_latency: Option, _is_latency_header_enabled: Option, + _merchant_account: &domain::MerchantAccount, ) -> RouterResponse { let payment_intent = payment_data.get_payment_intent(); let payment_attempt = payment_data.get_optional_payment_attempt(); 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 70aa43951050..d008367af333 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 @@ -41,4 +41,5 @@ ALTER TABLE payment_attempt DROP COLUMN payment_method_type_v2, DROP COLUMN external_reference_id, DROP COLUMN tax_on_surcharge, DROP COLUMN payment_method_billing_address, + DROP COLUMN redirection_data, DROP COLUMN connector_payment_data; 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 64decb1a0a87..a402beac139c 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 @@ -21,7 +21,7 @@ ADD COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, -- 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; +ADD COLUMN should_collect_cvv_during_payment BOOLEAN NOT NULL; ALTER TABLE payment_intent ADD COLUMN merchant_reference_id VARCHAR(64), @@ -50,4 +50,5 @@ ADD COLUMN payment_method_type_v2 VARCHAR, ADD COLUMN external_reference_id VARCHAR(128), ADD COLUMN tax_on_surcharge BIGINT, ADD COLUMN payment_method_billing_address BYTEA, + ADD COLUMN redirection_data JSONB, ADD COLUMN connector_payment_data VARCHAR(512); diff --git a/v2_migrations/2024-10-08-081847_drop_v1_columns/down.sql b/v2_migrations/2024-10-08-081847_drop_v1_columns/down.sql index 55c5b3f749d4..64cbd2233eac 100644 --- a/v2_migrations/2024-10-08-081847_drop_v1_columns/down.sql +++ b/v2_migrations/2024-10-08-081847_drop_v1_columns/down.sql @@ -87,6 +87,7 @@ ADD COLUMN IF NOT EXISTS attempt_id VARCHAR(64) NOT NULL, ADD COLUMN tax_amount bigint, ADD COLUMN straight_through_algorithm JSONB, ADD COLUMN confirm BOOLEAN, + ADD COLUMN authentication_data JSONB, ADD COLUMN payment_method_billing_address_id VARCHAR(64); -- Create the index which was dropped because of dropping the column diff --git a/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql b/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql index 0d22741b9420..507b29dadf7d 100644 --- a/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql +++ b/v2_migrations/2024-10-08-081847_drop_v1_columns/up.sql @@ -85,4 +85,5 @@ ALTER TABLE payment_attempt DROP COLUMN attempt_id, DROP COLUMN tax_amount, DROP COLUMN straight_through_algorithm, DROP COLUMN confirm, + DROP COLUMN authentication_data, DROP COLUMN payment_method_billing_address_id;