diff --git a/Cargo.lock b/Cargo.lock index 6ea7937eca..abb65b07a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3677,10 +3677,12 @@ dependencies = [ "common_utils", "diesel_models", "error-stack", + "futures 0.3.30", "http 0.2.12", "masking", "mime", "router_derive", + "router_env", "serde", "serde_json", "serde_with", diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index def64e35db..58a881db59 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -9,8 +9,9 @@ license.workspace = true [features] signals = ["dep:signal-hook-tokio", "dep:signal-hook", "dep:tokio", "dep:router_env", "dep:futures"] -async_ext = ["dep:futures", "dep:async-trait"] +async_ext = ["dep:async-trait", "dep:futures"] logs = ["dep:router_env"] +metrics = ["dep:router_env", "dep:futures"] [dependencies] async-trait = { version = "0.1.79", optional = true } @@ -29,7 +30,9 @@ rand = "0.8.5" regex = "1.10.4" reqwest = { version = "0.11.27", features = ["json", "native-tls", "gzip", "multipart"] } ring = { version = "0.17.8", features = ["std", "wasm32_unknown_unknown_js"] } +rust_decimal = "1.35" rustc-hash = "1.1.0" +semver = { version = "1.0.22", features = ["serde"] } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" serde_urlencoded = "0.7.1" @@ -38,12 +41,10 @@ strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"], optional = true } -semver = { version = "1.0.22", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } uuid = { version = "1.8.0", features = ["v7"] } # First party crates -rust_decimal = "1.35" rusty-money = { git = "https://github.com/varunsrin/rusty_money", rev = "bbc0150742a0fff905225ff11ee09388e9babdcc", features = ["iso", "crypto"] } common_enums = { version = "0.1.0", path = "../common_enums" } masking = { version = "0.1.0", path = "../masking" } diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 4e515e3d6a..ee0a474bcd 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -30,6 +30,9 @@ pub mod static_cache; pub mod types; pub mod validation; +#[cfg(feature = "metrics")] +pub mod metrics; + /// Date-time utilities. pub mod date_time { #[cfg(feature = "async_ext")] diff --git a/crates/common_utils/src/metrics.rs b/crates/common_utils/src/metrics.rs new file mode 100644 index 0000000000..36e531c855 --- /dev/null +++ b/crates/common_utils/src/metrics.rs @@ -0,0 +1,2 @@ +//! Utilities for metrics +pub mod utils; diff --git a/crates/common_utils/src/metrics/utils.rs b/crates/common_utils/src/metrics/utils.rs new file mode 100644 index 0000000000..71244ecc4f --- /dev/null +++ b/crates/common_utils/src/metrics/utils.rs @@ -0,0 +1,33 @@ +//! metric utility functions + +use std::time; + +use router_env::opentelemetry; + +/// Record the time taken by the future to execute +#[inline] +pub async fn time_future(future: F) -> (R, time::Duration) +where + F: futures::Future, +{ + let start = time::Instant::now(); + let result = future.await; + let time_spent = start.elapsed(); + (result, time_spent) +} + +/// Record the time taken (in seconds) by the operation for the given context +#[inline] +pub async fn record_operation_time( + future: F, + metric: &opentelemetry::metrics::Histogram, + metric_context: &opentelemetry::Context, + key_value: &[opentelemetry::KeyValue], +) -> R +where + F: futures::Future, +{ + let (result, time) = time_future(future).await; + metric.record(metric_context, time.as_secs_f64(), key_value); + result +} diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index 6bb286b42a..fbafd88961 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -8,9 +8,8 @@ use crate::{ }; #[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize)] -#[diesel(table_name = payment_attempt)] +#[diesel(table_name = payment_attempt, primary_key(attempt_id, merchant_id))] pub struct PaymentAttempt { - pub id: i32, pub payment_id: String, pub merchant_id: String, pub attempt_id: String, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index c54ff2a429..7fad83e7a0 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -7,9 +7,8 @@ use time::PrimitiveDateTime; use crate::{enums as storage_enums, schema::payment_intent}; #[derive(Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize)] -#[diesel(table_name = payment_intent)] +#[diesel(table_name = payment_intent, primary_key(payment_id, merchant_id))] pub struct PaymentIntent { - pub id: i32, pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, @@ -233,10 +232,8 @@ pub struct PaymentIntentUpdateInternal { #[diesel(deserialize_as = super::OptionalDieselArray)] pub order_details: Option>, pub attempt_count: Option, - pub profile_id: Option, - merchant_decision: Option, - payment_confirm_source: Option, - + pub merchant_decision: Option, + pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, pub incremental_authorization_allowed: Option, @@ -244,7 +241,6 @@ pub struct PaymentIntentUpdateInternal { pub session_expiry: Option, pub fingerprint_id: Option, pub request_external_three_ds_authentication: Option, - pub charges: Option, pub frm_metadata: Option, } @@ -271,7 +267,6 @@ impl PaymentIntentUpdate { statement_descriptor_suffix, order_details, attempt_count, - profile_id, merchant_decision, payment_confirm_source, updated_by, @@ -281,7 +276,6 @@ impl PaymentIntentUpdate { session_expiry, fingerprint_id, request_external_three_ds_authentication, - charges, frm_metadata, } = self.into(); PaymentIntent { @@ -307,7 +301,6 @@ impl PaymentIntentUpdate { .or(source.statement_descriptor_suffix), order_details: order_details.or(source.order_details), attempt_count: attempt_count.unwrap_or(source.attempt_count), - profile_id: profile_id.or(source.profile_id), merchant_decision: merchant_decision.or(source.merchant_decision), payment_confirm_source: payment_confirm_source.or(source.payment_confirm_source), updated_by, @@ -320,8 +313,6 @@ impl PaymentIntentUpdate { session_expiry: session_expiry.or(source.session_expiry), request_external_three_ds_authentication: request_external_three_ds_authentication .or(source.request_external_three_ds_authentication), - charges: charges.or(source.charges), - frm_metadata: frm_metadata.or(source.frm_metadata), ..source } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index ea356650c1..08d8f423b7 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -712,8 +712,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - payment_attempt (id) { - id -> Int4, + payment_attempt (attempt_id, merchant_id) { #[max_length = 64] payment_id -> Varchar, #[max_length = 64] @@ -802,8 +801,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - payment_intent (id) { - id -> Int4, + payment_intent (payment_id, merchant_id) { #[max_length = 64] payment_id -> Varchar, #[max_length = 64] diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index c1db2a4fb9..0bbf0fe9bc 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -16,11 +16,12 @@ payouts = ["api_models/payouts"] # First party deps api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils" } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics"] } masking = { version = "0.1.0", path = "../masking" } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } -cards = {version = "0.1.0", path = "../cards"} -router_derive = {version = "0.1.0", path = "../router_derive"} +cards = { version = "0.1.0", path = "../cards" } +router_derive = { version = "0.1.0", path = "../router_derive" } +router_env = { version = "0.1.0", path = "../router_env" } # Third party deps actix-web = "4.5.1" @@ -35,4 +36,4 @@ thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] } - +futures = "0.3.30" diff --git a/crates/router/src/types/domain/behaviour.rs b/crates/hyperswitch_domain_models/src/behaviour.rs similarity index 97% rename from crates/router/src/types/domain/behaviour.rs rename to crates/hyperswitch_domain_models/src/behaviour.rs index db7159fad5..9976dd25a9 100644 --- a/crates/router/src/types/domain/behaviour.rs +++ b/crates/hyperswitch_domain_models/src/behaviour.rs @@ -1,6 +1,5 @@ use common_utils::errors::{CustomResult, ValidationError}; - -use crate::pii::Secret; +use masking::Secret; /// Trait for converting domain types to storage models #[async_trait::async_trait] diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 863d1070c0..00d5929a22 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -11,6 +11,10 @@ pub mod router_flow_types; pub mod router_request_types; pub mod router_response_types; +pub mod behaviour; +pub mod merchant_key_store; +pub mod type_encryption; + #[cfg(not(feature = "payouts"))] pub trait PayoutAttemptInterface {} diff --git a/crates/router/src/types/domain/merchant_key_store.rs b/crates/hyperswitch_domain_models/src/merchant_key_store.rs similarity index 96% rename from crates/router/src/types/domain/merchant_key_store.rs rename to crates/hyperswitch_domain_models/src/merchant_key_store.rs index bf5e903999..29050b7eb4 100644 --- a/crates/router/src/types/domain/merchant_key_store.rs +++ b/crates/hyperswitch_domain_models/src/merchant_key_store.rs @@ -1,15 +1,13 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, custom_serde, date_time, + errors::{CustomResult, ValidationError}, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use time::PrimitiveDateTime; -use crate::{ - errors::{CustomResult, ValidationError}, - types::domain::types::TypeEncryption, -}; +use crate::type_encryption::TypeEncryption; #[derive(Clone, Debug, serde::Serialize)] pub struct MerchantKeyStore { diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 089cb29aec..c09df82f87 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -11,7 +11,6 @@ use crate::RemoteStorageObject; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct PaymentIntent { - pub id: i32, pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index ac0c74ea91..c36e29bfe6 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,14 +1,17 @@ use api_models::enums::Connector; use common_enums as storage_enums; -use common_utils::types::MinorUnit; +use common_utils::{ + errors::{CustomResult, ValidationError}, + types::MinorUnit, +}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use super::PaymentIntent; use crate::{ - errors, + behaviour, errors, mandates::{MandateDataType, MandateDetails}, - ForeignIDRef, + ForeignIDRef, RemoteStorageObject, }; #[async_trait::async_trait] @@ -107,7 +110,6 @@ pub trait PaymentAttemptInterface { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct PaymentAttempt { - pub id: i32, pub payment_id: String, pub merchant_id: String, pub attempt_id: String, @@ -458,3 +460,164 @@ impl ForeignIDRef for PaymentAttempt { self.attempt_id.clone() } } + +use diesel_models::{ + PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, +}; + +#[async_trait::async_trait] +impl behaviour::Conversion for PaymentIntent { + type DstType = DieselPaymentIntent; + type NewDstType = DieselPaymentIntentNew; + + async fn convert(self) -> CustomResult { + Ok(DieselPaymentIntent { + payment_id: self.payment_id, + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + connector_id: self.connector_id, + shipping_address_id: self.shipping_address_id, + billing_address_id: self.billing_address_id, + statement_descriptor_name: self.statement_descriptor_name, + statement_descriptor_suffix: self.statement_descriptor_suffix, + created_at: self.created_at, + modified_at: self.modified_at, + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + business_country: self.business_country, + business_label: self.business_label, + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + merchant_decision: self.merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + incremental_authorization_allowed: self.incremental_authorization_allowed, + authorization_count: self.authorization_count, + fingerprint_id: self.fingerprint_id, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + }) + } + + async fn convert_back( + storage_model: Self::DstType, + _key: &masking::Secret>, + ) -> CustomResult + where + Self: Sized, + { + Ok(Self { + payment_id: storage_model.payment_id, + merchant_id: storage_model.merchant_id, + status: storage_model.status, + amount: storage_model.amount, + currency: storage_model.currency, + amount_captured: storage_model.amount_captured, + customer_id: storage_model.customer_id, + description: storage_model.description, + return_url: storage_model.return_url, + metadata: storage_model.metadata, + connector_id: storage_model.connector_id, + shipping_address_id: storage_model.shipping_address_id, + billing_address_id: storage_model.billing_address_id, + statement_descriptor_name: storage_model.statement_descriptor_name, + statement_descriptor_suffix: storage_model.statement_descriptor_suffix, + created_at: storage_model.created_at, + modified_at: storage_model.modified_at, + last_synced: storage_model.last_synced, + setup_future_usage: storage_model.setup_future_usage, + off_session: storage_model.off_session, + client_secret: storage_model.client_secret, + active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), + business_country: storage_model.business_country, + business_label: storage_model.business_label, + order_details: storage_model.order_details, + allowed_payment_method_types: storage_model.allowed_payment_method_types, + connector_metadata: storage_model.connector_metadata, + feature_metadata: storage_model.feature_metadata, + attempt_count: storage_model.attempt_count, + profile_id: storage_model.profile_id, + merchant_decision: storage_model.merchant_decision, + payment_link_id: storage_model.payment_link_id, + payment_confirm_source: storage_model.payment_confirm_source, + updated_by: storage_model.updated_by, + surcharge_applicable: storage_model.surcharge_applicable, + request_incremental_authorization: storage_model.request_incremental_authorization, + incremental_authorization_allowed: storage_model.incremental_authorization_allowed, + authorization_count: storage_model.authorization_count, + fingerprint_id: storage_model.fingerprint_id, + session_expiry: storage_model.session_expiry, + request_external_three_ds_authentication: storage_model + .request_external_three_ds_authentication, + charges: storage_model.charges, + frm_metadata: storage_model.frm_metadata, + }) + } + + async fn construct_new(self) -> CustomResult { + Ok(DieselPaymentIntentNew { + payment_id: self.payment_id, + merchant_id: self.merchant_id, + status: self.status, + amount: self.amount, + currency: self.currency, + amount_captured: self.amount_captured, + customer_id: self.customer_id, + description: self.description, + return_url: self.return_url, + metadata: self.metadata, + connector_id: self.connector_id, + shipping_address_id: self.shipping_address_id, + billing_address_id: self.billing_address_id, + statement_descriptor_name: self.statement_descriptor_name, + statement_descriptor_suffix: self.statement_descriptor_suffix, + created_at: Some(self.created_at), + modified_at: Some(self.modified_at), + last_synced: self.last_synced, + setup_future_usage: self.setup_future_usage, + off_session: self.off_session, + client_secret: self.client_secret, + active_attempt_id: self.active_attempt.get_id(), + business_country: self.business_country, + business_label: self.business_label, + order_details: self.order_details, + allowed_payment_method_types: self.allowed_payment_method_types, + connector_metadata: self.connector_metadata, + feature_metadata: self.feature_metadata, + attempt_count: self.attempt_count, + profile_id: self.profile_id, + merchant_decision: self.merchant_decision, + payment_link_id: self.payment_link_id, + payment_confirm_source: self.payment_confirm_source, + updated_by: self.updated_by, + surcharge_applicable: self.surcharge_applicable, + request_incremental_authorization: self.request_incremental_authorization, + incremental_authorization_allowed: self.incremental_authorization_allowed, + authorization_count: self.authorization_count, + fingerprint_id: self.fingerprint_id, + session_expiry: self.session_expiry, + request_external_three_ds_authentication: self.request_external_three_ds_authentication, + charges: self.charges, + frm_metadata: self.frm_metadata, + }) + } +} diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index d413e97289..2c99360d4f 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -8,19 +8,21 @@ use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use super::{payment_attempt::PaymentAttempt, PaymentIntent}; -use crate::{errors, RemoteStorageObject}; +use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject}; #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( &self, this: PaymentIntent, payment_intent: PaymentIntentUpdate, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; async fn insert_payment_intent( &self, - new: PaymentIntentNew, + new: PaymentIntent, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; @@ -28,6 +30,7 @@ pub trait PaymentIntentInterface { &self, payment_id: &str, merchant_id: &str, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; @@ -42,6 +45,7 @@ pub trait PaymentIntentInterface { &self, merchant_id: &str, filters: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; @@ -50,6 +54,7 @@ pub trait PaymentIntentInterface { &self, merchant_id: &str, time_range: &api_models::payments::TimeRange, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; @@ -58,6 +63,7 @@ pub trait PaymentIntentInterface { &self, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; @@ -440,6 +446,247 @@ impl From for PaymentIntentUpdateInternal { } } +use diesel_models::PaymentIntentUpdate as DieselPaymentIntentUpdate; + +impl From for DieselPaymentIntentUpdate { + fn from(value: PaymentIntentUpdate) -> Self { + match value { + PaymentIntentUpdate::ResponseUpdate { + status, + amount_captured, + fingerprint_id, + return_url, + updated_by, + incremental_authorization_allowed, + } => Self::ResponseUpdate { + status, + amount_captured, + fingerprint_id, + return_url, + updated_by, + incremental_authorization_allowed, + }, + PaymentIntentUpdate::MetadataUpdate { + metadata, + updated_by, + } => Self::MetadataUpdate { + metadata, + updated_by, + }, + PaymentIntentUpdate::ReturnUrlUpdate { + return_url, + status, + customer_id, + shipping_address_id, + billing_address_id, + updated_by, + } => Self::ReturnUrlUpdate { + return_url, + status, + customer_id, + shipping_address_id, + billing_address_id, + updated_by, + }, + PaymentIntentUpdate::MerchantStatusUpdate { + status, + shipping_address_id, + billing_address_id, + updated_by, + } => Self::MerchantStatusUpdate { + status, + shipping_address_id, + billing_address_id, + updated_by, + }, + PaymentIntentUpdate::PGStatusUpdate { + status, + updated_by, + incremental_authorization_allowed, + } => Self::PGStatusUpdate { + status, + updated_by, + incremental_authorization_allowed, + }, + PaymentIntentUpdate::Update { + amount, + currency, + setup_future_usage, + status, + customer_id, + shipping_address_id, + billing_address_id, + return_url, + business_country, + business_label, + description, + statement_descriptor_name, + statement_descriptor_suffix, + order_details, + metadata, + payment_confirm_source, + updated_by, + fingerprint_id, + session_expiry, + request_external_three_ds_authentication, + frm_metadata, + } => Self::Update { + amount, + currency, + setup_future_usage, + status, + customer_id, + shipping_address_id, + billing_address_id, + return_url, + business_country, + business_label, + description, + statement_descriptor_name, + statement_descriptor_suffix, + order_details, + metadata, + payment_confirm_source, + updated_by, + fingerprint_id, + session_expiry, + request_external_three_ds_authentication, + frm_metadata, + }, + PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { + active_attempt_id, + attempt_count, + updated_by, + } => Self::PaymentAttemptAndAttemptCountUpdate { + active_attempt_id, + attempt_count, + updated_by, + }, + PaymentIntentUpdate::StatusAndAttemptUpdate { + status, + active_attempt_id, + attempt_count, + updated_by, + } => Self::StatusAndAttemptUpdate { + status, + active_attempt_id, + attempt_count, + updated_by, + }, + PaymentIntentUpdate::ApproveUpdate { + status, + merchant_decision, + updated_by, + } => Self::ApproveUpdate { + status, + merchant_decision, + updated_by, + }, + PaymentIntentUpdate::RejectUpdate { + status, + merchant_decision, + updated_by, + } => Self::RejectUpdate { + status, + merchant_decision, + updated_by, + }, + PaymentIntentUpdate::SurchargeApplicableUpdate { + surcharge_applicable, + updated_by, + } => Self::SurchargeApplicableUpdate { + surcharge_applicable: Some(surcharge_applicable), + updated_by, + }, + PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => { + Self::IncrementalAuthorizationAmountUpdate { amount } + } + PaymentIntentUpdate::AuthorizationCountUpdate { + authorization_count, + } => Self::AuthorizationCountUpdate { + authorization_count, + }, + PaymentIntentUpdate::CompleteAuthorizeUpdate { + shipping_address_id, + } => Self::CompleteAuthorizeUpdate { + shipping_address_id, + }, + } + } +} + +impl From for diesel_models::PaymentIntentUpdateInternal { + fn from(value: PaymentIntentUpdateInternal) -> Self { + let modified_at = Some(common_utils::date_time::now()); + + let PaymentIntentUpdateInternal { + amount, + currency, + status, + amount_captured, + customer_id, + return_url, + setup_future_usage, + off_session, + metadata, + billing_address_id, + shipping_address_id, + modified_at: _, + active_attempt_id, + business_country, + business_label, + description, + statement_descriptor_name, + statement_descriptor_suffix, + order_details, + attempt_count, + merchant_decision, + payment_confirm_source, + updated_by, + surcharge_applicable, + incremental_authorization_allowed, + authorization_count, + session_expiry, + fingerprint_id, + request_external_three_ds_authentication, + frm_metadata, + } = value; + + Self { + amount, + currency, + status, + amount_captured, + customer_id, + return_url, + setup_future_usage, + off_session, + metadata, + billing_address_id, + shipping_address_id, + modified_at, + active_attempt_id, + business_country, + business_label, + description, + statement_descriptor_name, + statement_descriptor_suffix, + order_details, + attempt_count, + merchant_decision, + payment_confirm_source, + updated_by, + surcharge_applicable, + incremental_authorization_allowed, + authorization_count, + session_expiry, + fingerprint_id, + request_external_three_ds_authentication, + frm_metadata, + } + } +} + pub enum PaymentIntentFetchConstraints { Single { payment_intent_id: String }, List(Box), diff --git a/crates/hyperswitch_domain_models/src/type_encryption.rs b/crates/hyperswitch_domain_models/src/type_encryption.rs new file mode 100644 index 0000000000..82d904ccf4 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/type_encryption.rs @@ -0,0 +1,237 @@ +use async_trait::async_trait; +use common_utils::{ + crypto, + errors::{self, CustomResult}, + ext_traits::AsyncExt, + metrics::utils::record_operation_time, +}; +use diesel_models::encryption::Encryption; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; +use router_env::{instrument, tracing}; + +#[async_trait] +pub trait TypeEncryption< + T, + V: crypto::EncodeMessage + crypto::DecodeMessage, + S: masking::Strategy, +>: Sized +{ + async fn encrypt( + masked_data: Secret, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; + + async fn decrypt( + encrypted_data: Encryption, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; +} + +#[async_trait] +impl< + V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, + S: masking::Strategy + Send, + > TypeEncryption for crypto::Encryptable> +{ + #[instrument(skip_all)] + async fn encrypt( + masked_data: Secret, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let encrypted_data = crypt_algo.encode_message(key, masked_data.peek().as_bytes())?; + + Ok(Self::new(masked_data, encrypted_data.into())) + } + + #[instrument(skip_all)] + async fn decrypt( + encrypted_data: Encryption, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let encrypted = encrypted_data.into_inner(); + let data = crypt_algo.decode_message(key, encrypted.clone())?; + + let value: String = std::str::from_utf8(&data) + .change_context(errors::CryptoError::DecodingFailed)? + .to_string(); + + Ok(Self::new(value.into(), encrypted)) + } +} + +#[async_trait] +impl< + V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, + S: masking::Strategy + Send, + > TypeEncryption + for crypto::Encryptable> +{ + #[instrument(skip_all)] + async fn encrypt( + masked_data: Secret, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let data = serde_json::to_vec(&masked_data.peek()) + .change_context(errors::CryptoError::DecodingFailed)?; + let encrypted_data = crypt_algo.encode_message(key, &data)?; + + Ok(Self::new(masked_data, encrypted_data.into())) + } + + #[instrument(skip_all)] + async fn decrypt( + encrypted_data: Encryption, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let encrypted = encrypted_data.into_inner(); + let data = crypt_algo.decode_message(key, encrypted.clone())?; + + let value: serde_json::Value = + serde_json::from_slice(&data).change_context(errors::CryptoError::DecodingFailed)?; + + Ok(Self::new(value.into(), encrypted)) + } +} + +#[async_trait] +impl< + V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, + S: masking::Strategy> + Send, + > TypeEncryption, V, S> for crypto::Encryptable, S>> +{ + #[instrument(skip_all)] + async fn encrypt( + masked_data: Secret, S>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let encrypted_data = crypt_algo.encode_message(key, masked_data.peek())?; + + Ok(Self::new(masked_data, encrypted_data.into())) + } + + #[instrument(skip_all)] + async fn decrypt( + encrypted_data: Encryption, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + let encrypted = encrypted_data.into_inner(); + let data = crypt_algo.decode_message(key, encrypted.clone())?; + + Ok(Self::new(data.into(), encrypted)) + } +} + +pub trait Lift { + type SelfWrapper; + type OtherWrapper; + + fn lift(self, func: Func) -> Self::OtherWrapper + where + Func: Fn(Self::SelfWrapper) -> Self::OtherWrapper; +} + +impl Lift for Option { + type SelfWrapper = Option; + type OtherWrapper = CustomResult, E>; + + fn lift(self, func: Func) -> Self::OtherWrapper + where + Func: Fn(Self::SelfWrapper) -> Self::OtherWrapper, + { + func(self) + } +} + +#[async_trait] +pub trait AsyncLift { + type SelfWrapper; + type OtherWrapper; + + async fn async_lift(self, func: Func) -> Self::OtherWrapper + where + Func: Fn(Self::SelfWrapper) -> F + Send + Sync, + F: futures::Future> + Send; +} + +#[async_trait] +impl + Lift = V> + Send> AsyncLift for V { + type SelfWrapper = >::SelfWrapper; + type OtherWrapper = >::OtherWrapper; + + async fn async_lift(self, func: Func) -> Self::OtherWrapper + where + Func: Fn(Self::SelfWrapper) -> F + Send + Sync, + F: futures::Future> + Send, + { + func(self).await + } +} + +#[inline] +pub async fn encrypt( + inner: Secret, + key: &[u8], +) -> CustomResult>, errors::CryptoError> +where + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + record_operation_time( + crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256), + &metrics::ENCRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await +} + +#[inline] +pub async fn encrypt_optional( + inner: Option>, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + Secret: Send, + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + inner.async_map(|f| encrypt(f, key)).await.transpose() +} + +#[inline] +pub async fn decrypt>( + inner: Option, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + crypto::Encryptable>: TypeEncryption, +{ + record_operation_time( + inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)), + &metrics::DECRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await + .transpose() +} + +pub(crate) mod metrics { + use router_env::{global_meter, histogram_metric, metrics_context, once_cell}; + + metrics_context!(CONTEXT); + global_meter!(GLOBAL_METER, "ROUTER_API"); + + // Encryption and Decryption metrics + histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER); + histogram_metric!(DECRYPTION_TIME, GLOBAL_METER); +} diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index c8eff11530..871eed8689 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -71,7 +71,7 @@ qrcode = "0.14.0" rand = "0.8.5" rand_chacha = "0.3.1" regex = "1.10.4" -reqwest = { version = "0.11.27", features = ["json", "native-tls","__rustls", "gzip", "multipart"] } +reqwest = { version = "0.11.27", features = ["json", "native-tls", "__rustls", "gzip", "multipart"] } ring = "0.17.8" roxmltree = "0.19.0" rust_decimal = { version = "1.35.0", features = ["serde-with-float", "serde-with-str"] } @@ -102,7 +102,7 @@ api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] analytics = { version = "0.1.0", path = "../analytics", optional = true } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs"] } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics"] } hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } @@ -126,7 +126,7 @@ isocountry = "0.3.2" iso_currency = "0.4.4" actix-http = "3.6.0" events = { version = "0.1.0", path = "../events" } -totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"]} +totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"] } serde_repr = "0.1.19" unidecode = "0.3.0" diff --git a/crates/router/src/compatibility/stripe/payment_intents.rs b/crates/router/src/compatibility/stripe/payment_intents.rs index b8ef4137f9..79b6b0fc00 100644 --- a/crates/router/src/compatibility/stripe/payment_intents.rs +++ b/crates/router/src/compatibility/stripe/payment_intents.rs @@ -497,7 +497,9 @@ pub async fn payment_intent_list( state.into_inner(), &req, payload, - |state, auth, req, _| payments::list_payments(state, auth.merchant_account, req), + |state, auth, req, _| { + payments::list_payments(state, auth.merchant_account, auth.key_store, req) + }, &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index 6c0d3fcd6c..118dbb3f66 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -303,6 +303,7 @@ async fn delete_card_bin_blocklist_entry( pub async fn validate_data_for_blocklist( state: &SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, ) -> CustomResult where @@ -407,6 +408,7 @@ where merchant_decision: Some(MerchantDecision::Rejected.to_string()), updated_by: merchant_account.storage_scheme.to_string(), }, + key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/cards_info.rs b/crates/router/src/core/cards_info.rs index bc01a786d1..b0c1aca532 100644 --- a/crates/router/src/core/cards_info.rs +++ b/crates/router/src/core/cards_info.rs @@ -23,6 +23,7 @@ fn verify_iin_length(card_iin: &str) -> Result<(), errors::ApiErrorResponse> { pub async fn retrieve_card_info( state: routes::SessionState, merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, request: api_models::cards_info::CardsInfoRequest, ) -> RouterResponse { let db = state.store.as_ref(); @@ -31,6 +32,7 @@ pub async fn retrieve_card_info( helpers::verify_payment_intent_time_and_client_secret( db, &merchant_account, + &key_store, request.client_secret, ) .await?; diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 8836128690..f42f3ae6a1 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -91,6 +91,7 @@ pub async fn accept_dispute( .find_payment_intent_by_payment_id_merchant_id( &dispute.payment_id, &merchant_account.merchant_id, + &key_store, merchant_account.storage_scheme, ) .await @@ -204,6 +205,7 @@ pub async fn submit_evidence( .find_payment_intent_by_payment_id_merchant_id( &dispute.payment_id, &merchant_account.merchant_id, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/files/helpers.rs b/crates/router/src/core/files/helpers.rs index 0be198377b..98d13812ad 100644 --- a/crates/router/src/core/files/helpers.rs +++ b/crates/router/src/core/files/helpers.rs @@ -264,6 +264,7 @@ pub async fn upload_and_get_provider_provider_file_id_profile_id( .find_payment_intent_by_payment_id_merchant_id( &dispute.payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index f7f7108c2b..a0a7ca3bf3 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -417,6 +417,7 @@ where .to_update_tracker()? .update_tracker( &*state.store, + &key_store, frm_data.clone(), payment_data, None, @@ -491,6 +492,7 @@ where .to_update_tracker()? .update_tracker( &*state.store, + &key_store, frm_data.to_owned(), payment_data, None, @@ -514,7 +516,7 @@ where merchant_account, frm_configs, &mut frm_suggestion, - key_store, + key_store.clone(), payment_data, customer, should_continue_capture, @@ -525,6 +527,7 @@ where .to_update_tracker()? .update_tracker( &*state.store, + &key_store, frm_data.to_owned(), payment_data, frm_suggestion, @@ -653,6 +656,7 @@ pub async fn frm_fulfillment_core( .find_payment_intent_by_payment_id_merchant_id( &req.payment_id.clone(), &merchant_account.merchant_id, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs index 33a370ac84..8c6f8b7bf5 100644 --- a/crates/router/src/core/fraud_check/operation.rs +++ b/crates/router/src/core/fraud_check/operation.rs @@ -102,6 +102,7 @@ pub trait UpdateTracker: Send { async fn update_tracker<'b>( &'b self, db: &dyn StorageInterface, + key_store: &domain::MerchantKeyStore, frm_data: D, payment_data: &mut payments::PaymentData, _frm_suggestion: Option, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index cc4fc0370e..7c97eee8b4 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -330,6 +330,7 @@ impl UpdateTracker for FraudCheckPost { async fn update_tracker<'b>( &'b self, db: &dyn StorageInterface, + key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, frm_suggestion: Option, @@ -520,6 +521,7 @@ impl UpdateTracker for FraudCheckPost { merchant_decision, updated_by: frm_data.merchant_account.storage_scheme.to_string(), }, + key_store, frm_data.merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs index 272cd5aa70..c153acc754 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs @@ -219,6 +219,7 @@ impl UpdateTracker for FraudCheckPre { async fn update_tracker<'b>( &'b self, db: &dyn StorageInterface, + _key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, _frm_suggestion: Option, diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index 9fa4b47821..3301c75b53 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -78,9 +78,13 @@ pub async fn revoke_mandate( common_enums::MandateStatus::Active | common_enums::MandateStatus::Inactive | common_enums::MandateStatus::Pending => { - let profile_id = - helpers::get_profile_id_for_mandate(&state, &merchant_account, mandate.clone()) - .await?; + let profile_id = helpers::get_profile_id_for_mandate( + &state, + &merchant_account, + &key_store, + mandate.clone(), + ) + .await?; let merchant_connector_account = payment_helper::get_merchant_connector_account( &state, diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index b0c727925d..7fb0cfa28a 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -14,6 +14,7 @@ use crate::{ pub async fn get_profile_id_for_mandate( state: &SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, mandate: Mandate, ) -> CustomResult { let profile_id = if let Some(ref payment_id) = mandate.original_payment_id { @@ -22,6 +23,7 @@ pub async fn get_profile_id_for_mandate( .find_payment_intent_by_payment_id_merchant_id( payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index e4ef38ae35..99bff1fd69 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -49,6 +49,7 @@ pub async fn retrieve_payment_link( pub async fn initiate_payment_link_flow( state: SessionState, merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, merchant_id: String, payment_id: String, ) -> RouterResponse { @@ -57,6 +58,7 @@ pub async fn initiate_payment_link_flow( .find_payment_intent_by_payment_id_merchant_id( &payment_id, &merchant_id, + &key_store, merchant_account.storage_scheme, ) .await @@ -503,6 +505,7 @@ fn check_payment_link_invalid_conditions( pub async fn get_payment_link_status( state: SessionState, merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, merchant_id: String, payment_id: String, ) -> RouterResponse { @@ -511,6 +514,7 @@ pub async fn get_payment_link_status( .find_payment_intent_by_payment_id_merchant_id( &payment_id, &merchant_id, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index f14997b20a..3e23b24294 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -62,12 +62,7 @@ use crate::{ }, db, logger, pii::prelude::*, - routes::{ - self, - app::SessionStateInfo, - metrics::{self, request}, - payment_methods::ParentPaymentMethodToken, - }, + routes::{self, app::SessionStateInfo, metrics, payment_methods::ParentPaymentMethodToken}, services, types::{ api::{self, routing as routing_types, PaymentMethodCreateExt}, @@ -1105,7 +1100,7 @@ pub async fn add_card_to_locker( errors::VaultError, > { metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); - let add_card_to_hs_resp = request::record_operation_time( + let add_card_to_hs_resp = common_utils::metrics::utils::record_operation_time( async { add_card_hs( state, @@ -1130,6 +1125,7 @@ pub async fn add_card_to_locker( }) }, &metrics::CARD_ADD_TIME, + &metrics::CONTEXT, &[router_env::opentelemetry::KeyValue::new("locker", "rust")], ) .await?; @@ -1146,7 +1142,7 @@ pub async fn get_card_from_locker( ) -> errors::RouterResult { metrics::GET_FROM_LOCKER.add(&metrics::CONTEXT, 1, &[]); - let get_card_from_rs_locker_resp = request::record_operation_time( + let get_card_from_rs_locker_resp = common_utils::metrics::utils::record_operation_time( async { get_card_from_hs_locker( state, @@ -1171,6 +1167,7 @@ pub async fn get_card_from_locker( }) }, &metrics::CARD_GET_TIME, + &metrics::CONTEXT, &[router_env::opentelemetry::KeyValue::new("locker", "rust")], ) .await?; @@ -1187,7 +1184,7 @@ pub async fn delete_card_from_locker( ) -> errors::RouterResult { metrics::DELETE_FROM_LOCKER.add(&metrics::CONTEXT, 1, &[]); - request::record_operation_time( + common_utils::metrics::utils::record_operation_time( async move { delete_card_from_hs_locker(state, customer_id, merchant_id, card_reference) .await @@ -1197,6 +1194,7 @@ pub async fn delete_card_from_locker( }) }, &metrics::CARD_DELETE_TIME, + &metrics::CONTEXT, &[], ) .await @@ -1759,6 +1757,7 @@ pub async fn list_payment_methods( helpers::verify_payment_intent_time_and_client_secret( db, &merchant_account, + &key_store, req.client_secret.clone(), ) .await? @@ -2685,6 +2684,7 @@ pub async fn list_payment_methods( Box::pin(call_surcharge_decision_management( state, &merchant_account, + &key_store, &business_profile, payment_attempt, payment_intent, @@ -2763,9 +2763,11 @@ async fn validate_payment_method_and_client_secret( Ok(()) } +#[allow(clippy::too_many_arguments)] pub async fn call_surcharge_decision_management( state: routes::SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, business_profile: &BusinessProfile, payment_attempt: &storage::PaymentAttempt, payment_intent: storage::PaymentIntent, @@ -2804,6 +2806,7 @@ pub async fn call_surcharge_decision_management( surcharge_applicable: true, updated_by: merchant_account.storage_scheme.to_string(), }, + key_store, merchant_account.storage_scheme, ) .await @@ -2816,6 +2819,7 @@ pub async fn call_surcharge_decision_management( pub async fn call_surcharge_decision_management_for_saved_card( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, business_profile: &BusinessProfile, payment_attempt: &storage::PaymentAttempt, payment_intent: storage::PaymentIntent, @@ -2851,6 +2855,7 @@ pub async fn call_surcharge_decision_management_for_saved_card( surcharge_applicable: true, updated_by: merchant_account.storage_scheme.to_string(), }, + key_store, merchant_account.storage_scheme, ) .await @@ -3143,6 +3148,7 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( helpers::verify_payment_intent_time_and_client_secret( db, &merchant_account, + &key_store, cloned_secret, ) .await?; @@ -3478,6 +3484,7 @@ pub async fn list_customer_payment_method( call_surcharge_decision_management_for_saved_card( state, &merchant_account, + &key_store, &business_profile, &payment_attempt, payment_intent, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index f445d476f4..9e9156992f 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -330,6 +330,7 @@ where &validate_result.payment_id, payment_data, router_data, + &key_store, merchant_account.storage_scheme, ) .await? @@ -434,6 +435,7 @@ where &validate_result.payment_id, payment_data, router_data, + &key_store, merchant_account.storage_scheme, ) .await? @@ -1158,6 +1160,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { .find_payment_intent_by_payment_id_merchant_id( &payment_id, &merchant_id, + &merchant_key_store, merchant_account.storage_scheme, ) .await @@ -1437,7 +1440,7 @@ where *payment_data = pd; // Validating the blocklist guard and generate the fingerprint - blocklist_guard(state, merchant_account, operation, payment_data).await?; + blocklist_guard(state, merchant_account, key_store, operation, payment_data).await?; let updated_customer = call_create_connector_customer_if_required( state, @@ -1622,6 +1625,7 @@ where async fn blocklist_guard( state: &SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, operation: &BoxedOperation<'_, F, ApiRequest>, payment_data: &mut PaymentData, ) -> CustomResult @@ -1650,7 +1654,7 @@ where if blocklist_guard_enabled { Ok(operation .to_domain()? - .guard_payment_against_blocklist(state, merchant_account, payment_data) + .guard_payment_against_blocklist(state, merchant_account, key_store, payment_data) .await?) } else { Ok(false) @@ -2660,16 +2664,22 @@ pub fn is_operation_complete_authorize(operation: &Op) -> bool { pub async fn list_payments( state: SessionState, merchant: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, constraints: api::PaymentListConstraints, ) -> RouterResponse { use hyperswitch_domain_models::errors::StorageError; helpers::validate_payment_list_request(&constraints)?; let merchant_id = &merchant.merchant_id; let db = state.store.as_ref(); - let payment_intents = - helpers::filter_by_constraints(db, &constraints, merchant_id, merchant.storage_scheme) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + let payment_intents = helpers::filter_by_constraints( + db, + &constraints, + merchant_id, + &key_store, + merchant.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let collected_futures = payment_intents.into_iter().map(|pi| { async { @@ -2726,6 +2736,7 @@ pub async fn list_payments( pub async fn apply_filters_on_payments( state: SessionState, merchant: domain::MerchantAccount, + merchant_key_store: domain::MerchantKeyStore, constraints: api::PaymentListFilterConstraints, ) -> RouterResponse { let limit = &constraints.limit; @@ -2735,6 +2746,7 @@ pub async fn apply_filters_on_payments( .get_filtered_payment_intents_attempt( &merchant.merchant_id, &constraints.clone().into(), + &merchant_key_store, merchant.storage_scheme, ) .await @@ -2779,6 +2791,7 @@ pub async fn apply_filters_on_payments( pub async fn get_filters_for_payments( state: SessionState, merchant: domain::MerchantAccount, + merchant_key_store: domain::MerchantKeyStore, time_range: api::TimeRange, ) -> RouterResponse { let db = state.store.as_ref(); @@ -2786,6 +2799,7 @@ pub async fn get_filters_for_payments( .filter_payment_intents_by_time_range_constraints( &merchant.merchant_id, &time_range, + &merchant_key_store, merchant.storage_scheme, ) .await @@ -3891,7 +3905,12 @@ pub async fn payment_external_authentication( let storage_scheme = merchant_account.storage_scheme; let payment_id = req.payment_id; let payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + &key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let attempt_id = payment_intent.active_attempt.get_id().clone(); diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 0c696c1b83..e9a793efb8 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -604,6 +604,7 @@ pub async fn get_token_for_recurring_mandate( db.find_payment_intent_by_payment_id_merchant_id( payment_id, &mandate.merchant_id, + merchant_key_store, merchant_account.storage_scheme, ) .await @@ -2412,12 +2413,14 @@ pub(super) async fn filter_by_constraints( db: &dyn StorageInterface, constraints: &api::PaymentListConstraints, merchant_id: &str, + key_store: &domain::MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { let result = db .filter_payment_intent_by_constraints( merchant_id, &constraints.clone().into(), + key_store, storage_scheme, ) .await?; @@ -2833,6 +2836,7 @@ pub(crate) fn validate_pm_or_token_given( pub async fn verify_payment_intent_time_and_client_secret( db: &dyn StorageInterface, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, client_secret: Option, ) -> error_stack::Result, errors::ApiErrorResponse> { client_secret @@ -2843,6 +2847,7 @@ pub async fn verify_payment_intent_time_and_client_secret( .find_payment_intent_by_payment_id_merchant_id( &payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -2938,7 +2943,6 @@ mod tests { #[test] fn test_authenticate_client_secret_fulfillment_time_not_expired() { let payment_intent = PaymentIntent { - id: 21, payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, @@ -2998,7 +3002,6 @@ mod tests { #[test] fn test_authenticate_client_secret_fulfillment_time_expired() { let payment_intent = PaymentIntent { - id: 21, payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, @@ -3057,7 +3060,6 @@ mod tests { #[test] fn test_authenticate_client_secret_expired() { let payment_intent = PaymentIntent { - id: 21, payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, @@ -3589,6 +3591,7 @@ impl AttemptType { fetched_payment_intent: PaymentIntent, fetched_payment_attempt: PaymentAttempt, db: &dyn StorageInterface, + key_store: &domain::MerchantKeyStore, storage_scheme: storage::enums::MerchantStorageScheme, ) -> RouterResult<(PaymentIntent, PaymentAttempt)> { match self { @@ -3630,6 +3633,7 @@ impl AttemptType { attempt_count: new_attempt_count, updated_by: storage_scheme.to_string(), }, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 45e45bbe9c..b7eaf5fc0e 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -183,6 +183,7 @@ pub trait Domain: Send + Sync { &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -226,6 +227,7 @@ pub trait PostUpdateTracker: Send { payment_id: &api::PaymentIdType, payment_data: D, response: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult where @@ -322,6 +324,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -394,6 +397,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -467,6 +471,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -529,6 +534,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index a492cb7603..172c5943c0 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -52,7 +52,12 @@ impl GetTracker, api::PaymentsCaptureRequest> .change_context(errors::ApiErrorResponse::PaymentNotFound)?; payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -200,7 +205,7 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, _updated_customer: Option, - _merchant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( @@ -224,6 +229,7 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for .update_payment_intent( payment_data.payment_intent, intent_status_update, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index ce79f9392c..94591d44e4 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -50,7 +50,12 @@ impl GetTracker, api::PaymentsCancelRequest> .change_context(errors::ApiErrorResponse::PaymentNotFound)?; let payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -213,7 +218,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, - _mechant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( @@ -242,6 +247,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for .update_payment_intent( payment_data.payment_intent, payment_intent_update, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 02c69681a8..f6e6dee14a 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -53,7 +53,12 @@ impl GetTracker, api::PaymentsCaptu .change_context(errors::ApiErrorResponse::PaymentNotFound)?; payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 1ff95c37f6..b2e8696730 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -52,7 +52,12 @@ impl GetTracker, api::PaymentsRequest> for Co .change_context(errors::ApiErrorResponse::PaymentNotFound)?; payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; payment_intent.setup_future_usage = request @@ -418,6 +423,7 @@ impl Domain for CompleteAuthorize { &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -435,7 +441,7 @@ impl UpdateTracker, api::PaymentsRequest> for Comple _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, _updated_customer: Option, - _merchant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> @@ -450,7 +456,12 @@ impl UpdateTracker, api::PaymentsRequest> for Comple let payment_intent = payment_data.payment_intent.clone(); let updated_payment_intent = db - .update_payment_intent(payment_intent, payment_intent_update, storage_scheme) + .update_payment_intent( + payment_intent, + payment_intent_update, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index cff51ed809..bfd63111e2 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -74,6 +74,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .find_payment_intent_by_payment_id_merchant_id( &payment_id, m_merchant_id.as_str(), + key_store, storage_scheme, ) .await @@ -304,6 +305,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_intent, payment_attempt, &*state.store, + key_store, storage_scheme, ) .await?; @@ -860,9 +862,16 @@ impl Domain for PaymentConfirm { &'a self, state: &SessionState, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, ) -> CustomResult { - blocklist_utils::validate_data_for_blocklist(state, merchant_account, payment_data).await + blocklist_utils::validate_data_for_blocklist( + state, + merchant_account, + key_store, + payment_data, + ) + .await } #[instrument(skip_all)] @@ -1218,6 +1227,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_db = state.clone().store; let m_storage_scheme = storage_scheme.to_string(); let session_expiry = m_payment_data_payment_intent.session_expiry; + let m_key_store = key_store.clone(); let payment_intent_fut = tokio::spawn( async move { @@ -1246,6 +1256,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen request_external_three_ds_authentication: None, frm_metadata: m_frm_metadata, }, + &m_key_store, storage_scheme, ) .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index bb249e9a9c..870fd78abe 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -281,7 +281,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; payment_intent = db - .insert_payment_intent(payment_intent_new, storage_scheme) + .insert_payment_intent(payment_intent_new, merchant_key_store, storage_scheme) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { payment_id: payment_id.clone(), @@ -545,6 +545,7 @@ impl Domain for PaymentCreate { &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -562,7 +563,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, - _merchant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> @@ -639,6 +640,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen billing_address_id: None, updated_by: storage_scheme.to_string(), }, + key_store, storage_scheme, ) .await @@ -964,8 +966,8 @@ impl PaymentCreate { active_attempt_id: String, profile_id: String, session_expiry: PrimitiveDateTime, - ) -> RouterResult { - let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now()); + ) -> RouterResult { + let created_at @ modified_at @ last_synced = common_utils::date_time::now(); let status = helpers::payment_intent_status_fsm( request @@ -1021,7 +1023,7 @@ impl PaymentCreate { .change_context(errors::ApiErrorResponse::InternalServerError)? .map(Secret::new); - Ok(storage::PaymentIntentNew { + Ok(storage::PaymentIntent { payment_id: payment_id.to_string(), merchant_id: merchant_account.merchant_id.to_string(), status, @@ -1030,7 +1032,7 @@ impl PaymentCreate { description: request.description.clone(), created_at, modified_at, - last_synced, + last_synced: Some(last_synced), client_secret: Some(client_secret), setup_future_usage: request.setup_future_usage, off_session: request.off_session, diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index cf8217718c..a3a8130a9f 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -47,7 +47,12 @@ impl GetTracker, PaymentsCancelRequest> for P .change_context(errors::ApiErrorResponse::PaymentNotFound)?; let payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -197,7 +202,7 @@ impl UpdateTracker, PaymentsCancelRequest> for Payme _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, - _mechant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _should_decline_transaction: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, PaymentsCancelRequest>, PaymentData)> @@ -231,6 +236,7 @@ impl UpdateTracker, PaymentsCancelRequest> for Payme .update_payment_intent( payment_data.payment_intent, intent_status_update, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 4843d1145d..4b529d2b39 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -59,6 +59,7 @@ impl PostUpdateTracker, types::PaymentsAuthor types::PaymentsAuthorizeData, types::PaymentsResponseData, >, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -73,6 +74,7 @@ impl PostUpdateTracker, types::PaymentsAuthor payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -245,6 +247,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu types::PaymentsIncrementalAuthorizationData, types::PaymentsResponseData, >, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -305,6 +308,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu .update_payment_intent( payment_data.payment_intent.clone(), payment_intent_update, + key_store, storage_scheme, ) .await @@ -372,6 +376,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for payment_id: &api::PaymentIdType, payment_data: PaymentData, router_data: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -382,6 +387,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await @@ -422,6 +428,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -432,6 +439,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -450,6 +458,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -460,6 +469,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -476,7 +486,7 @@ impl PostUpdateTracker, types::PaymentsCancelData> f payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, - + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -487,6 +497,7 @@ impl PostUpdateTracker, types::PaymentsCancelData> f payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -505,7 +516,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, - + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -516,6 +527,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -532,7 +544,7 @@ impl PostUpdateTracker, types::PaymentsRejectData> f payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, - + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -543,6 +555,7 @@ impl PostUpdateTracker, types::PaymentsRejectData> f payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -565,7 +578,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa types::SetupMandateRequestData, types::PaymentsResponseData, >, - + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -581,6 +594,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa payment_id, payment_data, router_data, + key_store, storage_scheme, )) .await?; @@ -662,6 +676,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData payment_id: &api::PaymentIdType, payment_data: PaymentData, response: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> where @@ -672,6 +687,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData payment_id, payment_data, response, + key_store, storage_scheme, )) .await @@ -708,6 +724,7 @@ async fn payment_response_update_tracker( _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> { // Update additional payment data with the payment method response that we received from connector @@ -1133,6 +1150,7 @@ async fn payment_response_update_tracker( }; let m_db = state.clone().store; + let m_key_store = key_store.clone(); let m_payment_data_payment_intent = payment_data.payment_intent.clone(); let m_payment_intent_update = payment_intent_update.clone(); let payment_intent_fut = tokio::spawn( @@ -1140,6 +1158,7 @@ async fn payment_response_update_tracker( m_db.update_payment_intent( m_payment_data_payment_intent, m_payment_intent_update, + &m_key_store, storage_scheme, ) .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 4d81914aca..cd63d3aedb 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -52,7 +52,12 @@ impl GetTracker, api::PaymentsSessionRequest> let storage_scheme = merchant_account.storage_scheme; let mut payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -224,7 +229,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, _updated_customer: Option, - _mechant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( @@ -244,6 +249,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for metadata, updated_by: storage_scheme.to_string(), }, + key_store, storage_scheme, ) .await @@ -460,6 +466,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> errors::CustomResult { Ok(false) diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 7c1c117a72..865da4fb79 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -50,7 +50,12 @@ impl GetTracker, api::PaymentsStartRequest> f .change_context(errors::ApiErrorResponse::PaymentNotFound)?; payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -337,6 +342,7 @@ where &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 9971ea76aa..afe571275b 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -133,6 +133,7 @@ impl Domain for PaymentStatus { &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -219,7 +220,7 @@ async fn get_tracker_for_sync< >( payment_id: &api::PaymentIdType, merchant_account: &domain::MerchantAccount, - mechant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, db: &dyn StorageInterface, request: &api::PaymentsRetrieveRequest, operation: Op, @@ -231,6 +232,7 @@ async fn get_tracker_for_sync< db, payment_id, &merchant_account.merchant_id, + key_store, storage_scheme, ) .await?; @@ -245,7 +247,7 @@ async fn get_tracker_for_sync< let shipping_address = helpers::get_address_by_id( db, payment_intent.shipping_address_id.clone(), - mechant_key_store, + key_store, &payment_intent.payment_id.clone(), &merchant_account.merchant_id, merchant_account.storage_scheme, @@ -254,7 +256,7 @@ async fn get_tracker_for_sync< let billing_address = helpers::get_address_by_id( db, payment_intent.billing_address_id.clone(), - mechant_key_store, + key_store, &payment_intent.payment_id.clone(), &merchant_account.merchant_id, merchant_account.storage_scheme, @@ -264,7 +266,7 @@ async fn get_tracker_for_sync< let payment_method_billing = helpers::get_address_by_id( db, payment_attempt.payment_method_billing_address_id.clone(), - mechant_key_store, + key_store, &payment_intent.payment_id.clone(), &merchant_account.merchant_id, merchant_account.storage_scheme, @@ -514,11 +516,11 @@ impl ValidateRequest for Payme } } -#[inline] pub async fn get_payment_intent_payment_attempt( db: &dyn StorageInterface, payment_id: &api::PaymentIdType, merchant_id: &str, + key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<(storage::PaymentIntent, storage::PaymentAttempt)> { let get_pi_pa = || async { @@ -526,7 +528,12 @@ pub async fn get_payment_intent_payment_attempt( match payment_id { api_models::payments::PaymentIdType::PaymentIntentId(ref id) => { pi = db - .find_payment_intent_by_payment_id_merchant_id(id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + id, + merchant_id, + key_store, + storage_scheme, + ) .await?; pa = db .find_payment_attempt_by_payment_id_merchant_id_attempt_id( @@ -549,6 +556,7 @@ pub async fn get_payment_intent_payment_attempt( .find_payment_intent_by_payment_id_merchant_id( pa.payment_id.as_str(), merchant_id, + key_store, storage_scheme, ) .await?; @@ -561,6 +569,7 @@ pub async fn get_payment_intent_payment_attempt( .find_payment_intent_by_payment_id_merchant_id( pa.payment_id.as_str(), merchant_id, + key_store, storage_scheme, ) .await?; @@ -578,6 +587,7 @@ pub async fn get_payment_intent_payment_attempt( .find_payment_intent_by_payment_id_merchant_id( pa.payment_id.as_str(), merchant_id, + key_store, storage_scheme, ) .await?; diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index ffa122e54a..a299b70e89 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -56,7 +56,12 @@ impl GetTracker, api::PaymentsRequest> for Pa let db = &*state.store; payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -548,6 +553,7 @@ impl Domain for PaymentUpdate { &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut PaymentData, ) -> CustomResult { Ok(false) @@ -565,7 +571,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, _updated_customer: Option, - _key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> @@ -721,6 +727,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .request_external_three_ds_authentication, frm_metadata, }, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 26ffd65035..249306693a 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -45,7 +45,7 @@ impl payment_id: &api::PaymentIdType, request: &PaymentsIncrementalAuthorizationRequest, merchant_account: &domain::MerchantAccount, - _key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _payment_confirm_source: Option, ) -> RouterResult> @@ -58,7 +58,12 @@ impl .change_context(errors::ApiErrorResponse::PaymentNotFound)?; let payment_intent = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -180,7 +185,7 @@ impl UpdateTracker, PaymentsIncrementalAut _customer: Option, storage_scheme: enums::MerchantStorageScheme, _updated_customer: Option, - _mechant_key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, _frm_suggestion: Option, _header_payload: api::HeaderPayload, ) -> RouterResult<( @@ -238,6 +243,7 @@ impl UpdateTracker, PaymentsIncrementalAut storage::PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count: new_authorization_count, }, + key_store, storage_scheme, ) .await @@ -336,6 +342,7 @@ impl Domain &'a self, _state: &SessionState, _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, _payment_data: &mut payments::PaymentData, ) -> CustomResult { Ok(false) diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 82a08064a3..a04af7b0fe 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -299,6 +299,7 @@ where state, connector.connector_name.to_string(), payment_data, + key_store, merchant_account.storage_scheme, router_data, is_step_up, @@ -329,6 +330,7 @@ pub async fn modify_trackers( state: &routes::SessionState, connector: String, payment_data: &mut payments::PaymentData, + key_store: &domain::MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, router_data: types::RouterData, is_step_up: bool, @@ -462,6 +464,7 @@ where attempt_count: new_attempt_count, updated_by: storage_scheme.to_string(), }, + key_store, storage_scheme, ) .await diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 09d18963a2..e31a4dd75c 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -98,6 +98,7 @@ pub async fn create_link_token( let payment_intent = oss_helpers::verify_payment_intent_time_and_client_secret( &*state.store, &merchant_account, + &key_store, payload.client_secret, ) .await?; @@ -280,6 +281,7 @@ async fn store_bank_details_in_payment_methods( .find_payment_intent_by_payment_id_merchant_id( &payload.payment_id, &merchant_account.merchant_id, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 20b1b7a2f7..9fac4e595a 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -57,6 +57,7 @@ pub async fn refund_create_core( .find_payment_intent_by_payment_id_merchant_id( &req.payment_id, merchant_id, + &key_store, merchant_account.storage_scheme, ) .await @@ -363,6 +364,7 @@ pub async fn refund_retrieve_core( .find_payment_intent_by_payment_id_merchant_id( payment_id, merchant_id, + &key_store, merchant_account.storage_scheme, ) .await @@ -1127,6 +1129,7 @@ pub async fn trigger_refund_execute_workflow( .find_payment_intent_by_payment_id_merchant_id( &payment_attempt.payment_id, &refund.merchant_id, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/router/src/core/user/sample_data.rs b/crates/router/src/core/user/sample_data.rs index bab620620f..e1ccb3b033 100644 --- a/crates/router/src/core/user/sample_data.rs +++ b/crates/router/src/core/user/sample_data.rs @@ -1,12 +1,13 @@ use api_models::user::sample_data::SampleDataRequest; use common_utils::errors::ReportSwitchExt; use diesel_models::{user::sample_data::PaymentAttemptBatchNew, RefundNew}; -use hyperswitch_domain_models::payments::payment_intent::PaymentIntentNew; +use error_stack::ResultExt; +use hyperswitch_domain_models::payments::PaymentIntent; pub type SampleDataApiResponse = SampleDataResult>; use crate::{ - core::errors::sample_data::SampleDataResult, + core::errors::sample_data::{SampleDataError, SampleDataResult}, routes::{app::ReqState, SessionState}, services::{authentication::UserFromToken, ApplicationResponse}, utils::user::sample_data::generate_sample_data, @@ -21,8 +22,18 @@ pub async fn generate_sample_data_for_user( let sample_data = generate_sample_data(&state, req, user_from_token.merchant_id.as_str()).await?; + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + &user_from_token.merchant_id, + &state.store.get_master_key().to_vec().into(), + ) + .await + .change_context(SampleDataError::InternalServerError) + .attach_printable("Not able to fetch merchant key store")?; // If not able to fetch merchant key store for any reason, this should be an internal server error + let (payment_intents, payment_attempts, refunds): ( - Vec, + Vec, Vec, Vec, ) = sample_data.into_iter().fold( @@ -39,7 +50,7 @@ pub async fn generate_sample_data_for_user( state .store - .insert_payment_intents_batch_for_sample_data(payment_intents) + .insert_payment_intents_batch_for_sample_data(payment_intents, &key_store) .await .switch()?; state @@ -64,9 +75,19 @@ pub async fn delete_sample_data_for_user( ) -> SampleDataApiResponse<()> { let merchant_id_del = user_from_token.merchant_id.as_str(); + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + &user_from_token.merchant_id, + &state.store.get_master_key().to_vec().into(), + ) + .await + .change_context(SampleDataError::InternalServerError) + .attach_printable("Not able to fetch merchant key store")?; // If not able to fetch merchant key store for any reason, this should be an internal server error + state .store - .delete_payment_intents_for_sample_data(merchant_id_del) + .delete_payment_intents_for_sample_data(merchant_id_del, &key_store) .await .switch()?; state diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 75b830cbb4..ca36a53418 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1326,11 +1326,12 @@ impl PaymentIntentInterface for KafkaStore { &self, this: storage::PaymentIntent, payment_intent: storage::PaymentIntentUpdate, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { let intent = self .diesel_store - .update_payment_intent(this.clone(), payment_intent, storage_scheme) + .update_payment_intent(this.clone(), payment_intent, key_store, storage_scheme) .await?; if let Err(er) = self @@ -1346,13 +1347,14 @@ impl PaymentIntentInterface for KafkaStore { async fn insert_payment_intent( &self, - new: storage::PaymentIntentNew, + new: storage::PaymentIntent, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { logger::debug!("Inserting PaymentIntent Via KafkaStore"); let intent = self .diesel_store - .insert_payment_intent(new, storage_scheme) + .insert_payment_intent(new, key_store, storage_scheme) .await?; if let Err(er) = self @@ -1370,10 +1372,16 @@ impl PaymentIntentInterface for KafkaStore { &self, payment_id: &str, merchant_id: &str, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .find_payment_intent_by_payment_id_merchant_id(payment_id, merchant_id, storage_scheme) + .find_payment_intent_by_payment_id_merchant_id( + payment_id, + merchant_id, + key_store, + storage_scheme, + ) .await } @@ -1382,10 +1390,11 @@ impl PaymentIntentInterface for KafkaStore { &self, merchant_id: &str, filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { self.diesel_store - .filter_payment_intent_by_constraints(merchant_id, filters, storage_scheme) + .filter_payment_intent_by_constraints(merchant_id, filters, key_store, storage_scheme) .await } @@ -1394,12 +1403,14 @@ impl PaymentIntentInterface for KafkaStore { &self, merchant_id: &str, time_range: &api_models::payments::TimeRange, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { self.diesel_store .filter_payment_intents_by_time_range_constraints( merchant_id, time_range, + key_store, storage_scheme, ) .await @@ -1410,6 +1421,7 @@ impl PaymentIntentInterface for KafkaStore { &self, merchant_id: &str, constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, + key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult< Vec<( @@ -1419,7 +1431,12 @@ impl PaymentIntentInterface for KafkaStore { errors::DataStorageError, > { self.diesel_store - .get_filtered_payment_intents_attempt(merchant_id, constraints, storage_scheme) + .get_filtered_payment_intents_attempt( + merchant_id, + constraints, + key_store, + storage_scheme, + ) .await } @@ -2567,14 +2584,15 @@ impl DashboardMetadataInterface for KafkaStore { impl BatchSampleDataInterface for KafkaStore { async fn insert_payment_intents_batch_for_sample_data( &self, - batch: Vec, + batch: Vec, + key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< Vec, hyperswitch_domain_models::errors::StorageError, > { let payment_intents_list = self .diesel_store - .insert_payment_intents_batch_for_sample_data(batch) + .insert_payment_intents_batch_for_sample_data(batch, key_store) .await?; for payment_intent in payment_intents_list.iter() { @@ -2629,13 +2647,14 @@ impl BatchSampleDataInterface for KafkaStore { async fn delete_payment_intents_for_sample_data( &self, merchant_id: &str, + key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< Vec, hyperswitch_domain_models::errors::StorageError, > { let payment_intents_list = self .diesel_store - .delete_payment_intents_for_sample_data(merchant_id) + .delete_payment_intents_for_sample_data(merchant_id, key_store) .await?; for payment_intent in payment_intents_list.iter() { diff --git a/crates/router/src/db/user/sample_data.rs b/crates/router/src/db/user/sample_data.rs index f7926021af..5a23666c7b 100644 --- a/crates/router/src/db/user/sample_data.rs +++ b/crates/router/src/db/user/sample_data.rs @@ -5,9 +5,12 @@ use diesel_models::{ user::sample_data::PaymentAttemptBatchNew, }; use error_stack::{Report, ResultExt}; +use futures::{future::try_join_all, FutureExt}; use hyperswitch_domain_models::{ + behaviour::Conversion, errors::StorageError, - payments::{payment_attempt::PaymentAttempt, payment_intent::PaymentIntentNew, PaymentIntent}, + merchant_key_store::MerchantKeyStore, + payments::{payment_attempt::PaymentAttempt, PaymentIntent}, }; use storage_impl::DataModelExt; @@ -17,7 +20,8 @@ use crate::{connection::pg_connection_write, core::errors::CustomResult, service pub trait BatchSampleDataInterface { async fn insert_payment_intents_batch_for_sample_data( &self, - batch: Vec, + batch: Vec, + key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; async fn insert_payment_attempts_batch_for_sample_data( @@ -33,6 +37,7 @@ pub trait BatchSampleDataInterface { async fn delete_payment_intents_for_sample_data( &self, merchant_id: &str, + key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; async fn delete_payment_attempts_for_sample_data( @@ -50,20 +55,30 @@ pub trait BatchSampleDataInterface { impl BatchSampleDataInterface for Store { async fn insert_payment_intents_batch_for_sample_data( &self, - batch: Vec, + batch: Vec, + key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { let conn = pg_connection_write(self) .await .change_context(StorageError::DatabaseConnectionError)?; - let new_intents = batch.into_iter().map(|i| i.to_storage_model()).collect(); + let new_intents = try_join_all(batch.into_iter().map(|payment_intent| async { + payment_intent + .construct_new() + .await + .change_context(StorageError::EncryptionError) + })) + .await?; + sample_data_queries::insert_payment_intents(&conn, new_intents) .await .map_err(diesel_error_to_data_error) .map(|v| { - v.into_iter() - .map(PaymentIntent::from_storage_model) - .collect() - }) + try_join_all(v.into_iter().map(|payment_intent| { + PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + })) + .map(|join_result| join_result.change_context(StorageError::DecryptionError)) + })? + .await } async fn insert_payment_attempts_batch_for_sample_data( @@ -97,6 +112,7 @@ impl BatchSampleDataInterface for Store { async fn delete_payment_intents_for_sample_data( &self, merchant_id: &str, + key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { let conn = pg_connection_write(self) .await @@ -105,10 +121,12 @@ impl BatchSampleDataInterface for Store { .await .map_err(diesel_error_to_data_error) .map(|v| { - v.into_iter() - .map(PaymentIntent::from_storage_model) - .collect() - }) + try_join_all(v.into_iter().map(|payment_intent| { + PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + })) + .map(|join_result| join_result.change_context(StorageError::DecryptionError)) + })? + .await } async fn delete_payment_attempts_for_sample_data( @@ -144,7 +162,8 @@ impl BatchSampleDataInterface for Store { impl BatchSampleDataInterface for storage_impl::MockDb { async fn insert_payment_intents_batch_for_sample_data( &self, - _batch: Vec, + _batch: Vec, + _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { Err(StorageError::MockDbError)? } @@ -166,6 +185,7 @@ impl BatchSampleDataInterface for storage_impl::MockDb { async fn delete_payment_intents_for_sample_data( &self, _merchant_id: &str, + _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { Err(StorageError::MockDbError)? } diff --git a/crates/router/src/routes/cards_info.rs b/crates/router/src/routes/cards_info.rs index ca59e072a9..889b6e0ec4 100644 --- a/crates/router/src/routes/cards_info.rs +++ b/crates/router/src/routes/cards_info.rs @@ -46,7 +46,9 @@ pub async fn card_iin_info( state, &req, payload, - |state, auth, req, _| cards_info::retrieve_card_info(state, auth.merchant_account, req), + |state, auth, req, _| { + cards_info::retrieve_card_info(state, auth.merchant_account, auth.key_store, req) + }, &*auth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/routes/metrics.rs b/crates/router/src/routes/metrics.rs index fffb5e20c9..0d55a4b45c 100644 --- a/crates/router/src/routes/metrics.rs +++ b/crates/router/src/routes/metrics.rs @@ -91,10 +91,6 @@ histogram_metric!(CARD_ADD_TIME, GLOBAL_METER); histogram_metric!(CARD_GET_TIME, GLOBAL_METER); histogram_metric!(CARD_DELETE_TIME, GLOBAL_METER); -// Encryption and Decryption metrics -histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER); -histogram_metric!(DECRYPTION_TIME, GLOBAL_METER); - // Apple Pay Flow Metrics counter_metric!(APPLE_PAY_MANUAL_FLOW, GLOBAL_METER); counter_metric!(APPLE_PAY_SIMPLIFIED_FLOW, GLOBAL_METER); diff --git a/crates/router/src/routes/metrics/request.rs b/crates/router/src/routes/metrics/request.rs index 905f089a0c..41f8c8d807 100644 --- a/crates/router/src/routes/metrics/request.rs +++ b/crates/router/src/routes/metrics/request.rs @@ -1,4 +1,4 @@ -use router_env::{metrics::add_attributes, opentelemetry}; +use router_env::metrics::add_attributes; use super::utils as metric_utils; use crate::services::ApplicationResponse; @@ -25,20 +25,6 @@ where result } -#[inline] -pub async fn record_operation_time( - future: F, - metric: &once_cell::sync::Lazy>, - key_value: &[opentelemetry::KeyValue], -) -> R -where - F: futures::Future, -{ - let (result, time) = metric_utils::time_future(future).await; - metric.record(&super::CONTEXT, time.as_secs_f64(), key_value); - result -} - pub fn status_code_metrics(status_code: String, flow: String, merchant_id: String) { super::REQUEST_STATUS.add( &super::CONTEXT, diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index f375a18ab6..fed182f03a 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -71,6 +71,7 @@ pub async fn initiate_payment_link( initiate_payment_link_flow( state, auth.merchant_account, + auth.key_store, payload.merchant_id.clone(), payload.payment_id.clone(), ) @@ -144,6 +145,7 @@ pub async fn payment_link_status( get_payment_link_status( state, auth.merchant_account, + auth.key_store, payload.merchant_id.clone(), payload.payment_id.clone(), ) diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index a89410a2b0..e6e4d23867 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -957,7 +957,9 @@ pub async fn payments_list( state, &req, payload, - |state, auth, req, _| payments::list_payments(state, auth.merchant_account, req), + |state, auth, req, _| { + payments::list_payments(state, auth.merchant_account, auth.key_store, req) + }, auth::auth_type( &auth::ApiKeyAuth, &auth::JWTAuth(Permission::PaymentRead), @@ -982,7 +984,7 @@ pub async fn payments_list_by_filter( &req, payload, |state, auth: auth::AuthenticationData, req, _| { - payments::apply_filters_on_payments(state, auth.merchant_account, req) + payments::apply_filters_on_payments(state, auth.merchant_account, auth.key_store, req) }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, @@ -1004,7 +1006,7 @@ pub async fn get_filters_for_payments( &req, payload, |state, auth: auth::AuthenticationData, req, _| { - payments::get_filters_for_payments(state, auth.merchant_account, req) + payments::get_filters_for_payments(state, auth.merchant_account, auth.key_store, req) }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 0c6ce07e51..dcb1b5c130 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -51,8 +51,7 @@ use crate::{ logger, routes::{ app::{AppStateInfo, ReqState, SessionStateInfo}, - metrics::{self, request as metrics_request}, - AppState, SessionState, + metrics, AppState, SessionState, }, types::{ self, @@ -537,9 +536,10 @@ pub async fn send_request( .attach_printable("Unable to send request to connector") }; - let response = metrics_request::record_operation_time( + let response = common_utils::metrics::utils::record_operation_time( send_request, &metrics::EXTERNAL_REQUEST_TIME, + &metrics::CONTEXT, &[metrics_tag.clone()], ) .await; @@ -564,9 +564,10 @@ pub async fn send_request( logger::info!( "Retrying request due to connection closed before message could complete" ); - metrics_request::record_operation_time( + common_utils::metrics::utils::record_operation_time( cloned_request, &metrics::EXTERNAL_REQUEST_TIME, + &metrics::CONTEXT, &[metrics_tag], ) .await diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index d18ae0d019..be93f30bfb 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -1,10 +1,15 @@ mod address; -pub mod behaviour; +pub mod behaviour { + pub use hyperswitch_domain_models::behaviour::{Conversion, ReverseConversion}; +} + mod customer; mod event; mod merchant_account; mod merchant_connector_account; -mod merchant_key_store; +mod merchant_key_store { + pub use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore; +} pub mod payments; pub mod types; #[cfg(feature = "olap")] diff --git a/crates/router/src/types/domain/types.rs b/crates/router/src/types/domain/types.rs index 0ef4e8579d..176b88b3f5 100644 --- a/crates/router/src/types/domain/types.rs +++ b/crates/router/src/types/domain/types.rs @@ -1,225 +1,3 @@ -use async_trait::async_trait; -use common_utils::{ - crypto, - errors::{self, CustomResult}, - ext_traits::AsyncExt, +pub use hyperswitch_domain_models::type_encryption::{ + decrypt, encrypt, encrypt_optional, AsyncLift, Lift, TypeEncryption, }; -use diesel_models::encryption::Encryption; -use error_stack::ResultExt; -use masking::{PeekInterface, Secret}; -use router_env::{instrument, tracing}; - -use crate::routes::metrics::{request, DECRYPTION_TIME, ENCRYPTION_TIME}; - -#[async_trait] -pub trait TypeEncryption< - T, - V: crypto::EncodeMessage + crypto::DecodeMessage, - S: masking::Strategy, ->: Sized -{ - async fn encrypt( - masked_data: Secret, - key: &[u8], - crypt_algo: V, - ) -> CustomResult; - - async fn decrypt( - encrypted_data: Encryption, - key: &[u8], - crypt_algo: V, - ) -> CustomResult; -} - -#[async_trait] -impl< - V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, - > TypeEncryption for crypto::Encryptable> -{ - #[instrument(skip_all)] - async fn encrypt( - masked_data: Secret, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let encrypted_data = crypt_algo.encode_message(key, masked_data.peek().as_bytes())?; - - Ok(Self::new(masked_data, encrypted_data.into())) - } - - #[instrument(skip_all)] - async fn decrypt( - encrypted_data: Encryption, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let encrypted = encrypted_data.into_inner(); - let data = crypt_algo.decode_message(key, encrypted.clone())?; - - let value: String = std::str::from_utf8(&data) - .change_context(errors::CryptoError::DecodingFailed)? - .to_string(); - - Ok(Self::new(value.into(), encrypted)) - } -} - -#[async_trait] -impl< - V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, - > TypeEncryption - for crypto::Encryptable> -{ - #[instrument(skip_all)] - async fn encrypt( - masked_data: Secret, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let data = serde_json::to_vec(&masked_data.peek()) - .change_context(errors::CryptoError::DecodingFailed)?; - let encrypted_data = crypt_algo.encode_message(key, &data)?; - - Ok(Self::new(masked_data, encrypted_data.into())) - } - - #[instrument(skip_all)] - async fn decrypt( - encrypted_data: Encryption, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let encrypted = encrypted_data.into_inner(); - let data = crypt_algo.decode_message(key, encrypted.clone())?; - - let value: serde_json::Value = - serde_json::from_slice(&data).change_context(errors::CryptoError::DecodingFailed)?; - - Ok(Self::new(value.into(), encrypted)) - } -} - -#[async_trait] -impl< - V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy> + Send, - > TypeEncryption, V, S> for crypto::Encryptable, S>> -{ - #[instrument(skip_all)] - async fn encrypt( - masked_data: Secret, S>, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let encrypted_data = crypt_algo.encode_message(key, masked_data.peek())?; - - Ok(Self::new(masked_data, encrypted_data.into())) - } - - #[instrument(skip_all)] - async fn decrypt( - encrypted_data: Encryption, - key: &[u8], - crypt_algo: V, - ) -> CustomResult { - let encrypted = encrypted_data.into_inner(); - let data = crypt_algo.decode_message(key, encrypted.clone())?; - - Ok(Self::new(data.into(), encrypted)) - } -} - -pub trait Lift { - type SelfWrapper; - type OtherWrapper; - - fn lift(self, func: Func) -> Self::OtherWrapper - where - Func: Fn(Self::SelfWrapper) -> Self::OtherWrapper; -} - -impl Lift for Option { - type SelfWrapper = Option; - type OtherWrapper = CustomResult, E>; - - fn lift(self, func: Func) -> Self::OtherWrapper - where - Func: Fn(Self::SelfWrapper) -> Self::OtherWrapper, - { - func(self) - } -} - -#[async_trait] -pub trait AsyncLift { - type SelfWrapper; - type OtherWrapper; - - async fn async_lift(self, func: Func) -> Self::OtherWrapper - where - Func: Fn(Self::SelfWrapper) -> F + Send + Sync, - F: futures::Future> + Send; -} - -#[async_trait] -impl + Lift = V> + Send> AsyncLift for V { - type SelfWrapper = >::SelfWrapper; - type OtherWrapper = >::OtherWrapper; - - async fn async_lift(self, func: Func) -> Self::OtherWrapper - where - Func: Fn(Self::SelfWrapper) -> F + Send + Sync, - F: futures::Future> + Send, - { - func(self).await - } -} - -#[inline] -pub async fn encrypt( - inner: Secret, - key: &[u8], -) -> CustomResult>, errors::CryptoError> -where - S: masking::Strategy, - crypto::Encryptable>: TypeEncryption, -{ - request::record_operation_time( - crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256), - &ENCRYPTION_TIME, - &[], - ) - .await -} - -#[inline] -pub async fn encrypt_optional( - inner: Option>, - key: &[u8], -) -> CustomResult>>, errors::CryptoError> -where - Secret: Send, - S: masking::Strategy, - crypto::Encryptable>: TypeEncryption, -{ - inner.async_map(|f| encrypt(f, key)).await.transpose() -} - -#[inline] -pub async fn decrypt>( - inner: Option, - key: &[u8], -) -> CustomResult>>, errors::CryptoError> -where - crypto::Encryptable>: TypeEncryption, -{ - request::record_operation_time( - inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)), - &DECRYPTION_TIME, - &[], - ) - .await - .transpose() -} diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 2aabed475b..9662220d43 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -197,12 +197,14 @@ pub async fn find_payment_intent_from_payment_id_type( db: &dyn StorageInterface, payment_id_type: payments::PaymentIdType, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, ) -> CustomResult { match payment_id_type { payments::PaymentIdType::PaymentIntentId(payment_id) => db .find_payment_intent_by_payment_id_merchant_id( &payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -219,6 +221,7 @@ pub async fn find_payment_intent_from_payment_id_type( db.find_payment_intent_by_payment_id_merchant_id( &attempt.payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -236,6 +239,7 @@ pub async fn find_payment_intent_from_payment_id_type( db.find_payment_intent_by_payment_id_merchant_id( &attempt.payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -251,6 +255,7 @@ pub async fn find_payment_intent_from_refund_id_type( db: &dyn StorageInterface, refund_id_type: webhooks::RefundIdType, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, connector_name: &str, ) -> CustomResult { let refund = match refund_id_type { @@ -283,6 +288,7 @@ pub async fn find_payment_intent_from_refund_id_type( db.find_payment_intent_by_payment_id_merchant_id( &attempt.payment_id, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -293,6 +299,7 @@ pub async fn find_payment_intent_from_mandate_id_type( db: &dyn StorageInterface, mandate_id_type: webhooks::MandateIdType, merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, ) -> CustomResult { let mandate = match mandate_id_type { webhooks::MandateIdType::MandateId(mandate_id) => db @@ -318,6 +325,7 @@ pub async fn find_payment_intent_from_mandate_id_type( .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("original_payment_id not present in mandate record")?, &merchant_account.merchant_id, + key_store, merchant_account.storage_scheme, ) .await @@ -493,8 +501,13 @@ pub async fn get_mca_from_object_reference_id( get_mca_from_payment_intent( db, merchant_account, - find_payment_intent_from_payment_id_type(db, payment_id_type, merchant_account) - .await?, + find_payment_intent_from_payment_id_type( + db, + payment_id_type, + merchant_account, + key_store, + ) + .await?, key_store, connector_name, ) @@ -508,6 +521,7 @@ pub async fn get_mca_from_object_reference_id( db, refund_id_type, merchant_account, + key_store, connector_name, ) .await?, @@ -520,8 +534,13 @@ pub async fn get_mca_from_object_reference_id( get_mca_from_payment_intent( db, merchant_account, - find_payment_intent_from_mandate_id_type(db, mandate_id_type, merchant_account) - .await?, + find_payment_intent_from_mandate_id_type( + db, + mandate_id_type, + merchant_account, + key_store, + ) + .await?, key_store, connector_name, ) diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 9be2ddb57a..a2909e5f3c 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -5,7 +5,7 @@ use api_models::{ use common_utils::{id_type, types::MinorUnit}; use diesel_models::{user::sample_data::PaymentAttemptBatchNew, RefundNew}; use error_stack::ResultExt; -use hyperswitch_domain_models::payments::payment_intent::PaymentIntentNew; +use hyperswitch_domain_models::payments::PaymentIntent; use rand::{prelude::SliceRandom, thread_rng, Rng}; use time::OffsetDateTime; @@ -20,7 +20,7 @@ pub async fn generate_sample_data( state: &SessionState, req: SampleDataRequest, merchant_id: &str, -) -> SampleDataResult)>> { +) -> SampleDataResult)>> { let merchant_id = merchant_id.to_string(); let sample_data_size: usize = req.record.unwrap_or(100); @@ -101,7 +101,7 @@ pub async fn generate_sample_data( let mut rng = thread_rng(); random_array.shuffle(&mut rng); - let mut res: Vec<(PaymentIntentNew, PaymentAttemptBatchNew, Option)> = Vec::new(); + let mut res: Vec<(PaymentIntent, PaymentAttemptBatchNew, Option)> = Vec::new(); let start_time = req .start_time .unwrap_or(common_utils::date_time::now() - time::Duration::days(7)) @@ -172,7 +172,7 @@ pub async fn generate_sample_data( let is_failed_payment = (random_array.get(num - 1).unwrap_or(&0) % failure_after_attempts) == 0; - let payment_intent = PaymentIntentNew { + let payment_intent = PaymentIntent { payment_id: payment_id.clone(), merchant_id: merchant_id.clone(), status: match is_failed_payment { @@ -186,8 +186,8 @@ pub async fn generate_sample_data( .unwrap_or(&common_enums::Currency::USD), ), description: Some("This is a sample payment".to_string()), - created_at: Some(created_at), - modified_at: Some(modified_at), + created_at, + modified_at, last_synced: Some(last_synced), client_secret: Some(client_secret), business_country: business_country_default, diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index 666de8c337..5e3223ec67 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -149,6 +149,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { .update_payment_intent( payment_data.payment_intent, payment_intent_update, + &key_store, merchant_account.storage_scheme, ) .await diff --git a/crates/storage_impl/src/mock_db/payment_attempt.rs b/crates/storage_impl/src/mock_db/payment_attempt.rs index 0e8df898f1..547fc9f7d7 100644 --- a/crates/storage_impl/src/mock_db/payment_attempt.rs +++ b/crates/storage_impl/src/mock_db/payment_attempt.rs @@ -97,12 +97,9 @@ impl PaymentAttemptInterface for MockDb { storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult { let mut payment_attempts = self.payment_attempts.lock().await; - #[allow(clippy::as_conversions)] - let id = payment_attempts.len() as i32; let time = common_utils::date_time::now(); let payment_attempt = payment_attempt.populate_derived_fields(); let payment_attempt = PaymentAttempt { - id, payment_id: payment_attempt.payment_id, merchant_id: payment_attempt.merchant_id, attempt_id: payment_attempt.attempt_id, diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index a12b06b291..bf3ce0baaa 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -2,16 +2,17 @@ use common_utils::errors::CustomResult; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use hyperswitch_domain_models::{ + behaviour::Conversion, errors::StorageError, + merchant_key_store::MerchantKeyStore, payments::{ payment_attempt::PaymentAttempt, - payment_intent::{PaymentIntentInterface, PaymentIntentNew, PaymentIntentUpdate}, + payment_intent::{PaymentIntentInterface, PaymentIntentUpdate}, PaymentIntent, }, }; use super::MockDb; -use crate::DataModelExt; #[async_trait::async_trait] impl PaymentIntentInterface for MockDb { @@ -20,6 +21,7 @@ impl PaymentIntentInterface for MockDb { &self, _merchant_id: &str, _filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, + _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, StorageError> { // [#172]: Implement function for `MockDb` @@ -30,6 +32,7 @@ impl PaymentIntentInterface for MockDb { &self, _merchant_id: &str, _time_range: &api_models::payments::TimeRange, + _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, StorageError> { // [#172]: Implement function for `MockDb` @@ -50,6 +53,7 @@ impl PaymentIntentInterface for MockDb { &self, _merchant_id: &str, _constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, + _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, StorageError> { // [#172]: Implement function for `MockDb` @@ -59,60 +63,13 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::panic)] async fn insert_payment_intent( &self, - new: PaymentIntentNew, - storage_scheme: storage_enums::MerchantStorageScheme, + new: PaymentIntent, + _key_store: &MerchantKeyStore, + _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult { let mut payment_intents = self.payment_intents.lock().await; - let time = common_utils::date_time::now(); - let payment_intent = PaymentIntent { - #[allow(clippy::as_conversions)] - id: i32::try_from(payment_intents.len()).change_context(StorageError::MockDbError)?, - payment_id: new.payment_id, - merchant_id: new.merchant_id, - status: new.status, - amount: new.amount, - currency: new.currency, - amount_captured: new.amount_captured, - customer_id: new.customer_id, - description: new.description, - return_url: new.return_url, - metadata: new.metadata, - connector_id: new.connector_id, - shipping_address_id: new.shipping_address_id, - billing_address_id: new.billing_address_id, - statement_descriptor_name: new.statement_descriptor_name, - statement_descriptor_suffix: new.statement_descriptor_suffix, - created_at: new.created_at.unwrap_or(time), - modified_at: new.modified_at.unwrap_or(time), - last_synced: new.last_synced, - setup_future_usage: new.setup_future_usage, - off_session: new.off_session, - client_secret: new.client_secret, - business_country: new.business_country, - business_label: new.business_label, - active_attempt: new.active_attempt, - order_details: new.order_details, - allowed_payment_method_types: new.allowed_payment_method_types, - connector_metadata: new.connector_metadata, - feature_metadata: new.feature_metadata, - attempt_count: new.attempt_count, - profile_id: new.profile_id, - merchant_decision: new.merchant_decision, - payment_link_id: new.payment_link_id, - payment_confirm_source: new.payment_confirm_source, - updated_by: storage_scheme.to_string(), - surcharge_applicable: new.surcharge_applicable, - request_incremental_authorization: new.request_incremental_authorization, - incremental_authorization_allowed: new.incremental_authorization_allowed, - authorization_count: new.authorization_count, - fingerprint_id: new.fingerprint_id, - session_expiry: new.session_expiry, - request_external_three_ds_authentication: new.request_external_three_ds_authentication, - charges: new.charges, - frm_metadata: new.frm_metadata, - }; - payment_intents.push(payment_intent.clone()); - Ok(payment_intent) + payment_intents.push(new.clone()); + Ok(new) } // safety: only used for testing @@ -121,18 +78,29 @@ impl PaymentIntentInterface for MockDb { &self, this: PaymentIntent, update: PaymentIntentUpdate, + key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult { let mut payment_intents = self.payment_intents.lock().await; let payment_intent = payment_intents .iter_mut() - .find(|item| item.id == this.id) + .find(|item| item.payment_id == this.payment_id && item.merchant_id == this.merchant_id) .unwrap(); - *payment_intent = PaymentIntent::from_storage_model( - update - .to_storage_model() - .apply_changeset(this.to_storage_model()), - ); + + let diesel_payment_intent_update = diesel_models::PaymentIntentUpdate::from(update); + let diesel_payment_intent = payment_intent + .clone() + .convert() + .await + .change_context(StorageError::EncryptionError)?; + + *payment_intent = PaymentIntent::convert_back( + diesel_payment_intent_update.apply_changeset(diesel_payment_intent), + key_store.key.get_inner(), + ) + .await + .change_context(StorageError::DecryptionError)?; + Ok(payment_intent.clone()) } @@ -142,6 +110,7 @@ impl PaymentIntentInterface for MockDb { &self, payment_id: &str, merchant_id: &str, + _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult { let payment_intents = self.payment_intents.lock().await; diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index 1e60c3e02f..70cb60f521 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -14,6 +14,7 @@ use diesel_models::{ }; use error_stack::ResultExt; use hyperswitch_domain_models::{ + behaviour::Conversion, errors, mandates::{MandateAmountData, MandateDataType, MandateDetails}, payments::{ @@ -192,11 +193,13 @@ impl PaymentAttemptInterface for RouterStore { _storage_scheme: MerchantStorageScheme, ) -> CustomResult { let conn = pg_connection_read(self).await?; - let intents = pi - .iter() - .cloned() - .map(|pi| pi.to_storage_model()) - .collect::>(); + let intents = futures::future::try_join_all(pi.iter().cloned().map(|pi| async { + pi.convert() + .await + .change_context(errors::StorageError::EncryptionError) + })) + .await?; + DieselPaymentAttempt::get_filters_for_payments(&conn, intents.as_slice(), merchant_id) .await .map_err(|er| { @@ -352,7 +355,6 @@ impl PaymentAttemptInterface for KVRouterStore { }; let key_str = key.to_string(); let created_attempt = PaymentAttempt { - id: Default::default(), payment_id: payment_attempt.payment_id.clone(), merchant_id: payment_attempt.merchant_id.clone(), attempt_id: payment_attempt.attempt_id.clone(), @@ -1137,7 +1139,6 @@ impl DataModelExt for PaymentAttempt { fn to_storage_model(self) -> Self::StorageModel { DieselPaymentAttempt { - id: self.id, payment_id: self.payment_id, merchant_id: self.merchant_id, attempt_id: self.attempt_id, @@ -1207,7 +1208,6 @@ impl DataModelExt for PaymentAttempt { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), - id: storage_model.id, payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index f40a9c3e2d..62854cb261 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -4,7 +4,7 @@ use api_models::payments::AmountFilter; use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; #[cfg(feature = "olap")] use common_utils::errors::ReportSwitchExt; -use common_utils::{date_time, ext_traits::Encode}; +use common_utils::ext_traits::{AsyncExt, Encode}; #[cfg(feature = "olap")] use diesel::{associations::HasTable, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_models::{ @@ -12,8 +12,7 @@ use diesel_models::{ kv, payment_attempt::PaymentAttempt as DieselPaymentAttempt, payment_intent::{ - PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, - PaymentIntentUpdate as DieselPaymentIntentUpdate, + PaymentIntent as DieselPaymentIntent, PaymentIntentUpdate as DieselPaymentIntentUpdate, }, }; #[cfg(feature = "olap")] @@ -25,10 +24,12 @@ use error_stack::ResultExt; #[cfg(feature = "olap")] use hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints; use hyperswitch_domain_models::{ + behaviour::Conversion, errors::StorageError, + merchant_key_store::MerchantKeyStore, payments::{ payment_attempt::PaymentAttempt, - payment_intent::{PaymentIntentInterface, PaymentIntentNew, PaymentIntentUpdate}, + payment_intent::{PaymentIntentInterface, PaymentIntentUpdate}, PaymentIntent, }, RemoteStorageObject, @@ -52,12 +53,13 @@ use crate::{ impl PaymentIntentInterface for KVRouterStore { async fn insert_payment_intent( &self, - new: PaymentIntentNew, + payment_intent: PaymentIntent, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { - let merchant_id = new.merchant_id.clone(); - let payment_id = new.payment_id.clone(); - let field = format!("pi_{}", new.payment_id); + let merchant_id = payment_intent.merchant_id.clone(); + let payment_id = payment_intent.payment_id.clone(); + let field = format!("pi_{}", payment_intent.payment_id); let key = PartitionKey::MerchantIdPaymentId { merchant_id: &merchant_id, payment_id: &payment_id, @@ -67,70 +69,35 @@ impl PaymentIntentInterface for KVRouterStore { match storage_scheme { MerchantStorageScheme::PostgresOnly => { self.router_store - .insert_payment_intent(new, storage_scheme) + .insert_payment_intent(payment_intent, merchant_key_store, storage_scheme) .await } MerchantStorageScheme::RedisKv => { let key_str = key.to_string(); - let created_intent = PaymentIntent { - id: 0i32, - payment_id: new.payment_id.clone(), - merchant_id: new.merchant_id.clone(), - status: new.status, - amount: new.amount, - currency: new.currency, - amount_captured: new.amount_captured, - customer_id: new.customer_id.clone(), - description: new.description.clone(), - return_url: new.return_url.clone(), - metadata: new.metadata.clone(), - frm_metadata: new.frm_metadata.clone(), - connector_id: new.connector_id.clone(), - shipping_address_id: new.shipping_address_id.clone(), - billing_address_id: new.billing_address_id.clone(), - statement_descriptor_name: new.statement_descriptor_name.clone(), - statement_descriptor_suffix: new.statement_descriptor_suffix.clone(), - created_at: new.created_at.unwrap_or_else(date_time::now), - modified_at: new.created_at.unwrap_or_else(date_time::now), - last_synced: new.last_synced, - setup_future_usage: new.setup_future_usage, - off_session: new.off_session, - client_secret: new.client_secret.clone(), - business_country: new.business_country, - business_label: new.business_label.clone(), - active_attempt: new.active_attempt.clone(), - order_details: new.order_details.clone(), - allowed_payment_method_types: new.allowed_payment_method_types.clone(), - connector_metadata: new.connector_metadata.clone(), - feature_metadata: new.feature_metadata.clone(), - attempt_count: new.attempt_count, - profile_id: new.profile_id.clone(), - merchant_decision: new.merchant_decision.clone(), - payment_link_id: new.payment_link_id.clone(), - payment_confirm_source: new.payment_confirm_source, - updated_by: storage_scheme.to_string(), - surcharge_applicable: new.surcharge_applicable, - request_incremental_authorization: new.request_incremental_authorization, - incremental_authorization_allowed: new.incremental_authorization_allowed, - authorization_count: new.authorization_count, - fingerprint_id: new.fingerprint_id.clone(), - session_expiry: new.session_expiry, - request_external_three_ds_authentication: new - .request_external_three_ds_authentication, - charges: new.charges.clone(), - }; + let new_payment_intent = payment_intent + .clone() + .construct_new() + .await + .change_context(StorageError::EncryptionError)?; + let redis_entry = kv::TypedSql { op: kv::DBOperation::Insert { - insertable: kv::Insertable::PaymentIntent(new.to_storage_model()), + insertable: kv::Insertable::PaymentIntent(new_payment_intent), }, }; + let diesel_payment_intent = payment_intent + .clone() + .convert() + .await + .change_context(StorageError::EncryptionError)?; + match kv_wrapper::( self, KvOperation::::HSetNx( &field, - &created_intent.clone().to_storage_model(), + &diesel_payment_intent, redis_entry, ), key, @@ -144,7 +111,7 @@ impl PaymentIntentInterface for KVRouterStore { key: Some(key_str), } .into()), - Ok(HsetnxReply::KeySet) => Ok(created_intent), + Ok(HsetnxReply::KeySet) => Ok(payment_intent), Err(error) => Err(error.change_context(StorageError::KVError)), } } @@ -156,6 +123,7 @@ impl PaymentIntentInterface for KVRouterStore { &self, this: PaymentIntent, payment_intent_update: PaymentIntentUpdate, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { let merchant_id = this.merchant_id.clone(); @@ -174,14 +142,22 @@ impl PaymentIntentInterface for KVRouterStore { match storage_scheme { MerchantStorageScheme::PostgresOnly => { self.router_store - .update_payment_intent(this, payment_intent_update, storage_scheme) + .update_payment_intent( + this, + payment_intent_update, + merchant_key_store, + storage_scheme, + ) .await } MerchantStorageScheme::RedisKv => { let key_str = key.to_string(); - let diesel_intent_update = payment_intent_update.to_storage_model(); - let origin_diesel_intent = this.to_storage_model(); + let diesel_intent_update = DieselPaymentIntentUpdate::from(payment_intent_update); + let origin_diesel_intent = this + .convert() + .await + .change_context(StorageError::EncryptionError)?; let diesel_intent = diesel_intent_update .clone() @@ -213,7 +189,12 @@ impl PaymentIntentInterface for KVRouterStore { .try_into_hset() .change_context(StorageError::KVError)?; - Ok(PaymentIntent::from_storage_model(diesel_intent)) + let payment_intent = + PaymentIntent::convert_back(diesel_intent, merchant_key_store.key.get_inner()) + .await + .change_context(StorageError::DecryptionError)?; + + Ok(payment_intent) } } } @@ -223,6 +204,7 @@ impl PaymentIntentInterface for KVRouterStore { &self, payment_id: &str, merchant_id: &str, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { let database_call = || async { @@ -236,7 +218,7 @@ impl PaymentIntentInterface for KVRouterStore { }; let storage_scheme = decide_storage_scheme::<_, DieselPaymentIntent>(self, storage_scheme, Op::Find).await; - match storage_scheme { + let diesel_payment_intent = match storage_scheme { MerchantStorageScheme::PostgresOnly => database_call().await, MerchantStorageScheme::RedisKv => { @@ -259,8 +241,11 @@ impl PaymentIntentInterface for KVRouterStore { )) .await } - } - .map(PaymentIntent::from_storage_model) + }?; + + PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) + .await + .change_context(StorageError::DecryptionError) } async fn get_active_payment_attempt( @@ -295,10 +280,16 @@ impl PaymentIntentInterface for KVRouterStore { &self, merchant_id: &str, filters: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { self.router_store - .filter_payment_intent_by_constraints(merchant_id, filters, storage_scheme) + .filter_payment_intent_by_constraints( + merchant_id, + filters, + merchant_key_store, + storage_scheme, + ) .await } @@ -307,12 +298,14 @@ impl PaymentIntentInterface for KVRouterStore { &self, merchant_id: &str, time_range: &api_models::payments::TimeRange, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { self.router_store .filter_payment_intents_by_time_range_constraints( merchant_id, time_range, + merchant_key_store, storage_scheme, ) .await @@ -323,10 +316,16 @@ impl PaymentIntentInterface for KVRouterStore { &self, merchant_id: &str, filters: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { self.router_store - .get_filtered_payment_intents_attempt(merchant_id, filters, storage_scheme) + .get_filtered_payment_intents_attempt( + merchant_id, + filters, + merchant_key_store, + storage_scheme, + ) .await } @@ -352,18 +351,25 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn insert_payment_intent( &self, - new: PaymentIntentNew, + payment_intent: PaymentIntent, + merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { let conn = pg_connection_write(self).await?; - new.to_storage_model() + let diesel_payment_intent = payment_intent + .construct_new() + .await + .change_context(StorageError::EncryptionError)? .insert(&conn) .await .map_err(|er| { let new_err = diesel_error_to_data_error(er.current_context()); er.change_context(new_err) - }) - .map(PaymentIntent::from_storage_model) + })?; + + PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] @@ -371,17 +377,26 @@ impl PaymentIntentInterface for crate::RouterStore { &self, this: PaymentIntent, payment_intent: PaymentIntentUpdate, + merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { let conn = pg_connection_write(self).await?; - this.to_storage_model() - .update(&conn, payment_intent.to_storage_model()) + let diesel_payment_intent_update = DieselPaymentIntentUpdate::from(payment_intent); + + let diesel_payment_intent = this + .convert() + .await + .change_context(StorageError::EncryptionError)? + .update(&conn, diesel_payment_intent_update) .await .map_err(|er| { let new_err = diesel_error_to_data_error(er.current_context()); er.change_context(new_err) - }) - .map(PaymentIntent::from_storage_model) + })?; + + PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] @@ -389,16 +404,26 @@ impl PaymentIntentInterface for crate::RouterStore { &self, payment_id: &str, merchant_id: &str, + merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result { let conn = pg_connection_read(self).await?; + DieselPaymentIntent::find_by_payment_id_merchant_id(&conn, payment_id, merchant_id) .await - .map(PaymentIntent::from_storage_model) .map_err(|er| { let new_err = diesel_error_to_data_error(er.current_context()); er.change_context(new_err) }) + .async_and_then(|diesel_payment_intent| async { + PaymentIntent::convert_back( + diesel_payment_intent, + merchant_key_store.key.get_inner(), + ) + .await + .change_context(StorageError::DecryptionError) + }) + .await } #[instrument(skip_all)] @@ -435,9 +460,11 @@ impl PaymentIntentInterface for crate::RouterStore { &self, merchant_id: &str, filters: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { use common_utils::errors::ReportSwitchExt; + use futures::{future::try_join_all, FutureExt}; let conn = connection::pg_connection_read(self).await.switch()?; let conn = async_bb8_diesel::Connection::as_async_conn(&conn); @@ -473,6 +500,7 @@ impl PaymentIntentInterface for crate::RouterStore { .find_payment_intent_by_payment_id_merchant_id( starting_after_id, merchant_id, + merchant_key_store, storage_scheme, ) .await? @@ -490,6 +518,7 @@ impl PaymentIntentInterface for crate::RouterStore { .find_payment_intent_by_payment_id_merchant_id( ending_before_id, merchant_id, + merchant_key_store, storage_scheme, ) .await? @@ -529,18 +558,21 @@ impl PaymentIntentInterface for crate::RouterStore { ) .await .map(|payment_intents| { - payment_intents - .into_iter() - .map(PaymentIntent::from_storage_model) - .collect::>() + try_join_all(payment_intents.into_iter().map(|diesel_payment_intent| { + PaymentIntent::convert_back( + diesel_payment_intent, + merchant_key_store.key.get_inner(), + ) + })) + .map(|join_result| join_result.change_context(StorageError::DecryptionError)) }) .map_err(|er| { StorageError::DatabaseError( error_stack::report!(diesel_models::errors::DatabaseError::from(er)) .attach_printable("Error filtering payment records"), ) - .into() - }) + })? + .await } #[cfg(feature = "olap")] @@ -549,12 +581,18 @@ impl PaymentIntentInterface for crate::RouterStore { &self, merchant_id: &str, time_range: &api_models::payments::TimeRange, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { // TODO: Remove this redundant function let payment_filters = (*time_range).into(); - self.filter_payment_intent_by_constraints(merchant_id, &payment_filters, storage_scheme) - .await + self.filter_payment_intent_by_constraints( + merchant_id, + &payment_filters, + merchant_key_store, + storage_scheme, + ) + .await } #[cfg(feature = "olap")] @@ -563,8 +601,11 @@ impl PaymentIntentInterface for crate::RouterStore { &self, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, + merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { + use futures::{future::try_join_all, FutureExt}; + let conn = connection::pg_connection_read(self).await.switch()?; let conn = async_bb8_diesel::Connection::as_async_conn(&conn); let mut query = DieselPaymentIntent::table() @@ -601,6 +642,7 @@ impl PaymentIntentInterface for crate::RouterStore { .find_payment_intent_by_payment_id_merchant_id( starting_after_id, merchant_id, + merchant_key_store, storage_scheme, ) .await? @@ -618,6 +660,7 @@ impl PaymentIntentInterface for crate::RouterStore { .find_payment_intent_by_payment_id_merchant_id( ending_before_id, merchant_id, + merchant_key_store, storage_scheme, ) .await? @@ -701,23 +744,24 @@ impl PaymentIntentInterface for crate::RouterStore { .get_results_async::<(DieselPaymentIntent, DieselPaymentAttempt)>(conn) .await .map(|results| { - results - .into_iter() - .map(|(pi, pa)| { - ( - PaymentIntent::from_storage_model(pi), - PaymentAttempt::from_storage_model(pa), - ) - }) - .collect() + try_join_all(results.into_iter().map(|(pi, pa)| { + PaymentIntent::convert_back(pi, merchant_key_store.key.get_inner()).map( + |payment_intent| { + payment_intent.map(|payment_intent| { + (payment_intent, PaymentAttempt::from_storage_model(pa)) + }) + }, + ) + })) + .map(|join_result| join_result.change_context(StorageError::DecryptionError)) }) .map_err(|er| { StorageError::DatabaseError( error_stack::report!(diesel_models::errors::DatabaseError::from(er)) .attach_printable("Error filtering payment records"), ) - .into() - }) + })? + .await } #[cfg(feature = "olap")] @@ -802,381 +846,3 @@ impl PaymentIntentInterface for crate::RouterStore { }) } } - -impl DataModelExt for PaymentIntentNew { - type StorageModel = DieselPaymentIntentNew; - - fn to_storage_model(self) -> Self::StorageModel { - DieselPaymentIntentNew { - payment_id: self.payment_id, - merchant_id: self.merchant_id, - status: self.status, - amount: self.amount, - currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - frm_metadata: self.frm_metadata, - connector_id: self.connector_id, - shipping_address_id: self.shipping_address_id, - billing_address_id: self.billing_address_id, - statement_descriptor_name: self.statement_descriptor_name, - statement_descriptor_suffix: self.statement_descriptor_suffix, - created_at: self.created_at, - modified_at: self.modified_at, - last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - business_country: self.business_country, - business_label: self.business_label, - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, - connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - merchant_decision: self.merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, - updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - incremental_authorization_allowed: self.incremental_authorization_allowed, - authorization_count: self.authorization_count, - fingerprint_id: self.fingerprint_id, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - } - } - - fn from_storage_model(storage_model: Self::StorageModel) -> Self { - Self { - payment_id: storage_model.payment_id, - merchant_id: storage_model.merchant_id, - status: storage_model.status, - amount: storage_model.amount, - currency: storage_model.currency, - amount_captured: storage_model.amount_captured, - customer_id: storage_model.customer_id, - description: storage_model.description, - return_url: storage_model.return_url, - metadata: storage_model.metadata, - frm_metadata: storage_model.frm_metadata, - connector_id: storage_model.connector_id, - shipping_address_id: storage_model.shipping_address_id, - billing_address_id: storage_model.billing_address_id, - statement_descriptor_name: storage_model.statement_descriptor_name, - statement_descriptor_suffix: storage_model.statement_descriptor_suffix, - created_at: storage_model.created_at, - modified_at: storage_model.modified_at, - last_synced: storage_model.last_synced, - setup_future_usage: storage_model.setup_future_usage, - off_session: storage_model.off_session, - client_secret: storage_model.client_secret, - active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), - business_country: storage_model.business_country, - business_label: storage_model.business_label, - order_details: storage_model.order_details, - allowed_payment_method_types: storage_model.allowed_payment_method_types, - connector_metadata: storage_model.connector_metadata, - feature_metadata: storage_model.feature_metadata, - attempt_count: storage_model.attempt_count, - profile_id: storage_model.profile_id, - merchant_decision: storage_model.merchant_decision, - payment_link_id: storage_model.payment_link_id, - payment_confirm_source: storage_model.payment_confirm_source, - updated_by: storage_model.updated_by, - surcharge_applicable: storage_model.surcharge_applicable, - request_incremental_authorization: storage_model.request_incremental_authorization, - incremental_authorization_allowed: storage_model.incremental_authorization_allowed, - authorization_count: storage_model.authorization_count, - fingerprint_id: storage_model.fingerprint_id, - session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: storage_model - .request_external_three_ds_authentication, - charges: storage_model.charges, - } - } -} - -impl DataModelExt for PaymentIntent { - type StorageModel = DieselPaymentIntent; - - fn to_storage_model(self) -> Self::StorageModel { - DieselPaymentIntent { - id: self.id, - payment_id: self.payment_id, - merchant_id: self.merchant_id, - status: self.status, - amount: self.amount, - currency: self.currency, - amount_captured: self.amount_captured, - customer_id: self.customer_id, - description: self.description, - return_url: self.return_url, - metadata: self.metadata, - connector_id: self.connector_id, - shipping_address_id: self.shipping_address_id, - billing_address_id: self.billing_address_id, - statement_descriptor_name: self.statement_descriptor_name, - statement_descriptor_suffix: self.statement_descriptor_suffix, - created_at: self.created_at, - modified_at: self.modified_at, - last_synced: self.last_synced, - setup_future_usage: self.setup_future_usage, - off_session: self.off_session, - client_secret: self.client_secret, - active_attempt_id: self.active_attempt.get_id(), - business_country: self.business_country, - business_label: self.business_label, - order_details: self.order_details, - allowed_payment_method_types: self.allowed_payment_method_types, - connector_metadata: self.connector_metadata, - feature_metadata: self.feature_metadata, - attempt_count: self.attempt_count, - profile_id: self.profile_id, - merchant_decision: self.merchant_decision, - payment_link_id: self.payment_link_id, - payment_confirm_source: self.payment_confirm_source, - updated_by: self.updated_by, - surcharge_applicable: self.surcharge_applicable, - request_incremental_authorization: self.request_incremental_authorization, - incremental_authorization_allowed: self.incremental_authorization_allowed, - authorization_count: self.authorization_count, - fingerprint_id: self.fingerprint_id, - session_expiry: self.session_expiry, - request_external_three_ds_authentication: self.request_external_three_ds_authentication, - charges: self.charges, - frm_metadata: self.frm_metadata, - } - } - - fn from_storage_model(storage_model: Self::StorageModel) -> Self { - Self { - id: storage_model.id, - payment_id: storage_model.payment_id, - merchant_id: storage_model.merchant_id, - status: storage_model.status, - amount: storage_model.amount, - currency: storage_model.currency, - amount_captured: storage_model.amount_captured, - customer_id: storage_model.customer_id, - description: storage_model.description, - return_url: storage_model.return_url, - metadata: storage_model.metadata, - connector_id: storage_model.connector_id, - shipping_address_id: storage_model.shipping_address_id, - billing_address_id: storage_model.billing_address_id, - statement_descriptor_name: storage_model.statement_descriptor_name, - statement_descriptor_suffix: storage_model.statement_descriptor_suffix, - created_at: storage_model.created_at, - modified_at: storage_model.modified_at, - last_synced: storage_model.last_synced, - setup_future_usage: storage_model.setup_future_usage, - off_session: storage_model.off_session, - client_secret: storage_model.client_secret, - active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id), - business_country: storage_model.business_country, - business_label: storage_model.business_label, - order_details: storage_model.order_details, - allowed_payment_method_types: storage_model.allowed_payment_method_types, - connector_metadata: storage_model.connector_metadata, - feature_metadata: storage_model.feature_metadata, - attempt_count: storage_model.attempt_count, - profile_id: storage_model.profile_id, - merchant_decision: storage_model.merchant_decision, - payment_link_id: storage_model.payment_link_id, - payment_confirm_source: storage_model.payment_confirm_source, - updated_by: storage_model.updated_by, - surcharge_applicable: storage_model.surcharge_applicable, - request_incremental_authorization: storage_model.request_incremental_authorization, - incremental_authorization_allowed: storage_model.incremental_authorization_allowed, - authorization_count: storage_model.authorization_count, - fingerprint_id: storage_model.fingerprint_id, - session_expiry: storage_model.session_expiry, - request_external_three_ds_authentication: storage_model - .request_external_three_ds_authentication, - charges: storage_model.charges, - frm_metadata: storage_model.frm_metadata, - } - } -} - -impl DataModelExt for PaymentIntentUpdate { - type StorageModel = DieselPaymentIntentUpdate; - - fn to_storage_model(self) -> Self::StorageModel { - match self { - Self::ResponseUpdate { - status, - amount_captured, - fingerprint_id, - return_url, - updated_by, - incremental_authorization_allowed, - } => DieselPaymentIntentUpdate::ResponseUpdate { - status, - amount_captured, - fingerprint_id, - return_url, - updated_by, - incremental_authorization_allowed, - }, - Self::MetadataUpdate { - metadata, - updated_by, - } => DieselPaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - }, - Self::ReturnUrlUpdate { - return_url, - status, - customer_id, - shipping_address_id, - billing_address_id, - updated_by, - } => DieselPaymentIntentUpdate::ReturnUrlUpdate { - return_url, - status, - customer_id, - shipping_address_id, - billing_address_id, - updated_by, - }, - Self::MerchantStatusUpdate { - status, - shipping_address_id, - billing_address_id, - updated_by, - } => DieselPaymentIntentUpdate::MerchantStatusUpdate { - status, - shipping_address_id, - billing_address_id, - updated_by, - }, - Self::PGStatusUpdate { - status, - updated_by, - incremental_authorization_allowed, - } => DieselPaymentIntentUpdate::PGStatusUpdate { - status, - updated_by, - incremental_authorization_allowed, - }, - Self::Update { - amount, - currency, - setup_future_usage, - status, - customer_id, - shipping_address_id, - billing_address_id, - return_url, - business_country, - business_label, - description, - statement_descriptor_name, - statement_descriptor_suffix, - order_details, - metadata, - payment_confirm_source, - updated_by, - fingerprint_id, - session_expiry, - request_external_three_ds_authentication, - frm_metadata, - } => DieselPaymentIntentUpdate::Update { - amount, - currency, - setup_future_usage, - status, - customer_id, - shipping_address_id, - billing_address_id, - return_url, - business_country, - business_label, - description, - statement_descriptor_name, - statement_descriptor_suffix, - order_details, - metadata, - payment_confirm_source, - updated_by, - fingerprint_id, - session_expiry, - request_external_three_ds_authentication, - frm_metadata, - }, - Self::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - } => DieselPaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { - active_attempt_id, - attempt_count, - updated_by, - }, - Self::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - } => DieselPaymentIntentUpdate::StatusAndAttemptUpdate { - status, - active_attempt_id, - attempt_count, - updated_by, - }, - Self::ApproveUpdate { - status, - merchant_decision, - updated_by, - } => DieselPaymentIntentUpdate::ApproveUpdate { - status, - merchant_decision, - updated_by, - }, - Self::RejectUpdate { - status, - merchant_decision, - updated_by, - } => DieselPaymentIntentUpdate::RejectUpdate { - status, - merchant_decision, - updated_by, - }, - Self::SurchargeApplicableUpdate { - surcharge_applicable, - updated_by, - } => DieselPaymentIntentUpdate::SurchargeApplicableUpdate { - surcharge_applicable: Some(surcharge_applicable), - updated_by, - }, - Self::IncrementalAuthorizationAmountUpdate { amount } => { - DieselPaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } - } - Self::AuthorizationCountUpdate { - authorization_count, - } => DieselPaymentIntentUpdate::AuthorizationCountUpdate { - authorization_count, - }, - Self::CompleteAuthorizeUpdate { - shipping_address_id, - } => DieselPaymentIntentUpdate::CompleteAuthorizeUpdate { - shipping_address_id, - }, - } - } - - #[allow(clippy::todo)] - fn from_storage_model(_storage_model: Self::StorageModel) -> Self { - todo!("Reverse map should no longer be needed") - } -} diff --git a/migrations/2024-06-03-090859_make_id_as_optional_for_payments/down.sql b/migrations/2024-06-03-090859_make_id_as_optional_for_payments/down.sql new file mode 100644 index 0000000000..9766ed42d0 --- /dev/null +++ b/migrations/2024-06-03-090859_make_id_as_optional_for_payments/down.sql @@ -0,0 +1,18 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_attempt +ALTER COLUMN id +SET NOT NULL; + +ALTER TABLE payment_attempt DROP CONSTRAINT payment_attempt_pkey; + +ALTER TABLE payment_attempt +ADD PRIMARY KEY (id); + +ALTER TABLE payment_intent +ALTER COLUMN id +SET NOT NULL; + +ALTER TABLE payment_intent DROP CONSTRAINT payment_intent_pkey; + +ALTER TABLE payment_intent +ADD PRIMARY KEY (id); diff --git a/migrations/2024-06-03-090859_make_id_as_optional_for_payments/up.sql b/migrations/2024-06-03-090859_make_id_as_optional_for_payments/up.sql new file mode 100644 index 0000000000..3c050d6e96 --- /dev/null +++ b/migrations/2024-06-03-090859_make_id_as_optional_for_payments/up.sql @@ -0,0 +1,19 @@ +-- First drop the primary key of payment_intent +ALTER TABLE payment_intent DROP CONSTRAINT payment_intent_pkey; + +-- Create new primary key +ALTER TABLE payment_intent +ADD PRIMARY KEY (payment_id, merchant_id); + +-- Make the previous primary key as optional +ALTER TABLE payment_intent +ALTER COLUMN id DROP NOT NULL; + +-- Follow the same steps for payment attempt as well +ALTER TABLE payment_attempt DROP CONSTRAINT payment_attempt_pkey; + +ALTER TABLE payment_attempt +ADD PRIMARY KEY (attempt_id, merchant_id); + +ALTER TABLE payment_attempt +ALTER COLUMN id DROP NOT NULL; diff --git a/migrations/2024-06-03-133421_remove_id_from_payment_table/down.sql b/migrations/2024-06-03-133421_remove_id_from_payment_table/down.sql new file mode 100644 index 0000000000..5b704469b9 --- /dev/null +++ b/migrations/2024-06-03-133421_remove_id_from_payment_table/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_intent +ADD id SERIAL; + +ALTER TABLE payment_attempt +ADD id SERIAL; diff --git a/migrations/2024-06-03-133421_remove_id_from_payment_table/up.sql b/migrations/2024-06-03-133421_remove_id_from_payment_table/up.sql new file mode 100644 index 0000000000..221ab4e9ae --- /dev/null +++ b/migrations/2024-06-03-133421_remove_id_from_payment_table/up.sql @@ -0,0 +1,6 @@ +-- The following queries must be run after the newer version of the application is deployed. +-- Running these queries can even be deferred for some time (a couple of weeks or even a month) until the +-- new version being deployed is considered stable +ALTER TABLE payment_intent DROP COLUMN id; + +ALTER TABLE payment_attempt DROP COLUMN id;