From 1e249300b35e6422f21fcf3a3453b844c62e79b8 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 22 Oct 2024 07:28:56 +0530 Subject: [PATCH 01/52] refactor: introduce strict types in intent Domain and Diesel models --- crates/api_models/src/payments.rs | 13 +- crates/common_enums/Cargo.toml | 1 + crates/common_enums/src/enums.rs | 4 + crates/common_enums/src/enums/payments.rs | 14 ++ crates/diesel_models/src/lib.rs | 1 + crates/diesel_models/src/payment_intent.rs | 6 +- crates/diesel_models/src/types.rs | 31 ++++ crates/hyperswitch_domain_models/src/lib.rs | 9 + .../hyperswitch_domain_models/src/payments.rs | 29 ++-- .../src/payments/payment_intent.rs | 71 +++++--- crates/hyperswitch_domain_models/src/types.rs | 4 + .../src/types/payments.rs | 163 ++++++++++++++++++ crates/openapi/src/openapi_v2.rs | 2 +- 13 files changed, 295 insertions(+), 53 deletions(-) create mode 100644 crates/common_enums/src/enums/payments.rs create mode 100644 crates/diesel_models/src/types.rs create mode 100644 crates/hyperswitch_domain_models/src/types/payments.rs diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index fd1286a555ea..25afa20d26bf 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -5,6 +5,7 @@ use std::{ }; pub mod additional_info; use cards::CardNumber; +use common_enums::ProductType; #[cfg(feature = "v2")] use common_utils::id_type::GlobalPaymentId; use common_utils::{ @@ -4949,18 +4950,6 @@ pub struct OrderDetailsWithAmount { impl masking::SerializableSecret for OrderDetailsWithAmount {} -#[derive(Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone, ToSchema)] -#[serde(rename_all = "snake_case")] -pub enum ProductType { - #[default] - Physical, - Digital, - Travel, - Ride, - Event, - Accommodation, -} - #[derive(Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone, ToSchema)] pub struct OrderDetails { /// Name of the product that is being purchased diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index da03b530eb8c..92fc2f02066b 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -22,6 +22,7 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order # First party crates router_derive = { version = "0.1.0", path = "../router_derive" } +masking = { version = "0.1.0", path = "../masking" } [dev-dependencies] serde_json = "1.0.115" diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index e36a6d74994e..d40f8c3d7741 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1,5 +1,7 @@ +mod payments; use std::num::{ParseFloatError, TryFromIntError}; +pub use payments::*; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -1685,6 +1687,8 @@ pub enum PaymentMethodType { OpenBankingPIS, } +impl masking::SerializableSecret for PaymentMethodType {} + /// Indicates the type of payment method. Eg: 'card', 'wallet', etc. #[derive( Clone, diff --git a/crates/common_enums/src/enums/payments.rs b/crates/common_enums/src/enums/payments.rs new file mode 100644 index 000000000000..895303bab4f5 --- /dev/null +++ b/crates/common_enums/src/enums/payments.rs @@ -0,0 +1,14 @@ +use serde; +use utoipa::ToSchema; + +#[derive(Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize, Clone, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum ProductType { + #[default] + Physical, + Digital, + Travel, + Ride, + Event, + Accommodation, +} diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 598035524a72..c7a3818d5fea 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -41,6 +41,7 @@ pub mod refund; pub mod reverse_lookup; pub mod role; pub mod routing_algorithm; +pub mod types; pub mod unified_translations; #[allow(unused_qualifications)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index f53594075af1..3c235ff1e93e 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -9,6 +9,8 @@ use crate::enums as storage_enums; use crate::schema::payment_intent; #[cfg(feature = "v2")] use crate::schema_v2::payment_intent; +#[cfg(feature = "v2")] +use crate::types::OrderDetailsWithAmount; #[cfg(feature = "v2")] #[derive(Clone, Debug, PartialEq, Identifiable, Queryable, Serialize, Deserialize, Selectable)] @@ -32,8 +34,8 @@ pub struct PaymentIntent { pub setup_future_usage: Option, pub client_secret: common_utils::types::ClientSecret, pub active_attempt_id: Option, - #[diesel(deserialize_as = super::OptionalDieselArray)] - pub order_details: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray>)] + pub order_details: Option>>, pub allowed_payment_method_types: Option, pub connector_metadata: Option, pub feature_metadata: Option, diff --git a/crates/diesel_models/src/types.rs b/crates/diesel_models/src/types.rs new file mode 100644 index 000000000000..2935017aeb1f --- /dev/null +++ b/crates/diesel_models/src/types.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct OrderDetailsWithAmount { + /// Name of the product that is being purchased + pub product_name: String, + /// The quantity of the product to be purchased + pub quantity: u16, + /// the amount per quantity of product + pub amount: i64, + // Does the order includes shipping + pub requires_shipping: Option, + /// The image URL of the product + pub product_img_link: Option, + /// ID of the product that is being purchased + pub product_id: Option, + /// Category of the product that is being purchased + pub category: Option, + /// Sub category of the product that is being purchased + pub sub_category: Option, + /// Brand of the product that is being purchased + pub brand: Option, + /// Type of the product that is being purchased + pub product_type: Option, + /// The tax code for the product + pub product_tax_code: Option, +} + +impl masking::SerializableSecret for OrderDetailsWithAmount {} + +common_utils::impl_to_sql_from_sql_json!(OrderDetailsWithAmount); diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 4803514617fd..89b6b95762c8 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -30,6 +30,8 @@ pub trait PayoutAttemptInterface {} #[cfg(not(feature = "payouts"))] pub trait PayoutsInterface {} +pub use crate::types::OrderDetailsWithAmount as DomainOrderDetailsWithAmount; + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub enum RemoteStorageObject { ForeignID(String), @@ -62,6 +64,13 @@ pub trait ApiModelToDieselModelConvertor { fn convert_from(from: F) -> Self; } +pub trait ApiDieselConvertor { + fn from_api(api_model: Api) -> Self; + fn to_api(&self) -> Api; + fn from_diesel(diesel_model: Diesel) -> Self; + fn to_diesel(&self) -> Diesel; +} + #[cfg(feature = "v2")] impl ApiModelToDieselModelConvertor for diesel_models::payment_intent::PaymentLinkConfigRequestForPayments diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 4c8cb9c38ea3..ca7941870ebc 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -3,8 +3,6 @@ use std::marker::PhantomData; #[cfg(feature = "v2")] use api_models::payments::Address; -#[cfg(feature = "v2")] -use api_models::payments::OrderDetailsWithAmount; use common_utils::{self, crypto::Encryptable, id_type, pii, types::MinorUnit}; use diesel_models::payment_intent::TaxDetails; #[cfg(feature = "v2")] @@ -20,7 +18,7 @@ use common_enums as storage_enums; use self::payment_attempt::PaymentAttempt; use crate::RemoteStorageObject; #[cfg(feature = "v2")] -use crate::{business_profile, merchant_account}; +use crate::{business_profile, merchant_account, types::OrderDetailsWithAmount}; #[cfg(feature = "v2")] use crate::{errors, ApiModelToDieselModelConvertor}; @@ -219,7 +217,7 @@ pub struct PaymentIntent { pub order_details: Option>>, /// This is the list of payment method types that are allowed for the payment intent. /// This field allows the merchant to restrict the payment methods that can be used for the payment intent. - pub allowed_payment_method_types: Option, + pub allowed_payment_method_types: Option>>, /// This metadata contains details about pub connector_metadata: Option, pub feature_metadata: Option, @@ -303,10 +301,6 @@ impl PaymentIntent { shipping_address: Option>>, ) -> common_utils::errors::CustomResult { - let allowed_payment_method_types = request - .get_allowed_payment_method_types_as_value() - .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting allowed payment method types as value")?; let connector_metadata = request .get_connector_metadata_as_value() .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) @@ -317,6 +311,16 @@ impl PaymentIntent { .attach_printable("Error getting feature metadata as value")?; let request_incremental_authorization = Self::get_request_incremental_authorization_value(&request)?; + let allowed_payment_method_types = + request + .allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types + .into_iter() + .map(Secret::new) + .collect() + }); + let session_expiry = common_utils::date_time::now().saturating_add(time::Duration::seconds( request.session_expiry.map(i64::from).unwrap_or( @@ -326,9 +330,12 @@ impl PaymentIntent { ), )); let client_secret = payment_id.generate_client_secret(); - let order_details = request - .order_details - .map(|order_details| order_details.into_iter().map(Secret::new).collect()); + let order_details = request.order_details.map(|order_details| { + order_details + .into_iter() + .map(|order_detail| Secret::new(OrderDetailsWithAmount::from(order_detail))) + .collect() + }); Ok(Self { id: payment_id.clone(), merchant_id: merchant_account.get_id().clone(), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index ebd630beb622..37f672550428 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -32,7 +32,8 @@ use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, - RemoteStorageObject, + types::OrderDetailsWithAmount, + ApiDieselConvertor, RemoteStorageObject, }; #[async_trait::async_trait] pub trait PaymentIntentInterface { @@ -1531,18 +1532,22 @@ impl behaviour::Conversion for PaymentIntent { setup_future_usage: Some(setup_future_usage), client_secret, active_attempt_id: active_attempt.map(|attempt| attempt.get_id()), - order_details: order_details - .map(|order_details| { - order_details - .into_iter() - .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) - .collect::, _>>() + order_details: order_details.map(|order_details| { + order_details + .into_iter() + .map(|order_detail| Secret::new(order_detail.expose().to_diesel())) + .collect::>() + }), + allowed_payment_method_types: allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types + .encode_to_value() + .change_context(ValidationError::InvalidValue { + message: "Failed to serialize allowed_payment_method_types".to_string(), + }) }) - .transpose() - .change_context(ValidationError::InvalidValue { - message: "invalid value found for order_details".to_string(), - })?, - allowed_payment_method_types, + .transpose()? + .map(Secret::new), connector_metadata, feature_metadata, attempt_count, @@ -1639,7 +1644,13 @@ impl behaviour::Conversion for PaymentIntent { .transpose() .change_context(common_utils::errors::CryptoError::DecodingFailed) .attach_printable("Error while deserializing Address")?; - + let allowed_payment_method_types = storage_model + .allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types.parse_value("Vec") + }) + .transpose() + .change_context(common_utils::errors::CryptoError::DecodingFailed)?; Ok::>(Self { merchant_id: storage_model.merchant_id, status: storage_model.status, @@ -1658,19 +1669,15 @@ impl behaviour::Conversion for PaymentIntent { active_attempt: storage_model .active_attempt_id .map(RemoteStorageObject::ForeignID), - order_details: storage_model - .order_details - .map(|order_details| { - order_details - .into_iter() - .map(|order_detail| { - order_detail.expose().parse_value("OrderDetailsWithAmount") - }) - .collect::, _>>() - }) - .transpose() - .change_context(common_utils::errors::CryptoError::DecodingFailed)?, - allowed_payment_method_types: storage_model.allowed_payment_method_types, + order_details: storage_model.order_details.map(|order_details| { + order_details + .into_iter() + .map(|order_detail| { + Secret::new(OrderDetailsWithAmount::from_diesel(order_detail.expose())) + }) + .collect::>() + }), + allowed_payment_method_types, connector_metadata: storage_model.connector_metadata, feature_metadata: storage_model.feature_metadata, attempt_count: storage_model.attempt_count, @@ -1750,7 +1757,17 @@ impl behaviour::Conversion for PaymentIntent { .change_context(ValidationError::InvalidValue { message: "Invalid value found for ".to_string(), })?, - allowed_payment_method_types: self.allowed_payment_method_types, + allowed_payment_method_types: self + .allowed_payment_method_types + .map(|allowed_payment_method_types| { + allowed_payment_method_types + .encode_to_value() + .change_context(ValidationError::InvalidValue { + message: "Failed to serialize allowed_payment_method_types".to_string(), + }) + }) + .transpose()? + .map(Secret::new), connector_metadata: self.connector_metadata, feature_metadata: self.feature_metadata, attempt_count: self.attempt_count, diff --git a/crates/hyperswitch_domain_models/src/types.rs b/crates/hyperswitch_domain_models/src/types.rs index eeb63bbb508f..ffbd9b64b911 100644 --- a/crates/hyperswitch_domain_models/src/types.rs +++ b/crates/hyperswitch_domain_models/src/types.rs @@ -1,3 +1,7 @@ +mod payments; + +pub use payments::*; + use crate::{ router_data::{AccessToken, RouterData}, router_flow_types::{ diff --git a/crates/hyperswitch_domain_models/src/types/payments.rs b/crates/hyperswitch_domain_models/src/types/payments.rs new file mode 100644 index 000000000000..e6d763cb4a1b --- /dev/null +++ b/crates/hyperswitch_domain_models/src/types/payments.rs @@ -0,0 +1,163 @@ +use api_models::payments::OrderDetailsWithAmount as ApiOrderDetailsWithAmount; +use diesel_models::types::OrderDetailsWithAmount as DieselOrderDetailsWithAmount; +use serde; + +#[derive(Clone, Debug, PartialEq, serde::Serialize)] +pub struct OrderDetailsWithAmount { + /// Name of the product that is being purchased + pub product_name: String, + /// The quantity of the product to be purchased + pub quantity: u16, + /// the amount per quantity of product + pub amount: i64, + // Does the order includes shipping + pub requires_shipping: Option, + /// The image URL of the product + pub product_img_link: Option, + /// ID of the product that is being purchased + pub product_id: Option, + /// Category of the product that is being purchased + pub category: Option, + /// Sub category of the product that is being purchased + pub sub_category: Option, + /// Brand of the product that is being purchased + pub brand: Option, + /// Type of the product that is being purchased + pub product_type: Option, + /// The tax code for the product + pub product_tax_code: Option, +} + +impl masking::SerializableSecret for OrderDetailsWithAmount {} + +impl crate::ApiDieselConvertor + for OrderDetailsWithAmount +{ + fn from_api(api_model: ApiOrderDetailsWithAmount) -> Self { + Self::from(api_model) + } + + fn to_api(&self) -> ApiOrderDetailsWithAmount { + let Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = self.clone(); + ApiOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } + + fn from_diesel(diesel_model: DieselOrderDetailsWithAmount) -> Self { + Self::from(diesel_model) + } + + fn to_diesel(&self) -> DieselOrderDetailsWithAmount { + let Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = self.clone(); + DieselOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } +} + +impl From for OrderDetailsWithAmount { + fn from(value: ApiOrderDetailsWithAmount) -> Self { + let ApiOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = value; + Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } +} + +impl From for OrderDetailsWithAmount { + fn from(value: DieselOrderDetailsWithAmount) -> Self { + let DieselOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = value; + Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } +} diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index bf95fbe743aa..45aa62504d7c 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -179,6 +179,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::customers::CustomerResponse, api_models::admin::AcceptedCountries, api_models::admin::AcceptedCurrencies, + api_models::enums::ProductType, api_models::enums::PaymentType, api_models::enums::PaymentMethod, api_models::enums::PaymentMethodType, @@ -335,7 +336,6 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::ApplePayShippingContactFields, api_models::payments::ApplePayAddressParameters, api_models::payments::AmountInfo, - api_models::payments::ProductType, api_models::payments::GooglePayWalletData, api_models::payments::PayPalWalletData, api_models::payments::PaypalRedirection, From 4be8ccbc19e49ade05fb10da034dae182dc85d2d Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 22 Oct 2024 15:01:49 +0530 Subject: [PATCH 02/52] add v1 and v2 feature flag to struct AuthenticationData --- crates/api_models/src/payments.rs | 2 +- crates/router/src/analytics.rs | 498 +++++++++++++++++- .../connector/riskified/transformers/api.rs | 2 +- .../connector/signifyd/transformers/api.rs | 4 +- .../router/src/core/payments/transformers.rs | 16 +- crates/router/src/db/kafka_store.rs | 3 +- crates/router/src/db/merchant_account.rs | 31 +- crates/router/src/routes/admin.rs | 4 +- crates/router/src/routes/app.rs | 30 +- crates/router/src/routes/cards_info.rs | 1 + crates/router/src/routes/currency.rs | 2 + crates/router/src/routes/disputes.rs | 9 + crates/router/src/routes/files.rs | 5 + crates/router/src/routes/payment_methods.rs | 12 +- crates/router/src/routes/payments.rs | 2 +- crates/router/src/routes/payout_link.rs | 1 + crates/router/src/routes/payouts.rs | 8 +- crates/router/src/routes/profiles.rs | 2 + crates/router/src/routes/routing.rs | 91 +++- crates/router/src/routes/verification.rs | 1 + crates/router/src/routes/verify_connector.rs | 1 + crates/router/src/services/authentication.rs | 469 ++++++++++++++++- 22 files changed, 1132 insertions(+), 62 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 25afa20d26bf..ac43a609d79d 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -370,7 +370,7 @@ pub struct PaymentsCreateIntentResponse { /// Use this parameter to restrict the Payment Method Types to show for a given PaymentIntent #[schema(value_type = Option>)] - pub allowed_payment_method_types: Option, + pub allowed_payment_method_types: Option>, /// Metadata is useful for storing additional, unstructured information on an object. #[schema(value_type = Option, example = r#"{ "udf1": "some-value", "udf2": "some-value" }"#)] diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index aa6db56bb348..e3acebd4eac1 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -47,6 +47,11 @@ pub mod routes { pub struct Analytics; impl Analytics { + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { web::scope("/analytics") .app_data(web::Data::new(state)) @@ -449,6 +454,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element. @@ -495,6 +501,50 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + /// # Panics + /// + /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element. + pub async fn get_profile_payment_metrics( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json<[GetPaymentMetricRequest; 1]>, + ) -> impl Responder { + // safety: This shouldn't panic owing to the data type + #[allow(clippy::expect_used)] + let payload = json_payload + .into_inner() + .to_vec() + .pop() + .expect("Couldn't get GetPaymentMetricRequest"); + let flow = AnalyticsFlow::GetPaymentMetrics; + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::payments::get_metrics(&state.pool, &auth, req) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + /// # Panics /// /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. @@ -575,6 +625,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. @@ -621,6 +672,50 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + /// # Panics + /// + /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. + pub async fn get_profile_payment_intent_metrics( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>, + ) -> impl Responder { + // safety: This shouldn't panic owing to the data type + #[allow(clippy::expect_used)] + let payload = json_payload + .into_inner() + .to_vec() + .pop() + .expect("Couldn't get GetPaymentIntentMetricRequest"); + let flow = AnalyticsFlow::GetPaymentIntentMetrics; + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::payment_intents::get_metrics(&state.pool, &auth, req) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + /// # Panics /// /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. @@ -701,6 +796,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. @@ -747,6 +843,50 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + /// # Panics + /// + /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. + pub async fn get_profile_refund_metrics( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json<[GetRefundMetricRequest; 1]>, + ) -> impl Responder { + #[allow(clippy::expect_used)] + // safety: This shouldn't panic owing to the data type + let payload = json_payload + .into_inner() + .to_vec() + .pop() + .expect("Couldn't get GetRefundMetricRequest"); + let flow = AnalyticsFlow::GetRefundsMetrics; + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::refunds::get_metrics(&state.pool, &auth, req) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + /// # Panics /// /// Panics if `json_payload` array does not contain one `GetFrmMetricRequest` element. @@ -961,6 +1101,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_profile_payment_filters( state: web::Data, req: actix_web::HttpRequest, @@ -997,6 +1138,40 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + pub async fn get_profile_payment_filters( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json, + ) -> impl Responder { + let flow = AnalyticsFlow::GetPaymentFilters; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::payments::get_filters(&state.pool, req, &auth) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + pub async fn get_payment_intents_filters( state: web::Data, req: actix_web::HttpRequest, @@ -1086,6 +1261,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_profile_refund_filters( state: web::Data, req: actix_web::HttpRequest, @@ -1121,6 +1297,39 @@ pub mod routes { )) .await } + #[cfg(feature = "v2")] + pub async fn get_profile_refund_filters( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json, + ) -> impl Responder { + let flow = AnalyticsFlow::GetRefundFilters; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::refunds::get_filters(&state.pool, req, &auth) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } pub async fn get_frm_filters( state: web::Data, @@ -1275,6 +1484,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn generate_merchant_refund_report( state: web::Data, req: actix_web::HttpRequest, @@ -1326,6 +1536,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn generate_org_refund_report( state: web::Data, req: actix_web::HttpRequest, @@ -1375,6 +1586,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn generate_profile_refund_report( state: web::Data, req: actix_web::HttpRequest, @@ -1431,6 +1643,61 @@ pub mod routes { .await } + // #[cfg(feature = "v2")] + // pub async fn generate_profile_refund_report( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GenerateRefundReport; + // Box::pin(api::server_wrap( + // flow, + // state.clone(), + // &req, + // json_payload.into_inner(), + // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { + // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) + // .await + // .change_context(AnalyticsError::UnknownError)?; + + // let user_email = UserEmail::from_pii_email(user.email) + // .change_context(AnalyticsError::UnknownError)? + // .get_secret(); + + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth + // .profile.get_id(); + // let lambda_req = GenerateReportRequest { + // request: payload, + // merchant_id: Some(merchant_id.clone()), + // auth: AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }, + // email: user_email, + // }; + + // let json_bytes = + // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; + // invoke_lambda( + // &state.conf.report_download_config.refund_function, + // &state.conf.report_download_config.region, + // &json_bytes, + // ) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::GenerateReport, + // minimum_entity_level: EntityType::Profile, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } + #[cfg(feature = "v1")] pub async fn generate_merchant_dispute_report( state: web::Data, req: actix_web::HttpRequest, @@ -1481,7 +1748,7 @@ pub mod routes { )) .await } - + #[cfg(feature = "v1")] pub async fn generate_org_dispute_report( state: web::Data, req: actix_web::HttpRequest, @@ -1531,6 +1798,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn generate_profile_dispute_report( state: web::Data, req: actix_web::HttpRequest, @@ -1587,6 +1855,61 @@ pub mod routes { .await } + // #[cfg(feature = "v2")] + // pub async fn generate_profile_dispute_report( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GenerateDisputeReport; + // Box::pin(api::server_wrap( + // flow, + // state.clone(), + // &req, + // json_payload.into_inner(), + // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { + // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) + // .await + // .change_context(AnalyticsError::UnknownError)?; + + // let user_email = UserEmail::from_pii_email(user.email) + // .change_context(AnalyticsError::UnknownError)? + // .get_secret(); + + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth + // .profile.get_id(); + // let lambda_req = GenerateReportRequest { + // request: payload, + // merchant_id: Some(merchant_id.clone()), + // auth: AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }, + // email: user_email, + // }; + + // let json_bytes = + // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; + // invoke_lambda( + // &state.conf.report_download_config.dispute_function, + // &state.conf.report_download_config.region, + // &json_bytes, + // ) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::GenerateReport, + // minimum_entity_level: EntityType::Profile, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } + #[cfg(feature = "v1")] pub async fn generate_merchant_payment_report( state: web::Data, req: actix_web::HttpRequest, @@ -1637,7 +1960,7 @@ pub mod routes { )) .await } - + #[cfg(feature = "v1")] pub async fn generate_org_payment_report( state: web::Data, req: actix_web::HttpRequest, @@ -1687,6 +2010,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn generate_profile_payment_report( state: web::Data, req: actix_web::HttpRequest, @@ -1742,6 +2066,60 @@ pub mod routes { .await } + // #[cfg(feature = "v2")] + // pub async fn generate_profile_payment_report( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GeneratePaymentReport; + // Box::pin(api::server_wrap( + // flow, + // state.clone(), + // &req, + // json_payload.into_inner(), + // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { + // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) + // .await + // .change_context(AnalyticsError::UnknownError)?; + + // let user_email = UserEmail::from_pii_email(user.email) + // .change_context(AnalyticsError::UnknownError)? + // .get_secret(); + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth + // .profile.get_id(); + // let lambda_req = GenerateReportRequest { + // request: payload, + // merchant_id: Some(merchant_id.clone()), + // auth: AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }, + // email: user_email, + // }; + + // let json_bytes = + // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; + // invoke_lambda( + // &state.conf.report_download_config.payment_function, + // &state.conf.report_download_config.region, + // &json_bytes, + // ) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::GenerateReport, + // minimum_entity_level: EntityType::Profile, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } + /// # Panics /// /// Panics if `json_payload` array does not contain one `GetApiEventMetricRequest` element. @@ -2104,6 +2482,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_profile_dispute_filters( state: web::Data, req: actix_web::HttpRequest, @@ -2140,6 +2519,40 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + pub async fn get_profile_dispute_filters( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json, + ) -> impl Responder { + let flow = AnalyticsFlow::GetDisputeFilters; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::disputes::get_filters(&state.pool, req, &auth) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + pub async fn get_org_dispute_filters( state: web::Data, req: actix_web::HttpRequest, @@ -2210,6 +2623,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. @@ -2256,6 +2670,50 @@ pub mod routes { .await } + #[cfg(feature = "v2")] + /// # Panics + /// + /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. + pub async fn get_profile_dispute_metrics( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json<[GetDisputeMetricRequest; 1]>, + ) -> impl Responder { + // safety: This shouldn't panic owing to the data type + #[allow(clippy::expect_used)] + let payload = json_payload + .into_inner() + .to_vec() + .pop() + .expect("Couldn't get GetDisputeMetricRequest"); + let flow = AnalyticsFlow::GetDisputeMetrics; + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::disputes::get_metrics(&state.pool, &auth, req) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } + /// # Panics /// /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. @@ -2357,6 +2815,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_profile_sankey( state: web::Data, req: actix_web::HttpRequest, @@ -2393,4 +2852,39 @@ pub mod routes { )) .await } + + #[cfg(feature = "v2")] + pub async fn get_profile_sankey( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: web::Json, + ) -> impl Responder { + let flow = AnalyticsFlow::GetSankey; + let payload = json_payload.into_inner(); + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move { + let org_id = auth.merchant_account.get_org_id(); + let merchant_id = auth.merchant_account.get_id(); + let profile_id = auth.profile.get_id(); + let auth: AuthInfo = AuthInfo::ProfileLevel { + org_id: org_id.clone(), + merchant_id: merchant_id.clone(), + profile_ids: vec![profile_id.clone()], + }; + analytics::payment_intents::get_sankey(&state.pool, &auth, req) + .await + .map(ApplicationResponse::Json) + }, + &auth::JWTAuth { + permission: Permission::Analytics, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await + } } diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index 2e0ac3b00473..f6743007bf73 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -113,7 +113,7 @@ pub struct LineItem { price: i64, quantity: i32, title: String, - product_type: Option, + product_type: Option, requires_shipping: Option, product_id: Option, category: Option, diff --git a/crates/router/src/connector/signifyd/transformers/api.rs b/crates/router/src/connector/signifyd/transformers/api.rs index 6aeda8f8d470..e3ed6777c118 100644 --- a/crates/router/src/connector/signifyd/transformers/api.rs +++ b/crates/router/src/connector/signifyd/transformers/api.rs @@ -153,7 +153,7 @@ impl TryFrom<&frm_types::FrmSaleRouterData> for SignifydPaymentsSaleRequest { item_is_digital: order_detail .product_type .as_ref() - .map(|product| (product == &api_models::payments::ProductType::Digital)), + .map(|product| (product == &common_enums::ProductType::Digital)), }) .collect::>(); let metadata: SignifydFrmMetadata = item @@ -390,7 +390,7 @@ impl TryFrom<&frm_types::FrmCheckoutRouterData> for SignifydPaymentsCheckoutRequ item_is_digital: order_detail .product_type .as_ref() - .map(|product| (product == &api_models::payments::ProductType::Digital)), + .map(|product| (product == &common_enums::ProductType::Digital)), }) .collect::>(); let metadata: SignifydFrmMetadata = item diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 03ae1c880087..3ecd4eb6e3c9 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -16,7 +16,9 @@ use diesel_models::{ payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId, }; use error_stack::{report, ResultExt}; -use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types}; +use hyperswitch_domain_models::{ + payments::payment_intent::CustomerData, router_request_types, ApiDieselConvertor, +}; use masking::{ExposeInterface, Maskable, PeekInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; @@ -568,10 +570,18 @@ where order_details: payment_intent.order_details.clone().map(|order_details| { order_details .into_iter() - .map(|order_detail| order_detail.expose()) + .map(|order_detail| order_detail.expose().to_api()) .collect() }), - allowed_payment_method_types: payment_intent.allowed_payment_method_types.clone(), + allowed_payment_method_types: payment_intent + .allowed_payment_method_types + .clone() + .map(|allowed_payment_method_types| { + allowed_payment_method_types + .into_iter() + .map(ExposeInterface::expose) + .collect() + }), metadata: payment_intent.metadata.clone(), connector_metadata: payment_intent.connector_metadata.clone(), feature_metadata: payment_intent.feature_metadata.clone(), diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 3ef9a2d0376f..e54df3070ad4 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1007,7 +1007,8 @@ impl MerchantAccountInterface for KafkaStore { &self, state: &KeyManagerState, publishable_key: &str, - ) -> CustomResult { + ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError> + { self.diesel_store .find_merchant_account_by_publishable_key(state, publishable_key) .await diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index 13a778001ae0..1a25f5b6654d 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -68,7 +68,7 @@ where &self, state: &KeyManagerState, publishable_key: &str, - ) -> CustomResult; + ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError>; #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( @@ -229,7 +229,8 @@ impl MerchantAccountInterface for Store { &self, state: &KeyManagerState, publishable_key: &str, - ) -> CustomResult { + ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError> + { let fetch_by_pub_key_func = || async { let conn = connection::pg_connection_read(self).await?; @@ -261,20 +262,15 @@ impl MerchantAccountInterface for Store { &self.get_master_key().to_vec().into(), ) .await?; - - Ok(authentication::AuthenticationData { - merchant_account: merchant_account - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError)?, - - key_store, - profile_id: None, - }) + let domain_merchant_account = merchant_account + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?; + Ok((domain_merchant_account, key_store)) } #[cfg(feature = "olap")] @@ -531,7 +527,8 @@ impl MerchantAccountInterface for MockDb { &self, _state: &KeyManagerState, _publishable_key: &str, - ) -> CustomResult { + ) -> CustomResult<(domain::MerchantAccount, domain::MerchantKeyStore), errors::StorageError> + { // [#172]: Implement function for `MockDb` Err(errors::StorageError::MockDbError)? } diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 78238d3af07c..9970b0ed4135 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -324,7 +324,7 @@ pub async fn connector_create( state, &req, payload, - |state, auth_data, req, _| { + |state, auth_data: auth::AuthenticationData, req, _| { create_connector( state, req, @@ -526,6 +526,8 @@ pub async fn connector_list( ) .await } + +#[cfg(all(feature = "v1", feature = "olap"))] /// Merchant Connector - List /// /// List Merchant Connector Details for the merchant diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 274ed6a5cc03..cbb113effb7b 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -46,7 +46,7 @@ use super::pm_auth; use super::poll::retrieve_poll_status; #[cfg(feature = "olap")] use super::routing; -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v1"))] use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(all(feature = "oltp", feature = "v1"))] use super::webhooks::*; @@ -66,6 +66,8 @@ pub use crate::analytics::opensearch::OpenSearchClient; use crate::analytics::AnalyticsProvider; #[cfg(feature = "partial-auth")] use crate::errors::RouterResult; +#[cfg(feature = "v1")] +use crate::routes::cards_info::card_iin_info; #[cfg(all(feature = "frm", feature = "oltp"))] use crate::routes::fraud_check as frm_routes; #[cfg(all(feature = "recon", feature = "olap"))] @@ -74,7 +76,6 @@ pub use crate::{ configs::settings, db::{CommonStorageInterface, GlobalStorageInterface, StorageImpl, StorageInterface}, events::EventsHandler, - routes::cards_info::card_iin_info, services::{get_cache_store, get_store}, }; use crate::{ @@ -654,6 +655,7 @@ pub struct Forex; #[cfg(any(feature = "olap", feature = "oltp"))] impl Forex { + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { web::scope("/forex") .app_data(web::Data::new(state.clone())) @@ -663,6 +665,10 @@ impl Forex { web::resource("/convert_from_minor").route(web::get().to(currency::convert_forex)), ) } + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } } #[cfg(feature = "olap")] @@ -1039,6 +1045,11 @@ pub struct Payouts; #[cfg(feature = "payouts")] impl Payouts { + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payouts").app_data(web::Data::new(state)); route = route.service(web::resource("/create").route(web::post().to(payouts_create))); @@ -1545,11 +1556,16 @@ impl Disputes { pub struct Cards; impl Cards { + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { web::scope("/cards") .app_data(web::Data::new(state)) .service(web::resource("/{bin}").route(web::get().to(card_iin_info))) } + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } } pub struct Files; @@ -1610,6 +1626,7 @@ pub struct PayoutLink; #[cfg(feature = "payouts")] impl PayoutLink { + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payout_link").app_data(web::Data::new(state)); route = route.service( @@ -1617,6 +1634,10 @@ impl PayoutLink { ); route } + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } } pub struct Profile; @@ -1732,6 +1753,7 @@ pub struct ProfileNew; #[cfg(feature = "olap")] impl ProfileNew { + #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { web::scope("/account/{account_id}/profile") .app_data(web::Data::new(state)) @@ -1742,6 +1764,10 @@ impl ProfileNew { web::resource("/connectors").route(web::get().to(admin::connector_list_profile)), ) } + #[cfg(feature = "v2")] + pub fn server(state: AppState) -> Scope { + todo!() + } } pub struct Gsm; diff --git a/crates/router/src/routes/cards_info.rs b/crates/router/src/routes/cards_info.rs index 889b6e0ec401..1fe2d6db34dc 100644 --- a/crates/router/src/routes/cards_info.rs +++ b/crates/router/src/routes/cards_info.rs @@ -7,6 +7,7 @@ use crate::{ services::{api, authentication as auth}, }; +#[cfg(feature = "v1")] /// Cards Info - Retrieve /// /// Retrieve the card information given the card bin diff --git a/crates/router/src/routes/currency.rs b/crates/router/src/routes/currency.rs index 4d800ddf1a5e..b3969509bf13 100644 --- a/crates/router/src/routes/currency.rs +++ b/crates/router/src/routes/currency.rs @@ -7,6 +7,7 @@ use crate::{ services::{api, authentication as auth}, }; +#[cfg(feature = "v1")] pub async fn retrieve_forex(state: web::Data, req: HttpRequest) -> HttpResponse { let flow = Flow::RetrieveForexFlow; Box::pin(api::server_wrap( @@ -25,6 +26,7 @@ pub async fn retrieve_forex(state: web::Data, req: HttpRequest) -> Htt .await } +#[cfg(feature = "v1")] pub async fn convert_forex( state: web::Data, req: HttpRequest, diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index 22c1e3f19888..9c456ae2ce1b 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -14,6 +14,7 @@ use crate::{ types::api::disputes as dispute_types, }; +#[cfg(feature = "v1")] /// Disputes - Retrieve Dispute #[utoipa::path( get, @@ -112,6 +113,7 @@ pub async fn retrieve_disputes_list( .await } +#[cfg(feature = "v1")] /// Disputes - List Disputes for The Given Business Profiles #[utoipa::path( get, @@ -205,6 +207,7 @@ pub async fn get_disputes_filters(state: web::Data, req: HttpRequest) .await } +#[cfg(feature = "v1")] /// Disputes - Disputes Filters Profile #[utoipa::path( get, @@ -247,6 +250,7 @@ pub async fn get_disputes_filters_profile( .await } +#[cfg(feature = "v1")] /// Disputes - Accept Dispute #[utoipa::path( get, @@ -298,6 +302,8 @@ pub async fn accept_dispute( )) .await } + +#[cfg(feature = "v1")] /// Disputes - Submit Dispute Evidence #[utoipa::path( post, @@ -344,6 +350,7 @@ pub async fn submit_dispute_evidence( )) .await } +#[cfg(feature = "v1")] /// Disputes - Attach Evidence to Dispute /// /// To attach an evidence file to dispute @@ -399,6 +406,7 @@ pub async fn attach_dispute_evidence( .await } +#[cfg(feature = "v1")] /// Disputes - Retrieve Dispute #[utoipa::path( get, @@ -518,6 +526,7 @@ pub async fn get_disputes_aggregate( .await } +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::DisputesAggregate))] pub async fn get_disputes_aggregate_profile( state: web::Data, diff --git a/crates/router/src/routes/files.rs b/crates/router/src/routes/files.rs index 2ebb1176aeaa..e68a7a63b338 100644 --- a/crates/router/src/routes/files.rs +++ b/crates/router/src/routes/files.rs @@ -12,6 +12,7 @@ use crate::{ types::api::files, }; +#[cfg(feature = "v1")] /// Files - Create /// /// To create a file @@ -56,6 +57,8 @@ pub async fn files_create( )) .await } + +#[cfg(feature = "v1")] /// Files - Delete /// /// To delete a file @@ -100,6 +103,8 @@ pub async fn files_delete( )) .await } + +#[cfg(feature = "v1")] /// Files - Retrieve /// /// To retrieve a file diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 8d95ee64ad0d..f2f665b210ad 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -87,7 +87,7 @@ pub async fn create_payment_method_api( state, &req, json_payload.into_inner(), - |state, auth: auth::AuthenticationDataV2, req, _| async move { + |state, auth: auth::AuthenticationData, req, _| async move { Box::pin(create_payment_method( &state, req, @@ -116,7 +116,7 @@ pub async fn create_payment_method_intent_api( state, &req, json_payload.into_inner(), - |state, auth: auth::AuthenticationDataV2, req, _| async move { + |state, auth: auth::AuthenticationData, req, _| async move { Box::pin(payment_method_intent_create( &state, req, @@ -198,7 +198,7 @@ pub async fn payment_method_update_api( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, req, _| { + |state, auth: auth::AuthenticationData, req, _| { update_payment_method( state, auth.merchant_account, @@ -231,7 +231,7 @@ pub async fn payment_method_retrieve_api( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, pm, _| { + |state, auth: auth::AuthenticationData, pm, _| { retrieve_payment_method(state, pm, auth.key_store, auth.merchant_account) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -258,7 +258,7 @@ pub async fn payment_method_delete_api( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, pm, _| { + |state, auth: auth::AuthenticationData, pm, _| { delete_payment_method(state, pm, auth.key_store, auth.merchant_account) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -722,6 +722,7 @@ pub async fn initiate_pm_collect_link_flow( .await } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] /// Generate a form link for collecting payment methods for a customer #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodCollectLink))] pub async fn render_pm_collect_link( @@ -857,6 +858,7 @@ pub async fn payment_method_delete_api( .await } +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::ListCountriesCurrencies))] pub async fn list_countries_currencies_for_connector_payment_method( state: web::Data, diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index eba1fa84c985..c718a960bbe9 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -125,7 +125,7 @@ pub async fn payments_create_intent( state, &req, json_payload.into_inner(), - |state, auth: auth::AuthenticationDataV2, req, req_state| { + |state, auth: auth::AuthenticationData, req, req_state| { payments::payments_intent_core::< api_types::CreateIntent, payment_types::PaymentsCreateIntentResponse, diff --git a/crates/router/src/routes/payout_link.rs b/crates/router/src/routes/payout_link.rs index 87157435721c..25528b21ed85 100644 --- a/crates/router/src/routes/payout_link.rs +++ b/crates/router/src/routes/payout_link.rs @@ -12,6 +12,7 @@ use crate::{ }, AppState, }; +#[cfg(feature = "v1")] pub async fn render_payout_link( state: web::Data, req: actix_web::HttpRequest, diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs index ad860195a2a6..21dd3e9d98dc 100644 --- a/crates/router/src/routes/payouts.rs +++ b/crates/router/src/routes/payouts.rs @@ -50,6 +50,8 @@ pub async fn payouts_create( )) .await } + +#[cfg(all(feature = "v1", feature = "payouts"))] /// Payouts - Retrieve #[instrument(skip_all, fields(flow = ?Flow::PayoutsRetrieve))] pub async fn payouts_retrieve( @@ -248,7 +250,7 @@ pub async fn payouts_list( } /// Payouts - List Profile -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "payouts", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::PayoutsList))] pub async fn payouts_list_profile( state: web::Data, @@ -328,7 +330,7 @@ pub async fn payouts_list_by_filter( } /// Payouts - Filtered list -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "payouts", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::PayoutsList))] pub async fn payouts_list_by_filter_profile( state: web::Data, @@ -401,7 +403,7 @@ pub async fn payouts_list_available_filters_for_merchant( } /// Payouts - Available filters for Profile -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "payouts", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::PayoutsFilter))] pub async fn payouts_list_available_filters_for_profile( state: web::Data, diff --git a/crates/router/src/routes/profiles.rs b/crates/router/src/routes/profiles.rs index f2dc322af17c..77677c94beda 100644 --- a/crates/router/src/routes/profiles.rs +++ b/crates/router/src/routes/profiles.rs @@ -253,6 +253,7 @@ pub async fn profiles_list( .await } +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::ProfileList))] pub async fn profiles_list_at_profile_level( state: web::Data, @@ -347,6 +348,7 @@ pub async fn toggle_extended_card_info( .await } +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::MerchantConnectorsList))] pub async fn payment_connector_list_profile( state: web::Data, diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 4c8d89fa87d5..800902f1049e 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -16,7 +16,7 @@ use crate::{ routes::AppState, services::{api as oss_api, authentication as auth, authorization::permissions::Permission}, }; -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all)] pub async fn routing_create_config( state: web::Data, @@ -59,6 +59,49 @@ pub async fn routing_create_config( .await } +#[cfg(all(feature = "olap", feature = "v2"))] +#[instrument(skip_all)] +pub async fn routing_create_config( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, + transaction_type: &enums::TransactionType, +) -> impl Responder { + let flow = Flow::RoutingCreateConfig; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth: auth::AuthenticationData, payload, _| { + routing::create_routing_algorithm_under_profile( + state, + auth.merchant_account, + auth.key_store, + Some(auth.profile.get_id().clone()), + payload, + transaction_type, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::RoutingWrite, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth { + permission: Permission::RoutingWrite, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await +} + #[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all)] pub async fn routing_link_config( @@ -153,7 +196,7 @@ pub async fn routing_link_config( .await } -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all)] pub async fn routing_retrieve_config( state: web::Data, @@ -195,6 +238,48 @@ pub async fn routing_retrieve_config( .await } +#[cfg(all(feature = "olap", feature = "v2"))] +#[instrument(skip_all)] +pub async fn routing_retrieve_config( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> impl Responder { + let algorithm_id = path.into_inner(); + let flow = Flow::RoutingRetrieveConfig; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + algorithm_id, + |state, auth: auth::AuthenticationData, algorithm_id, _| { + routing::retrieve_routing_algorithm_from_algorithm_id( + state, + auth.merchant_account, + auth.key_store, + Some(auth.profile.get_id().clone()), + algorithm_id, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth { + permission: Permission::RoutingRead, + minimum_entity_level: EntityType::Profile, + }, + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth { + permission: Permission::RoutingRead, + minimum_entity_level: EntityType::Profile, + }, + api_locking::LockAction::NotApplicable, + )) + .await +} + #[cfg(feature = "olap")] #[instrument(skip_all)] pub async fn list_routing_configs( @@ -237,7 +322,7 @@ pub async fn list_routing_configs( .await } -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all)] pub async fn list_routing_configs_for_profile( state: web::Data, diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs index 17c946c4810b..a9b6255b693e 100644 --- a/crates/router/src/routes/verification.rs +++ b/crates/router/src/routes/verification.rs @@ -9,6 +9,7 @@ use crate::{ services::{api, authentication as auth, authorization::permissions::Permission}, }; +#[cfg(all(feature = "olap", feature = "v1"))] #[instrument(skip_all, fields(flow = ?Flow::Verification))] pub async fn apple_pay_merchant_registration( state: web::Data, diff --git a/crates/router/src/routes/verify_connector.rs b/crates/router/src/routes/verify_connector.rs index 29f8c154bc0d..6e982a9eebe8 100644 --- a/crates/router/src/routes/verify_connector.rs +++ b/crates/router/src/routes/verify_connector.rs @@ -9,6 +9,7 @@ use crate::{ services::{self, authentication as auth, authorization::permissions::Permission}, }; +#[cfg(feature = "v1")] #[instrument(skip_all, fields(flow = ?Flow::VerifyPaymentConnector))] pub async fn payment_connector_verify( state: web::Data, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 92e2177633cd..e23e25269282 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -19,8 +19,10 @@ use router_env::logger; use serde::Serialize; use self::blacklist::BlackList; +#[cfg(all(feature = "partial-auth", feature = "v1"))] +use self::detached::ExtractedPayload; #[cfg(feature = "partial-auth")] -use self::detached::{ExtractedPayload, GetAuthType}; +use self::detached::GetAuthType; use super::authorization::{self, permissions::Permission}; #[cfg(feature = "olap")] use super::jwt; @@ -30,7 +32,7 @@ use crate::configs::Settings; use crate::consts; #[cfg(feature = "olap")] use crate::core::errors::UserResult; -#[cfg(feature = "partial-auth")] +#[cfg(all(feature = "partial-auth", feature = "v1"))] use crate::core::metrics; use crate::{ core::{ @@ -51,6 +53,7 @@ pub mod decision; #[cfg(feature = "partial-auth")] mod detached; +#[cfg(feature = "v1")] #[derive(Clone, Debug)] pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, @@ -58,8 +61,9 @@ pub struct AuthenticationData { pub profile_id: Option, } +#[cfg(feature = "v2")] #[derive(Clone, Debug)] -pub struct AuthenticationDataV2 { +pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, pub key_store: domain::MerchantKeyStore, pub profile: domain::Profile, @@ -276,13 +280,15 @@ impl AuthInfo for () { } } +#[cfg(feature = "v1")] impl AuthInfo for AuthenticationData { fn get_merchant_id(&self) -> Option<&id_type::MerchantId> { Some(self.merchant_account.get_id()) } } -impl AuthInfo for AuthenticationDataV2 { +#[cfg(feature = "v2")] +impl AuthInfo for AuthenticationData { fn get_merchant_id(&self) -> Option<&id_type::MerchantId> { Some(self.merchant_account.get_id()) } @@ -365,8 +371,9 @@ where } } +#[cfg(feature = "v2")] #[async_trait] -impl AuthenticateAndFetch for ApiKeyAuth +impl AuthenticateAndFetch for ApiKeyAuth where A: SessionStateInfo + Sync, { @@ -374,7 +381,7 @@ where &self, request_headers: &HeaderMap, state: &A, - ) -> RouterResult<(AuthenticationDataV2, AuthenticationType)> { + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let api_key = get_api_key(request_headers) .change_context(errors::ApiErrorResponse::Unauthorized)? .trim(); @@ -441,7 +448,7 @@ where .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; - let auth = AuthenticationDataV2 { + let auth = AuthenticationData { merchant_account: merchant, key_store, profile, @@ -456,6 +463,7 @@ where } } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for ApiKeyAuth where @@ -553,7 +561,7 @@ where } } -#[cfg(feature = "partial-auth")] +#[cfg(all(feature = "partial-auth", feature = "v1"))] #[async_trait] impl AuthenticateAndFetch for HeaderAuth where @@ -638,12 +646,12 @@ where } } -#[cfg(feature = "partial-auth")] +#[cfg(all(feature = "partial-auth", feature = "v2"))] #[async_trait] -impl AuthenticateAndFetch for HeaderAuth +impl AuthenticateAndFetch for HeaderAuth where A: SessionStateInfo + Sync, - I: AuthenticateAndFetch + I: AuthenticateAndFetch + AuthenticateAndFetch + GetAuthType + Sync @@ -653,7 +661,7 @@ where &self, request_headers: &HeaderMap, state: &A, - ) -> RouterResult<(AuthenticationDataV2, AuthenticationType)> { + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let (auth_data, auth_type): (AuthenticationData, AuthenticationType) = self .0 .authenticate_and_fetch(request_headers, state) @@ -671,7 +679,7 @@ where ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; - let auth_data_v2 = AuthenticationDataV2 { + let auth_data_v2 = AuthenticationData { merchant_account: auth_data.merchant_account, key_store: auth_data.key_store, profile, @@ -680,7 +688,7 @@ where } } -#[cfg(feature = "partial-auth")] +#[cfg(all(feature = "partial-auth", feature = "v1"))] async fn construct_authentication_data( state: &A, merchant_id: &id_type::MerchantId, @@ -867,6 +875,7 @@ where #[derive(Debug)] pub struct AdminApiAuthWithMerchantIdFromRoute(pub id_type::MerchantId); +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for AdminApiAuthWithMerchantIdFromRoute where @@ -927,6 +936,73 @@ where } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for AdminApiAuthWithMerchantIdFromRoute +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + AdminApiAuth + .authenticate_and_fetch(request_headers, state) + .await?; + + let merchant_id = self.0.clone(); + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch merchant key store for the merchant id") + } + })?; + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + let merchant = state + .store() + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch merchant account for the merchant id") + } + })?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile, + }; + + Ok(( + auth, + AuthenticationType::AdminApiAuthWithMerchantId { merchant_id }, + )) + } +} + /// A helper struct to extract headers from the request struct HeaderMapStruct<'a> { headers: &'a HeaderMap, @@ -974,6 +1050,7 @@ impl<'a> HeaderMapStruct<'a> { #[derive(Debug)] pub struct AdminApiAuthWithMerchantIdFromHeader; +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for AdminApiAuthWithMerchantIdFromHeader where @@ -1033,9 +1110,77 @@ where } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for AdminApiAuthWithMerchantIdFromHeader +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + AdminApiAuth + .authenticate_and_fetch(request_headers, state) + .await?; + + let merchant_id = HeaderMapStruct::new(request_headers).get_merchant_id_from_header()?; + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::MerchantAccountNotFound) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch merchant key store for the merchant id") + } + })?; + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + let merchant = state + .store() + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch merchant account for the merchant id") + } + })?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile, + }; + Ok(( + auth, + AuthenticationType::AdminApiAuthWithMerchantId { merchant_id }, + )) + } +} + #[derive(Debug)] pub struct EphemeralKeyAuth; +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for EphemeralKeyAuth where @@ -1059,9 +1204,24 @@ where .await } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for EphemeralKeyAuth +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + todo!() + } +} #[derive(Debug)] pub struct MerchantIdAuth(pub id_type::MerchantId); +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for MerchantIdAuth where @@ -1126,6 +1286,7 @@ impl GetAuthType for PublishableKeyAuth { } } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for PublishableKeyAuth where @@ -1150,9 +1311,13 @@ where e.change_context(errors::ApiErrorResponse::InternalServerError) } }) - .map(|auth| { + .map(|(merchant_account, key_store)| { ( - auth.clone(), + AuthenticationData { + merchant_account, + key_store, + profile_id, + }, AuthenticationType::PublishableKey { merchant_id: auth.merchant_account.get_id().clone(), }, @@ -1161,6 +1326,52 @@ where } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for PublishableKeyAuth +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let publishable_key = + get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; + let key_manager_state = &(&state.session_state()).into(); + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + + let (merchant_account, key_store) = state + .store() + .find_merchant_account_by_publishable_key(key_manager_state, publishable_key) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + } + })?; + let merchant_id = merchant_account.get_id().clone(); + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + Ok(( + AuthenticationData { + merchant_account, + key_store, + profile, + }, + AuthenticationType::PublishableKey { merchant_id }, + )) + } +} + #[derive(Debug)] pub(crate) struct JWTAuth { pub permission: Permission, @@ -1374,6 +1585,7 @@ where } } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuthMerchantFromHeader where @@ -1388,7 +1600,6 @@ where if payload.check_in_blacklist(state).await? { return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); } - let role_info = authorization::get_role_info(state, &payload).await?; authorization::check_permission(&self.required_permission, &role_info)?; authorization::check_entity(self.minimum_entity_level, &role_info)?; @@ -1441,6 +1652,82 @@ where } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for JWTAuthMerchantFromHeader +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let payload = parse_jwt_payload::(request_headers, state).await?; + if payload.check_in_blacklist(state).await? { + return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); + } + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + + let role_info = authorization::get_role_info(state, &payload).await?; + authorization::check_permission(&self.required_permission, &role_info)?; + authorization::check_entity(self.minimum_entity_level, &role_info)?; + + let merchant_id_from_header = + HeaderMapStruct::new(request_headers).get_merchant_id_from_header()?; + + // Check if token has access to MerchantId that has been requested through headers + if payload.merchant_id != merchant_id_from_header { + return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); + } + + let key_manager_state = &(&state.session_state()).into(); + + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant key store for the merchant id")?; + + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + let merchant = state + .store() + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant account for the merchant id")?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile, + }; + + Ok(( + auth, + AuthenticationType::MerchantJwt { + merchant_id: payload.merchant_id, + user_id: Some(payload.user_id), + }, + )) + } +} + #[async_trait] impl AuthenticateAndFetch<(), A> for JWTAuthMerchantFromRoute where @@ -1474,6 +1761,7 @@ where } } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuthMerchantFromRoute where @@ -1534,6 +1822,75 @@ where )) } } + +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for JWTAuthMerchantFromRoute +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let payload = parse_jwt_payload::(request_headers, state).await?; + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + if payload.check_in_blacklist(state).await? { + return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); + } + + if payload.merchant_id != self.merchant_id { + return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); + } + + let role_info = authorization::get_role_info(state, &payload).await?; + authorization::check_permission(&self.required_permission, &role_info)?; + authorization::check_entity(self.minimum_entity_level, &role_info)?; + + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant key store for the merchant id")?; + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + let merchant = state + .store() + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant account for the merchant id")?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile, + }; + Ok(( + auth.clone(), + AuthenticationType::MerchantJwt { + merchant_id: auth.merchant_account.get_id().clone(), + user_id: Some(payload.user_id), + }, + )) + } +} pub struct JWTAuthMerchantAndProfileFromRoute { pub merchant_id: id_type::MerchantId, pub profile_id: id_type::ProfileId, @@ -1541,6 +1898,7 @@ pub struct JWTAuthMerchantAndProfileFromRoute { pub minimum_entity_level: EntityType, } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuthMerchantAndProfileFromRoute where @@ -1617,6 +1975,7 @@ pub struct JWTAuthProfileFromRoute { pub minimum_entity_level: EntityType, } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuthProfileFromRoute where @@ -1695,6 +2054,72 @@ where } } +#[cfg(feature = "v2")] +#[async_trait] +impl AuthenticateAndFetch for JWTAuthProfileFromRoute +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let payload = parse_jwt_payload::(request_headers, state).await?; + if payload.check_in_blacklist(state).await? { + return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); + } + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; + + let role_info = authorization::get_role_info(state, &payload).await?; + authorization::check_permission(&self.required_permission, &role_info)?; + authorization::check_entity(self.minimum_entity_level, &role_info)?; + + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant key store for the merchant id")?; + + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + let merchant = state + .store() + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) + .attach_printable("Failed to fetch merchant account for the merchant id")?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile, + }; + Ok(( + auth.clone(), + AuthenticationType::MerchantJwt { + merchant_id: auth.merchant_account.get_id().clone(), + user_id: Some(payload.user_id), + }, + )) + } +} + pub async fn parse_jwt_payload(headers: &HeaderMap, state: &A) -> RouterResult where T: serde::de::DeserializeOwned, @@ -1713,6 +2138,7 @@ where decode_jwt(&token, state).await } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuth where @@ -1770,8 +2196,9 @@ where } } +#[cfg(feature = "v2")] #[async_trait] -impl AuthenticateAndFetch for JWTAuth +impl AuthenticateAndFetch for JWTAuth where A: SessionStateInfo + Sync, { @@ -1779,7 +2206,7 @@ where &self, request_headers: &HeaderMap, state: &A, - ) -> RouterResult<(AuthenticationDataV2, AuthenticationType)> { + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let payload = parse_jwt_payload::(request_headers, state).await?; if payload.check_in_blacklist(state).await? { return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); @@ -1821,7 +2248,7 @@ where .to_not_found_response(errors::ApiErrorResponse::InvalidJwtToken) .attach_printable("Failed to fetch merchant account for the merchant id")?; let merchant_id = merchant.get_id().clone(); - let auth = AuthenticationDataV2 { + let auth = AuthenticationData { merchant_account: merchant, key_store, profile, @@ -1838,6 +2265,7 @@ where pub type AuthenticationDataWithUserId = (AuthenticationData, String); +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for JWTAuth where @@ -1948,6 +2376,7 @@ where } } +#[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for DashboardNoPermissionAuth where From 7aa6e95ad7d454a9b851af2c83910fa22efb3f34 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 22 Oct 2024 16:36:39 +0530 Subject: [PATCH 03/52] address clippy lints --- .../src/payments/payment_intent.rs | 6 ++++-- crates/router/src/core/payments/transformers.rs | 4 +++- crates/router/src/db/kafka_store.rs | 2 +- crates/router/src/db/merchant_account.rs | 1 - crates/router/src/services/authentication.rs | 5 +++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 37f672550428..348510d2ab2e 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -32,9 +32,11 @@ use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, - types::OrderDetailsWithAmount, - ApiDieselConvertor, RemoteStorageObject, + RemoteStorageObject, }; +#[cfg(feature = "v2")] +use crate::{types::OrderDetailsWithAmount, ApiDieselConvertor}; + #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 3ecd4eb6e3c9..fe8de70615cc 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -17,7 +17,7 @@ use diesel_models::{ }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ - payments::payment_intent::CustomerData, router_request_types, ApiDieselConvertor, + payments::payment_intent::CustomerData, router_request_types, }; use masking::{ExposeInterface, Maskable, PeekInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; @@ -44,6 +44,8 @@ use crate::{ }, utils::{OptionExt, ValueExt}, }; +#[cfg(feature = "v2")] +use hyperswitch_domain_models::ApiDieselConvertor; pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>( state: &'a SessionState, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index e54df3070ad4..858113145545 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -73,7 +73,7 @@ use crate::{ unified_translations::UnifiedTranslationsInterface, CommonStorageInterface, GlobalStorageInterface, MasterKeyInterface, StorageInterface, }, - services::{authentication, kafka::KafkaProducer, Store}, + services::{kafka::KafkaProducer, Store}, types::{domain, storage, AccessToken}, }; diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index 1a25f5b6654d..6bb79e0ffc6b 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -13,7 +13,6 @@ use crate::{ connection, core::errors::{self, CustomResult}, db::merchant_key_store::MerchantKeyStoreInterface, - services::authentication, types::{ domain::{ self, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index e23e25269282..451824b1f709 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1312,14 +1312,15 @@ where } }) .map(|(merchant_account, key_store)| { + let merchant_id = merchant_account.get_id().clone(); ( AuthenticationData { merchant_account, key_store, - profile_id, + profile_id: None, }, AuthenticationType::PublishableKey { - merchant_id: auth.merchant_account.get_id().clone(), + merchant_id, }, ) }) From 1d07bba05af3c027f3388e7eab76c7acba4d1979 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:08:01 +0000 Subject: [PATCH 04/52] chore: run formatter --- crates/router/src/core/payments/transformers.rs | 8 +++----- crates/router/src/services/authentication.rs | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index fe8de70615cc..9917a20e43b2 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -16,9 +16,9 @@ use diesel_models::{ payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId, }; use error_stack::{report, ResultExt}; -use hyperswitch_domain_models::{ - payments::payment_intent::CustomerData, router_request_types, -}; +#[cfg(feature = "v2")] +use hyperswitch_domain_models::ApiDieselConvertor; +use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types}; use masking::{ExposeInterface, Maskable, PeekInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; @@ -44,8 +44,6 @@ use crate::{ }, utils::{OptionExt, ValueExt}, }; -#[cfg(feature = "v2")] -use hyperswitch_domain_models::ApiDieselConvertor; pub async fn construct_router_data_to_update_calculated_tax<'a, F, T>( state: &'a SessionState, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 451824b1f709..5f3a745ddb1e 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1319,9 +1319,7 @@ where key_store, profile_id: None, }, - AuthenticationType::PublishableKey { - merchant_id, - }, + AuthenticationType::PublishableKey { merchant_id }, ) }) } From 560878be135c06a5993f8c492c0bb3c714c71ca0 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 23 Oct 2024 14:23:33 +0530 Subject: [PATCH 05/52] feature_metadata field in payment intent to strict type --- crates/api_models/src/payments.rs | 2 +- crates/common_utils/src/hashing.rs | 2 +- crates/diesel_models/src/payment_intent.rs | 10 +- crates/diesel_models/src/types.rs | 30 +++- crates/hyperswitch_domain_models/src/lib.rs | 135 ++++++++++++++- .../hyperswitch_domain_models/src/payments.rs | 14 +- .../src/payments/payment_intent.rs | 23 +-- crates/hyperswitch_domain_models/src/types.rs | 4 - .../src/types/payments.rs | 163 ------------------ crates/masking/src/strategy.rs | 2 +- .../router/src/core/payments/transformers.rs | 9 +- 11 files changed, 182 insertions(+), 212 deletions(-) delete mode 100644 crates/hyperswitch_domain_models/src/types/payments.rs diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ac43a609d79d..eab4ec17a7cc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -382,7 +382,7 @@ pub struct PaymentsCreateIntentResponse { /// Additional data that might be required by hyperswitch based on the requested features by the merchants. #[schema(value_type = Option)] - pub feature_metadata: Option, + pub feature_metadata: Option, /// Whether to generate the payment link for this payment or not (if applicable) #[schema(value_type = EnablePaymentLinkRequest)] diff --git a/crates/common_utils/src/hashing.rs b/crates/common_utils/src/hashing.rs index d08cd9f0868a..0982ca537881 100644 --- a/crates/common_utils/src/hashing.rs +++ b/crates/common_utils/src/hashing.rs @@ -1,7 +1,7 @@ use masking::{PeekInterface, Secret, Strategy}; use serde::{Deserialize, Serialize, Serializer}; -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, PartialEq, Debug, Deserialize)] /// Represents a hashed string using blake3's hashing strategy. pub struct HashedString>(Secret); diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 3c235ff1e93e..8e7bb234e848 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -10,7 +10,7 @@ use crate::schema::payment_intent; #[cfg(feature = "v2")] use crate::schema_v2::payment_intent; #[cfg(feature = "v2")] -use crate::types::OrderDetailsWithAmount; +use crate::types::{FeatureMetadata, OrderDetailsWithAmount}; #[cfg(feature = "v2")] #[derive(Clone, Debug, PartialEq, Identifiable, Queryable, Serialize, Deserialize, Selectable)] @@ -38,7 +38,7 @@ pub struct PaymentIntent { pub order_details: Option>>, pub allowed_payment_method_types: Option, pub connector_metadata: Option, - pub feature_metadata: Option, + pub feature_metadata: Option, pub attempt_count: i16, pub profile_id: common_utils::id_type::ProfileId, pub payment_link_id: Option, @@ -231,11 +231,11 @@ pub struct PaymentIntentNew { pub setup_future_usage: Option, pub client_secret: common_utils::types::ClientSecret, pub active_attempt_id: Option, - #[diesel(deserialize_as = super::OptionalDieselArray)] - pub order_details: Option>, + #[diesel(deserialize_as = super::OptionalDieselArray>)] + pub order_details: Option>>, pub allowed_payment_method_types: Option, pub connector_metadata: Option, - pub feature_metadata: Option, + pub feature_metadata: Option, pub attempt_count: i16, pub profile_id: common_utils::id_type::ProfileId, pub payment_link_id: Option, diff --git a/crates/diesel_models/src/types.rs b/crates/diesel_models/src/types.rs index 2935017aeb1f..001d25c1fb4a 100644 --- a/crates/diesel_models/src/types.rs +++ b/crates/diesel_models/src/types.rs @@ -1,6 +1,12 @@ +use common_utils::{hashing::HashedString, pii}; +use diesel::{ + sql_types::{Json, Jsonb}, + AsExpression, FromSqlRow, +}; +use masking::{Secret, WithType}; use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromSqlRow, AsExpression)] +#[diesel(sql_type = Jsonb)] pub struct OrderDetailsWithAmount { /// Name of the product that is being purchased pub product_name: String, @@ -29,3 +35,23 @@ pub struct OrderDetailsWithAmount { impl masking::SerializableSecret for OrderDetailsWithAmount {} common_utils::impl_to_sql_from_sql_json!(OrderDetailsWithAmount); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, FromSqlRow, AsExpression)] +#[diesel(sql_type = Json)] +pub struct FeatureMetadata { + /// Redirection response coming in request as metadata field only for redirection scenarios + pub redirect_response: Option, + // TODO: Convert this to hashedstrings to avoid PII sensitive data + /// Additional tags to be used for global search + pub search_tags: Option>>, +} +impl masking::SerializableSecret for FeatureMetadata {} +common_utils::impl_to_sql_from_sql_json!(FeatureMetadata); + +#[derive(Default, Debug, Eq, PartialEq, Deserialize, Serialize, Clone)] +pub struct RedirectResponse { + pub param: Option>, + pub json_payload: Option, +} +impl masking::SerializableSecret for RedirectResponse {} +common_utils::impl_to_sql_from_sql_json!(RedirectResponse); diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 89b6b95762c8..ff2b3b1adc12 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -30,7 +30,11 @@ pub trait PayoutAttemptInterface {} #[cfg(not(feature = "payouts"))] pub trait PayoutsInterface {} -pub use crate::types::OrderDetailsWithAmount as DomainOrderDetailsWithAmount; +use api_models::payments::{ + FeatureMetadata as ApiFeatureMetadata, OrderDetailsWithAmount as ApiOrderDetailsWithAmount, + RedirectResponse as ApiRedirectResponse, +}; +use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount, RedirectResponse}; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub enum RemoteStorageObject { @@ -62,15 +66,125 @@ use std::fmt::Debug; pub trait ApiModelToDieselModelConvertor { /// Convert from a foreign type to the current type fn convert_from(from: F) -> Self; + fn convert_back(self) -> F; } -pub trait ApiDieselConvertor { - fn from_api(api_model: Api) -> Self; - fn to_api(&self) -> Api; - fn from_diesel(diesel_model: Diesel) -> Self; - fn to_diesel(&self) -> Diesel; +impl ApiModelToDieselModelConvertor for FeatureMetadata { + fn convert_from(from: ApiFeatureMetadata) -> Self { + let ApiFeatureMetadata { + redirect_response, + search_tags, + } = from; + Self { + redirect_response: redirect_response.map(RedirectResponse::convert_from), + search_tags, + } + } + + fn convert_back(self) -> ApiFeatureMetadata { + let Self { + redirect_response, + search_tags, + } = self; + ApiFeatureMetadata { + redirect_response: redirect_response + .map(|redirect_response| redirect_response.convert_back()), + search_tags, + } + } +} + +impl ApiModelToDieselModelConvertor for RedirectResponse { + fn convert_from(from: ApiRedirectResponse) -> Self { + let ApiRedirectResponse { + param, + json_payload, + } = from; + Self { + param, + json_payload, + } + } + + fn convert_back(self) -> ApiRedirectResponse { + let Self { + param, + json_payload, + } = self; + ApiRedirectResponse { + param, + json_payload, + } + } } +impl ApiModelToDieselModelConvertor for OrderDetailsWithAmount { + fn convert_from(from: ApiOrderDetailsWithAmount) -> Self { + let ApiOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = from; + Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } + + fn convert_back(self) -> ApiOrderDetailsWithAmount { + let Self { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } = self; + ApiOrderDetailsWithAmount { + product_name, + quantity, + amount, + requires_shipping, + product_img_link, + product_id, + category, + sub_category, + brand, + product_type, + product_tax_code, + } + } +} + +// pub trait ApiDieselConvertor { +// fn from_api(api_model: Api) -> Self; +// fn to_api(&self) -> Api; +// fn from_diesel(diesel_model: Diesel) -> Self; +// fn to_diesel(&self) -> Diesel; +// } + #[cfg(feature = "v2")] impl ApiModelToDieselModelConvertor for diesel_models::payment_intent::PaymentLinkConfigRequestForPayments @@ -95,6 +209,9 @@ impl ApiModelToDieselModelConvertor }), } } + fn convert_back(self) -> api_models::admin::PaymentLinkConfigRequest { + todo!() + } } #[cfg(feature = "v2")] @@ -110,6 +227,9 @@ impl ApiModelToDieselModelConvertor api_models::admin::PaymentLinkTransactionDetails { + todo!() + } } #[cfg(feature = "v2")] @@ -123,6 +243,9 @@ impl ApiModelToDieselModelConvertor api_models::admin::TransactionDetailsUiConfiguration { + todo!() + } } #[cfg(feature = "v2")] diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index ca7941870ebc..a5f97e03bcbd 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -14,11 +14,13 @@ pub mod payment_attempt; pub mod payment_intent; use common_enums as storage_enums; +#[cfg(feature = "v2")] +use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount}; use self::payment_attempt::PaymentAttempt; use crate::RemoteStorageObject; #[cfg(feature = "v2")] -use crate::{business_profile, merchant_account, types::OrderDetailsWithAmount}; +use crate::{business_profile, merchant_account}; #[cfg(feature = "v2")] use crate::{errors, ApiModelToDieselModelConvertor}; @@ -220,7 +222,7 @@ pub struct PaymentIntent { pub allowed_payment_method_types: Option>>, /// This metadata contains details about pub connector_metadata: Option, - pub feature_metadata: Option, + pub feature_metadata: Option, /// Number of attempts that have been made for the order pub attempt_count: i16, /// The profile id for the payment. @@ -305,10 +307,6 @@ impl PaymentIntent { .get_connector_metadata_as_value() .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) .attach_printable("Error getting connector metadata as value")?; - let feature_metadata = request - .get_feature_metadata_as_value() - .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) - .attach_printable("Error getting feature metadata as value")?; let request_incremental_authorization = Self::get_request_incremental_authorization_value(&request)?; let allowed_payment_method_types = @@ -333,7 +331,7 @@ impl PaymentIntent { let order_details = request.order_details.map(|order_details| { order_details .into_iter() - .map(|order_detail| Secret::new(OrderDetailsWithAmount::from(order_detail))) + .map(|order_detail| Secret::new(OrderDetailsWithAmount::convert_from(order_detail))) .collect() }); Ok(Self { @@ -357,7 +355,7 @@ impl PaymentIntent { order_details, allowed_payment_method_types, connector_metadata, - feature_metadata, + feature_metadata: request.feature_metadata.map(FeatureMetadata::convert_from), // Attempt count is 0 in create intent as no attempt is made yet attempt_count: 0, profile_id: profile.get_id().clone(), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 348510d2ab2e..40f86120a9b0 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -14,6 +14,8 @@ use common_utils::{ MinorUnit, }, }; +#[cfg(feature = "v2")] +use diesel_models::types::OrderDetailsWithAmount; use diesel_models::{ PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; @@ -34,8 +36,6 @@ use crate::{ type_encryption::{crypto_operation, CryptoOperation}, RemoteStorageObject, }; -#[cfg(feature = "v2")] -use crate::{types::OrderDetailsWithAmount, ApiDieselConvertor}; #[async_trait::async_trait] pub trait PaymentIntentInterface { @@ -1537,7 +1537,7 @@ impl behaviour::Conversion for PaymentIntent { order_details: order_details.map(|order_details| { order_details .into_iter() - .map(|order_detail| Secret::new(order_detail.expose().to_diesel())) + .map(|order_detail| Secret::new(order_detail.expose())) .collect::>() }), allowed_payment_method_types: allowed_payment_method_types @@ -1674,9 +1674,7 @@ impl behaviour::Conversion for PaymentIntent { order_details: storage_model.order_details.map(|order_details| { order_details .into_iter() - .map(|order_detail| { - Secret::new(OrderDetailsWithAmount::from_diesel(order_detail.expose())) - }) + .map(|order_detail| Secret::new(order_detail.expose())) .collect::>() }), allowed_payment_method_types, @@ -1747,18 +1745,7 @@ impl behaviour::Conversion for PaymentIntent { setup_future_usage: Some(self.setup_future_usage), client_secret: self.client_secret, active_attempt_id: self.active_attempt.map(|attempt| attempt.get_id()), - order_details: self - .order_details - .map(|order_details| { - order_details - .into_iter() - .map(|order_detail| order_detail.encode_to_value().map(Secret::new)) - .collect::, _>>() - }) - .transpose() - .change_context(ValidationError::InvalidValue { - message: "Invalid value found for ".to_string(), - })?, + order_details: self.order_details, allowed_payment_method_types: self .allowed_payment_method_types .map(|allowed_payment_method_types| { diff --git a/crates/hyperswitch_domain_models/src/types.rs b/crates/hyperswitch_domain_models/src/types.rs index ffbd9b64b911..eeb63bbb508f 100644 --- a/crates/hyperswitch_domain_models/src/types.rs +++ b/crates/hyperswitch_domain_models/src/types.rs @@ -1,7 +1,3 @@ -mod payments; - -pub use payments::*; - use crate::{ router_data::{AccessToken, RouterData}, router_flow_types::{ diff --git a/crates/hyperswitch_domain_models/src/types/payments.rs b/crates/hyperswitch_domain_models/src/types/payments.rs deleted file mode 100644 index e6d763cb4a1b..000000000000 --- a/crates/hyperswitch_domain_models/src/types/payments.rs +++ /dev/null @@ -1,163 +0,0 @@ -use api_models::payments::OrderDetailsWithAmount as ApiOrderDetailsWithAmount; -use diesel_models::types::OrderDetailsWithAmount as DieselOrderDetailsWithAmount; -use serde; - -#[derive(Clone, Debug, PartialEq, serde::Serialize)] -pub struct OrderDetailsWithAmount { - /// Name of the product that is being purchased - pub product_name: String, - /// The quantity of the product to be purchased - pub quantity: u16, - /// the amount per quantity of product - pub amount: i64, - // Does the order includes shipping - pub requires_shipping: Option, - /// The image URL of the product - pub product_img_link: Option, - /// ID of the product that is being purchased - pub product_id: Option, - /// Category of the product that is being purchased - pub category: Option, - /// Sub category of the product that is being purchased - pub sub_category: Option, - /// Brand of the product that is being purchased - pub brand: Option, - /// Type of the product that is being purchased - pub product_type: Option, - /// The tax code for the product - pub product_tax_code: Option, -} - -impl masking::SerializableSecret for OrderDetailsWithAmount {} - -impl crate::ApiDieselConvertor - for OrderDetailsWithAmount -{ - fn from_api(api_model: ApiOrderDetailsWithAmount) -> Self { - Self::from(api_model) - } - - fn to_api(&self) -> ApiOrderDetailsWithAmount { - let Self { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } = self.clone(); - ApiOrderDetailsWithAmount { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } - } - - fn from_diesel(diesel_model: DieselOrderDetailsWithAmount) -> Self { - Self::from(diesel_model) - } - - fn to_diesel(&self) -> DieselOrderDetailsWithAmount { - let Self { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } = self.clone(); - DieselOrderDetailsWithAmount { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } - } -} - -impl From for OrderDetailsWithAmount { - fn from(value: ApiOrderDetailsWithAmount) -> Self { - let ApiOrderDetailsWithAmount { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } = value; - Self { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } - } -} - -impl From for OrderDetailsWithAmount { - fn from(value: DieselOrderDetailsWithAmount) -> Self { - let DieselOrderDetailsWithAmount { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } = value; - Self { - product_name, - quantity, - amount, - requires_shipping, - product_img_link, - product_id, - category, - sub_category, - brand, - product_type, - product_tax_code, - } - } -} diff --git a/crates/masking/src/strategy.rs b/crates/masking/src/strategy.rs index eb705ca490a7..b497cc3ed4f4 100644 --- a/crates/masking/src/strategy.rs +++ b/crates/masking/src/strategy.rs @@ -8,7 +8,7 @@ pub trait Strategy { /// Debug with type #[cfg_attr(feature = "serde", derive(serde::Deserialize))] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum WithType {} impl Strategy for WithType { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 9917a20e43b2..b4b2a41a6ee6 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -17,7 +17,7 @@ use diesel_models::{ }; use error_stack::{report, ResultExt}; #[cfg(feature = "v2")] -use hyperswitch_domain_models::ApiDieselConvertor; +use hyperswitch_domain_models::ApiModelToDieselModelConvertor; use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types}; use masking::{ExposeInterface, Maskable, PeekInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; @@ -570,7 +570,7 @@ where order_details: payment_intent.order_details.clone().map(|order_details| { order_details .into_iter() - .map(|order_detail| order_detail.expose().to_api()) + .map(|order_detail| order_detail.expose().convert_back()) .collect() }), allowed_payment_method_types: payment_intent @@ -584,7 +584,10 @@ where }), metadata: payment_intent.metadata.clone(), connector_metadata: payment_intent.connector_metadata.clone(), - feature_metadata: payment_intent.feature_metadata.clone(), + feature_metadata: payment_intent + .feature_metadata + .clone() + .map(|feature_metadata| feature_metadata.convert_back()), payment_link_enabled: payment_intent.enable_payment_link.clone(), payment_link_config: payment_intent .payment_link_config From d7a283329b3afc752de814a633704e709e5ea527 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 23 Oct 2024 15:41:46 +0530 Subject: [PATCH 06/52] remove unwanted comments --- crates/hyperswitch_domain_models/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index ff2b3b1adc12..4e40132339ac 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -178,13 +178,6 @@ impl ApiModelToDieselModelConvertor for OrderDetailsW } } -// pub trait ApiDieselConvertor { -// fn from_api(api_model: Api) -> Self; -// fn to_api(&self) -> Api; -// fn from_diesel(diesel_model: Diesel) -> Self; -// fn to_diesel(&self) -> Diesel; -// } - #[cfg(feature = "v2")] impl ApiModelToDieselModelConvertor for diesel_models::payment_intent::PaymentLinkConfigRequestForPayments From a51df0d1e235ecd7027cdc5fce8b1afaa7a1237c Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 24 Oct 2024 11:42:30 +0530 Subject: [PATCH 07/52] remove secret --- crates/hyperswitch_domain_models/src/payments.rs | 12 ++---------- crates/router/src/core/payments/transformers.rs | 10 +--------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index a5f97e03bcbd..4f6b1df4041c 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -219,7 +219,7 @@ pub struct PaymentIntent { pub order_details: Option>>, /// This is the list of payment method types that are allowed for the payment intent. /// This field allows the merchant to restrict the payment methods that can be used for the payment intent. - pub allowed_payment_method_types: Option>>, + pub allowed_payment_method_types: Option>, /// This metadata contains details about pub connector_metadata: Option, pub feature_metadata: Option, @@ -309,15 +309,7 @@ impl PaymentIntent { .attach_printable("Error getting connector metadata as value")?; let request_incremental_authorization = Self::get_request_incremental_authorization_value(&request)?; - let allowed_payment_method_types = - request - .allowed_payment_method_types - .map(|allowed_payment_method_types| { - allowed_payment_method_types - .into_iter() - .map(Secret::new) - .collect() - }); + let allowed_payment_method_types = request.allowed_payment_method_types; let session_expiry = common_utils::date_time::now().saturating_add(time::Duration::seconds( diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index b4b2a41a6ee6..3f0e7ae83936 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -573,15 +573,7 @@ where .map(|order_detail| order_detail.expose().convert_back()) .collect() }), - allowed_payment_method_types: payment_intent - .allowed_payment_method_types - .clone() - .map(|allowed_payment_method_types| { - allowed_payment_method_types - .into_iter() - .map(ExposeInterface::expose) - .collect() - }), + allowed_payment_method_types: payment_intent.allowed_payment_method_types.clone(), metadata: payment_intent.metadata.clone(), connector_metadata: payment_intent.connector_metadata.clone(), feature_metadata: payment_intent From 478d8bc2b7e618d11e2c08664d02297fa1820df5 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 24 Oct 2024 12:48:26 +0530 Subject: [PATCH 08/52] fix clippy lints --- crates/openapi/src/openapi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 28a7f44c7a87..27d3e0d6fa9a 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -401,7 +401,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::ApplePayShippingContactFields, api_models::payments::ApplePayAddressParameters, api_models::payments::AmountInfo, - api_models::payments::ProductType, + api_models::enums::ProductType, api_models::payments::GooglePayWalletData, api_models::payments::PayPalWalletData, api_models::payments::PaypalRedirection, From e3a5fc032f51f08f3d4024e161985ad43d61ab56 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:29:32 +0000 Subject: [PATCH 09/52] chore: update Cargo.lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 347d4e587979..da22e87aad5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2048,6 +2048,7 @@ name = "common_enums" version = "0.1.0" dependencies = [ "diesel", + "masking", "router_derive", "serde", "serde_json", From 0b8486758e93401ffd91b09cdb2010ccdd4765e9 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 24 Oct 2024 15:36:26 +0530 Subject: [PATCH 10/52] implement unimplemented todos --- crates/hyperswitch_domain_models/src/lib.rs | 47 +++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 4e40132339ac..137e5fe3961f 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -203,7 +203,29 @@ impl ApiModelToDieselModelConvertor } } fn convert_back(self) -> api_models::admin::PaymentLinkConfigRequest { - todo!() + let Self { + theme, + logo, + seller_name, + sdk_layout, + display_sdk_only, + enabled_saved_payment_method, + transaction_details, + } = self; + api_models::admin::PaymentLinkConfigRequest { + theme, + logo, + seller_name, + sdk_layout, + display_sdk_only, + enabled_saved_payment_method, + transaction_details: transaction_details.map(|transaction_details| { + transaction_details + .into_iter() + .map(|transaction_detail| transaction_detail.convert_back()) + .collect() + }), + } } } @@ -221,7 +243,17 @@ impl ApiModelToDieselModelConvertor api_models::admin::PaymentLinkTransactionDetails { - todo!() + let Self { + key, + value, + ui_configuration, + } = self; + api_models::admin::PaymentLinkTransactionDetails { + key, + value, + ui_configuration: ui_configuration + .map(|ui_configuration| ui_configuration.convert_back()), + } } } @@ -237,7 +269,16 @@ impl ApiModelToDieselModelConvertor api_models::admin::TransactionDetailsUiConfiguration { - todo!() + let Self { + position, + is_key_bold, + is_value_bold, + } = self; + api_models::admin::TransactionDetailsUiConfiguration { + position, + is_key_bold, + is_value_bold, + } } } From 59bbb163e40da36c2e0c49ef2b7357f11762e2b1 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 25 Oct 2024 12:48:42 +0530 Subject: [PATCH 11/52] use profile_id from jwt token --- crates/router/src/services/authentication.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index d4dce8db8b89..6f2cc6bb5af8 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1639,7 +1639,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: None, + profile_id: payload.profile_id, }; Ok(( From 38ad68540fbe10f0fa6ce0ae95f149595501fc8e Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 25 Oct 2024 17:11:14 +0530 Subject: [PATCH 12/52] avoid using OrderDetailsWithAmount from api models in router crate --- crates/hyperswitch_connectors/src/utils.rs | 3 +- .../src/router_request_types.rs | 10 +-- .../src/router_request_types/fraud_check.rs | 7 +- crates/hyperswitch_domain_models/src/types.rs | 2 + crates/router/src/analytics.rs | 24 ++--- .../src/connector/adyen/transformers.rs | 3 +- crates/router/src/connector/utils.rs | 4 +- crates/router/src/core/fraud_check/types.rs | 6 +- crates/router/src/core/utils.rs | 6 +- crates/router/src/routes/payments.rs | 2 +- crates/router/src/routes/routing.rs | 6 +- crates/router/src/services/authentication.rs | 88 ++++++++++++++++--- crates/router/tests/connectors/payme.rs | 3 +- crates/router/tests/connectors/zen.rs | 2 +- 14 files changed, 110 insertions(+), 56 deletions(-) diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 652ec82c5ed8..36802e3ffad8 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; -use api_models::payments::{self, Address, AddressDetails, OrderDetailsWithAmount, PhoneDetails}; +use api_models::payments::{self, Address, AddressDetails, PhoneDetails}; use base64::Engine; use common_enums::{ enums, @@ -24,6 +24,7 @@ use hyperswitch_domain_models::{ PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSyncData, RefundsData, ResponseId, SetupMandateRequestData, }, + types::OrderDetailsWithAmount, }; use hyperswitch_interfaces::{api, errors}; use image::Luma; diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index d29672058f88..38d716db102c 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -7,7 +7,7 @@ use common_utils::{ id_type, pii, types::{self as common_types, MinorUnit}, }; -use diesel_models::enums as storage_enums; +use diesel_models::{enums as storage_enums, types::OrderDetailsWithAmount}; use error_stack::ResultExt; use masking::Secret; use serde::Serialize; @@ -49,7 +49,7 @@ pub struct PaymentsAuthorizeData { pub customer_acceptance: Option, pub setup_mandate_details: Option, pub browser_info: Option, - pub order_details: Option>, + pub order_details: Option>, pub order_category: Option, pub session_token: Option, pub enrolled_for_3ds: bool, @@ -280,7 +280,7 @@ pub struct PaymentsPreProcessingData { pub payment_method_type: Option, pub setup_mandate_details: Option, pub capture_method: Option, - pub order_details: Option>, + pub order_details: Option>, pub router_return_url: Option, pub webhook_url: Option, pub complete_authorize_url: Option, @@ -815,7 +815,7 @@ pub struct PaymentsSessionData { pub currency: common_enums::Currency, pub country: Option, pub surcharge_details: Option, - pub order_details: Option>, + pub order_details: Option>, // Minor Unit amount for amount frame work pub minor_amount: MinorUnit, @@ -826,7 +826,7 @@ pub struct PaymentsTaxCalculationData { pub amount: MinorUnit, pub currency: storage_enums::Currency, pub shipping_cost: Option, - pub order_details: Option>, + pub order_details: Option>, pub shipping_address: Address, } diff --git a/crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs b/crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs index 30a87c5c13d2..4d7f4bfdf495 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types/fraud_check.rs @@ -4,6 +4,7 @@ use common_utils::{ events::{ApiEventMetric, ApiEventsType}, pii::Email, }; +use diesel_models::types::OrderDetailsWithAmount; use masking::Secret; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -12,7 +13,7 @@ use crate::router_request_types; #[derive(Debug, Clone)] pub struct FraudCheckSaleData { pub amount: i64, - pub order_details: Option>, + pub order_details: Option>, pub currency: Option, pub email: Option, } @@ -20,7 +21,7 @@ pub struct FraudCheckSaleData { #[derive(Debug, Clone)] pub struct FraudCheckCheckoutData { pub amount: i64, - pub order_details: Option>, + pub order_details: Option>, pub currency: Option, pub browser_info: Option, pub payment_method_data: Option, @@ -31,7 +32,7 @@ pub struct FraudCheckCheckoutData { #[derive(Debug, Clone)] pub struct FraudCheckTransactionData { pub amount: i64, - pub order_details: Option>, + pub order_details: Option>, pub currency: Option, pub payment_method: Option, pub error_code: Option, diff --git a/crates/hyperswitch_domain_models/src/types.rs b/crates/hyperswitch_domain_models/src/types.rs index eeb63bbb508f..2515f0c757bc 100644 --- a/crates/hyperswitch_domain_models/src/types.rs +++ b/crates/hyperswitch_domain_models/src/types.rs @@ -1,3 +1,5 @@ +pub use diesel_models::types::OrderDetailsWithAmount; + use crate::{ router_data::{AccessToken, RouterData}, router_flow_types::{ diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index cd0760f78c89..fa0bb0dc9fa6 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -534,8 +534,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -702,8 +701,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -870,8 +868,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -1148,8 +1145,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -1303,8 +1299,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -2504,8 +2499,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -2662,8 +2656,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) @@ -2832,8 +2825,7 @@ pub mod routes { .map(ApplicationResponse::Json) }, &auth::JWTAuth { - permission: Permission::Analytics, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileAnalyticsRead, }, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 21c9c34409a7..390c7e0753e0 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1763,8 +1763,7 @@ pub fn get_address_info( } fn get_line_items(item: &AdyenRouterData<&types::PaymentsAuthorizeRouterData>) -> Vec { - let order_details: Option> = - item.router_data.request.order_details.clone(); + let order_details = item.router_data.request.order_details.clone(); match order_details { Some(od) => od .iter() diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 9e9e43734dbb..9b3dd9dfce63 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -7,7 +7,7 @@ use std::{ use api_models::payouts::{self, PayoutVendorAccountDetails}; use api_models::{ enums::{CanadaStatesAbbreviation, UsStatesAbbreviation}, - payments::{self, OrderDetailsWithAmount}, + payments, }; use base64::Engine; use common_utils::{ @@ -18,7 +18,7 @@ use common_utils::{ pii::{self, Email, IpAddress}, types::{AmountConvertor, MinorUnit}, }; -use diesel_models::enums; +use diesel_models::{enums, types::OrderDetailsWithAmount}; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ mandates, diff --git a/crates/router/src/core/fraud_check/types.rs b/crates/router/src/core/fraud_check/types.rs index 3f5988777fd5..a260b70b870c 100644 --- a/crates/router/src/core/fraud_check/types.rs +++ b/crates/router/src/core/fraud_check/types.rs @@ -13,7 +13,7 @@ pub use hyperswitch_domain_models::router_request_types::fraud_check::{ use masking::Serialize; use serde::Deserialize; use utoipa::ToSchema; - +pub use hyperswitch_domain_models::types::OrderDetailsWithAmount; use super::operation::BoxedFraudCheckOperation; use crate::types::{ domain::MerchantAccount, @@ -54,7 +54,7 @@ pub struct FrmData { pub fraud_check: FraudCheck, pub address: PaymentAddress, pub connector_details: ConnectorDetailsCore, - pub order_details: Option>, + pub order_details: Option>, pub refund: Option, pub frm_metadata: Option, } @@ -79,7 +79,7 @@ pub struct PaymentToFrmData { pub merchant_account: MerchantAccount, pub address: PaymentAddress, pub connector_details: ConnectorDetailsCore, - pub order_details: Option>, + pub order_details: Option>, pub frm_metadata: Option, } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index def869079117..e538f9fe0bf3 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -2,10 +2,8 @@ use std::{collections::HashSet, marker::PhantomData, str::FromStr}; #[cfg(feature = "payouts")] use api_models::payouts::PayoutVendorAccountDetails; -use api_models::{ - enums::{DisputeStage, DisputeStatus}, - payments::OrderDetailsWithAmount, -}; +use api_models::enums::{DisputeStage, DisputeStatus}; +use hyperswitch_domain_models::types::OrderDetailsWithAmount; use common_enums::{IntentStatus, RequestIncrementalAuthorization}; #[cfg(feature = "payouts")] use common_utils::{crypto::Encryptable, pii::Email}; diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 1e7e389055d7..3ac27add13b8 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -2052,7 +2052,7 @@ pub async fn payment_confirm_intent( state, &req, internal_payload, - |state, auth: auth::AuthenticationDataV2, req, req_state| async { + |state, auth: auth::AuthenticationData, req, req_state| async { let payment_id = req.global_payment_id; let request = req.payload; diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 9f2128e97de5..57f4fec05653 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -91,8 +91,7 @@ pub async fn routing_create_config( ), #[cfg(feature = "release")] &auth::JWTAuth { - permission: Permission::RoutingWrite, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileRoutingWrite, }, api_locking::LockAction::NotApplicable, )) @@ -263,8 +262,7 @@ pub async fn routing_retrieve_config( ), #[cfg(feature = "release")] &auth::JWTAuth { - permission: Permission::RoutingRead, - minimum_entity_level: EntityType::Profile, + permission: Permission::ProfileRoutingRead, }, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 6f2cc6bb5af8..d4ddfdce81e8 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1136,7 +1136,8 @@ where .authenticate_and_fetch(request_headers, state) .await?; - let merchant_id = HeaderMapStruct::new(request_headers).get_merchant_id_from_header()?; + let merchant_id = HeaderMapStruct::new(request_headers) + .get_id_type_from_header::(headers::X_MERCHANT_ID)?; let profile_id = get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? .get_required_value(headers::X_PROFILE_ID)?; @@ -1191,7 +1192,7 @@ where #[derive(Debug)] pub struct EphemeralKeyAuth; -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] #[async_trait] impl AuthenticateAndFetch for EphemeralKeyAuth where @@ -1215,24 +1216,79 @@ where .await } } -#[cfg(feature = "v2")] +// #[cfg(feature = "v2")] +// #[async_trait] +// impl AuthenticateAndFetch for EphemeralKeyAuth +// where +// A: SessionStateInfo + Sync, +// { +// async fn authenticate_and_fetch( +// &self, +// request_headers: &HeaderMap, +// state: &A, +// ) -> RouterResult<(AuthenticationData, AuthenticationType)> { +// todo!() +// } +// } +#[derive(Debug)] +pub struct MerchantIdAuth(pub id_type::MerchantId); + +#[cfg(feature = "v1")] #[async_trait] -impl AuthenticateAndFetch for EphemeralKeyAuth +impl AuthenticateAndFetch for MerchantIdAuth where A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, - request_headers: &HeaderMap, + _request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - todo!() + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &self.0, + &state.store().get_master_key().to_vec().into(), + ) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to fetch merchant key store for the merchant id") + } + })?; + + let merchant = state + .store() + .find_merchant_account_by_merchant_id(key_manager_state, &self.0, &key_store) + .await + .map_err(|e| { + if e.current_context().is_db_not_found() { + e.change_context(errors::ApiErrorResponse::Unauthorized) + } else { + e.change_context(errors::ApiErrorResponse::InternalServerError) + } + })?; + + let auth = AuthenticationData { + merchant_account: merchant, + key_store, + profile_id: None, + }; + Ok(( + auth.clone(), + AuthenticationType::MerchantId { + merchant_id: auth.merchant_account.get_id().clone(), + }, + )) } } -#[derive(Debug)] -pub struct MerchantIdAuth(pub id_type::MerchantId); -#[cfg(feature = "v1")] +#[cfg(feature = "v2")] #[async_trait] impl AuthenticateAndFetch for MerchantIdAuth where @@ -1240,10 +1296,13 @@ where { async fn authenticate_and_fetch( &self, - _request_headers: &HeaderMap, + request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let key_manager_state = &(&state.session_state()).into(); + let profile_id = + get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)? + .get_required_value(headers::X_PROFILE_ID)?; let key_store = state .store() .get_merchant_key_store_by_merchant_id( @@ -1261,6 +1320,11 @@ where } })?; + let profile = state + .store() + .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state .store() .find_merchant_account_by_merchant_id(key_manager_state, &self.0, &key_store) @@ -1276,7 +1340,7 @@ where let auth = AuthenticationData { merchant_account: merchant, key_store, - profile_id: None, + profile, }; Ok(( auth.clone(), @@ -1845,7 +1909,6 @@ where let role_info = authorization::get_role_info(state, &payload).await?; authorization::check_permission(&self.required_permission, &role_info)?; - authorization::check_entity(self.minimum_entity_level, &role_info)?; let key_manager_state = &(&state.session_state()).into(); let key_store = state @@ -2068,7 +2131,6 @@ where let role_info = authorization::get_role_info(state, &payload).await?; authorization::check_permission(&self.required_permission, &role_info)?; - authorization::check_entity(self.minimum_entity_level, &role_info)?; let key_manager_state = &(&state.session_state()).into(); let key_store = state diff --git a/crates/router/tests/connectors/payme.rs b/crates/router/tests/connectors/payme.rs index e85c57df622f..91e5d9baec43 100644 --- a/crates/router/tests/connectors/payme.rs +++ b/crates/router/tests/connectors/payme.rs @@ -1,7 +1,8 @@ use std::str::FromStr; -use api_models::payments::{Address, AddressDetails, OrderDetailsWithAmount}; +use api_models::payments::{Address, AddressDetails}; use common_utils::pii::Email; +use hyperswitch_domain_models::types::OrderDetailsWithAmount; use masking::Secret; use router::types::{self, domain, storage::enums, PaymentAddress}; diff --git a/crates/router/tests/connectors/zen.rs b/crates/router/tests/connectors/zen.rs index 20948a90c6d3..d39971ec586a 100644 --- a/crates/router/tests/connectors/zen.rs +++ b/crates/router/tests/connectors/zen.rs @@ -1,8 +1,8 @@ use std::str::FromStr; -use api_models::payments::OrderDetailsWithAmount; use cards::CardNumber; use common_utils::{pii::Email, types::MinorUnit}; +use hyperswitch_domain_models::types::OrderDetailsWithAmount; use masking::Secret; use router::types::{self, domain, storage::enums}; From ba1675976807fef7a8eba5e9fc2686b567620e5b Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 25 Oct 2024 17:11:56 +0530 Subject: [PATCH 13/52] run +nightly fmt --- crates/router/src/core/fraud_check/types.rs | 9 ++++++--- crates/router/src/core/utils.rs | 5 ++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/router/src/core/fraud_check/types.rs b/crates/router/src/core/fraud_check/types.rs index a260b70b870c..2aa486fdb3c7 100644 --- a/crates/router/src/core/fraud_check/types.rs +++ b/crates/router/src/core/fraud_check/types.rs @@ -7,13 +7,16 @@ use api_models::{ use common_enums::FrmSuggestion; use common_utils::pii::SecretSerdeValue; use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; -pub use hyperswitch_domain_models::router_request_types::fraud_check::{ - Address, Destination, FrmFulfillmentRequest, FulfillmentStatus, Fulfillments, Product, +pub use hyperswitch_domain_models::{ + router_request_types::fraud_check::{ + Address, Destination, FrmFulfillmentRequest, FulfillmentStatus, Fulfillments, Product, + }, + types::OrderDetailsWithAmount, }; use masking::Serialize; use serde::Deserialize; use utoipa::ToSchema; -pub use hyperswitch_domain_models::types::OrderDetailsWithAmount; + use super::operation::BoxedFraudCheckOperation; use crate::types::{ domain::MerchantAccount, diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index e538f9fe0bf3..1c18d3c133fc 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -1,9 +1,8 @@ use std::{collections::HashSet, marker::PhantomData, str::FromStr}; +use api_models::enums::{DisputeStage, DisputeStatus}; #[cfg(feature = "payouts")] use api_models::payouts::PayoutVendorAccountDetails; -use api_models::enums::{DisputeStage, DisputeStatus}; -use hyperswitch_domain_models::types::OrderDetailsWithAmount; use common_enums::{IntentStatus, RequestIncrementalAuthorization}; #[cfg(feature = "payouts")] use common_utils::{crypto::Encryptable, pii::Email}; @@ -15,7 +14,7 @@ use common_utils::{ use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ merchant_connector_account::MerchantConnectorAccount, payment_address::PaymentAddress, - router_data::ErrorResponse, + router_data::ErrorResponse, types::OrderDetailsWithAmount, }; #[cfg(feature = "payouts")] use masking::{ExposeInterface, PeekInterface}; From 8c7ec98abe1e3b829e7bb2073bfb4808dc633ad9 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 29 Oct 2024 15:34:01 +0530 Subject: [PATCH 14/52] address review comments --- crates/router/src/services/authentication.rs | 98 ++++++++++++-------- 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 6cabe36aaa66..69f88cadf42c 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -433,7 +433,12 @@ where let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &stored_api_key.merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; @@ -672,9 +677,10 @@ where let key_manager_state = &(&state.session_state()).into(); let profile = state .store() - .find_business_profile_by_profile_id( + .find_business_profile_by_merchant_id_profile_id( key_manager_state, &auth_data.key_store, + auth_data.merchant_account.get_id(), &profile_id, ) .await @@ -974,7 +980,12 @@ where })?; let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -1099,14 +1110,7 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::MerchantAccountNotFound) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant = state .store() @@ -1163,17 +1167,15 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::MerchantAccountNotFound) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -1228,20 +1230,6 @@ where .await } } -// #[cfg(feature = "v2")] -// #[async_trait] -// impl AuthenticateAndFetch for EphemeralKeyAuth -// where -// A: SessionStateInfo + Sync, -// { -// async fn authenticate_and_fetch( -// &self, -// request_headers: &HeaderMap, -// state: &A, -// ) -> RouterResult<(AuthenticationData, AuthenticationType)> { -// todo!() -// } -// } #[derive(Debug)] pub struct MerchantIdAuth(pub id_type::MerchantId); @@ -1334,7 +1322,12 @@ where let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &self.0, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -1444,7 +1437,12 @@ where let merchant_id = merchant_account.get_id().clone(); let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; Ok(( @@ -1773,7 +1771,12 @@ where let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &payload.merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -1935,7 +1938,12 @@ where .attach_printable("Failed to fetch merchant key store for the merchant id")?; let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &payload.merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -2158,7 +2166,12 @@ where let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &payload.merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state @@ -2298,7 +2311,12 @@ where let profile = state .store() - .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &key_store, + &payload.merchant_id, + &profile_id, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state From b364e223e9d98c0de1f479bf5b42292f864dcd40 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 29 Oct 2024 17:21:24 +0530 Subject: [PATCH 15/52] use to_not_found_response instead of if else block --- crates/router/src/services/authentication.rs | 104 +++---------------- 1 file changed, 12 insertions(+), 92 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 69f88cadf42c..cbd694f3acdb 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -907,27 +907,13 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state .store() .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant account for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -970,14 +956,7 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let profile = state .store() .find_business_profile_by_merchant_id_profile_id( @@ -992,14 +971,7 @@ where .store() .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant account for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -1116,14 +1088,7 @@ where .store() .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant account for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -1182,14 +1147,7 @@ where .store() .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant account for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -1253,26 +1211,13 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant = state .store() .find_merchant_account_by_merchant_id(key_manager_state, &self.0, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -1311,14 +1256,7 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to fetch merchant key store for the merchant id") - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let profile = state .store() @@ -1334,13 +1272,7 @@ where .store() .find_merchant_account_by_merchant_id(key_manager_state, &self.0, &key_store) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let auth = AuthenticationData { merchant_account: merchant, @@ -1384,13 +1316,7 @@ where .store() .find_merchant_account_by_publishable_key(key_manager_state, publishable_key) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - } - }) + .to_not_found_response(errors::ApiErrorResponse::Unauthorized) .map(|(merchant_account, key_store)| { let merchant_id = merchant_account.get_id().clone(); ( @@ -1427,13 +1353,7 @@ where .store() .find_merchant_account_by_publishable_key(key_manager_state, publishable_key) .await - .map_err(|e| { - if e.current_context().is_db_not_found() { - e.change_context(errors::ApiErrorResponse::Unauthorized) - } else { - e.change_context(errors::ApiErrorResponse::InternalServerError) - } - })?; + .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; let merchant_id = merchant_account.get_id().clone(); let profile = state .store() From 0ebac3103ff65c593e386d438329ac13b6a41a39 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 30 Oct 2024 12:39:05 +0530 Subject: [PATCH 16/52] chore: add feature flags to Analytics route functions --- crates/router/src/analytics.rs | 718 ++++++++++++--------------------- 1 file changed, 256 insertions(+), 462 deletions(-) diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index fa0bb0dc9fa6..517a68610dc0 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -48,7 +48,7 @@ pub mod routes { impl Analytics { #[cfg(feature = "v2")] - pub fn server(state: AppState) -> Scope { + pub fn server(_state: AppState) -> Scope { todo!() } #[cfg(feature = "v1")] @@ -498,49 +498,6 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - /// # Panics - /// - /// Panics if `json_payload` array does not contain one `GetPaymentMetricRequest` element. - pub async fn get_profile_payment_metrics( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json<[GetPaymentMetricRequest; 1]>, - ) -> impl Responder { - // safety: This shouldn't panic owing to the data type - #[allow(clippy::expect_used)] - let payload = json_payload - .into_inner() - .to_vec() - .pop() - .expect("Couldn't get GetPaymentMetricRequest"); - let flow = AnalyticsFlow::GetPaymentMetrics; - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::payments::get_metrics(&state.pool, &auth, req) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } - /// # Panics /// /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. @@ -665,48 +622,48 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - /// # Panics - /// - /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. - pub async fn get_profile_payment_intent_metrics( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>, - ) -> impl Responder { - // safety: This shouldn't panic owing to the data type - #[allow(clippy::expect_used)] - let payload = json_payload - .into_inner() - .to_vec() - .pop() - .expect("Couldn't get GetPaymentIntentMetricRequest"); - let flow = AnalyticsFlow::GetPaymentIntentMetrics; - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::payment_intents::get_metrics(&state.pool, &auth, req) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // /// # Panics + // /// + // /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. + // pub async fn get_profile_payment_intent_metrics( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>, + // ) -> impl Responder { + // // safety: This shouldn't panic owing to the data type + // #[allow(clippy::expect_used)] + // let payload = json_payload + // .into_inner() + // .to_vec() + // .pop() + // .expect("Couldn't get GetPaymentIntentMetricRequest"); + // let flow = AnalyticsFlow::GetPaymentIntentMetrics; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // payload, + // |state, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::payment_intents::get_metrics(&state.pool, &auth, req) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } /// # Panics /// @@ -832,48 +789,48 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - /// # Panics - /// - /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. - pub async fn get_profile_refund_metrics( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json<[GetRefundMetricRequest; 1]>, - ) -> impl Responder { - #[allow(clippy::expect_used)] - // safety: This shouldn't panic owing to the data type - let payload = json_payload - .into_inner() - .to_vec() - .pop() - .expect("Couldn't get GetRefundMetricRequest"); - let flow = AnalyticsFlow::GetRefundsMetrics; - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::refunds::get_metrics(&state.pool, &auth, req) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // /// # Panics + // /// + // /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. + // pub async fn get_profile_refund_metrics( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json<[GetRefundMetricRequest; 1]>, + // ) -> impl Responder { + // #[allow(clippy::expect_used)] + // // safety: This shouldn't panic owing to the data type + // let payload = json_payload + // .into_inner() + // .to_vec() + // .pop() + // .expect("Couldn't get GetRefundMetricRequest"); + // let flow = AnalyticsFlow::GetRefundsMetrics; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // payload, + // |state, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::refunds::get_metrics(&state.pool, &auth, req) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } /// # Panics /// @@ -1119,38 +1076,38 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - pub async fn get_profile_payment_filters( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json, - ) -> impl Responder { - let flow = AnalyticsFlow::GetPaymentFilters; - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::payments::get_filters(&state.pool, req, &auth) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // pub async fn get_profile_payment_filters( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GetPaymentFilters; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // json_payload.into_inner(), + // |state, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::payments::get_filters(&state.pool, req, &auth) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } pub async fn get_payment_intents_filters( state: web::Data, @@ -1273,38 +1230,38 @@ pub mod routes { )) .await } - #[cfg(feature = "v2")] - pub async fn get_profile_refund_filters( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json, - ) -> impl Responder { - let flow = AnalyticsFlow::GetRefundFilters; - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::refunds::get_filters(&state.pool, req, &auth) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // pub async fn get_profile_refund_filters( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GetRefundFilters; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // json_payload.into_inner(), + // |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::refunds::get_filters(&state.pool, req, &auth) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } pub async fn get_frm_filters( state: web::Data, @@ -1609,61 +1566,6 @@ pub mod routes { )) .await } - - // #[cfg(feature = "v2")] - // pub async fn generate_profile_refund_report( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GenerateRefundReport; - // Box::pin(api::server_wrap( - // flow, - // state.clone(), - // &req, - // json_payload.into_inner(), - // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { - // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) - // .await - // .change_context(AnalyticsError::UnknownError)?; - - // let user_email = UserEmail::from_pii_email(user.email) - // .change_context(AnalyticsError::UnknownError)? - // .get_secret(); - - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth - // .profile.get_id(); - // let lambda_req = GenerateReportRequest { - // request: payload, - // merchant_id: Some(merchant_id.clone()), - // auth: AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }, - // email: user_email, - // }; - - // let json_bytes = - // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; - // invoke_lambda( - // &state.conf.report_download_config.refund_function, - // &state.conf.report_download_config.region, - // &json_bytes, - // ) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::GenerateReport, - // minimum_entity_level: EntityType::Profile, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } #[cfg(feature = "v1")] pub async fn generate_merchant_dispute_report( state: web::Data, @@ -1819,60 +1721,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // pub async fn generate_profile_dispute_report( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GenerateDisputeReport; - // Box::pin(api::server_wrap( - // flow, - // state.clone(), - // &req, - // json_payload.into_inner(), - // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { - // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) - // .await - // .change_context(AnalyticsError::UnknownError)?; - - // let user_email = UserEmail::from_pii_email(user.email) - // .change_context(AnalyticsError::UnknownError)? - // .get_secret(); - - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth - // .profile.get_id(); - // let lambda_req = GenerateReportRequest { - // request: payload, - // merchant_id: Some(merchant_id.clone()), - // auth: AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }, - // email: user_email, - // }; - - // let json_bytes = - // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; - // invoke_lambda( - // &state.conf.report_download_config.dispute_function, - // &state.conf.report_download_config.region, - // &json_bytes, - // ) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::GenerateReport, - // minimum_entity_level: EntityType::Profile, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } #[cfg(feature = "v1")] pub async fn generate_merchant_payment_report( state: web::Data, @@ -2027,60 +1875,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // pub async fn generate_profile_payment_report( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GeneratePaymentReport; - // Box::pin(api::server_wrap( - // flow, - // state.clone(), - // &req, - // json_payload.into_inner(), - // |state, (auth, user_id): auth::AuthenticationDataWithUserId, payload, _| async move { - // let user = UserInterface::find_user_by_id(&*state.global_store, &user_id) - // .await - // .change_context(AnalyticsError::UnknownError)?; - - // let user_email = UserEmail::from_pii_email(user.email) - // .change_context(AnalyticsError::UnknownError)? - // .get_secret(); - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth - // .profile.get_id(); - // let lambda_req = GenerateReportRequest { - // request: payload, - // merchant_id: Some(merchant_id.clone()), - // auth: AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }, - // email: user_email, - // }; - - // let json_bytes = - // serde_json::to_vec(&lambda_req).map_err(|_| AnalyticsError::UnknownError)?; - // invoke_lambda( - // &state.conf.report_download_config.payment_function, - // &state.conf.report_download_config.region, - // &json_bytes, - // ) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::GenerateReport, - // minimum_entity_level: EntityType::Profile, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - /// # Panics /// /// Panics if `json_payload` array does not contain one `GetApiEventMetricRequest` element. @@ -2473,38 +2267,38 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - pub async fn get_profile_dispute_filters( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json, - ) -> impl Responder { - let flow = AnalyticsFlow::GetDisputeFilters; - Box::pin(api::server_wrap( - flow, - state, - &req, - json_payload.into_inner(), - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::disputes::get_filters(&state.pool, req, &auth) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // pub async fn get_profile_dispute_filters( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GetDisputeFilters; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // json_payload.into_inner(), + // |state, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::disputes::get_filters(&state.pool, req, &auth) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } pub async fn get_org_dispute_filters( state: web::Data, @@ -2620,48 +2414,48 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - /// # Panics - /// - /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. - pub async fn get_profile_dispute_metrics( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json<[GetDisputeMetricRequest; 1]>, - ) -> impl Responder { - // safety: This shouldn't panic owing to the data type - #[allow(clippy::expect_used)] - let payload = json_payload - .into_inner() - .to_vec() - .pop() - .expect("Couldn't get GetDisputeMetricRequest"); - let flow = AnalyticsFlow::GetDisputeMetrics; - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::disputes::get_metrics(&state.pool, &auth, req) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // /// # Panics + // /// + // /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. + // pub async fn get_profile_dispute_metrics( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json<[GetDisputeMetricRequest; 1]>, + // ) -> impl Responder { + // // safety: This shouldn't panic owing to the data type + // #[allow(clippy::expect_used)] + // let payload = json_payload + // .into_inner() + // .to_vec() + // .pop() + // .expect("Couldn't get GetDisputeMetricRequest"); + // let flow = AnalyticsFlow::GetDisputeMetrics; + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // payload, + // |state, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::disputes::get_metrics(&state.pool, &auth, req) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } /// # Panics /// @@ -2798,37 +2592,37 @@ pub mod routes { .await } - #[cfg(feature = "v2")] - pub async fn get_profile_sankey( - state: web::Data, - req: actix_web::HttpRequest, - json_payload: web::Json, - ) -> impl Responder { - let flow = AnalyticsFlow::GetSankey; - let payload = json_payload.into_inner(); - Box::pin(api::server_wrap( - flow, - state, - &req, - payload, - |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move { - let org_id = auth.merchant_account.get_org_id(); - let merchant_id = auth.merchant_account.get_id(); - let profile_id = auth.profile.get_id(); - let auth: AuthInfo = AuthInfo::ProfileLevel { - org_id: org_id.clone(), - merchant_id: merchant_id.clone(), - profile_ids: vec![profile_id.clone()], - }; - analytics::payment_intents::get_sankey(&state.pool, &auth, req) - .await - .map(ApplicationResponse::Json) - }, - &auth::JWTAuth { - permission: Permission::ProfileAnalyticsRead, - }, - api_locking::LockAction::NotApplicable, - )) - .await - } + // #[cfg(feature = "v2")] + // pub async fn get_profile_sankey( + // state: web::Data, + // req: actix_web::HttpRequest, + // json_payload: web::Json, + // ) -> impl Responder { + // let flow = AnalyticsFlow::GetSankey; + // let payload = json_payload.into_inner(); + // Box::pin(api::server_wrap( + // flow, + // state, + // &req, + // payload, + // |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move { + // let org_id = auth.merchant_account.get_org_id(); + // let merchant_id = auth.merchant_account.get_id(); + // let profile_id = auth.profile.get_id(); + // let auth: AuthInfo = AuthInfo::ProfileLevel { + // org_id: org_id.clone(), + // merchant_id: merchant_id.clone(), + // profile_ids: vec![profile_id.clone()], + // }; + // analytics::payment_intents::get_sankey(&state.pool, &auth, req) + // .await + // .map(ApplicationResponse::Json) + // }, + // &auth::JWTAuth { + // permission: Permission::ProfileAnalyticsRead, + // }, + // api_locking::LockAction::NotApplicable, + // )) + // .await + // } } From 82838f67c1a6378a0302045d57b4b4cf8608e214 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 30 Oct 2024 12:41:59 +0530 Subject: [PATCH 17/52] remove commented code --- crates/router/src/analytics.rs | 261 --------------------------------- 1 file changed, 261 deletions(-) diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index 517a68610dc0..088e2b12764d 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -622,49 +622,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // /// # Panics - // /// - // /// Panics if `json_payload` array does not contain one `GetPaymentIntentMetricRequest` element. - // pub async fn get_profile_payment_intent_metrics( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json<[GetPaymentIntentMetricRequest; 1]>, - // ) -> impl Responder { - // // safety: This shouldn't panic owing to the data type - // #[allow(clippy::expect_used)] - // let payload = json_payload - // .into_inner() - // .to_vec() - // .pop() - // .expect("Couldn't get GetPaymentIntentMetricRequest"); - // let flow = AnalyticsFlow::GetPaymentIntentMetrics; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // payload, - // |state, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::payment_intents::get_metrics(&state.pool, &auth, req) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - /// # Panics /// /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. @@ -789,49 +746,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // /// # Panics - // /// - // /// Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element. - // pub async fn get_profile_refund_metrics( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json<[GetRefundMetricRequest; 1]>, - // ) -> impl Responder { - // #[allow(clippy::expect_used)] - // // safety: This shouldn't panic owing to the data type - // let payload = json_payload - // .into_inner() - // .to_vec() - // .pop() - // .expect("Couldn't get GetRefundMetricRequest"); - // let flow = AnalyticsFlow::GetRefundsMetrics; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // payload, - // |state, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::refunds::get_metrics(&state.pool, &auth, req) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - /// # Panics /// /// Panics if `json_payload` array does not contain one `GetFrmMetricRequest` element. @@ -1076,39 +990,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // pub async fn get_profile_payment_filters( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GetPaymentFilters; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // json_payload.into_inner(), - // |state, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::payments::get_filters(&state.pool, req, &auth) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - pub async fn get_payment_intents_filters( state: web::Data, req: actix_web::HttpRequest, @@ -1230,38 +1111,6 @@ pub mod routes { )) .await } - // #[cfg(feature = "v2")] - // pub async fn get_profile_refund_filters( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GetRefundFilters; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // json_payload.into_inner(), - // |state, auth: AuthenticationData, req: GetRefundFilterRequest, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::refunds::get_filters(&state.pool, req, &auth) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } pub async fn get_frm_filters( state: web::Data, @@ -2267,39 +2116,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // pub async fn get_profile_dispute_filters( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GetDisputeFilters; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // json_payload.into_inner(), - // |state, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::disputes::get_filters(&state.pool, req, &auth) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - pub async fn get_org_dispute_filters( state: web::Data, req: actix_web::HttpRequest, @@ -2414,49 +2230,6 @@ pub mod routes { .await } - // #[cfg(feature = "v2")] - // /// # Panics - // /// - // /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. - // pub async fn get_profile_dispute_metrics( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json<[GetDisputeMetricRequest; 1]>, - // ) -> impl Responder { - // // safety: This shouldn't panic owing to the data type - // #[allow(clippy::expect_used)] - // let payload = json_payload - // .into_inner() - // .to_vec() - // .pop() - // .expect("Couldn't get GetDisputeMetricRequest"); - // let flow = AnalyticsFlow::GetDisputeMetrics; - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // payload, - // |state, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::disputes::get_metrics(&state.pool, &auth, req) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } - /// # Panics /// /// Panics if `json_payload` array does not contain one `GetDisputeMetricRequest` element. @@ -2591,38 +2364,4 @@ pub mod routes { )) .await } - - // #[cfg(feature = "v2")] - // pub async fn get_profile_sankey( - // state: web::Data, - // req: actix_web::HttpRequest, - // json_payload: web::Json, - // ) -> impl Responder { - // let flow = AnalyticsFlow::GetSankey; - // let payload = json_payload.into_inner(); - // Box::pin(api::server_wrap( - // flow, - // state, - // &req, - // payload, - // |state: crate::routes::SessionState, auth: AuthenticationData, req, _| async move { - // let org_id = auth.merchant_account.get_org_id(); - // let merchant_id = auth.merchant_account.get_id(); - // let profile_id = auth.profile.get_id(); - // let auth: AuthInfo = AuthInfo::ProfileLevel { - // org_id: org_id.clone(), - // merchant_id: merchant_id.clone(), - // profile_ids: vec![profile_id.clone()], - // }; - // analytics::payment_intents::get_sankey(&state.pool, &auth, req) - // .await - // .map(ApplicationResponse::Json) - // }, - // &auth::JWTAuth { - // permission: Permission::ProfileAnalyticsRead, - // }, - // api_locking::LockAction::NotApplicable, - // )) - // .await - // } } From a55d4686977de8b6d12e4f5328999e74140572a2 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 30 Oct 2024 12:48:34 +0530 Subject: [PATCH 18/52] address review comments --- crates/common_enums/src/enums.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 6af2d7502558..18b6b1492ae6 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1,7 +1,7 @@ mod payments; use std::num::{ParseFloatError, TryFromIntError}; -pub use payments::*; +pub use payments::ProductType; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; From be77504a1a136f3c7dd00e35cdb3cab3981d507f Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 4 Nov 2024 15:58:42 +0530 Subject: [PATCH 19/52] address spell check errors --- crates/router/src/core/user.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 822c29b21d99..09cd04ccd75f 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1319,7 +1319,7 @@ pub async fn list_user_roles_details( )) .await .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to construct proifle map")? + .attach_printable("Failed to construct profle map")? .into_iter() .map(|profile| (profile.get_id().to_owned(), profile.profile_name)) .collect::>(); From 6645d062942a9e415969d68c88ddf60fe5ceb47f Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 5 Nov 2024 09:23:26 +0530 Subject: [PATCH 20/52] fix cargo check errors --- crates/hyperswitch_domain_models/src/payments.rs | 2 -- crates/router/src/routes/payments.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 8afb5e5d8dc7..a21788e1bb0a 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -3,8 +3,6 @@ use std::marker::PhantomData; #[cfg(feature = "v2")] use api_models::payments::Address; -#[cfg(feature = "v2")] -use api_models::payments::OrderDetailsWithAmount; use common_utils::{ self, crypto::Encryptable, diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 4b22e4837f8a..ba785fbee8e6 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -183,7 +183,7 @@ pub async fn payments_get_intent( state, &req, payload, - |state, auth: auth::AuthenticationDataV2, req, req_state| { + |state, auth: auth::AuthenticationData, req, req_state| { payments::payments_intent_core::< api_types::PaymentGetIntent, payment_types::PaymentsIntentResponse, From c02924f0f7d49441d719d3ec34680b0d5c8d7133 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 5 Nov 2024 13:48:44 +0530 Subject: [PATCH 21/52] fix: spell_check --- crates/router/src/core/user.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 09cd04ccd75f..052b96aadc91 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1319,7 +1319,7 @@ pub async fn list_user_roles_details( )) .await .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to construct profle map")? + .attach_printable("Failed to construct profile map")? .into_iter() .map(|profile| (profile.get_id().to_owned(), profile.profile_name)) .collect::>(); From bfd4a86a14aa79bdc865c4278d79ae85468e0ef2 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 12:15:53 +0530 Subject: [PATCH 22/52] feat: add support for sdk session call in v2 --- crates/api_models/src/events/payment.rs | 1 - crates/api_models/src/payments.rs | 14 + crates/common_utils/src/types.rs | 2 +- crates/diesel_models/src/payment_intent.rs | 22 +- .../hyperswitch_domain_models/src/payments.rs | 4 +- .../src/payments/payment_intent.rs | 19 + crates/router/src/core/payments.rs | 82 +++- .../src/core/payments/flows/session_flow.rs | 46 +++ crates/router/src/core/payments/operations.rs | 11 +- .../operations/payment_create_intent.rs | 2 + .../payments/operations/payment_get_intent.rs | 2 + .../payments/operations/payment_response.rs | 52 ++- .../operations/payment_session_intent.rs | 375 ++++++++++++++++++ .../src/core/payments/session_operation.rs | 204 ++++++++++ .../router/src/core/payments/transformers.rs | 174 +++++++- crates/router/src/routes/payments.rs | 65 ++- 16 files changed, 1056 insertions(+), 19 deletions(-) create mode 100644 crates/router/src/core/payments/operations/payment_session_intent.rs create mode 100644 crates/router/src/core/payments/session_operation.rs diff --git a/crates/api_models/src/events/payment.rs b/crates/api_models/src/events/payment.rs index b9dc1476fdb8..75cb673463b8 100644 --- a/crates/api_models/src/events/payment.rs +++ b/crates/api_models/src/events/payment.rs @@ -357,7 +357,6 @@ impl ApiEventMetric for PaymentsManualUpdateResponse { } } -#[cfg(feature = "v1")] impl ApiEventMetric for PaymentsSessionResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Payment { diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2ace2b956e4a..2e33e679d9d9 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -5878,6 +5878,7 @@ pub struct ApplepayErrorResponse { pub status_message: String, } +#[cfg(feature = "v1")] #[derive(Default, Debug, serde::Serialize, Clone, ToSchema)] pub struct PaymentsSessionResponse { /// The identifier for the payment @@ -5890,6 +5891,19 @@ pub struct PaymentsSessionResponse { pub session_token: Vec, } +#[cfg(feature = "v2")] +#[derive(Debug, serde::Serialize, Clone, ToSchema)] +pub struct PaymentsSessionResponse { + /// The identifier for the payment + #[schema(value_type = String)] + pub payment_id: id_type::GlobalPaymentId, + /// This is a token which expires after 15 minutes, used from the client to authenticate and create sessions from the SDK + #[schema(value_type = String)] + pub client_secret: Secret, + /// The list of session token object + pub session_token: Vec, +} + #[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] pub struct PaymentRetrieveBody { /// The identifier for the Merchant Account. diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index ce2be525989e..9a25bb29d5e5 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -820,7 +820,7 @@ mod client_secret_type { Ok(row) } } - + crate::impl_serializable_secret_id_type!(ClientSecret); #[cfg(test)] mod client_secret_tests { #![allow(clippy::expect_used)] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 883e23930977..30e0f13d9431 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -365,6 +365,11 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, + /// Update the payment intent details on payment intent session token call, before calling the connector + MetadataUpdate { + metadata: masking::Secret, + updated_by: String, + }, } #[cfg(feature = "v1")] @@ -518,7 +523,7 @@ pub struct PaymentIntentUpdateInternal { // pub customer_id: Option, // pub return_url: Option<>, // pub setup_future_usage: Option, - // pub metadata: Option, + pub metadata: Option, pub modified_at: PrimitiveDateTime, // pub active_attempt_id: Option, // pub description: Option, @@ -592,7 +597,7 @@ impl PaymentIntentUpdate { // customer_id, // return_url, // setup_future_usage, - // metadata, + metadata, modified_at: _, // active_attempt_id, // description, @@ -618,7 +623,7 @@ impl PaymentIntentUpdate { // customer_id: customer_id.or(source.customer_id), // return_url: return_url.or(source.return_url), // setup_future_usage: setup_future_usage.or(source.setup_future_usage), - // metadata: metadata.or(source.metadata), + metadata: metadata.or(source.metadata), modified_at: common_utils::date_time::now(), // active_attempt_id: active_attempt_id.unwrap_or(source.active_attempt_id), // description: description.or(source.description), @@ -738,11 +743,22 @@ impl From for PaymentIntentUpdateInternal { PaymentIntentUpdate::ConfirmIntent { status, updated_by } => Self { status: Some(status), modified_at: common_utils::date_time::now(), + metadata: None, updated_by, }, PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self { status: Some(status), modified_at: common_utils::date_time::now(), + metadata: None, + updated_by, + }, + PaymentIntentUpdate::MetadataUpdate { + metadata, + updated_by, + } => Self { + status: None, + modified_at: common_utils::date_time::now(), + metadata: Some(metadata), updated_by, }, } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index a21788e1bb0a..7e6e6ffb6652 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; #[cfg(feature = "v2")] -use api_models::payments::Address; +use api_models::payments::{Address, SessionToken}; use common_utils::{ self, crypto::Encryptable, @@ -509,6 +509,8 @@ where { pub flow: PhantomData, pub payment_intent: PaymentIntent, + pub email: Option, + pub sessions_token: Vec, } // TODO: Check if this can be merged with existing payment data diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 3b26ff94e547..13055e475c32 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -279,6 +279,10 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, + MetadataUpdate { + metadata: Secret, + updated_by: String, + }, } #[cfg(feature = "v2")] @@ -373,6 +377,14 @@ impl From for PaymentIntentUpdateInternal { updated_by, ..Default::default() }, + PaymentIntentUpdate::MetadataUpdate { + metadata, + updated_by, + } => Self { + metadata: Some(metadata), + updated_by, + ..Default::default() + }, } } } @@ -588,6 +600,13 @@ impl From for DieselPaymentIntentUpdate { PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => { Self::ConfirmIntentPostUpdate { status, updated_by } } + PaymentIntentUpdate::MetadataUpdate { + metadata, + updated_by, + } => Self::MetadataUpdate { + metadata, + updated_by, + }, } } } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5d75001b118a..1390293b12c5 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -8,6 +8,8 @@ pub mod operations; #[cfg(feature = "retry")] pub mod retry; pub mod routing; +#[cfg(feature = "v2")] +pub mod session_operation; pub mod tokenization; pub mod transformers; pub mod types; @@ -50,6 +52,8 @@ use router_env::{instrument, metrics::add_attributes, tracing}; #[cfg(feature = "olap")] use router_types::transformers::ForeignFrom; use scheduler::utils as pt_utils; +#[cfg(feature = "v2")] +pub use session_operation::payments_session_core; #[cfg(feature = "olap")] use strum::IntoEnumIterator; use time; @@ -172,7 +176,6 @@ where .await .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) .attach_printable("Failed while fetching/creating customer")?; - let connector = get_connector_choice( &operation, state, @@ -185,7 +188,6 @@ where None, ) .await?; - // TODO: do not use if let let payment_data = if let Some(connector_call_type) = connector { match connector_call_type { @@ -2789,6 +2791,7 @@ where ) .await?; + #[cfg(feature = "v1")] payment_data.set_surcharge_details(session_surcharge_details.as_ref().and_then( |session_surcharge_details| { session_surcharge_details.fetch_surcharge_details( @@ -3201,6 +3204,7 @@ pub fn is_preprocessing_required_for_wallets(connector_name: String) -> bool { connector_name == *"trustpay" || connector_name == *"payme" } +#[cfg(feature = "v1")] #[instrument(skip_all)] pub async fn construct_profile_id_and_get_mca<'a, F, D>( state: &'a SessionState, @@ -3215,7 +3219,6 @@ where F: Clone, D: OperationSessionGetters + Send + Sync + Clone, { - #[cfg(feature = "v1")] let profile_id = payment_data .get_payment_intent() .profile_id @@ -3242,6 +3245,37 @@ where Ok(merchant_connector_account) } +#[cfg(feature = "v2")] +#[instrument(skip_all)] +pub async fn construct_profile_id_and_get_mca<'a, F, D>( + state: &'a SessionState, + merchant_account: &domain::MerchantAccount, + payment_data: &D, + connector_name: &str, + merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>, + key_store: &domain::MerchantKeyStore, + _should_validate: bool, +) -> RouterResult +where + F: Clone, + D: OperationSessionGetters + Send + Sync + Clone, +{ + let profile_id = payment_data.get_payment_intent().profile_id.clone(); + + let merchant_connector_account = helpers::get_merchant_connector_account( + state, + merchant_account.get_id(), + None, + key_store, + &profile_id, + connector_name, + merchant_connector_id, + ) + .await?; + + Ok(merchant_connector_account) +} + fn is_payment_method_tokenization_enabled_for_connector( state: &SessionState, connector_name: &str, @@ -4439,7 +4473,43 @@ where }; Ok(connector) } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +pub async fn get_connector_choice_for_sdk_session_token( + operation: &BoxedOperation<'_, F, Req, D>, + state: &SessionState, + req: &Req, + merchant_account: &domain::MerchantAccount, + _business_profile: &domain::Profile, + key_store: &domain::MerchantKeyStore, + payment_data: &mut D, + _eligible_connectors: Option>, + _mandate_type: Option, +) -> RouterResult> +where + F: Send + Clone, + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, +{ + let connector_choice = operation + .to_domain()? + .get_connector( + merchant_account, + &state.clone(), + req, + payment_data.get_payment_intent(), + key_store, + ) + .await?; + let connector = Some(match connector_choice { + api::ConnectorChoice::SessionMultiple(connectors) => { + // todo perform session routing here + ConnectorCallType::SessionMultiple(connectors) + } + _ => return Err(errors::ApiErrorResponse::InternalServerError.into()), + }); + Ok(connector) +} async fn get_eligible_connector_for_nti( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -6538,7 +6608,7 @@ impl OperationSessionGetters for PaymentIntentData { } fn get_sessions_token(&self) -> Vec { - todo!() + self.sessions_token.clone() } fn get_token_data(&self) -> Option<&storage::PaymentTokenData> { @@ -6589,8 +6659,8 @@ impl OperationSessionSetters for PaymentIntentData { todo!() } - fn push_sessions_token(&mut self, _token: api::SessionToken) { - todo!() + fn push_sessions_token(&mut self, token: api::SessionToken) { + self.sessions_token.push(token); } fn set_surcharge_details(&mut self, _surcharge_details: Option) { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index ba8054696a11..66198b87b99f 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -6,6 +6,8 @@ use common_utils::{ types::{AmountConvertor, StringMajorUnitForConnector}, }; use error_stack::{Report, ResultExt}; +#[cfg(feature = "v2")] +use hyperswitch_domain_models::payments::PaymentIntentData; use masking::ExposeInterface; use router_env::metrics::add_attributes; @@ -26,6 +28,50 @@ use crate::{ utils::OptionExt, }; +#[cfg(feature = "v2")] +#[async_trait] +impl + ConstructFlowSpecificData + for PaymentIntentData +{ + async fn construct_router_data<'a>( + &self, + state: &routes::SessionState, + connector_id: &str, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + customer: &Option, + merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, + header_payload: Option, + ) -> RouterResult { + Box::pin(transformers::construct_payment_router_data_for_sdk_session( + state, + self.clone(), + connector_id, + merchant_account, + key_store, + customer, + merchant_connector_account, + merchant_recipient_data, + header_payload, + )) + .await + } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &routes::SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } +} + +#[cfg(feature = "v1")] #[async_trait] impl ConstructFlowSpecificData diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index b03f41ac0fd1..f7caf04581fd 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -17,6 +17,8 @@ pub mod payment_reject; pub mod payment_response; #[cfg(feature = "v1")] pub mod payment_session; +#[cfg(feature = "v2")] +pub mod payment_session_intent; #[cfg(feature = "v1")] pub mod payment_start; #[cfg(feature = "v1")] @@ -42,10 +44,6 @@ use async_trait::async_trait; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; -#[cfg(feature = "v2")] -pub use self::payment_confirm_intent::PaymentIntentConfirm; -#[cfg(feature = "v2")] -pub use self::payment_create_intent::PaymentIntentCreate; #[cfg(feature = "v2")] pub use self::payment_get_intent::PaymentGetIntent; pub use self::payment_response::PaymentResponse; @@ -59,6 +57,11 @@ pub use self::{ payments_incremental_authorization::PaymentIncrementalAuthorization, tax_calculation::PaymentSessionUpdate, }; +#[cfg(feature = "v2")] +pub use self::{ + payment_confirm_intent::PaymentIntentConfirm, payment_create_intent::PaymentIntentCreate, + payment_session_intent::PaymentSessionIntent, +}; use super::{helpers, CustomerDetails, OperationSessionGetters, OperationSessionSetters}; use crate::{ core::errors::{self, CustomResult, RouterResult}, diff --git a/crates/router/src/core/payments/operations/payment_create_intent.rs b/crates/router/src/core/payments/operations/payment_create_intent.rs index bd82a67e6270..938719d24d20 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -174,6 +174,8 @@ impl GetTracker, PaymentsCrea let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, + email: None, + sessions_token: vec![], }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_get_intent.rs b/crates/router/src/core/payments/operations/payment_get_intent.rs index 55ddc3b482a6..9a53db090f71 100644 --- a/crates/router/src/core/payments/operations/payment_get_intent.rs +++ b/crates/router/src/core/payments/operations/payment_get_intent.rs @@ -108,6 +108,8 @@ impl GetTracker, PaymentsGetI let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, + email: None, + sessions_token: vec![], }; let get_trackers_response = operations::GetTrackerResponse { diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 0234b97032c7..59f083814cfb 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -13,7 +13,7 @@ use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt; #[cfg(feature = "v2")] -use hyperswitch_domain_models::payments::PaymentConfirmData; +use hyperswitch_domain_models::payments::{PaymentConfirmData, PaymentIntentData}; use router_derive; use router_env::{instrument, logger, metrics::add_attributes, tracing}; use storage_impl::DataModelExt; @@ -2144,6 +2144,56 @@ async fn update_payment_method_status_and_ntid( Ok(()) } +#[cfg(feature = "v2")] +impl Operation for &PaymentResponse { + type Data = PaymentIntentData; + fn to_post_update_tracker( + &self, + ) -> RouterResult< + &(dyn PostUpdateTracker + Send + Sync), + > { + Ok(*self) + } +} + +#[cfg(feature = "v2")] +impl Operation for PaymentResponse { + type Data = PaymentIntentData; + fn to_post_update_tracker( + &self, + ) -> RouterResult< + &(dyn PostUpdateTracker + Send + Sync), + > { + Ok(self) + } +} + +#[cfg(feature = "v2")] +#[async_trait] +impl PostUpdateTracker, types::PaymentsSessionData> + for PaymentResponse +{ + async fn update_tracker<'b>( + &'b self, + state: &'b SessionState, + mut payment_data: PaymentIntentData, + response: types::RouterData, + key_store: &domain::MerchantKeyStore, + storage_scheme: enums::MerchantStorageScheme, + locale: &Option, + #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connector: Vec< + RoutableConnectorChoice, + >, + #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile, + ) -> RouterResult> + where + F: 'b + Send + Sync, + { + // TODO: Implement this + Ok(payment_data) + } +} + #[cfg(feature = "v2")] impl Operation for &PaymentResponse { type Data = PaymentConfirmData; diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs new file mode 100644 index 000000000000..ad848018a383 --- /dev/null +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -0,0 +1,375 @@ +use std::marker::PhantomData; + +use api_models::{ + admin::PaymentMethodsEnabled, enums::FrmSuggestion, payments::PaymentsSessionRequest, +}; +use async_trait::async_trait; +use common_utils::{errors::CustomResult, ext_traits::ValueExt}; +use error_stack::ResultExt; +use masking::ExposeInterface; +use router_env::{instrument, logger, tracing}; + +use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; +use crate::{ + core::{ + errors::{self, RouterResult, StorageErrorExt}, + payments::{self, helpers, operations}, + }, + routes::{app::ReqState, SessionState}, + types::{ + api, domain, + storage::{self, enums}, + }, +}; + +#[derive(Debug, Clone, Copy)] +pub struct PaymentSessionIntent; + +impl Operation for &PaymentSessionIntent { + type Data = payments::PaymentIntentData; + fn to_validate_request( + &self, + ) -> RouterResult<&(dyn ValidateRequest + Send + Sync)> + { + Ok(*self) + } + fn to_get_tracker( + &self, + ) -> RouterResult<&(dyn GetTracker + Send + Sync)> { + Ok(*self) + } + fn to_domain(&self) -> RouterResult<&(dyn Domain)> { + Ok(*self) + } + fn to_update_tracker( + &self, + ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> + { + Ok(*self) + } +} + +impl Operation for PaymentSessionIntent { + type Data = payments::PaymentIntentData; + fn to_validate_request( + &self, + ) -> RouterResult<&(dyn ValidateRequest + Send + Sync)> + { + Ok(self) + } + fn to_get_tracker( + &self, + ) -> RouterResult<&(dyn GetTracker + Send + Sync)> { + Ok(self) + } + fn to_domain(&self) -> RouterResult<&dyn Domain> { + Ok(self) + } + fn to_update_tracker( + &self, + ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> + { + Ok(self) + } +} + +type PaymentsCreateIntentOperation<'b, F> = + BoxedOperation<'b, F, PaymentsSessionRequest, payments::PaymentIntentData>; + +#[async_trait] +impl GetTracker, PaymentsSessionRequest> + for PaymentSessionIntent +{ + #[instrument(skip_all)] + async fn get_trackers<'a>( + &'a self, + state: &'a SessionState, + payment_id: &common_utils::id_type::GlobalPaymentId, + request: &PaymentsSessionRequest, + merchant_account: &domain::MerchantAccount, + profile: &domain::Profile, + key_store: &domain::MerchantKeyStore, + _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + ) -> RouterResult< + operations::GetTrackerResponse< + 'a, + F, + PaymentsSessionRequest, + payments::PaymentIntentData, + >, + > { + let db = &*state.store; + let key_manager_state = &state.into(); + let storage_scheme = merchant_account.storage_scheme; + + let payment_intent = db + .find_payment_intent_by_id(key_manager_state, payment_id, key_store, storage_scheme) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + + helpers::validate_payment_status_against_not_allowed_statuses( + &payment_intent.status, + &[enums::IntentStatus::Failed, enums::IntentStatus::Succeeded], + "create a session token for", + )?; + + // do this in core function + // helpers::authenticate_client_secret(Some(&request.client_secret), &payment_intent)?; + + let payment_data = payments::PaymentIntentData { + flow: PhantomData, + payment_intent, + email: None, + sessions_token: vec![], + }; + + let get_trackers_response = operations::GetTrackerResponse { + operation: Box::new(self), + payment_data, + }; + + Ok(get_trackers_response) + } +} + +#[async_trait] +impl UpdateTracker, PaymentsSessionRequest> + for PaymentSessionIntent +{ + #[instrument(skip_all)] + async fn update_trackers<'b>( + &'b self, + state: &'b SessionState, + _req_state: ReqState, + mut payment_data: payments::PaymentIntentData, + _customer: Option, + storage_scheme: enums::MerchantStorageScheme, + _updated_customer: Option, + key_store: &domain::MerchantKeyStore, + _frm_suggestion: Option, + _header_payload: hyperswitch_domain_models::payments::HeaderPayload, + ) -> RouterResult<( + PaymentsCreateIntentOperation<'b, F>, + payments::PaymentIntentData, + )> + where + F: 'b + Send, + { + let metadata = payment_data.payment_intent.metadata.clone(); + payment_data.payment_intent = match metadata { + Some(metadata) => state + .store + .update_payment_intent( + &state.into(), + payment_data.payment_intent, + storage::PaymentIntentUpdate::MetadataUpdate { + metadata, + updated_by: storage_scheme.to_string(), + }, + key_store, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, + None => payment_data.payment_intent, + }; + + Ok((Box::new(self), payment_data)) + } +} + +impl ValidateRequest> + for PaymentSessionIntent +{ + #[instrument(skip_all)] + fn validate_request<'a, 'b>( + &'b self, + _request: &PaymentsSessionRequest, + merchant_account: &'a domain::MerchantAccount, + ) -> RouterResult<( + PaymentsCreateIntentOperation<'b, F>, + operations::ValidateResult, + )> { + Ok(( + Box::new(self), + operations::ValidateResult { + merchant_id: merchant_account.get_id().to_owned(), + storage_scheme: merchant_account.storage_scheme, + requeue: false, + }, + )) + } +} + +#[async_trait] +impl Domain> + for PaymentSessionIntent +{ + #[instrument(skip_all)] + async fn get_customer_details<'a>( + &'a self, + state: &SessionState, + payment_data: &mut payments::PaymentIntentData, + merchant_key_store: &domain::MerchantKeyStore, + storage_scheme: enums::MerchantStorageScheme, + ) -> CustomResult< + ( + BoxedOperation<'a, F, PaymentsSessionRequest, payments::PaymentIntentData>, + Option, + ), + errors::StorageError, + > { + match payment_data.payment_intent.customer_id.clone() { + Some(id) => { + let customer = state + .store + .find_customer_by_global_id( + &state.into(), + id.get_string_repr(), + &payment_data.payment_intent.merchant_id, + merchant_key_store, + storage_scheme, + ) + .await?; + payment_data.email = customer + .email + .clone() + .map(|email| common_utils::pii::Email::from(email)); + Ok((Box::new(self), Some(customer))) + } + None => Ok((Box::new(self), None)), + } + } + + #[instrument(skip_all)] + async fn make_pm_data<'a>( + &'a self, + _state: &'a SessionState, + _payment_data: &mut payments::PaymentIntentData, + _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, + _customer: &Option, + _business_profile: &domain::Profile, + ) -> RouterResult<( + PaymentsCreateIntentOperation<'a, F>, + Option, + Option, + )> { + Ok((Box::new(self), None, None)) + } + + async fn get_connector<'a>( + &'a self, + merchant_account: &domain::MerchantAccount, + state: &SessionState, + _request: &PaymentsSessionRequest, + payment_intent: &storage::PaymentIntent, + merchant_key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let db = &state.store; + let all_connector_accounts = db + .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), + merchant_account.get_id(), + false, + merchant_key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Database error when querying for merchant connector accounts")?; + let profile_id = &payment_intent.profile_id; + let filtered_connector_accounts = helpers::filter_mca_based_on_profile_and_connector_type( + all_connector_accounts, + profile_id, + common_enums::ConnectorType::PaymentProcessor, + ); + let mut connector_and_supporting_payment_method_type = Vec::new(); + filtered_connector_accounts + .iter() + .for_each(|connector_account| { + let res = connector_account + .payment_methods_enabled + .clone() + .unwrap_or_default() + .into_iter() + .map(|payment_methods_enabled| { + payment_methods_enabled + .parse_value::("payment_methods_enabled") + }) + .filter_map(|parsed_payment_method_result| { + parsed_payment_method_result + .inspect_err(|err| { + logger::error!(session_token_parsing_error=?err); + }) + .ok() + }) + .flat_map(|parsed_payment_methods_enabled| { + parsed_payment_methods_enabled + .payment_method_types + .unwrap_or_default() + .into_iter() + .filter(|payment_method_type| { + let is_invoke_sdk_client = matches!( + payment_method_type.payment_experience, + Some(api_models::enums::PaymentExperience::InvokeSdkClient) + ); + is_invoke_sdk_client + }) + .map(|payment_method_type| { + (connector_account, payment_method_type.payment_method_type) + }) + .collect::>() + }) + .collect::>(); + connector_and_supporting_payment_method_type.extend(res); + }); + let mut session_connector_data = + Vec::with_capacity(connector_and_supporting_payment_method_type.len()); + for (merchant_connector_account, payment_method_type) in + connector_and_supporting_payment_method_type + { + let connector_type = api::GetToken::from(payment_method_type); + if let Ok(connector_data) = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &merchant_connector_account.connector_name.to_string(), + connector_type, + Some(merchant_connector_account.get_id()), + ) + .inspect_err(|err| { + logger::error!(session_token_error=?err); + }) { + let new_session_connector_data = + api::SessionConnectorData::new(payment_method_type, connector_data, None); + session_connector_data.push(new_session_connector_data) + }; + } + + Ok(api::ConnectorChoice::SessionMultiple( + session_connector_data, + )) + } + + #[instrument(skip_all)] + async fn guard_payment_against_blocklist<'a>( + &'a self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _payment_data: &mut payments::PaymentIntentData, + ) -> CustomResult { + Ok(false) + } +} + +impl From for api::GetToken { + fn from(value: api_models::enums::PaymentMethodType) -> Self { + match value { + api_models::enums::PaymentMethodType::GooglePay => Self::GpayMetadata, + api_models::enums::PaymentMethodType::ApplePay => Self::ApplePayMetadata, + api_models::enums::PaymentMethodType::SamsungPay => Self::SamsungPayMetadata, + api_models::enums::PaymentMethodType::Paypal => Self::PaypalSdkMetadata, + api_models::enums::PaymentMethodType::Paze => Self::PazeMetadata, + _ => Self::Connector, + } + } +} diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs new file mode 100644 index 000000000000..88b404a86f80 --- /dev/null +++ b/crates/router/src/core/payments/session_operation.rs @@ -0,0 +1,204 @@ +use std::fmt::Debug; + +pub use common_enums::enums::CallConnectorAction; +use common_utils::id_type; +use error_stack::ResultExt; +pub use hyperswitch_domain_models::{ + mandates::{CustomerAcceptance, MandateData}, + payment_address::PaymentAddress, + payments::HeaderPayload, + router_data::{PaymentMethodToken, RouterData}, + router_request_types::CustomerDetails, +}; +use router_env::{instrument, tracing}; + +use crate::{ + core::{ + errors::{self, utils::StorageErrorExt, RouterResult}, + payments::{ + call_multiple_connectors_service, + flows::{ConstructFlowSpecificData, Feature}, + get_connector_choice_for_sdk_session_token, operations, + operations::{BoxedOperation, Operation, PaymentResponse}, + transformers, OperationSessionGetters, OperationSessionSetters, + }, + }, + errors::RouterResponse, + routes::{app::ReqState, SessionState}, + services, + types::{ + self as router_types, + api::{self, ConnectorCallType}, + domain, + }, +}; + +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +pub async fn payments_session_core( + state: SessionState, + req_state: ReqState, + merchant_account: domain::MerchantAccount, + profile: domain::Profile, + key_store: domain::MerchantKeyStore, + operation: Op, + req: Req, + payment_id: id_type::GlobalPaymentId, + call_connector_action: CallConnectorAction, + header_payload: HeaderPayload, +) -> RouterResponse +where + F: Send + Clone + Sync, + Req: Send + Sync, + FData: Send + Sync + Clone, + Op: Operation + Send + Sync + Clone, + Req: Debug, + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, + Res: transformers::ToResponse, + // To create connector flow specific interface data + D: ConstructFlowSpecificData, + RouterData: Feature, + + // To construct connector flow specific api + dyn api::Connector: + services::api::ConnectorIntegration, + + // To perform router related operation for PaymentResponse + PaymentResponse: Operation, +{ + let (payment_data, _req, customer, connector_http_status_code, external_latency) = + payments_session_operation_core::<_, _, _, _, _>( + &state, + req_state, + merchant_account, + key_store, + profile, + operation.clone(), + req, + payment_id, + call_connector_action, + header_payload.clone(), + ) + .await?; + + Res::generate_response( + payment_data, + customer, + &state.base_url, + operation, + &state.conf.connector_request_reference_id_config, + connector_http_status_code, + external_latency, + header_payload.x_hs_latency, + ) +} + +#[allow(clippy::too_many_arguments, clippy::type_complexity)] +#[instrument(skip_all, fields(payment_id, merchant_id))] +pub async fn payments_session_operation_core( + state: &SessionState, + req_state: ReqState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + profile: domain::Profile, + operation: Op, + req: Req, + payment_id: id_type::GlobalPaymentId, + call_connector_action: CallConnectorAction, + header_payload: HeaderPayload, +) -> RouterResult<(D, Req, Option, Option, Option)> +where + F: Send + Clone + Sync, + Req: Send + Sync, + Op: Operation + Send + Sync, + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, + + // To create connector flow specific interface data + D: ConstructFlowSpecificData, + RouterData: Feature, + + // To construct connector flow specific api + dyn api::Connector: + services::api::ConnectorIntegration, + + // To perform router related operation for PaymentResponse + PaymentResponse: Operation, + FData: Send + Sync + Clone, +{ + let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation); + + let (operation, _validate_result) = operation + .to_validate_request()? + .validate_request(&req, &merchant_account)?; + + let operations::GetTrackerResponse { + operation, + mut payment_data, + } = operation + .to_get_tracker()? + .get_trackers( + state, + &payment_id, + &req, + &merchant_account, + &profile, + &key_store, + &header_payload, + ) + .await?; + + let (_operation, customer) = operation + .to_domain()? + .get_customer_details( + state, + &mut payment_data, + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) + .attach_printable("Failed while fetching/creating customer")?; + + let connector = get_connector_choice_for_sdk_session_token( + &operation, + state, + &req, + &merchant_account, + &profile, + &key_store, + &mut payment_data, + None, + None, + ) + .await?; + + // TODO: do not use if let + let payment_data = if let Some(connector_call_type) = connector { + match connector_call_type { + ConnectorCallType::PreDetermined(_connectors) => { + todo!() + } + ConnectorCallType::Retryable(_connectors) => todo!(), + ConnectorCallType::SessionMultiple(connectors) => { + // todo: call surcharge manager for session token call. + Box::pin(call_multiple_connectors_service( + state, + &merchant_account, + &key_store, + connectors, + &operation, + payment_data, + &customer, + None, + &profile, + header_payload.clone(), + )) + .await? + } + } + } else { + todo!() + }; + + Ok((payment_data, req, customer, None, None)) +} diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 45d0f75ccec8..60fa4882ef84 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -17,7 +17,7 @@ use diesel_models::{ }; use error_stack::{report, ResultExt}; #[cfg(feature = "v2")] -use hyperswitch_domain_models::payments::PaymentConfirmData; +use hyperswitch_domain_models::payments::{PaymentConfirmData, PaymentIntentData}; #[cfg(feature = "v2")] use hyperswitch_domain_models::ApiModelToDieselModelConvertor; use hyperswitch_domain_models::{payments::payment_intent::CustomerData, router_request_types}; @@ -376,6 +376,149 @@ pub async fn construct_payment_router_data_for_authorize<'a>( Ok(router_data) } +#[cfg(feature = "v2")] +#[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn construct_payment_router_data_for_sdk_session<'a>( + _state: &'a SessionState, + payment_data: PaymentIntentData, + connector_id: &str, + merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + customer: &'a Option, + merchant_connector_account: &helpers::MerchantConnectorAccountType, + _merchant_recipient_data: Option, + header_payload: Option, +) -> RouterResult { + fp_utils::when(merchant_connector_account.is_disabled(), || { + Err(errors::ApiErrorResponse::MerchantConnectorAccountDisabled) + })?; + + let auth_type: types::ConnectorAuthType = merchant_connector_account + .get_connector_account_details() + .parse_value("ConnectorAuthType") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while parsing value for ConnectorAuthType")?; + + // TODO: Take Globalid and convert to connector reference id + let customer_id = customer + .to_owned() + .map(|customer| customer.id.clone()) + .map(std::borrow::Cow::Owned) + .map(common_utils::id_type::CustomerId::try_from) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Invalid global customer generated, not able to convert to reference id", + )?; + let order_details = payment_data + .payment_intent + .order_details + .clone() + .map(|order_details| { + order_details + .into_iter() + .map(|order_detail| order_detail.expose()) + .collect() + }); + // TODO: few fields are repeated in both routerdata and request + let request = types::PaymentsSessionData { + amount: payment_data + .payment_intent + .amount_details + .order_amount + .get_amount_as_i64(), + currency: payment_data.payment_intent.amount_details.currency, + country: payment_data + .payment_intent + .billing_address + .and_then(|billing_address| { + billing_address + .get_inner() + .peek() + .address + .as_ref() + .and_then(|address| address.country) + }), + // TODO: populate surcharge here + surcharge_details: None, + order_details, + email: payment_data.email, + minor_amount: payment_data.payment_intent.amount_details.order_amount, + }; + + // TODO: evaluate the fields in router data, if they are required or not + let router_data = types::RouterData { + flow: PhantomData, + merchant_id: merchant_account.get_id().clone(), + // TODO: evaluate why we need customer id at the connector level. We already have connector customer id. + customer_id, + connector: connector_id.to_owned(), + // TODO: evaluate why we need payment id at the connector level. We already have connector reference id + payment_id: payment_data.payment_intent.id.get_string_repr().to_owned(), + // TODO: evaluate why we need attempt id at the connector level. We already have connector reference id + attempt_id: "".to_string(), + status: enums::AttemptStatus::Started, + payment_method: enums::PaymentMethod::Wallet, + connector_auth_type: auth_type, + description: payment_data + .payment_intent + .description + .as_ref() + .map(|description| description.get_string_repr()) + .map(ToOwned::to_owned), + // TODO: evaluate why we need to send merchant's return url here + // This should be the return url of application, since application takes care of the redirection + return_url: payment_data + .payment_intent + .return_url + .as_ref() + .map(|description| description.get_string_repr()) + .map(ToOwned::to_owned), + // TODO: Create unified address + address: hyperswitch_domain_models::payment_address::PaymentAddress::default(), + auth_type: payment_data.payment_intent.authentication_type, + connector_meta_data: merchant_connector_account.get_metadata(), + connector_wallets_details: None, + request, + response: Err(hyperswitch_domain_models::router_data::ErrorResponse::default()), + amount_captured: None, + minor_amount_captured: None, + access_token: None, + session_token: None, + reference_id: None, + payment_method_status: None, + payment_method_token: None, + connector_customer: None, + recurring_mandate_payment_data: None, + // TODO: This has to be generated as the reference id based on the connector configuration + // Some connectros might not accept accept the global id. This has to be done when generating the reference id + connector_request_reference_id: "".to_string(), + preprocessing_id: None, + #[cfg(feature = "payouts")] + payout_method_data: None, + #[cfg(feature = "payouts")] + quote_id: None, + // TODO: take this based on the env + test_mode: Some(true), + payment_method_balance: None, + connector_api_version: None, + connector_http_status_code: None, + external_latency: None, + apple_pay_flow: None, + frm_metadata: None, + refund_id: None, + dispute_id: None, + connector_response: None, + integrity_check: Ok(()), + additional_merchant_data: None, + header_payload, + connector_mandate_request_reference_id: None, + }; + + Ok(router_data) +} + #[cfg(feature = "v2")] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] @@ -716,6 +859,35 @@ where } } +#[cfg(feature = "v2")] +impl ToResponse for api::PaymentsSessionResponse +where + F: Clone, + Op: Debug, + D: OperationSessionGetters, +{ + #[allow(clippy::too_many_arguments)] + fn generate_response( + payment_data: D, + _customer: Option, + _base_url: &str, + _operation: Op, + _connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, + _connector_http_status_code: Option, + _external_latency: Option, + _is_latency_header_enabled: Option, + ) -> RouterResponse { + Ok(services::ApplicationResponse::JsonWithHeaders(( + Self { + session_token: payment_data.get_sessions_token(), + payment_id: payment_data.get_payment_intent().id.clone(), + client_secret: Secret::new(payment_data.get_payment_intent().client_secret.clone()), + }, + vec![], + ))) + } +} + #[cfg(feature = "v1")] impl ToResponse for api::PaymentsDynamicTaxCalculationResponse where diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index ba785fbee8e6..f5181ef90c96 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -695,8 +695,61 @@ pub async fn payments_connector_session( state: web::Data, req: actix_web::HttpRequest, json_payload: web::Json, + path: web::Path, ) -> impl Responder { - "Session Response" + use hyperswitch_domain_models::payments::PaymentIntentData; + let flow = Flow::PaymentsSessionToken; + + let global_payment_id = path.into_inner(); + tracing::Span::current().record("payment_id", global_payment_id.get_string_repr()); + + let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId { + global_payment_id, + payload: json_payload.into_inner(), + }; + + let header_payload = match HeaderPayload::foreign_try_from(req.headers()) { + Ok(headers) => headers, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + + let locking_action = internal_payload.get_locking_input(flow.clone()); + + Box::pin(api::server_wrap( + flow, + state, + &req, + internal_payload, + |state, auth: auth::AuthenticationData, req, req_state| { + let payment_id = req.global_payment_id; + let request = req.payload; + let operation = payments::operations::PaymentSessionIntent; + payments::payments_session_core::< + api_types::Session, + payment_types::PaymentsSessionResponse, + _, + _, + _, + PaymentIntentData, + >( + state, + req_state, + auth.merchant_account, + auth.profile, + auth.key_store, + operation, + request, + payment_id, + payments::CallConnectorAction::Trigger, + header_payload.clone(), + ) + }, + &auth::HeaderAuth(auth::PublishableKeyAuth), + locking_action, + )) + .await } #[cfg(feature = "v1")] @@ -1786,6 +1839,16 @@ impl GetLockingInput for payment_types::PaymentsSessionRequest { } } +#[cfg(feature = "v2")] +impl GetLockingInput for payment_types::PaymentsSessionRequest { + fn get_locking_input(&self, flow: F) -> api_locking::LockAction + where + F: types::FlowMetric, + { + api_locking::LockAction::NotApplicable + } +} + #[cfg(feature = "v1")] impl GetLockingInput for payment_types::PaymentsDynamicTaxCalculationRequest { fn get_locking_input(&self, flow: F) -> api_locking::LockAction From 191468e358ec04ceb22d7eeac2fcf5e56ece513f Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 13:04:34 +0530 Subject: [PATCH 23/52] chore: address v2 clippy warnings --- .../src/core/payments/operations/payment_session_intent.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index ad848018a383..9be060a3cf11 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -231,10 +231,7 @@ impl Domain Ok((Box::new(self), None)), From 6fcff7ccfc351949e31565464d2be9d9a9a4ec58 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 16:55:32 +0530 Subject: [PATCH 24/52] chore: use email from customer and remaove email from PaymentIntentData --- crates/hyperswitch_domain_models/src/payments.rs | 1 - .../core/payments/operations/payment_create_intent.rs | 1 - .../src/core/payments/operations/payment_get_intent.rs | 1 - .../core/payments/operations/payment_session_intent.rs | 2 -- crates/router/src/core/payments/transformers.rs | 9 ++++++--- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 7e6e6ffb6652..59fd7a62366a 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -509,7 +509,6 @@ where { pub flow: PhantomData, pub payment_intent: PaymentIntent, - pub email: Option, pub sessions_token: Vec, } diff --git a/crates/router/src/core/payments/operations/payment_create_intent.rs b/crates/router/src/core/payments/operations/payment_create_intent.rs index 938719d24d20..60581743e3cd 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -174,7 +174,6 @@ impl GetTracker, PaymentsCrea let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, - email: None, sessions_token: vec![], }; diff --git a/crates/router/src/core/payments/operations/payment_get_intent.rs b/crates/router/src/core/payments/operations/payment_get_intent.rs index 9a53db090f71..1ca11227032d 100644 --- a/crates/router/src/core/payments/operations/payment_get_intent.rs +++ b/crates/router/src/core/payments/operations/payment_get_intent.rs @@ -108,7 +108,6 @@ impl GetTracker, PaymentsGetI let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, - email: None, sessions_token: vec![], }; diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 9be060a3cf11..8d45017fd4e0 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -119,7 +119,6 @@ impl GetTracker, PaymentsSess let payment_data = payments::PaymentIntentData { flow: PhantomData, payment_intent, - email: None, sessions_token: vec![], }; @@ -231,7 +230,6 @@ impl Domain Ok((Box::new(self), None)), diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 60fa4882ef84..56747f3bcd0d 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -7,8 +7,7 @@ use api_models::payments::{ use common_enums::{Currency, RequestIncrementalAuthorization}; use common_utils::{ consts::X_HS_LATENCY, - fp_utils, - pii::Email, + fp_utils, pii, types::{self as common_utils_type, AmountConvertor, MinorUnit, StringMajorUnitForConnector}, }; use diesel_models::{ @@ -411,6 +410,10 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( .attach_printable( "Invalid global customer generated, not able to convert to reference id", )?; + let email = customer + .as_ref() + .and_then(|customer| customer.email.clone()) + .map(pii::Email::from); let order_details = payment_data .payment_intent .order_details @@ -443,7 +446,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( // TODO: populate surcharge here surcharge_details: None, order_details, - email: payment_data.email, + email, minor_amount: payment_data.payment_intent.amount_details.order_amount, }; From 6306242dd5db9c03111c0a8e11919d9d156af5d1 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 17:13:51 +0530 Subject: [PATCH 25/52] chore: address v1 clippy warnings --- crates/router/src/core/payments/transformers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 56747f3bcd0d..1d4df06ee9ae 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1466,7 +1466,7 @@ where .and_then(|customer_data| customer_data.email.clone()) .or(customer_details_encrypted_data.email.or(customer .as_ref() - .and_then(|customer| customer.email.clone().map(Email::from)))), + .and_then(|customer| customer.email.clone().map(pii::Email::from)))), phone: customer_table_response .as_ref() .and_then(|customer_data| customer_data.phone.clone()) From d31c2c387628d42cf868dfcb19e263254b2a2b25 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 17:20:00 +0530 Subject: [PATCH 26/52] chore: remove client_secret from PaymentsSessionResponse --- crates/api_models/src/payments.rs | 3 --- crates/router/src/core/payments/transformers.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2e33e679d9d9..10e2efb54099 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -5897,9 +5897,6 @@ pub struct PaymentsSessionResponse { /// The identifier for the payment #[schema(value_type = String)] pub payment_id: id_type::GlobalPaymentId, - /// This is a token which expires after 15 minutes, used from the client to authenticate and create sessions from the SDK - #[schema(value_type = String)] - pub client_secret: Secret, /// The list of session token object pub session_token: Vec, } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 1d4df06ee9ae..fa4e6c8505b0 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -884,7 +884,6 @@ where Self { session_token: payment_data.get_sessions_token(), payment_id: payment_data.get_payment_intent().id.clone(), - client_secret: Secret::new(payment_data.get_payment_intent().client_secret.clone()), }, vec![], ))) From 5bcb3371e4fe32b7341b8ce5640a9f7990c641af Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:53:04 +0000 Subject: [PATCH 27/52] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 4c1559fe099b..a5a0afb8fda1 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -15101,7 +15101,6 @@ "type": "object", "required": [ "payment_id", - "client_secret", "session_token" ], "properties": { @@ -15109,10 +15108,6 @@ "type": "string", "description": "The identifier for the payment" }, - "client_secret": { - "type": "string", - "description": "This is a token which expires after 15 minutes, used from the client to authenticate and create sessions from the SDK" - }, "session_token": { "type": "array", "items": { From ffab8e669b7cc29e39bc38e5c8c248e732e75484 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 7 Nov 2024 17:41:39 +0530 Subject: [PATCH 28/52] chore: remove update tracker and post update tracker implementation for Session operation --- crates/router/src/core/payments.rs | 3 - .../payments/operations/payment_response.rs | 50 ---------------- .../operations/payment_session_intent.rs | 58 ------------------- .../src/core/payments/session_operation.rs | 6 -- 4 files changed, 117 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 1390293b12c5..5d50e7e15a3d 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2768,9 +2768,6 @@ where // To construct connector flow specific api dyn api::Connector: services::api::ConnectorIntegration, - - // To perform router related operation for PaymentResponse - PaymentResponse: Operation, { let call_connectors_start_time = Instant::now(); let mut join_handlers = Vec::with_capacity(connectors.len()); diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 59f083814cfb..77d65cc25d17 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2144,56 +2144,6 @@ async fn update_payment_method_status_and_ntid( Ok(()) } -#[cfg(feature = "v2")] -impl Operation for &PaymentResponse { - type Data = PaymentIntentData; - fn to_post_update_tracker( - &self, - ) -> RouterResult< - &(dyn PostUpdateTracker + Send + Sync), - > { - Ok(*self) - } -} - -#[cfg(feature = "v2")] -impl Operation for PaymentResponse { - type Data = PaymentIntentData; - fn to_post_update_tracker( - &self, - ) -> RouterResult< - &(dyn PostUpdateTracker + Send + Sync), - > { - Ok(self) - } -} - -#[cfg(feature = "v2")] -#[async_trait] -impl PostUpdateTracker, types::PaymentsSessionData> - for PaymentResponse -{ - async fn update_tracker<'b>( - &'b self, - state: &'b SessionState, - mut payment_data: PaymentIntentData, - response: types::RouterData, - key_store: &domain::MerchantKeyStore, - storage_scheme: enums::MerchantStorageScheme, - locale: &Option, - #[cfg(all(feature = "v1", feature = "dynamic_routing"))] routable_connector: Vec< - RoutableConnectorChoice, - >, - #[cfg(all(feature = "v1", feature = "dynamic_routing"))] business_profile: &domain::Profile, - ) -> RouterResult> - where - F: 'b + Send + Sync, - { - // TODO: Implement this - Ok(payment_data) - } -} - #[cfg(feature = "v2")] impl Operation for &PaymentResponse { type Data = PaymentConfirmData; diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 8d45017fd4e0..e447557c2f15 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -41,12 +41,6 @@ impl Operation for &PaymentSessionIn fn to_domain(&self) -> RouterResult<&(dyn Domain)> { Ok(*self) } - fn to_update_tracker( - &self, - ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> - { - Ok(*self) - } } impl Operation for PaymentSessionIntent { @@ -65,12 +59,6 @@ impl Operation for PaymentSessionInt fn to_domain(&self) -> RouterResult<&dyn Domain> { Ok(self) } - fn to_update_tracker( - &self, - ) -> RouterResult<&(dyn UpdateTracker + Send + Sync)> - { - Ok(self) - } } type PaymentsCreateIntentOperation<'b, F> = @@ -131,52 +119,6 @@ impl GetTracker, PaymentsSess } } -#[async_trait] -impl UpdateTracker, PaymentsSessionRequest> - for PaymentSessionIntent -{ - #[instrument(skip_all)] - async fn update_trackers<'b>( - &'b self, - state: &'b SessionState, - _req_state: ReqState, - mut payment_data: payments::PaymentIntentData, - _customer: Option, - storage_scheme: enums::MerchantStorageScheme, - _updated_customer: Option, - key_store: &domain::MerchantKeyStore, - _frm_suggestion: Option, - _header_payload: hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult<( - PaymentsCreateIntentOperation<'b, F>, - payments::PaymentIntentData, - )> - where - F: 'b + Send, - { - let metadata = payment_data.payment_intent.metadata.clone(); - payment_data.payment_intent = match metadata { - Some(metadata) => state - .store - .update_payment_intent( - &state.into(), - payment_data.payment_intent, - storage::PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by: storage_scheme.to_string(), - }, - key_store, - storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, - None => payment_data.payment_intent, - }; - - Ok((Box::new(self), payment_data)) - } -} - impl ValidateRequest> for PaymentSessionIntent { diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index 88b404a86f80..5b3d6d548db2 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -62,9 +62,6 @@ where // To construct connector flow specific api dyn api::Connector: services::api::ConnectorIntegration, - - // To perform router related operation for PaymentResponse - PaymentResponse: Operation, { let (payment_data, _req, customer, connector_http_status_code, external_latency) = payments_session_operation_core::<_, _, _, _, _>( @@ -120,9 +117,6 @@ where // To construct connector flow specific api dyn api::Connector: services::api::ConnectorIntegration, - - // To perform router related operation for PaymentResponse - PaymentResponse: Operation, FData: Send + Sync + Clone, { let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation); From 0a780731075ba5721c2202a8b661a8ff9e179f11 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 11 Nov 2024 11:10:32 +0530 Subject: [PATCH 29/52] chore: remove MetadataUpdate variant --- crates/diesel_models/src/payment_intent.rs | 22 +++---------------- .../src/payments/payment_intent.rs | 19 ---------------- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 30e0f13d9431..883e23930977 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -365,11 +365,6 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, - /// Update the payment intent details on payment intent session token call, before calling the connector - MetadataUpdate { - metadata: masking::Secret, - updated_by: String, - }, } #[cfg(feature = "v1")] @@ -523,7 +518,7 @@ pub struct PaymentIntentUpdateInternal { // pub customer_id: Option, // pub return_url: Option<>, // pub setup_future_usage: Option, - pub metadata: Option, + // pub metadata: Option, pub modified_at: PrimitiveDateTime, // pub active_attempt_id: Option, // pub description: Option, @@ -597,7 +592,7 @@ impl PaymentIntentUpdate { // customer_id, // return_url, // setup_future_usage, - metadata, + // metadata, modified_at: _, // active_attempt_id, // description, @@ -623,7 +618,7 @@ impl PaymentIntentUpdate { // customer_id: customer_id.or(source.customer_id), // return_url: return_url.or(source.return_url), // setup_future_usage: setup_future_usage.or(source.setup_future_usage), - metadata: metadata.or(source.metadata), + // metadata: metadata.or(source.metadata), modified_at: common_utils::date_time::now(), // active_attempt_id: active_attempt_id.unwrap_or(source.active_attempt_id), // description: description.or(source.description), @@ -743,22 +738,11 @@ impl From for PaymentIntentUpdateInternal { PaymentIntentUpdate::ConfirmIntent { status, updated_by } => Self { status: Some(status), modified_at: common_utils::date_time::now(), - metadata: None, updated_by, }, PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => Self { status: Some(status), modified_at: common_utils::date_time::now(), - metadata: None, - updated_by, - }, - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self { - status: None, - modified_at: common_utils::date_time::now(), - metadata: Some(metadata), updated_by, }, } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 13055e475c32..3b26ff94e547 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -279,10 +279,6 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, - MetadataUpdate { - metadata: Secret, - updated_by: String, - }, } #[cfg(feature = "v2")] @@ -377,14 +373,6 @@ impl From for PaymentIntentUpdateInternal { updated_by, ..Default::default() }, - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self { - metadata: Some(metadata), - updated_by, - ..Default::default() - }, } } } @@ -600,13 +588,6 @@ impl From for DieselPaymentIntentUpdate { PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by } => { Self::ConfirmIntentPostUpdate { status, updated_by } } - PaymentIntentUpdate::MetadataUpdate { - metadata, - updated_by, - } => Self::MetadataUpdate { - metadata, - updated_by, - }, } } } From 27681ccff5c8cd405d1f003b1077173d5e374903 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 11 Nov 2024 12:22:17 +0530 Subject: [PATCH 30/52] chore: address comments --- .../src/merchant_connector_account.rs | 23 +++++++++++++++++++ .../operations/payment_session_intent.rs | 8 +------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index ed2388488e27..992a4c64d0db 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "v2")] +use api_models::admin; +#[cfg(feature = "v2")] +use common_utils::ext_traits::ValueExt; use common_utils::{ crypto::Encryptable, date_time, @@ -13,6 +17,8 @@ use rustc_hash::FxHashMap; use serde_json::Value; use super::behaviour; +#[cfg(feature = "v2")] +use crate::errors::api_error_response::ApiErrorResponse; use crate::type_encryption::{crypto_operation, CryptoOperation}; #[cfg(feature = "v1")] @@ -87,6 +93,23 @@ impl MerchantConnectorAccount { pub fn get_id(&self) -> id_type::MerchantConnectorAccountId { self.id.clone() } + + pub fn get_parsed_payment_methods_enabled( + &self, + ) -> Vec> { + self.payment_methods_enabled + .clone() + .unwrap_or_default() + .into_iter() + .map(|payment_methods_enabled| { + payment_methods_enabled + .parse_value::("payment_methods_enabled") + .change_context(ApiErrorResponse::InvalidDataValue { + field_name: "payment_methods_enabled", + }) + }) + .collect() + } } #[cfg(feature = "v1")] diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index e447557c2f15..dad68b744048 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -225,14 +225,8 @@ impl Domain("payment_methods_enabled") - }) .filter_map(|parsed_payment_method_result| { parsed_payment_method_result .inspect_err(|err| { From c9a5fdfeebdaca0f3a6ab2899727dc7b91eccf0b Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 11 Nov 2024 15:06:52 +0530 Subject: [PATCH 31/52] chore: fix cargo run failure --- crates/router/src/analytics.rs | 4 ++-- crates/router/src/routes/app.rs | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index ed25725358a0..a559c71fd7ab 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -51,8 +51,8 @@ pub mod routes { impl Analytics { #[cfg(feature = "v2")] - pub fn server(_state: AppState) -> Scope { - todo!() + pub fn server(state: AppState) -> Scope { + web::scope("/analytics").app_data(web::Data::new(state)) } #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 9f0a83a053f6..148a8591aa8c 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -687,7 +687,7 @@ impl Forex { } #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { - todo!() + web::scope("/forex").app_data(web::Data::new(state.clone())) } } @@ -1065,9 +1065,8 @@ pub struct Payouts; #[cfg(feature = "payouts")] impl Payouts { - #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { - todo!() + web::scope("/payouts").app_data(web::Data::new(state)) } #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { @@ -1586,7 +1585,7 @@ impl Cards { } #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { - todo!() + web::scope("/cards").app_data(web::Data::new(state)) } } @@ -1658,7 +1657,7 @@ impl PayoutLink { } #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { - todo!() + web::scope("/payout_link").app_data(web::Data::new(state)) } } @@ -1788,7 +1787,7 @@ impl ProfileNew { } #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { - todo!() + web::scope("/account/{account_id}/profile").app_data(web::Data::new(state)) } } From 17535024136d118a3f69fa7787cdb14a03d18481 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 11 Nov 2024 19:09:20 +0530 Subject: [PATCH 32/52] chore: add v2 flag to server function in payouts route --- crates/router/src/routes/app.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 148a8591aa8c..71d86e028278 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1065,6 +1065,7 @@ pub struct Payouts; #[cfg(feature = "payouts")] impl Payouts { + #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { web::scope("/payouts").app_data(web::Data::new(state)) } From f44128fc6c5bf9bb08c05cc4a9e3e7a752cbb9fa Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 11 Nov 2024 22:19:49 +0530 Subject: [PATCH 33/52] address comments --- crates/router/src/core/payments.rs | 36 ---------- .../src/core/payments/session_operation.rs | 72 +++++++++---------- 2 files changed, 33 insertions(+), 75 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 6d01ddaa14fa..0f253c6802e2 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4476,43 +4476,7 @@ where }; Ok(connector) } -#[cfg(feature = "v2")] -#[allow(clippy::too_many_arguments)] -pub async fn get_connector_choice_for_sdk_session_token( - operation: &BoxedOperation<'_, F, Req, D>, - state: &SessionState, - req: &Req, - merchant_account: &domain::MerchantAccount, - _business_profile: &domain::Profile, - key_store: &domain::MerchantKeyStore, - payment_data: &mut D, - _eligible_connectors: Option>, - _mandate_type: Option, -) -> RouterResult> -where - F: Send + Clone, - D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, -{ - let connector_choice = operation - .to_domain()? - .get_connector( - merchant_account, - &state.clone(), - req, - payment_data.get_payment_intent(), - key_store, - ) - .await?; - let connector = Some(match connector_choice { - api::ConnectorChoice::SessionMultiple(connectors) => { - // todo perform session routing here - ConnectorCallType::SessionMultiple(connectors) - } - _ => return Err(errors::ApiErrorResponse::InternalServerError.into()), - }); - Ok(connector) -} async fn get_eligible_connector_for_nti( state: &SessionState, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index 1d85c78fac9e..ccf562a28a73 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -18,8 +18,8 @@ use crate::{ payments::{ call_multiple_connectors_service, flows::{ConstructFlowSpecificData, Feature}, - get_connector_choice_for_sdk_session_token, operations, - operations::{BoxedOperation, Operation, PaymentResponse}, + operations, + operations::{BoxedOperation, Operation}, transformers, OperationSessionGetters, OperationSessionSetters, }, }, @@ -28,7 +28,7 @@ use crate::{ services, types::{ self as router_types, - api::{self, ConnectorCallType}, + api::{self, ConnectorChoice}, domain, }, }; @@ -154,45 +154,39 @@ where .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) .attach_printable("Failed while fetching/creating customer")?; - let connector = get_connector_choice_for_sdk_session_token( - &operation, - state, - &req, - &merchant_account, - &profile, - &key_store, - &mut payment_data, - None, - None, - ) - .await?; + let connector = operation + .to_domain()? + .get_connector( + &merchant_account, + &state.clone(), + &req, + payment_data.get_payment_intent(), + &key_store, + ) + .await?; // TODO: do not use if let - let payment_data = if let Some(connector_call_type) = connector { - match connector_call_type { - ConnectorCallType::PreDetermined(_connectors) => { - todo!() - } - ConnectorCallType::Retryable(_connectors) => todo!(), - ConnectorCallType::SessionMultiple(connectors) => { - // todo: call surcharge manager for session token call. - Box::pin(call_multiple_connectors_service( - state, - &merchant_account, - &key_store, - connectors, - &operation, - payment_data, - &customer, - None, - &profile, - header_payload.clone(), - )) - .await? - } + let payment_data = match connector { + ConnectorChoice::StraightThrough(_connectors) => { + todo!() + } + ConnectorChoice::Decide => todo!(), + ConnectorChoice::SessionMultiple(connectors) => { + // todo: call surcharge manager for session token call. + Box::pin(call_multiple_connectors_service( + state, + &merchant_account, + &key_store, + connectors, + &operation, + payment_data, + &customer, + None, + &profile, + header_payload.clone(), + )) + .await? } - } else { - todo!() }; Ok((payment_data, req, customer, None, None)) From b15c16b1f8763ce51e3421aef09fa1596054059f Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 12 Nov 2024 12:45:39 +0530 Subject: [PATCH 34/52] implement fn perform_routing for Session operation domain --- .../src/merchant_connector_account.rs | 4 + crates/router/src/core/payments.rs | 113 +++++++++++++++++- .../operations/payment_session_intent.rs | 12 +- .../src/core/payments/session_operation.rs | 24 ++-- .../router/src/core/payments/transformers.rs | 3 +- 5 files changed, 133 insertions(+), 23 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 67183a09648e..7b4d8c43fc5f 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -96,6 +96,10 @@ impl MerchantConnectorAccount { self.id.clone() } + pub fn get_metadata(&self) -> Option { + self.metadata.clone() + } + pub fn get_parsed_payment_methods_enabled( &self, ) -> Vec> { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index fdb08111475d..32180f8e065b 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2891,6 +2891,118 @@ where } } +#[cfg(feature = "v2")] +#[allow(clippy::too_many_arguments)] +pub async fn call_multiple_connectors_service( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + connectors: Vec, + _operation: &Op, + mut payment_data: D, + customer: &Option, + _session_surcharge_details: Option, + business_profile: &domain::Profile, + header_payload: HeaderPayload, +) -> RouterResult +where + Op: Debug, + F: Send + Clone, + + // To create connector flow specific interface data + D: OperationSessionGetters + OperationSessionSetters + Send + Sync + Clone, + D: ConstructFlowSpecificData, + RouterData: Feature, + + // To construct connector flow specific api + dyn api::Connector: + services::api::ConnectorIntegration, +{ + let call_connectors_start_time = Instant::now(); + let mut join_handlers = Vec::with_capacity(connectors.len()); + for session_connector_data in connectors.iter() { + let merchant_connector_id = session_connector_data + .connector + .merchant_connector_id + .as_ref() + .get_required_value("merchant_connector_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("connector id is not set")?; + let merchant_connector_account = state + .store + .find_merchant_connector_account_by_id(&state.into(), merchant_connector_id, key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_connector_id.get_string_repr().to_owned(), + })?; + let connector_id = session_connector_data.connector.connector.id(); + let router_data = payment_data + .construct_router_data( + state, + connector_id, + merchant_account, + key_store, + customer, + &merchant_connector_account, + None, + None, + ) + .await?; + + let res = router_data.decide_flows( + state, + &session_connector_data.connector, + CallConnectorAction::Trigger, + None, + business_profile, + header_payload.clone(), + ); + + join_handlers.push(res); + } + + let result = join_all(join_handlers).await; + + for (connector_res, session_connector) in result.into_iter().zip(connectors) { + let connector_name = session_connector.connector.connector_name.to_string(); + match connector_res { + Ok(connector_response) => { + if let Ok(router_types::PaymentsResponseData::SessionResponse { + session_token, + .. + }) = connector_response.response.clone() + { + // If session token is NoSessionTokenReceived, it is not pushed into the sessions_token as there is no response or there can be some error + // In case of error, that error is already logged + if !matches!( + session_token, + api_models::payments::SessionToken::NoSessionTokenReceived, + ) { + payment_data.push_sessions_token(session_token); + } + } + if let Err(connector_error_response) = connector_response.response { + logger::error!( + "sessions_connector_error {} {:?}", + connector_name, + connector_error_response + ); + } + } + Err(api_error) => { + logger::error!("sessions_api_error {} {:?}", connector_name, api_error); + } + } + } + + let call_connectors_end_time = Instant::now(); + let call_connectors_duration = + call_connectors_end_time.saturating_duration_since(call_connectors_start_time); + tracing::info!(duration = format!("Duration taken: {}", call_connectors_duration.as_millis())); + + Ok(payment_data) +} + #[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] pub async fn call_multiple_connectors_service( @@ -2937,7 +3049,6 @@ where ) .await?; - #[cfg(feature = "v1")] payment_data.set_surcharge_details(session_surcharge_details.as_ref().and_then( |session_surcharge_details| { session_surcharge_details.fetch_surcharge_details( diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index dad68b744048..0b01d0b7f9f5 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -195,14 +195,14 @@ impl Domain( + async fn perform_routing<'a>( &'a self, merchant_account: &domain::MerchantAccount, + _business_profile: &domain::Profile, state: &SessionState, - _request: &PaymentsSessionRequest, - payment_intent: &storage::PaymentIntent, + payment_data: &mut payments::PaymentIntentData, merchant_key_store: &domain::MerchantKeyStore, - ) -> CustomResult { + ) -> CustomResult { let db = &state.store; let all_connector_accounts = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( @@ -214,7 +214,7 @@ impl Domain Domain( state: &SessionState, - req_state: ReqState, + _req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, profile: domain::Profile, operation: Op, req: Req, payment_id: id_type::GlobalPaymentId, - call_connector_action: CallConnectorAction, + _call_connector_action: CallConnectorAction, header_payload: HeaderPayload, ) -> RouterResult<(D, Req, Option, Option, Option)> where @@ -156,22 +152,22 @@ where let connector = operation .to_domain()? - .get_connector( + .perform_routing( &merchant_account, + &profile, &state.clone(), - &req, - payment_data.get_payment_intent(), + &mut payment_data, &key_store, ) .await?; - // TODO: do not use if let let payment_data = match connector { - ConnectorChoice::StraightThrough(_connectors) => { + api::ConnectorCallType::PreDetermined(_connector) => { todo!() } - ConnectorChoice::Decide => todo!(), - ConnectorChoice::SessionMultiple(connectors) => { + api::ConnectorCallType::Retryable(_connectors) => todo!(), + api::ConnectorCallType::Skip => todo!(), + api::ConnectorCallType::SessionMultiple(connectors) => { // todo: call surcharge manager for session token call. Box::pin(call_multiple_connectors_service( state, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index dad372e0fc1e..12a884b28220 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -516,7 +516,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, customer: &'a Option, - merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_connector_account: &domain::MerchantConnectorAccount, _merchant_recipient_data: Option, header_payload: Option, ) -> RouterResult { @@ -526,7 +526,6 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( let auth_type: types::ConnectorAuthType = merchant_connector_account .get_connector_account_details() - .parse_value("ConnectorAuthType") .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while parsing value for ConnectorAuthType")?; From 4ef98c8d824ce7940b190661003947915353d52b Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 12 Nov 2024 15:38:27 +0530 Subject: [PATCH 35/52] chore: introduce GenericListWrapper --- crates/common_utils/src/types.rs | 29 ++++++++ .../src/merchant_connector_account.rs | 66 +++++++++++++++++- .../operations/payment_session_intent.rs | 68 +++++-------------- 3 files changed, 112 insertions(+), 51 deletions(-) diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 82b585639294..9c552a0c9523 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -1516,3 +1516,32 @@ pub trait ConnectorTransactionIdTrait { self.get_optional_connector_transaction_id() } } + +/// Generic List Wrapper type +#[derive(Debug)] +pub struct GenericListWrapper(Vec); + +impl GenericListWrapper { + /// constructor for GenericListWrapper + pub fn new(list: Vec) -> Self { + Self(list) + } + /// returns iter of the enclosed Vec type + pub fn iter(&self) -> std::slice::Iter<'_, T> { + self.0.iter() + } +} + +impl Iterator for GenericListWrapper { + type Item = T; + + fn next(&mut self) -> Option { + self.0.pop() + } +} + +impl FromIterator for GenericListWrapper { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 7b4d8c43fc5f..47d5c4ed1e54 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -7,12 +7,13 @@ use common_utils::{ date_time, encryption::Encryption, errors::{CustomResult, ValidationError}, - id_type, pii, type_name, + id_type, pii, type_name, types, types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; +use router_env::logger; use rustc_hash::FxHashMap; use serde_json::Value; @@ -134,6 +135,69 @@ impl MerchantConnectorAccount { } } +pub type MerchantConnectorAccountList = types::GenericListWrapper; +pub trait MerchantConnectorAccountListTrait { + #[cfg(feature = "v2")] + fn get_connector_and_supporting_payment_method_type_for_session_call( + &self, + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)>; + fn filter_based_on_profile_and_connector_type( + self, + profile_id: &id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> Self; +} + +impl MerchantConnectorAccountListTrait for MerchantConnectorAccountList { + #[cfg(feature = "v2")] + fn get_connector_and_supporting_payment_method_type_for_session_call( + &self, + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { + let mut connector_and_supporting_payment_method_type = Vec::new(); + self.iter().for_each(|connector_account| { + let res = connector_account + .get_parsed_payment_methods_enabled() + .into_iter() + .filter_map(|parsed_payment_method_result| { + parsed_payment_method_result + .inspect_err(|err| { + logger::error!(session_token_parsing_error=?err); + }) + .ok() + }) + .flat_map(|parsed_payment_methods_enabled| { + parsed_payment_methods_enabled + .payment_method_types + .unwrap_or_default() + .into_iter() + .filter(|payment_method_type| { + let is_invoke_sdk_client = matches!( + payment_method_type.payment_experience, + Some(api_models::enums::PaymentExperience::InvokeSdkClient) + ); + is_invoke_sdk_client + }) + .map(|payment_method_type| { + (connector_account, payment_method_type.payment_method_type) + }) + .collect::>() + }) + .collect::>(); + connector_and_supporting_payment_method_type.extend(res); + }); + connector_and_supporting_payment_method_type + } + fn filter_based_on_profile_and_connector_type( + self, + profile_id: &id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> Self { + self.into_iter() + .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) + .collect() + } +} + #[cfg(feature = "v1")] #[derive(Debug)] pub enum MerchantConnectorAccountUpdate { diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 0b01d0b7f9f5..a361029654e7 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -1,24 +1,22 @@ use std::marker::PhantomData; -use api_models::{ - admin::PaymentMethodsEnabled, enums::FrmSuggestion, payments::PaymentsSessionRequest, -}; +use api_models::payments::PaymentsSessionRequest; use async_trait::async_trait; -use common_utils::{errors::CustomResult, ext_traits::ValueExt}; +use common_utils::errors::CustomResult; use error_stack::ResultExt; -use masking::ExposeInterface; use router_env::{instrument, logger, tracing}; -use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; +use super::{BoxedOperation, Domain, GetTracker, Operation, ValidateRequest}; use crate::{ core::{ errors::{self, RouterResult, StorageErrorExt}, payments::{self, helpers, operations}, }, - routes::{app::ReqState, SessionState}, + routes::SessionState, types::{ - api, domain, - storage::{self, enums}, + api, + domain::{self, MerchantConnectorAccountListTrait}, + storage::enums, }, }; @@ -73,9 +71,9 @@ impl GetTracker, PaymentsSess &'a self, state: &'a SessionState, payment_id: &common_utils::id_type::GlobalPaymentId, - request: &PaymentsSessionRequest, + _request: &PaymentsSessionRequest, merchant_account: &domain::MerchantAccount, - profile: &domain::Profile, + _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, ) -> RouterResult< @@ -214,46 +212,16 @@ impl Domain>() - }) - .collect::>(); - connector_and_supporting_payment_method_type.extend(res); - }); + let filtered_connector_accounts = all_connector_accounts + .filter_based_on_profile_and_connector_type( + &profile_id, + common_enums::ConnectorType::PaymentProcessor, + ); + let connector_and_supporting_payment_method_type = filtered_connector_accounts + .get_connector_and_supporting_payment_method_type_for_session_call(); let mut session_connector_data = Vec::with_capacity(connector_and_supporting_payment_method_type.len()); for (merchant_connector_account, payment_method_type) in From 05e534562d81b9915a621aecfd1af83f8c50b6e9 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 12 Nov 2024 16:36:51 +0530 Subject: [PATCH 36/52] chore: rename MerchantConnectorAccountList to MerchantConnectorAccounts --- .../src/merchant_connector_account.rs | 137 +++++++++--------- .../router/src/core/payment_methods/cards.rs | 2 +- .../operations/payment_session_intent.rs | 5 +- 3 files changed, 68 insertions(+), 76 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 47d5c4ed1e54..ff55c27b388d 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -135,69 +135,6 @@ impl MerchantConnectorAccount { } } -pub type MerchantConnectorAccountList = types::GenericListWrapper; -pub trait MerchantConnectorAccountListTrait { - #[cfg(feature = "v2")] - fn get_connector_and_supporting_payment_method_type_for_session_call( - &self, - ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)>; - fn filter_based_on_profile_and_connector_type( - self, - profile_id: &id_type::ProfileId, - connector_type: common_enums::ConnectorType, - ) -> Self; -} - -impl MerchantConnectorAccountListTrait for MerchantConnectorAccountList { - #[cfg(feature = "v2")] - fn get_connector_and_supporting_payment_method_type_for_session_call( - &self, - ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { - let mut connector_and_supporting_payment_method_type = Vec::new(); - self.iter().for_each(|connector_account| { - let res = connector_account - .get_parsed_payment_methods_enabled() - .into_iter() - .filter_map(|parsed_payment_method_result| { - parsed_payment_method_result - .inspect_err(|err| { - logger::error!(session_token_parsing_error=?err); - }) - .ok() - }) - .flat_map(|parsed_payment_methods_enabled| { - parsed_payment_methods_enabled - .payment_method_types - .unwrap_or_default() - .into_iter() - .filter(|payment_method_type| { - let is_invoke_sdk_client = matches!( - payment_method_type.payment_experience, - Some(api_models::enums::PaymentExperience::InvokeSdkClient) - ); - is_invoke_sdk_client - }) - .map(|payment_method_type| { - (connector_account, payment_method_type.payment_method_type) - }) - .collect::>() - }) - .collect::>(); - connector_and_supporting_payment_method_type.extend(res); - }); - connector_and_supporting_payment_method_type - } - fn filter_based_on_profile_and_connector_type( - self, - profile_id: &id_type::ProfileId, - connector_type: common_enums::ConnectorType, - ) -> Self { - self.into_iter() - .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) - .collect() - } -} - #[cfg(feature = "v1")] #[derive(Debug)] pub enum MerchantConnectorAccountUpdate { @@ -621,21 +558,77 @@ impl From for MerchantConnectorAccountUpdateInte } } -#[derive(Debug)] -pub struct MerchantConnectorAccounts(Vec); - -impl MerchantConnectorAccounts { - pub fn new(merchant_connector_accounts: Vec) -> Self { - Self(merchant_connector_accounts) +pub type MerchantConnectorAccounts = types::GenericListWrapper; +pub trait MerchantConnectorAccountsTrait { + #[cfg(feature = "v2")] + fn get_connector_and_supporting_payment_method_type_for_session_call( + &self, + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)>; + fn filter_based_on_profile_and_connector_type( + self, + profile_id: &id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> Self; + fn is_merchant_connector_account_id_in_connector_mandate_details( + &self, + profile_id: Option<&id_type::ProfileId>, + connector_mandate_details: &diesel_models::PaymentsMandateReference, + ) -> bool; +} +impl MerchantConnectorAccountsTrait for MerchantConnectorAccounts { + #[cfg(feature = "v2")] + fn get_connector_and_supporting_payment_method_type_for_session_call( + &self, + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { + let mut connector_and_supporting_payment_method_type = Vec::new(); + self.iter().for_each(|connector_account| { + let res = connector_account + .get_parsed_payment_methods_enabled() + .into_iter() + .filter_map(|parsed_payment_method_result| { + parsed_payment_method_result + .inspect_err(|err| { + logger::error!(session_token_parsing_error=?err); + }) + .ok() + }) + .flat_map(|parsed_payment_methods_enabled| { + parsed_payment_methods_enabled + .payment_method_types + .unwrap_or_default() + .into_iter() + .filter(|payment_method_type| { + let is_invoke_sdk_client = matches!( + payment_method_type.payment_experience, + Some(api_models::enums::PaymentExperience::InvokeSdkClient) + ); + is_invoke_sdk_client + }) + .map(|payment_method_type| { + (connector_account, payment_method_type.payment_method_type) + }) + .collect::>() + }) + .collect::>(); + connector_and_supporting_payment_method_type.extend(res); + }); + connector_and_supporting_payment_method_type } - - pub fn is_merchant_connector_account_id_in_connector_mandate_details( + fn filter_based_on_profile_and_connector_type( + self, + profile_id: &id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> Self { + self.into_iter() + .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) + .collect() + } + fn is_merchant_connector_account_id_in_connector_mandate_details( &self, profile_id: Option<&id_type::ProfileId>, connector_mandate_details: &diesel_models::PaymentsMandateReference, ) -> bool { let mca_ids = self - .0 .iter() .filter(|mca| { mca.disabled.is_some_and(|disabled| !disabled) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index fcf0c6ca4d1d..a815fd118778 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -91,7 +91,7 @@ use crate::{ services, types::{ api::{self, routing as routing_types, PaymentMethodCreateExt}, - domain::{self, Profile}, + domain::{self, MerchantConnectorAccountsTrait, Profile}, storage::{self, enums, PaymentMethodListContext, PaymentTokenData}, transformers::ForeignTryFrom, }, diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index a361029654e7..0af0b3e9992d 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -15,7 +15,7 @@ use crate::{ routes::SessionState, types::{ api, - domain::{self, MerchantConnectorAccountListTrait}, + domain::{self, MerchantConnectorAccountsTrait}, storage::enums, }, }; @@ -212,8 +212,7 @@ impl Domain Date: Tue, 12 Nov 2024 16:52:47 +0530 Subject: [PATCH 37/52] chore: address cargo clippy lints --- .../hyperswitch_domain_models/src/merchant_connector_account.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index ff55c27b388d..e10a22cd3581 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -13,6 +13,7 @@ use common_utils::{ use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; +#[cfg(feature = "v2")] use router_env::logger; use rustc_hash::FxHashMap; use serde_json::Value; From 9c5aaa6ec426de351b231af5792c38e761019112 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 12 Nov 2024 21:52:31 +0530 Subject: [PATCH 38/52] chore: introduce macro for generating list wrapper --- crates/common_utils/src/macros.rs | 35 ++++ crates/common_utils/src/types.rs | 29 ---- .../src/merchant_connector_account.rs | 151 ++++++++---------- .../router/src/core/payment_methods/cards.rs | 2 +- .../operations/payment_session_intent.rs | 6 +- 5 files changed, 106 insertions(+), 117 deletions(-) diff --git a/crates/common_utils/src/macros.rs b/crates/common_utils/src/macros.rs index 21cec6f60fce..fe1289acba03 100644 --- a/crates/common_utils/src/macros.rs +++ b/crates/common_utils/src/macros.rs @@ -369,6 +369,41 @@ mod id_type { } } +/// Create new generic list wrapper +#[macro_export] +macro_rules! create_list_wrapper { + ( + $wrapper_name:ident, + $type_name: ty, + impl_functions: { + $($function_def: tt)* + } + ) => { + pub struct $wrapper_name(Vec<$type_name>); + impl $wrapper_name { + pub fn new(list: Vec<$type_name>) -> Self { + Self(list) + } + pub fn iter(&self) -> std::slice::Iter<'_, $type_name> { + self.0.iter() + } + $($function_def)* + } + impl Iterator for $wrapper_name { + type Item = $type_name; + fn next(&mut self) -> Option { + self.0.pop() + } + } + + impl FromIterator<$type_name> for $wrapper_name { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } + } + }; +} + /// Get the type name for a type #[macro_export] macro_rules! type_name { diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 9c552a0c9523..82b585639294 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -1516,32 +1516,3 @@ pub trait ConnectorTransactionIdTrait { self.get_optional_connector_transaction_id() } } - -/// Generic List Wrapper type -#[derive(Debug)] -pub struct GenericListWrapper(Vec); - -impl GenericListWrapper { - /// constructor for GenericListWrapper - pub fn new(list: Vec) -> Self { - Self(list) - } - /// returns iter of the enclosed Vec type - pub fn iter(&self) -> std::slice::Iter<'_, T> { - self.0.iter() - } -} - -impl Iterator for GenericListWrapper { - type Item = T; - - fn next(&mut self) -> Option { - self.0.pop() - } -} - -impl FromIterator for GenericListWrapper { - fn from_iter>(iter: T) -> Self { - Self(iter.into_iter().collect()) - } -} diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index e10a22cd3581..242719a964a4 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -7,7 +7,7 @@ use common_utils::{ date_time, encryption::Encryption, errors::{CustomResult, ValidationError}, - id_type, pii, type_name, types, + id_type, pii, type_name, types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; @@ -559,87 +559,74 @@ impl From for MerchantConnectorAccountUpdateInte } } -pub type MerchantConnectorAccounts = types::GenericListWrapper; -pub trait MerchantConnectorAccountsTrait { - #[cfg(feature = "v2")] - fn get_connector_and_supporting_payment_method_type_for_session_call( - &self, - ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)>; - fn filter_based_on_profile_and_connector_type( - self, - profile_id: &id_type::ProfileId, - connector_type: common_enums::ConnectorType, - ) -> Self; - fn is_merchant_connector_account_id_in_connector_mandate_details( - &self, - profile_id: Option<&id_type::ProfileId>, - connector_mandate_details: &diesel_models::PaymentsMandateReference, - ) -> bool; -} -impl MerchantConnectorAccountsTrait for MerchantConnectorAccounts { - #[cfg(feature = "v2")] - fn get_connector_and_supporting_payment_method_type_for_session_call( - &self, - ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { - let mut connector_and_supporting_payment_method_type = Vec::new(); - self.iter().for_each(|connector_account| { - let res = connector_account - .get_parsed_payment_methods_enabled() - .into_iter() - .filter_map(|parsed_payment_method_result| { - parsed_payment_method_result - .inspect_err(|err| { - logger::error!(session_token_parsing_error=?err); - }) - .ok() - }) - .flat_map(|parsed_payment_methods_enabled| { - parsed_payment_methods_enabled - .payment_method_types - .unwrap_or_default() - .into_iter() - .filter(|payment_method_type| { - let is_invoke_sdk_client = matches!( - payment_method_type.payment_experience, - Some(api_models::enums::PaymentExperience::InvokeSdkClient) - ); - is_invoke_sdk_client - }) - .map(|payment_method_type| { - (connector_account, payment_method_type.payment_method_type) - }) - .collect::>() +common_utils::create_list_wrapper!( + MerchantConnectorAccounts, + MerchantConnectorAccount, + impl_functions: { + #[cfg(feature = "v2")] + pub fn get_connector_and_supporting_payment_method_type_for_session_call( + &self, + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { + let mut connector_and_supporting_payment_method_type = Vec::new(); + self.iter().for_each(|connector_account| { + let res = connector_account + .get_parsed_payment_methods_enabled() + .into_iter() + .filter_map(|parsed_payment_method_result| { + parsed_payment_method_result + .inspect_err(|err| { + logger::error!(session_token_parsing_error=?err); + }) + .ok() + }) + .flat_map(|parsed_payment_methods_enabled| { + parsed_payment_methods_enabled + .payment_method_types + .unwrap_or_default() + .into_iter() + .filter(|payment_method_type| { + let is_invoke_sdk_client = matches!( + payment_method_type.payment_experience, + Some(api_models::enums::PaymentExperience::InvokeSdkClient) + ); + is_invoke_sdk_client + }) + .map(|payment_method_type| { + (connector_account, payment_method_type.payment_method_type) + }) + .collect::>() + }) + .collect::>(); + connector_and_supporting_payment_method_type.extend(res); + }); + connector_and_supporting_payment_method_type + } + pub fn filter_based_on_profile_and_connector_type( + self, + profile_id: &id_type::ProfileId, + connector_type: common_enums::ConnectorType, + ) -> Self { + self.into_iter() + .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) + .collect() + } + pub fn is_merchant_connector_account_id_in_connector_mandate_details( + &self, + profile_id: Option<&id_type::ProfileId>, + connector_mandate_details: &diesel_models::PaymentsMandateReference, + ) -> bool { + let mca_ids = self + .iter() + .filter(|mca| { + mca.disabled.is_some_and(|disabled| !disabled) + && profile_id.is_some_and(|profile_id| *profile_id == mca.profile_id) }) - .collect::>(); - connector_and_supporting_payment_method_type.extend(res); - }); - connector_and_supporting_payment_method_type - } - fn filter_based_on_profile_and_connector_type( - self, - profile_id: &id_type::ProfileId, - connector_type: common_enums::ConnectorType, - ) -> Self { - self.into_iter() - .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) - .collect() - } - fn is_merchant_connector_account_id_in_connector_mandate_details( - &self, - profile_id: Option<&id_type::ProfileId>, - connector_mandate_details: &diesel_models::PaymentsMandateReference, - ) -> bool { - let mca_ids = self - .iter() - .filter(|mca| { - mca.disabled.is_some_and(|disabled| !disabled) - && profile_id.is_some_and(|profile_id| *profile_id == mca.profile_id) - }) - .map(|mca| mca.get_id()) - .collect::>(); + .map(|mca| mca.get_id()) + .collect::>(); - connector_mandate_details - .keys() - .any(|mca_id| mca_ids.contains(mca_id)) + connector_mandate_details + .keys() + .any(|mca_id| mca_ids.contains(mca_id)) + } } -} +); diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index a815fd118778..fcf0c6ca4d1d 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -91,7 +91,7 @@ use crate::{ services, types::{ api::{self, routing as routing_types, PaymentMethodCreateExt}, - domain::{self, MerchantConnectorAccountsTrait, Profile}, + domain::{self, Profile}, storage::{self, enums, PaymentMethodListContext, PaymentTokenData}, transformers::ForeignTryFrom, }, diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 0af0b3e9992d..38637ae4b22c 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -13,11 +13,7 @@ use crate::{ payments::{self, helpers, operations}, }, routes::SessionState, - types::{ - api, - domain::{self, MerchantConnectorAccountsTrait}, - storage::enums, - }, + types::{api, domain, storage::enums}, }; #[derive(Debug, Clone, Copy)] From 23c46e661f76c654e42ad1d7542a0066a3a76338 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 12 Nov 2024 22:04:42 +0530 Subject: [PATCH 39/52] chore: address v2 clippy lints --- .../src/core/payments/operations/payment_session_intent.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 38637ae4b22c..70b35119507b 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -212,7 +212,7 @@ impl Domain Date: Thu, 14 Nov 2024 13:03:52 +0530 Subject: [PATCH 40/52] resolve merge conflicts --- crates/router/src/routes/app.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 16982059b4c9..e96b3fe45223 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -685,13 +685,10 @@ impl Forex { web::resource("/convert_from_minor").route(web::get().to(currency::convert_forex)), ) } -<<<<<<< HEAD #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { web::scope("/forex").app_data(web::Data::new(state.clone())) } -======= ->>>>>>> origin/main } #[cfg(feature = "olap")] @@ -1068,14 +1065,11 @@ pub struct Payouts; #[cfg(all(feature = "payouts", feature = "v1"))] impl Payouts { -<<<<<<< HEAD #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { web::scope("/payouts").app_data(web::Data::new(state)) } #[cfg(feature = "v1")] -======= ->>>>>>> origin/main pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payouts").app_data(web::Data::new(state)); route = route.service(web::resource("/create").route(web::post().to(payouts_create))); @@ -1590,13 +1584,10 @@ impl Cards { .app_data(web::Data::new(state)) .service(web::resource("/{bin}").route(web::get().to(card_iin_info))) } -<<<<<<< HEAD #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { web::scope("/cards").app_data(web::Data::new(state)) } -======= ->>>>>>> origin/main } pub struct Files; @@ -1664,15 +1655,10 @@ impl PayoutLink { ); route } -<<<<<<< HEAD #[cfg(feature = "v2")] pub fn server(state: AppState) -> Scope { web::scope("/payout_link").app_data(web::Data::new(state)) } -======= ->>>>>>> origin/main -} - pub struct Profile; #[cfg(all(feature = "olap", feature = "v2"))] impl Profile { From e98450148a72a14e2d59480d40f730266cb46d33 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 14 Nov 2024 16:59:45 +0530 Subject: [PATCH 41/52] chore: add missing closing Braces --- crates/router/src/routes/app.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index e96b3fe45223..36f8d42182bb 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1659,6 +1659,7 @@ impl PayoutLink { pub fn server(state: AppState) -> Scope { web::scope("/payout_link").app_data(web::Data::new(state)) } +} pub struct Profile; #[cfg(all(feature = "olap", feature = "v2"))] impl Profile { From aeaeaf34fbed8cae6e97b4d0fb7e3ee1fe025eee Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 14 Nov 2024 17:00:11 +0530 Subject: [PATCH 42/52] chore: implement ValidateStatusForOperation for PaymentSessionIntent --- .../operations/payment_session_intent.rs | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 70b35119507b..e5355817956b 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -10,7 +10,7 @@ use super::{BoxedOperation, Domain, GetTracker, Operation, ValidateRequest}; use crate::{ core::{ errors::{self, RouterResult, StorageErrorExt}, - payments::{self, helpers, operations}, + payments::{self, operations}, }, routes::SessionState, types::{api, domain, storage::enums}, @@ -19,6 +19,34 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct PaymentSessionIntent; +impl operations::ValidateStatusForOperation for PaymentSessionIntent { + /// Validate if the current operation can be performed on the current status of the payment intent + fn validate_status_for_operation( + &self, + intent_status: common_enums::IntentStatus, + ) -> Result<(), errors::ApiErrorResponse> { + match intent_status { + common_enums::IntentStatus::RequiresPaymentMethod + | common_enums::IntentStatus::Cancelled + | common_enums::IntentStatus::Processing + | common_enums::IntentStatus::RequiresCustomerAction + | common_enums::IntentStatus::RequiresMerchantAction + | common_enums::IntentStatus::RequiresCapture + | common_enums::IntentStatus::PartiallyCaptured + | common_enums::IntentStatus::RequiresConfirmation + | common_enums::IntentStatus::PartiallyCapturedAndCapturable => Ok(()), + common_enums::IntentStatus::Succeeded + | common_enums::IntentStatus::Failed => { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: format!( + "You cannot create session token for this payment because it has status {intent_status}", + ), + }) + } + } + } +} + impl Operation for &PaymentSessionIntent { type Data = payments::PaymentIntentData; fn to_validate_request( @@ -71,7 +99,7 @@ impl GetTracker, PaymentsSess merchant_account: &domain::MerchantAccount, _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, - _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + header_payload: &hyperswitch_domain_models::payments::HeaderPayload, ) -> RouterResult< operations::GetTrackerResponse< 'a, @@ -89,14 +117,13 @@ impl GetTracker, PaymentsSess .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - helpers::validate_payment_status_against_not_allowed_statuses( - &payment_intent.status, - &[enums::IntentStatus::Failed, enums::IntentStatus::Succeeded], - "create a session token for", - )?; + self.validate_status_for_operation(payment_intent.status)?; - // do this in core function - // helpers::authenticate_client_secret(Some(&request.client_secret), &payment_intent)?; + let client_secret = header_payload + .client_secret + .as_ref() + .get_required_value("client_secret header")?; + payment_intent.validate_client_secret(client_secret)?; let payment_data = payments::PaymentIntentData { flow: PhantomData, From 026a246b0538e5e850472c2817f958e1e05006d1 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 14 Nov 2024 17:20:24 +0530 Subject: [PATCH 43/52] address comments --- crates/router/src/core/payments.rs | 31 ------------------- .../src/core/payments/flows/session_flow.rs | 16 ---------- .../operations/payment_session_intent.rs | 5 +-- 3 files changed, 3 insertions(+), 49 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 4bc7151fdf54..cf203d5cc5e2 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3505,37 +3505,6 @@ where Ok(merchant_connector_account) } -#[cfg(feature = "v2")] -#[instrument(skip_all)] -pub async fn construct_profile_id_and_get_mca<'a, F, D>( - state: &'a SessionState, - merchant_account: &domain::MerchantAccount, - payment_data: &D, - connector_name: &str, - merchant_connector_id: Option<&id_type::MerchantConnectorAccountId>, - key_store: &domain::MerchantKeyStore, - _should_validate: bool, -) -> RouterResult -where - F: Clone, - D: OperationSessionGetters + Send + Sync + Clone, -{ - let profile_id = payment_data.get_payment_intent().profile_id.clone(); - - let merchant_connector_account = helpers::get_merchant_connector_account( - state, - merchant_account.get_id(), - None, - key_store, - &profile_id, - connector_name, - merchant_connector_id, - ) - .await?; - - Ok(merchant_connector_account) -} - fn is_payment_method_tokenization_enabled_for_connector( state: &SessionState, connector_name: &str, diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index dbab15385337..265046f42d96 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -77,7 +77,6 @@ impl ConstructFlowSpecificData for PaymentData { - #[cfg(feature = "v1")] async fn construct_router_data<'a>( &self, state: &routes::SessionState, @@ -106,21 +105,6 @@ impl .await } - #[cfg(feature = "v2")] - async fn construct_router_data<'a>( - &self, - state: &routes::SessionState, - connector_id: &str, - merchant_account: &domain::MerchantAccount, - key_store: &domain::MerchantKeyStore, - customer: &Option, - merchant_connector_account: &domain::MerchantConnectorAccount, - merchant_recipient_data: Option, - header_payload: Option, - ) -> RouterResult { - todo!() - } - async fn get_merchant_recipient_data<'a>( &self, _state: &routes::SessionState, diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index e5355817956b..140618106e07 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -10,16 +10,17 @@ use super::{BoxedOperation, Domain, GetTracker, Operation, ValidateRequest}; use crate::{ core::{ errors::{self, RouterResult, StorageErrorExt}, - payments::{self, operations}, + payments::{self, operations, operations::ValidateStatusForOperation}, }, routes::SessionState, types::{api, domain, storage::enums}, + utils::ext_traits::OptionExt, }; #[derive(Debug, Clone, Copy)] pub struct PaymentSessionIntent; -impl operations::ValidateStatusForOperation for PaymentSessionIntent { +impl ValidateStatusForOperation for PaymentSessionIntent { /// Validate if the current operation can be performed on the current status of the payment intent fn validate_status_for_operation( &self, From e8f067895368c991852439449817b3defb754a19 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 15 Nov 2024 10:11:33 +0530 Subject: [PATCH 44/52] address comments --- crates/router/src/core/payments.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index cf203d5cc5e2..be9045f02944 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2929,6 +2929,7 @@ where .get_required_value("merchant_connector_id") .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("connector id is not set")?; + // TODO: make this DB call parallel let merchant_connector_account = state .store .find_merchant_connector_account_by_id(&state.into(), merchant_connector_id, key_store) From 6c36a6b42f2d4d12bdd47cc81a1ab2afc3d3ac9e Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 15 Nov 2024 12:39:56 +0530 Subject: [PATCH 45/52] address comments --- .../src/merchant_connector_account.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 242719a964a4..32a0d47b2e91 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -567,8 +567,7 @@ common_utils::create_list_wrapper!( pub fn get_connector_and_supporting_payment_method_type_for_session_call( &self, ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { - let mut connector_and_supporting_payment_method_type = Vec::new(); - self.iter().for_each(|connector_account| { + let connector_and_supporting_payment_method_type = self.iter().flat_map(|connector_account| { let res = connector_account .get_parsed_payment_methods_enabled() .into_iter() @@ -597,7 +596,7 @@ common_utils::create_list_wrapper!( .collect::>() }) .collect::>(); - connector_and_supporting_payment_method_type.extend(res); + res }); connector_and_supporting_payment_method_type } From 3008ded2c8da140ac765cbbf005eabc3fd60b0a1 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Fri, 15 Nov 2024 12:42:58 +0530 Subject: [PATCH 46/52] address comments --- .../hyperswitch_domain_models/src/merchant_connector_account.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 32a0d47b2e91..7358f495c6a1 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -570,6 +570,7 @@ common_utils::create_list_wrapper!( let connector_and_supporting_payment_method_type = self.iter().flat_map(|connector_account| { let res = connector_account .get_parsed_payment_methods_enabled() + // TODO: make payment_methods_enabled strict type in DB .into_iter() .filter_map(|parsed_payment_method_result| { parsed_payment_method_result From 0c70b31b0d259fedd8f88772eab0e463eb9729b0 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Tue, 19 Nov 2024 12:05:10 +0530 Subject: [PATCH 47/52] address clippy lints for v2 --- .../hyperswitch_domain_models/src/merchant_connector_account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 7358f495c6a1..35860f9b8848 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -598,7 +598,7 @@ common_utils::create_list_wrapper!( }) .collect::>(); res - }); + }).collect(); connector_and_supporting_payment_method_type } pub fn filter_based_on_profile_and_connector_type( From f24ddbf4f3bb2102ca2efb26b0203bf38a5bc919 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 20 Nov 2024 14:49:06 +0530 Subject: [PATCH 48/52] address clippy lints for v2 --- .../src/merchant_connector_account.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 35860f9b8848..51c75113dcda 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -568,7 +568,7 @@ common_utils::create_list_wrapper!( &self, ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { let connector_and_supporting_payment_method_type = self.iter().flat_map(|connector_account| { - let res = connector_account + connector_account .get_parsed_payment_methods_enabled() // TODO: make payment_methods_enabled strict type in DB .into_iter() @@ -596,8 +596,7 @@ common_utils::create_list_wrapper!( }) .collect::>() }) - .collect::>(); - res + .collect::>() }).collect(); connector_and_supporting_payment_method_type } From 86af92ed7b0f4e8963c4e8989480a9c6b9952bb2 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 21 Nov 2024 13:45:31 +0530 Subject: [PATCH 49/52] remove v1 feature flag --- crates/router/src/routes/app.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 36f8d42182bb..d658ee71e048 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -685,10 +685,6 @@ impl Forex { web::resource("/convert_from_minor").route(web::get().to(currency::convert_forex)), ) } - #[cfg(feature = "v2")] - pub fn server(state: AppState) -> Scope { - web::scope("/forex").app_data(web::Data::new(state.clone())) - } } #[cfg(feature = "olap")] From 972fc000d115fef190562144390a4883366a45ae Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 21 Nov 2024 14:47:53 +0530 Subject: [PATCH 50/52] only allow requires_payment_method status for payments session --- .../payments/operations/payment_session_intent.rs | 10 +++++----- crates/router/src/routes/app.rs | 13 ------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 140618106e07..7f125b53dcfe 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -27,20 +27,20 @@ impl ValidateStatusForOperation for PaymentSessionIntent { intent_status: common_enums::IntentStatus, ) -> Result<(), errors::ApiErrorResponse> { match intent_status { - common_enums::IntentStatus::RequiresPaymentMethod - | common_enums::IntentStatus::Cancelled + common_enums::IntentStatus::RequiresPaymentMethod => Ok(()), + common_enums::IntentStatus::Cancelled | common_enums::IntentStatus::Processing | common_enums::IntentStatus::RequiresCustomerAction | common_enums::IntentStatus::RequiresMerchantAction | common_enums::IntentStatus::RequiresCapture | common_enums::IntentStatus::PartiallyCaptured | common_enums::IntentStatus::RequiresConfirmation - | common_enums::IntentStatus::PartiallyCapturedAndCapturable => Ok(()), - common_enums::IntentStatus::Succeeded + | common_enums::IntentStatus::PartiallyCapturedAndCapturable + | common_enums::IntentStatus::Succeeded | common_enums::IntentStatus::Failed => { Err(errors::ApiErrorResponse::PreconditionFailed { message: format!( - "You cannot create session token for this payment because it has status {intent_status}", + "You cannot create session token for this payment because it has status {intent_status}. Expected status is requires_payment_method.", ), }) } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index d658ee71e048..1c570ddb0fc8 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1061,11 +1061,6 @@ pub struct Payouts; #[cfg(all(feature = "payouts", feature = "v1"))] impl Payouts { - #[cfg(feature = "v2")] - pub fn server(state: AppState) -> Scope { - web::scope("/payouts").app_data(web::Data::new(state)) - } - #[cfg(feature = "v1")] pub fn server(state: AppState) -> Scope { let mut route = web::scope("/payouts").app_data(web::Data::new(state)); route = route.service(web::resource("/create").route(web::post().to(payouts_create))); @@ -1580,10 +1575,6 @@ impl Cards { .app_data(web::Data::new(state)) .service(web::resource("/{bin}").route(web::get().to(card_iin_info))) } - #[cfg(feature = "v2")] - pub fn server(state: AppState) -> Scope { - web::scope("/cards").app_data(web::Data::new(state)) - } } pub struct Files; @@ -1651,10 +1642,6 @@ impl PayoutLink { ); route } - #[cfg(feature = "v2")] - pub fn server(state: AppState) -> Scope { - web::scope("/payout_link").app_data(web::Data::new(state)) - } } pub struct Profile; #[cfg(all(feature = "olap", feature = "v2"))] From f2a7800b663245a686bb71f25473a584c2bfd085 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 27 Nov 2024 12:14:12 +0530 Subject: [PATCH 51/52] fix compilation error due to merge with main --- .../operations/payment_session_intent.rs | 32 +++++-------------- .../src/core/payments/session_operation.rs | 7 ++-- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index 7f125b53dcfe..ee490408cb11 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -101,14 +101,7 @@ impl GetTracker, PaymentsSess _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse< - 'a, - F, - PaymentsSessionRequest, - payments::PaymentIntentData, - >, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); let storage_scheme = merchant_account.storage_scheme; @@ -132,10 +125,7 @@ impl GetTracker, PaymentsSess sessions_token: vec![], }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } @@ -149,18 +139,12 @@ impl ValidateRequest RouterResult<( - PaymentsCreateIntentOperation<'b, F>, - operations::ValidateResult, - )> { - Ok(( - Box::new(self), - operations::ValidateResult { - merchant_id: merchant_account.get_id().to_owned(), - storage_scheme: merchant_account.storage_scheme, - requeue: false, - }, - )) + ) -> RouterResult { + Ok(operations::ValidateResult { + merchant_id: merchant_account.get_id().to_owned(), + storage_scheme: merchant_account.storage_scheme, + requeue: false, + }) } } diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index 4c4d63f85b5f..d7b6ad0d345b 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -118,14 +118,11 @@ where { let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation); - let (operation, _validate_result) = operation + let _validate_result = operation .to_validate_request()? .validate_request(&req, &merchant_account)?; - let operations::GetTrackerResponse { - operation, - mut payment_data, - } = operation + let operations::GetTrackerResponse { mut payment_data } = operation .to_get_tracker()? .get_trackers( state, From 374f4390bd8f7fae24a8c645615483a8908944d7 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 27 Nov 2024 12:41:25 +0530 Subject: [PATCH 52/52] fix compilation error in v2 code --- crates/hyperswitch_domain_models/src/payments.rs | 2 +- crates/router/src/core/payments/transformers.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 022ddec361fd..b7a6c12500d1 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; #[cfg(feature = "v2")] -use api_models::payments::{Address, SessionToken}; +use api_models::payments::SessionToken; #[cfg(feature = "v2")] use common_utils::ext_traits::ValueExt; use common_utils::{ diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 8e65251ee3b1..4baeade05abf 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -571,7 +571,6 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( .and_then(|billing_address| { billing_address .get_inner() - .peek() .address .as_ref() .and_then(|address| address.country) @@ -650,6 +649,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( additional_merchant_data: None, header_payload, connector_mandate_request_reference_id: None, + psd2_sca_exemption_type: None, }; Ok(router_data)