From 7e44bbca63c1818c0fabdf2734d9b0ae5d639fe1 Mon Sep 17 00:00:00 2001 From: Vrishab Srivatsa <136090360+vsrivatsa-juspay@users.noreply.github.com> Date: Fri, 17 May 2024 15:57:30 +0530 Subject: [PATCH] feat: added client_source, client_version in payment_attempt from payments confirm request headers (#4657) --- crates/api_models/src/payments.rs | 4 +++- crates/diesel_models/src/payment_attempt.rs | 16 ++++++++++++++++ crates/diesel_models/src/schema.rs | 4 ++++ crates/diesel_models/src/user/sample_data.rs | 4 ++++ .../src/payments/payment_attempt.rs | 6 ++++++ crates/router/src/core/payments.rs | 12 ++++++------ crates/router/src/core/payments/helpers.rs | 2 ++ .../payments/operations/payment_confirm.rs | 11 +++++++++++ .../core/payments/operations/payment_create.rs | 2 ++ .../router/src/core/payments/transformers.rs | 3 ++- crates/router/src/lib.rs | 3 +++ crates/router/src/routes/payments.rs | 2 +- crates/router/src/types/transformers.rs | 12 +++++++++++- .../src/mock_db/payment_attempt.rs | 2 ++ .../src/payments/payment_attempt.rs | 18 ++++++++++++++++++ .../down.sql | 3 +++ .../up.sql | 3 +++ 17 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/down.sql create mode 100644 migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/up.sql diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 7c15f145e2f9..f00be78a0392 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -540,9 +540,11 @@ impl RequestSurchargeDetails { } } -#[derive(Default, Debug, Clone, Copy)] +#[derive(Default, Debug, Clone)] pub struct HeaderPayload { pub payment_confirm_source: Option, + pub client_source: Option, + pub client_version: Option, pub x_hs_latency: Option, } diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index d4d2ddaef03e..ffa9aee1153f 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -70,6 +70,8 @@ pub struct PaymentAttempt { pub mandate_data: Option, pub fingerprint_id: Option, pub payment_method_billing_address_id: Option, + pub client_source: Option, + pub client_version: Option, } impl PaymentAttempt { @@ -150,6 +152,8 @@ pub struct PaymentAttemptNew { pub mandate_data: Option, pub fingerprint_id: Option, pub payment_method_billing_address_id: Option, + pub client_source: Option, + pub client_version: Option, } impl PaymentAttemptNew { @@ -233,6 +237,8 @@ pub enum PaymentAttemptUpdate { authentication_connector: Option, authentication_id: Option, payment_method_billing_address_id: Option, + client_source: Option, + client_version: Option, }, VoidUpdate { status: storage_enums::AttemptStatus, @@ -388,6 +394,8 @@ pub struct PaymentAttemptUpdateInternal { authentication_id: Option, fingerprint_id: Option, payment_method_billing_address_id: Option, + client_source: Option, + client_version: Option, } impl PaymentAttemptUpdateInternal { @@ -453,6 +461,8 @@ impl PaymentAttemptUpdate { authentication_id, payment_method_billing_address_id, fingerprint_id, + client_source, + client_version, } = PaymentAttemptUpdateInternal::from(self).populate_derived_fields(&source); PaymentAttempt { amount: amount.unwrap_or(source.amount), @@ -501,6 +511,8 @@ impl PaymentAttemptUpdate { payment_method_billing_address_id: payment_method_billing_address_id .or(source.payment_method_billing_address_id), fingerprint_id: fingerprint_id.or(source.fingerprint_id), + client_source: client_source.or(source.client_source), + client_version: client_version.or(source.client_version), ..source } } @@ -587,6 +599,8 @@ impl From for PaymentAttemptUpdateInternal { payment_method_billing_address_id, fingerprint_id, payment_method_id, + client_source, + client_version, } => Self { amount: Some(amount), currency: Some(currency), @@ -616,6 +630,8 @@ impl From for PaymentAttemptUpdateInternal { fingerprint_id, payment_method_id, capture_method, + client_source, + client_version, ..Default::default() }, PaymentAttemptUpdate::VoidUpdate { diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 427f9560e034..61022f2f2531 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -778,6 +778,10 @@ diesel::table! { fingerprint_id -> Nullable, #[max_length = 64] payment_method_billing_address_id -> Nullable, + #[max_length = 64] + client_source -> Nullable, + #[max_length = 64] + client_version -> Nullable, } } diff --git a/crates/diesel_models/src/user/sample_data.rs b/crates/diesel_models/src/user/sample_data.rs index 93beeff57d2a..298d82cf31e0 100644 --- a/crates/diesel_models/src/user/sample_data.rs +++ b/crates/diesel_models/src/user/sample_data.rs @@ -73,6 +73,8 @@ pub struct PaymentAttemptBatchNew { pub mandate_data: Option, pub payment_method_billing_address_id: Option, pub fingerprint_id: Option, + pub client_source: Option, + pub client_version: Option, } #[allow(dead_code)] @@ -133,6 +135,8 @@ impl PaymentAttemptBatchNew { mandate_data: self.mandate_data, payment_method_billing_address_id: self.payment_method_billing_address_id, fingerprint_id: self.fingerprint_id, + client_source: self.client_source, + client_version: self.client_version, } } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index d6565a6be60f..fe6f3711e396 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -166,6 +166,8 @@ pub struct PaymentAttempt { pub mandate_data: Option, pub payment_method_billing_address_id: Option, pub fingerprint_id: Option, + pub client_source: Option, + pub client_version: Option, } impl PaymentAttempt { @@ -249,6 +251,8 @@ pub struct PaymentAttemptNew { pub mandate_data: Option, pub payment_method_billing_address_id: Option, pub fingerprint_id: Option, + pub client_source: Option, + pub client_version: Option, } impl PaymentAttemptNew { @@ -327,6 +331,8 @@ pub enum PaymentAttemptUpdate { payment_method_billing_address_id: Option, fingerprint_id: Option, payment_method_id: Option, + client_source: Option, + client_version: Option, }, RejectUpdate { status: storage_enums::AttemptStatus, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 1b47c267af15..76cd1307d890 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -294,7 +294,7 @@ where call_connector_action.clone(), &validate_result, schedule_time, - header_payload, + header_payload.clone(), #[cfg(feature = "frm")] frm_info.as_ref().and_then(|fi| fi.suggested_action), #[cfg(not(feature = "frm"))] @@ -364,7 +364,7 @@ where call_connector_action.clone(), &validate_result, schedule_time, - header_payload, + header_payload.clone(), #[cfg(feature = "frm")] frm_info.as_ref().and_then(|fi| fi.suggested_action), #[cfg(not(feature = "frm"))] @@ -497,7 +497,7 @@ where frm_info.and_then(|info| info.suggested_action), #[cfg(not(feature = "frm"))] None, - header_payload, + header_payload.clone(), ) .await?; } @@ -528,7 +528,7 @@ where None, &key_store, None, - header_payload, + header_payload.clone(), ) .await?; } @@ -785,7 +785,7 @@ where call_connector_action, auth_flow, eligible_routable_connectors, - header_payload, + header_payload.clone(), ) .await?; @@ -1579,7 +1579,7 @@ where updated_customer, key_store, frm_suggestion, - header_payload, + header_payload.clone(), ) .await?; diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 853b3d64adc2..b4dd06b8ce9d 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3554,6 +3554,8 @@ impl AttemptType { // New payment method billing address can be passed for a retry payment_method_billing_address_id: None, fingerprint_id: None, + client_source: None, + client_version: None, } } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index a0062fb81c35..7d1858bd0d7a 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1069,6 +1069,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .map(|surcharge_details| surcharge_details.final_amount) .unwrap_or(payment_data.payment_attempt.amount); + let client_source = header_payload + .client_source + .clone() + .or(payment_data.payment_attempt.client_source.clone()); + let client_version = header_payload + .client_version + .clone() + .or(payment_data.payment_attempt.client_version.clone()); + let m_payment_data_payment_attempt = payment_data.payment_attempt.clone(); let m_payment_method_id = payment_data.payment_attempt.payment_method_id.clone(); let m_browser_info = browser_info.clone(); @@ -1136,6 +1145,8 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_method_billing_address_id, fingerprint_id: m_fingerprint_id, payment_method_id: m_payment_method_id, + client_source, + client_version, }, storage_scheme, ) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 0c68289fb5f0..b12b5ec46009 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -930,6 +930,8 @@ impl PaymentCreate { fingerprint_id: None, authentication_connector: None, authentication_id: None, + client_source: None, + client_version: None, }, additional_pm_data, )) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 77ec848fb442..1fd76130597e 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -19,6 +19,7 @@ use crate::{ payments::{self, helpers}, utils as core_utils, }, + headers::X_PAYMENT_CONFIRM_SOURCE, routes::{metrics, AppState}, services::{self, RedirectForm}, types::{ @@ -488,7 +489,7 @@ where .unwrap_or_default(); if let Some(payment_confirm_source) = payment_intent.payment_confirm_source { headers.push(( - "payment_confirm_source".to_string(), + X_PAYMENT_CONFIRM_SOURCE.to_string(), Maskable::new_normal(payment_confirm_source.to_string()), )) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 27c9c7d2dd44..2e9ecc13dcb1 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -72,6 +72,9 @@ pub mod headers { pub const X_REQUEST_ID: &str = "X-Request-Id"; pub const STRIPE_COMPATIBLE_WEBHOOK_SIGNATURE: &str = "Stripe-Signature"; pub const STRIPE_COMPATIBLE_CONNECT_ACCOUNT: &str = "Stripe-Account"; + pub const X_CLIENT_VERSION: &str = "X-Client-Version"; + pub const X_CLIENT_SOURCE: &str = "X-Client-Source"; + pub const X_PAYMENT_CONFIRM_SOURCE: &str = "X-Payment-Confirm-Source"; pub const CONTENT_LENGTH: &str = "Content-Length"; } diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 6a1b424a3300..06bdd3ed05a7 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -497,7 +497,7 @@ pub async fn payments_confirm( req_state, auth.merchant_account, auth.key_store, - header_payload, + header_payload.clone(), req, auth_flow, ) diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 423a1ee90a6c..6fb98d944bf3 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -18,6 +18,7 @@ use masking::{ExposeInterface, PeekInterface}; use super::domain; use crate::{ core::errors, + headers::{X_CLIENT_SOURCE, X_CLIENT_VERSION, X_PAYMENT_CONFIRM_SOURCE}, services::authentication::get_header_value_by_key, types::{ api::{self as api_types, routing as routing_types}, @@ -1011,7 +1012,7 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { type Error = error_stack::Report; fn foreign_try_from(headers: &HeaderMap) -> Result { let payment_confirm_source: Option = - get_header_value_by_key("payment_confirm_source".into(), headers)? + get_header_value_by_key(X_PAYMENT_CONFIRM_SOURCE.into(), headers)? .map(|source| { source .to_owned() @@ -1038,8 +1039,17 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { let x_hs_latency = get_header_value_by_key(X_HS_LATENCY.into(), headers) .map(|value| value == Some("true")) .unwrap_or(false); + + let client_source = + get_header_value_by_key(X_CLIENT_SOURCE.into(), headers)?.map(|val| val.to_string()); + + let client_version = + get_header_value_by_key(X_CLIENT_VERSION.into(), headers)?.map(|val| val.to_string()); + Ok(Self { payment_confirm_source, + client_source, + client_version, x_hs_latency: Some(x_hs_latency), }) } diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index 33ef6d416f23..cc2f2abc0049 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -157,6 +157,8 @@ impl PaymentAttemptInterface for MockDb { mandate_data: payment_attempt.mandate_data, payment_method_billing_address_id: payment_attempt.payment_method_billing_address_id, fingerprint_id: payment_attempt.fingerprint_id, + client_source: payment_attempt.client_source, + client_version: payment_attempt.client_version, }; payment_attempts.push(payment_attempt.clone()); Ok(payment_attempt) diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index d090f646da32..d4709648c4f5 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -410,6 +410,8 @@ impl PaymentAttemptInterface for KVRouterStore { .payment_method_billing_address_id .clone(), fingerprint_id: payment_attempt.fingerprint_id.clone(), + client_source: payment_attempt.client_source.clone(), + client_version: payment_attempt.client_version.clone(), }; let field = format!("pa_{}", created_attempt.attempt_id); @@ -1164,6 +1166,8 @@ impl DataModelExt for PaymentAttempt { mandate_data: self.mandate_data.map(|d| d.to_storage_model()), payment_method_billing_address_id: self.payment_method_billing_address_id, fingerprint_id: self.fingerprint_id, + client_source: self.client_source, + client_version: self.client_version, } } @@ -1228,6 +1232,8 @@ impl DataModelExt for PaymentAttempt { .map(MandateDetails::from_storage_model), payment_method_billing_address_id: storage_model.payment_method_billing_address_id, fingerprint_id: storage_model.fingerprint_id, + client_source: storage_model.client_source, + client_version: storage_model.client_version, } } } @@ -1290,6 +1296,8 @@ impl DataModelExt for PaymentAttemptNew { mandate_data: self.mandate_data.map(|d| d.to_storage_model()), payment_method_billing_address_id: self.payment_method_billing_address_id, fingerprint_id: self.fingerprint_id, + client_source: self.client_source, + client_version: self.client_version, } } @@ -1352,6 +1360,8 @@ impl DataModelExt for PaymentAttemptNew { .map(MandateDetails::from_storage_model), payment_method_billing_address_id: storage_model.payment_method_billing_address_id, fingerprint_id: storage_model.fingerprint_id, + client_source: storage_model.client_source, + client_version: storage_model.client_version, } } } @@ -1470,6 +1480,8 @@ impl DataModelExt for PaymentAttemptUpdate { authentication_connector, authentication_id, payment_method_billing_address_id, + client_source, + client_version, } => DieselPaymentAttemptUpdate::ConfirmUpdate { amount, currency, @@ -1498,6 +1510,8 @@ impl DataModelExt for PaymentAttemptUpdate { authentication_connector, authentication_id, payment_method_billing_address_id, + client_source, + client_version, }, Self::VoidUpdate { status, @@ -1773,6 +1787,8 @@ impl DataModelExt for PaymentAttemptUpdate { authentication_connector, authentication_id, payment_method_billing_address_id, + client_source, + client_version, } => Self::ConfirmUpdate { amount, currency, @@ -1801,6 +1817,8 @@ impl DataModelExt for PaymentAttemptUpdate { authentication_connector, authentication_id, payment_method_billing_address_id, + client_source, + client_version, }, DieselPaymentAttemptUpdate::VoidUpdate { status, diff --git a/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/down.sql b/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/down.sql new file mode 100644 index 000000000000..5e91f5197d85 --- /dev/null +++ b/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_attempt DROP COLUMN IF EXISTS client_version; +ALTER TABLE payment_attempt DROP COLUMN IF EXISTS client_source; \ No newline at end of file diff --git a/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/up.sql b/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/up.sql new file mode 100644 index 000000000000..81584637c813 --- /dev/null +++ b/migrations/2024-05-15-133715_add_client_info_columns_in_payment_attempts/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS client_source VARCHAR(64) DEFAULT NULL; +ALTER TABLE payment_attempt ADD COLUMN IF NOT EXISTS client_version VARCHAR(64) DEFAULT NULL; \ No newline at end of file