From 1014f2338cfb89432376e465e0d4c735be68df07 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Wed, 13 Nov 2024 23:40:09 +0530 Subject: [PATCH 01/24] added is_platform_account column --- crates/diesel_models/src/merchant_account.rs | 7 ++++ crates/diesel_models/src/schema.rs | 1 + .../src/merchant_account.rs | 40 +++++++++++++++++++ crates/router/src/core/admin.rs | 1 + .../down.sql | 2 + .../up.sql | 3 ++ 6 files changed, 54 insertions(+) create mode 100644 migrations/2024-11-13-114939_platorm_merchant_account/down.sql create mode 100644 migrations/2024-11-13-114939_platorm_merchant_account/up.sql diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index 12d51311e874..cc9db945d69e 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -51,6 +51,7 @@ pub struct MerchantAccount { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, + pub is_platform_account: bool } #[cfg(feature = "v1")] @@ -83,6 +84,7 @@ pub struct MerchantAccountSetter { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, + pub is_platform_account: bool, } #[cfg(feature = "v1")] @@ -117,6 +119,7 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, + is_platform_account: item.is_platform_account } } } @@ -228,6 +231,7 @@ pub struct MerchantAccountNew { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, + pub is_platform_account: bool } #[cfg(feature = "v2")] @@ -319,6 +323,7 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, pub payment_link_config: Option, pub pm_collect_link_config: Option, + pub is_platform_account: Option } #[cfg(feature = "v1")] @@ -350,6 +355,7 @@ impl MerchantAccountUpdateInternal { recon_status, payment_link_config, pm_collect_link_config, + is_platform_account, } = self; MerchantAccount { @@ -385,6 +391,7 @@ impl MerchantAccountUpdateInternal { payment_link_config: payment_link_config.or(source.payment_link_config), pm_collect_link_config: pm_collect_link_config.or(source.pm_collect_link_config), version: source.version, + is_platform_account: is_platform_account.unwrap_or(source.is_platform_account) } } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 782d7f50eac2..2b3e7ba280cb 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -680,6 +680,7 @@ diesel::table! { payment_link_config -> Nullable, pm_collect_link_config -> Nullable, version -> ApiVersion, + is_platform_account -> Bool, } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 1e3302dab4a3..c3b9120c12cc 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -47,6 +47,7 @@ pub struct MerchantAccount { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, + pub is_platform_account: bool, } #[cfg(feature = "v1")] @@ -81,6 +82,7 @@ pub struct MerchantAccountSetter { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, + pub is_platform_account: bool } #[cfg(feature = "v1")] @@ -115,6 +117,7 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, + is_platform_account: item.is_platform_account } } } @@ -233,6 +236,7 @@ pub enum MerchantAccountUpdate { }, UnsetDefaultProfile, ModifiedAtUpdate, + ToPlatformAccount, } #[cfg(feature = "v2")] @@ -308,6 +312,7 @@ impl From for MerchantAccountUpdateInternal { organization_id: None, is_recon_enabled: None, recon_status: None, + is_platform_account: None, }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), @@ -335,6 +340,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_platform_account: None, }, MerchantAccountUpdate::ReconUpdate { recon_status } => Self { recon_status: Some(recon_status), @@ -362,6 +368,7 @@ impl From for MerchantAccountUpdateInternal { default_profile: None, payment_link_config: None, pm_collect_link_config: None, + is_platform_account: None, }, MerchantAccountUpdate::UnsetDefaultProfile => Self { default_profile: Some(None), @@ -389,6 +396,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_platform_account:None, }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -416,7 +424,36 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_platform_account: None, }, + MerchantAccountUpdate::ToPlatformAccount => Self { + modified_at: now, + merchant_name: None, + merchant_details: None, + return_url: None, + webhook_details: None, + sub_merchants_enabled: None, + parent_merchant_id: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + publishable_key: None, + storage_scheme: None, + locker_id: None, + metadata: None, + routing_algorithm: None, + primary_business_details: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + organization_id: None, + is_recon_enabled: None, + default_profile: None, + recon_status: None, + payment_link_config: None, + pm_collect_link_config: None, + is_platform_account: Some(true), + } } } } @@ -615,6 +652,7 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, version: self.version, + is_platform_account: self.is_platform_account }; Ok(diesel_models::MerchantAccount::from(setter)) @@ -692,6 +730,7 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, + is_platform_account: item.is_platform_account }) } .await @@ -730,6 +769,7 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, version: crate::consts::API_VERSION, + is_platform_account: self.is_platform_account, }) } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 68f2b9d63431..2f557550a368 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -403,6 +403,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { payment_link_config: None, pm_collect_link_config, version: hyperswitch_domain_models::consts::API_VERSION, + is_platform_account: false }, ) } diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/down.sql b/migrations/2024-11-13-114939_platorm_merchant_account/down.sql new file mode 100644 index 000000000000..a84cba91cac1 --- /dev/null +++ b/migrations/2024-11-13-114939_platorm_merchant_account/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_account DROP COLUMN IF EXISTS is_platform_account; diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/up.sql b/migrations/2024-11-13-114939_platorm_merchant_account/up.sql new file mode 100644 index 000000000000..8934a93128e7 --- /dev/null +++ b/migrations/2024-11-13-114939_platorm_merchant_account/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE merchant_account ADD COLUMN IF NOT EXISTS is_platform_account BOOL NOT NULL DEFAULT FALSE; + From 14601c0b10f435bc1c16438a9f1dadc0cc63bf2d Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Tue, 19 Nov 2024 16:23:56 +0530 Subject: [PATCH 02/24] added platform authentication functions --- crates/diesel_models/src/merchant_account.rs | 10 +- .../src/merchant_account.rs | 14 +-- crates/router/src/core/admin.rs | 2 +- crates/router/src/lib.rs | 1 + crates/router/src/services/authentication.rs | 100 +++++++++++++++++- 5 files changed, 113 insertions(+), 14 deletions(-) diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index cc9db945d69e..ce5624a2c28c 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -51,7 +51,7 @@ pub struct MerchantAccount { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_platform_account: bool + pub is_platform_account: bool, } #[cfg(feature = "v1")] @@ -119,7 +119,7 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_platform_account: item.is_platform_account + is_platform_account: item.is_platform_account, } } } @@ -231,7 +231,7 @@ pub struct MerchantAccountNew { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_platform_account: bool + pub is_platform_account: bool, } #[cfg(feature = "v2")] @@ -323,7 +323,7 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, pub payment_link_config: Option, pub pm_collect_link_config: Option, - pub is_platform_account: Option + pub is_platform_account: Option, } #[cfg(feature = "v1")] @@ -391,7 +391,7 @@ impl MerchantAccountUpdateInternal { payment_link_config: payment_link_config.or(source.payment_link_config), pm_collect_link_config: pm_collect_link_config.or(source.pm_collect_link_config), version: source.version, - is_platform_account: is_platform_account.unwrap_or(source.is_platform_account) + is_platform_account: is_platform_account.unwrap_or(source.is_platform_account), } } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index c3b9120c12cc..61227c546c69 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -82,7 +82,7 @@ pub struct MerchantAccountSetter { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_platform_account: bool + pub is_platform_account: bool, } #[cfg(feature = "v1")] @@ -117,7 +117,7 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_platform_account: item.is_platform_account + is_platform_account: item.is_platform_account, } } } @@ -236,7 +236,7 @@ pub enum MerchantAccountUpdate { }, UnsetDefaultProfile, ModifiedAtUpdate, - ToPlatformAccount, + ToPlatformAccount, } #[cfg(feature = "v2")] @@ -396,7 +396,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, - is_platform_account:None, + is_platform_account: None, }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -453,7 +453,7 @@ impl From for MerchantAccountUpdateInternal { payment_link_config: None, pm_collect_link_config: None, is_platform_account: Some(true), - } + }, } } } @@ -652,7 +652,7 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, version: self.version, - is_platform_account: self.is_platform_account + is_platform_account: self.is_platform_account, }; Ok(diesel_models::MerchantAccount::from(setter)) @@ -730,7 +730,7 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_platform_account: item.is_platform_account + is_platform_account: item.is_platform_account, }) } .await diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 2f557550a368..32304a56724c 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -403,7 +403,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { payment_link_config: None, pm_collect_link_config, version: hyperswitch_domain_models::consts::API_VERSION, - is_platform_account: false + is_platform_account: false, }, ) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 215a8b209cf0..6c7f8bea2e99 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -89,6 +89,7 @@ pub mod headers { pub const X_REDIRECT_URI: &str = "x-redirect-uri"; pub const X_TENANT_ID: &str = "x-tenant-id"; pub const X_CLIENT_SECRET: &str = "X-Client-Secret"; + pub const X_CONNECTED_MERCHANT_ID: &str = "x-connected-merchant-id"; } pub mod pii { diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index a22d6bf1df9f..f859dfd0c1bb 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,7 +11,7 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, id_type}; +use common_utils::{date_time, ext_traits::AsyncExt, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -57,6 +57,7 @@ mod detached; #[derive(Clone, Debug)] pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, + // pub platform_merchant_account: Option, pub key_store: domain::MerchantKeyStore, pub profile_id: Option, } @@ -540,6 +541,10 @@ where .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account + // let (merchant, platform_merchant_account) = + // get_platform_merchant_account(state, request_headers, merchant).await?; + let auth = AuthenticationData { merchant_account: merchant, key_store, @@ -1069,6 +1074,34 @@ impl<'a> HeaderMapStruct<'a> { ) }) } + + pub fn get_id_type_from_header_if_present(&self, key: &str) -> RouterResult> + where + T: TryFrom< + std::borrow::Cow<'static, str>, + Error = error_stack::Report, + >, + { + self.headers + .get(key) + .map(|value| value.to_str()) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "`{key}` in headers", + }) + .attach_printable(format!( + "Failed to convert header value to string for header key: {}", + key + ))? + .map(|value| { + T::try_from(std::borrow::Cow::Owned(value.to_owned())).change_context( + errors::ApiErrorResponse::InvalidRequestData { + message: format!("`{}` header is invalid", key), + }, + ) + }) + .transpose() + } } /// Get the merchant-id from `x-merchant-id` header @@ -2956,3 +2989,68 @@ where Ok((auth, auth_type)) } } + +async fn get_connected_merchant_account( + state: &A, + connected_merchant_id: id_type::MerchantId, + platform_org_id: id_type::OrganizationId, +) -> RouterResult +where + A: SessionStateInfo + Sync, +{ + let key_manager_state = &(&state.session_state()).into(); + let key_store = state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &connected_merchant_id, + &state.store().get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant key store for the merchant id")?; + + let connected_merchant_account = state + .store() + .find_merchant_account_by_merchant_id(key_manager_state, &connected_merchant_id, &key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant account for the merchant id")?; + + if platform_org_id != connected_merchant_account.organization_id { + return Err(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Access for merchant id Unauthorized"); + } + + Ok(connected_merchant_account) +} + +async fn get_platform_merchant_account( + state: &A, + request_headers: &HeaderMap, + merchant_account: domain::MerchantAccount, +) -> RouterResult<(domain::MerchantAccount, Option)> +where + A: SessionStateInfo + Sync, +{ + let connected_merchant_account = HeaderMapStruct::new(request_headers) + .get_id_type_from_header_if_present::( + headers::X_CONNECTED_MERCHANT_ID, + )? + .filter(|_| merchant_account.is_platform_account) + .async_map(|merchant_id| { + get_connected_merchant_account( + state, + merchant_id, + merchant_account.organization_id.clone(), + ) + }) + .await + .transpose()?; + + if let Some(connected_merchant_account) = connected_merchant_account { + Ok((connected_merchant_account, Some(merchant_account))) + } else { + Ok((merchant_account, None)) + } +} From 38fe0be14d80ffead7d6a73ec9d19116562da72f Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 21 Nov 2024 09:07:50 +0530 Subject: [PATCH 03/24] added non connected flows error --- .../src/errors/api_error_response.rs | 5 +++ .../router/src/compatibility/stripe/errors.rs | 4 +++ crates/router/src/services/authentication.rs | 36 +++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index b02da3960756..23fd6561d862 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -293,6 +293,8 @@ pub enum ApiErrorResponse { field_names: String, connector_transaction_id: Option, }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_42", message = "This API does not support platfrom account")] + PlatfromAccountAuthNotSupported, } #[derive(Clone)] @@ -659,6 +661,9 @@ impl ErrorSwitch for ApiErrorRespon ..Default::default() }) )), + Self::PlatfromAccountAuthNotSupported => { + AER::BadRequest(ApiError::new("IR", 42, "API does not support platform merchant account", None)) + } } } } diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index efe0ac157e01..50efe1b67f42 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -278,6 +278,8 @@ pub enum StripeErrorCode { InvalidTenant, #[error(error_type = StripeErrorType::HyperswitchError, code = "HE_01", message = "Failed to convert amount to {amount_type} type")] AmountConversionFailed { amount_type: &'static str }, + #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Platform Bad Request")] + PlatformBadRequest, // [#216]: https://github.com/juspay/hyperswitch/issues/216 // Implement the remaining stripe error codes @@ -677,6 +679,7 @@ impl From for StripeErrorCode { errors::ApiErrorResponse::AmountConversionFailed { amount_type } => { Self::AmountConversionFailed { amount_type } } + errors::ApiErrorResponse::PlatfromAccountAuthNotSupported => Self::PlatformBadRequest, } } } @@ -750,6 +753,7 @@ impl actix_web::ResponseError for StripeErrorCode { | Self::CurrencyConversionFailed | Self::PaymentMethodDeleteFailed | Self::ExtendedCardInfoNotFound + | Self::PlatformBadRequest | Self::LinkConfigurationError { .. } => StatusCode::BAD_REQUEST, Self::RefundFailed | Self::PayoutFailed diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index f859dfd0c1bb..3ae0ddf7bb18 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -57,7 +57,7 @@ mod detached; #[derive(Clone, Debug)] pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, - // pub platform_merchant_account: Option, + pub platform_merchant_account: Option, pub key_store: domain::MerchantKeyStore, pub profile_id: Option, } @@ -485,6 +485,8 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + let api_key = get_api_key(request_headers) .change_context(errors::ApiErrorResponse::Unauthorized)? .trim(); @@ -547,6 +549,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: None, }; @@ -736,6 +739,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: None, }; @@ -942,6 +946,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: None, }; @@ -1145,6 +1150,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: None, }; @@ -1252,9 +1258,11 @@ where { async fn authenticate_and_fetch( &self, - _request_headers: &HeaderMap, + request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() @@ -1274,6 +1282,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: None, }; @@ -1417,6 +1426,8 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + let publishable_key = get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; let key_manager_state = &(&state.session_state()).into(); @@ -1430,6 +1441,7 @@ where ( AuthenticationData { merchant_account, + platform_merchant_account: None, key_store, profile_id: None, }, @@ -1766,6 +1778,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -1948,6 +1961,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -2099,6 +2113,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -2171,6 +2186,7 @@ where // if both of them are same then proceed with the profile id present in the request let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: Some(self.profile_id.clone()), }; @@ -2186,6 +2202,7 @@ where // if profile_id is not present in the auth_layer itself then no change in behaviour let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -2336,6 +2353,7 @@ where let merchant_id = merchant.get_id().clone(); let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -2472,6 +2490,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -2590,6 +2609,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account: None, key_store, profile_id: payload.profile_id, }; @@ -3054,3 +3074,15 @@ where Ok((merchant_account, None)) } } + +fn throw_error_if_platform_merchant_authentication_required( + request_headers: &HeaderMap, +) -> RouterResult<()> { + HeaderMapStruct::new(request_headers) + .get_id_type_from_header_if_present::( + headers::X_CONNECTED_MERCHANT_ID, + )? + .map_or(Ok(()), |_| { + Err(errors::ApiErrorResponse::PlatfromAccountAuthNotSupported.into()) + }) +} From 0f3fb99be0b0e2dd538c86ef5c788bd89b18e0bf Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 25 Nov 2024 15:36:08 +0530 Subject: [PATCH 04/24] added platform auth in apikeyauth --- crates/router/src/services/authentication.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 37d17ba4ad45..c109a1747970 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -545,12 +545,12 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - // let (merchant, platform_merchant_account) = - // get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = + get_platform_merchant_account(state, request_headers, merchant).await?; let auth = AuthenticationData { merchant_account: merchant, - platform_merchant_account: None, + platform_merchant_account, key_store, profile_id: None, }; @@ -2459,7 +2459,6 @@ where if payload.profile_id != self.profile_id { return Err(report!(errors::ApiErrorResponse::InvalidJwtToken)); - } else { // if both of them are same then proceed with the profile id present in the request let auth = AuthenticationData { From 5510f7032c2ee5368bdce7758b0018217b8b5c7b Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Tue, 26 Nov 2024 23:38:51 +0530 Subject: [PATCH 05/24] added platform merchant id to payment --- crates/diesel_models/src/payment_intent.rs | 2 ++ crates/diesel_models/src/schema.rs | 2 ++ .../hyperswitch_domain_models/src/payments.rs | 1 + .../src/payments/payment_intent.rs | 3 +++ .../compatibility/stripe/payment_intents.rs | 7 +++++ .../src/compatibility/stripe/setup_intents.rs | 4 +++ crates/router/src/core/fraud_check.rs | 2 ++ .../router/src/core/fraud_check/operation.rs | 1 + .../fraud_check/operation/fraud_check_post.rs | 3 +++ crates/router/src/core/payments.rs | 21 ++++++++++++++- crates/router/src/core/payments/operations.rs | 1 + .../payments/operations/payment_approve.rs | 1 + .../payments/operations/payment_cancel.rs | 1 + .../payments/operations/payment_capture.rs | 1 + .../operations/payment_complete_authorize.rs | 1 + .../payments/operations/payment_confirm.rs | 1 + .../payments/operations/payment_create.rs | 5 ++++ .../operations/payment_post_session_tokens.rs | 1 + .../payments/operations/payment_reject.rs | 1 + .../payments/operations/payment_session.rs | 1 + .../core/payments/operations/payment_start.rs | 1 + .../payments/operations/payment_status.rs | 1 + .../payments/operations/payment_update.rs | 1 + .../payments_incremental_authorization.rs | 1 + .../payments/operations/tax_calculation.rs | 1 + crates/router/src/core/webhooks/incoming.rs | 5 ++++ crates/router/src/routes/payments.rs | 26 ++++++++++++++++--- crates/router/src/utils/user/sample_data.rs | 1 + .../src/workflows/outgoing_webhook_retry.rs | 1 + crates/router/src/workflows/payment_sync.rs | 1 + .../down.sql | 2 ++ .../up.sql | 1 + 32 files changed, 98 insertions(+), 4 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 26cb0b8c8a84..accdd2038cd9 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -136,6 +136,7 @@ pub struct PaymentIntent { pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub platform_merchant_id: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq)] @@ -352,6 +353,7 @@ pub struct PaymentIntentNew { pub organization_id: common_utils::id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub platform_merchant_id: Option, } #[cfg(feature = "v2")] diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index e08b86a04e7f..4ae0ab23646b 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -933,6 +933,8 @@ diesel::table! { organization_id -> Varchar, tax_details -> Nullable, skip_external_tax_calculation -> Nullable, + #[max_length = 64] + platform_merchant_id -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 1bab8ae3b76c..c69c0d831548 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -99,6 +99,7 @@ pub struct PaymentIntent { pub organization_id: id_type::OrganizationId, pub tax_details: Option, pub skip_external_tax_calculation: Option, + pub platform_merchant_id: Option, } impl PaymentIntent { diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 4f2053ec6f99..52565a0ccc05 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1551,6 +1551,7 @@ impl behaviour::Conversion for PaymentIntent { shipping_cost: self.shipping_cost, tax_details: self.tax_details, skip_external_tax_calculation: self.skip_external_tax_calculation, + platform_merchant_id: self.platform_merchant_id, }) } @@ -1638,6 +1639,7 @@ impl behaviour::Conversion for PaymentIntent { is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow, organization_id: storage_model.organization_id, skip_external_tax_calculation: storage_model.skip_external_tax_calculation, + platform_merchant_id: storage_model.platform_merchant_id, }) } .await @@ -1700,6 +1702,7 @@ impl behaviour::Conversion for PaymentIntent { shipping_cost: self.shipping_cost, tax_details: self.tax_details, skip_external_tax_calculation: self.skip_external_tax_calculation, + platform_merchant_id: self.platform_merchant_id, }) } } diff --git a/crates/router/src/compatibility/stripe/payment_intents.rs b/crates/router/src/compatibility/stripe/payment_intents.rs index 5bbb4e7cf22a..6652f42ee00e 100644 --- a/crates/router/src/compatibility/stripe/payment_intents.rs +++ b/crates/router/src/compatibility/stripe/payment_intents.rs @@ -92,6 +92,7 @@ pub async fn payment_intents_create( payments::CallConnectorAction::Trigger, eligible_connectors, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -162,6 +163,7 @@ pub async fn payment_intents_retrieve( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -240,6 +242,7 @@ pub async fn payment_intents_retrieve_with_gateway_creds( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -316,6 +319,7 @@ pub async fn payment_intents_update( payments::CallConnectorAction::Trigger, eligible_connectors, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -401,6 +405,7 @@ pub async fn payment_intents_confirm( payments::CallConnectorAction::Trigger, eligible_connectors, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -472,6 +477,7 @@ pub async fn payment_intents_capture( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -547,6 +553,7 @@ pub async fn payment_intents_cancel( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, diff --git a/crates/router/src/compatibility/stripe/setup_intents.rs b/crates/router/src/compatibility/stripe/setup_intents.rs index 919ced993aad..6dde49b0d620 100644 --- a/crates/router/src/compatibility/stripe/setup_intents.rs +++ b/crates/router/src/compatibility/stripe/setup_intents.rs @@ -78,6 +78,7 @@ pub async fn setup_intents_create( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -148,6 +149,7 @@ pub async fn setup_intents_retrieve( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -224,6 +226,7 @@ pub async fn setup_intents_update( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -301,6 +304,7 @@ pub async fn setup_intents_confirm( payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 70c35b460e86..99a60817303a 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -588,6 +588,7 @@ pub async fn post_payment_frm_core<'a, F, D>( customer: &Option, key_store: domain::MerchantKeyStore, should_continue_capture: &mut bool, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult> where F: Send + Clone, @@ -647,6 +648,7 @@ where payment_data, customer, should_continue_capture, + platform_merchant_account, ) .await?; logger::debug!("frm_post_tasks_data: {:?}", frm_data); diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs index d802339b675a..721afaa1e499 100644 --- a/crates/router/src/core/fraud_check/operation.rs +++ b/crates/router/src/core/fraud_check/operation.rs @@ -85,6 +85,7 @@ pub trait Domain: Send + Sync { _payment_data: &mut D, _customer: &Option, _should_continue_capture: &mut bool, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult> where F: Send + Clone, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index deb4a3621ff3..ef769e5dbefd 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -244,6 +244,7 @@ where payment_data: &mut D, customer: &Option, _should_continue_capture: &mut bool, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult> { if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud) && matches!( @@ -277,6 +278,7 @@ where payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + platform_merchant_account.map(|account| account.clone()), )) .await?; logger::debug!("payment_id : {:?} has been cancelled since it has been found fraudulent by configured frm connector",payment_data.get_payment_attempt().payment_id); @@ -334,6 +336,7 @@ where payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + platform_merchant_account.map(|account| account.clone()), )) .await?; logger::debug!("payment_id : {:?} has been captured since it has been found legit by configured frm connector",payment_data.get_payment_attempt().payment_id); diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5ac8f454c21f..87d94d1a9fe9 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -237,6 +237,7 @@ pub async fn payments_operation_core( auth_flow: services::AuthFlow, eligible_connectors: Option>, header_payload: HeaderPayload, + platform_merchant_account: Option, ) -> RouterResult<(D, Req, Option, Option, Option)> where F: Send + Clone + Sync, @@ -281,6 +282,7 @@ where &key_store, auth_flow, &header_payload, + platform_merchant_account.as_ref(), ) .await?; core_utils::validate_profile_id_from_auth_layer( @@ -696,6 +698,7 @@ where &customer, key_store.clone(), &mut should_continue_capture, + platform_merchant_account.as_ref(), )) .await?; } @@ -803,8 +806,8 @@ pub async fn proxy_for_payments_operation_core( req: Req, call_connector_action: CallConnectorAction, auth_flow: services::AuthFlow, - header_payload: HeaderPayload, + platform_merchant_account: Option, ) -> RouterResult<(D, Req, Option, Option, Option)> where F: Send + Clone + Sync, @@ -849,6 +852,7 @@ where &key_store, auth_flow, &header_payload, + platform_merchant_account.as_ref(), ) .await?; @@ -1304,6 +1308,7 @@ pub async fn payments_core( call_connector_action: CallConnectorAction, eligible_connectors: Option>, header_payload: HeaderPayload, + platform_merchant_account: Option, ) -> RouterResponse where F: Send + Clone + Sync, @@ -1342,6 +1347,7 @@ where auth_flow, eligible_routable_connectors, header_payload.clone(), + platform_merchant_account, ) .await?; @@ -1371,6 +1377,7 @@ pub async fn proxy_for_payments_core( auth_flow: services::AuthFlow, call_connector_action: CallConnectorAction, header_payload: HeaderPayload, + platform_merchant_account: Option, ) -> RouterResponse where F: Send + Clone + Sync, @@ -1402,6 +1409,7 @@ where call_connector_action, auth_flow, header_payload.clone(), + platform_merchant_account, ) .await?; @@ -1586,6 +1594,7 @@ pub trait PaymentRedirectFlow: Sync { connector_action: CallConnectorAction, connector: String, payment_id: id_type::PaymentId, + platform_merchant_account: Option, ) -> RouterResult; #[cfg(feature = "v2")] @@ -1623,6 +1632,7 @@ pub trait PaymentRedirectFlow: Sync { merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: PaymentsRedirectResponseData, + platform_merchant_account: Option, ) -> RouterResponse { metrics::REDIRECTION_TRIGGERED.add( &metrics::CONTEXT, @@ -1681,6 +1691,7 @@ pub trait PaymentRedirectFlow: Sync { flow_type, connector.clone(), resource_id.clone(), + platform_merchant_account, ) .await?; @@ -1714,6 +1725,7 @@ pub trait PaymentRedirectFlow: Sync { key_store, profile, request, + platform_merchant_account, ) .await?; @@ -1740,6 +1752,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { connector_action: CallConnectorAction, _connector: String, _payment_id: id_type::PaymentId, + platform_merchant_account: Option, ) -> RouterResult { let key_manager_state = &state.into(); @@ -1774,6 +1787,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { connector_action, None, HeaderPayload::default(), + platform_merchant_account, )) .await?; let payments_response = match response { @@ -1879,6 +1893,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { connector_action: CallConnectorAction, _connector: String, _payment_id: id_type::PaymentId, + platform_merchant_account: Option, ) -> RouterResult { let key_manager_state = &state.into(); @@ -1911,6 +1926,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { connector_action, None, HeaderPayload::default(), + platform_merchant_account, ), ) .await?; @@ -2137,6 +2153,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { connector_action: CallConnectorAction, connector: String, payment_id: id_type::PaymentId, + platform_merchant_account: Option, ) -> RouterResult { let merchant_id = merchant_account.get_id().clone(); let key_manager_state = &state.into(); @@ -2235,6 +2252,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { connector_action, None, HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator), + platform_merchant_account, )) .await? } else { @@ -2267,6 +2285,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { connector_action, None, HeaderPayload::default(), + platform_merchant_account, ), ) .await? diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index e936f3725455..a4d0b908b567 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -180,6 +180,7 @@ pub trait GetTracker: Send { mechant_key_store: &domain::MerchantKeyStore, auth_flow: services::AuthFlow, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>; #[cfg(feature = "v2")] diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 2bc18122b227..df007a09dc15 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -45,6 +45,7 @@ impl GetTracker, api::PaymentsCaptureRequest> key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse<'a, F, api::PaymentsCaptureRequest, PaymentData>, > { diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 8d7361cd257e..6323485c1fdd 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -44,6 +44,7 @@ impl GetTracker, api::PaymentsCancelRequest> key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse<'a, F, api::PaymentsCancelRequest, PaymentData>, > { diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 2451cbcd8e9d..cd73ac40717f 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -45,6 +45,7 @@ impl GetTracker, api::PaymentsCaptu key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse< 'a, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 850fa738cd84..64e6444a91a2 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -45,6 +45,7 @@ impl GetTracker, api::PaymentsRequest> for Co key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 8c82c38be714..812cb5ee537d 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -70,6 +70,7 @@ impl GetTracker, api::PaymentsRequest> for Pa key_store: &domain::MerchantKeyStore, auth_flow: services::AuthFlow, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let key_manager_state = &state.into(); diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 1f0884fe3849..a7a78d8ddd4b 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -78,6 +78,7 @@ impl GetTracker, api::PaymentsRequest> for Pa merchant_key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; @@ -304,6 +305,7 @@ impl GetTracker, api::PaymentsRequest> for Pa attempt_id, profile_id.clone(), session_expiry, + platform_merchant_account, ) .await?; @@ -1293,6 +1295,7 @@ impl PaymentCreate { active_attempt_id: String, profile_id: common_utils::id_type::ProfileId, session_expiry: PrimitiveDateTime, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult { let created_at @ modified_at @ last_synced = common_utils::date_time::now(); @@ -1479,6 +1482,8 @@ impl PaymentCreate { shipping_cost: request.shipping_cost, tax_details: None, skip_external_tax_calculation, + platform_merchant_id: platform_merchant_account + .map(|platform_merchant_account| platform_merchant_account.get_id().to_owned()), }) } diff --git a/crates/router/src/core/payments/operations/payment_post_session_tokens.rs b/crates/router/src/core/payments/operations/payment_post_session_tokens.rs index 846f739e0823..653e4af0e746 100644 --- a/crates/router/src/core/payments/operations/payment_post_session_tokens.rs +++ b/crates/router/src/core/payments/operations/payment_post_session_tokens.rs @@ -45,6 +45,7 @@ impl GetTracker, api::PaymentsPostSessionToke key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse< 'a, diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 23531d2342dc..ab1ec0b113d2 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -41,6 +41,7 @@ impl GetTracker, PaymentsCancelRequest> for P key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 1bc11854ad03..007048c84cf0 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -44,6 +44,7 @@ impl GetTracker, api::PaymentsSessionRequest> key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse<'a, F, api::PaymentsSessionRequest, PaymentData>, > { diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index cad5dcfbd676..a00a4959a472 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -41,6 +41,7 @@ impl GetTracker, api::PaymentsStartRequest> f key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse<'a, F, api::PaymentsStartRequest, PaymentData>, > { diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index a3a826ccd0e9..af5c3a62e02b 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -198,6 +198,7 @@ impl GetTracker, api::PaymentsRetrieveRequest key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse<'a, F, api::PaymentsRetrieveRequest, PaymentData>, > { diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 2f1a0c333fa6..440288daa147 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -56,6 +56,7 @@ impl GetTracker, api::PaymentsRequest> for Pa key_store: &domain::MerchantKeyStore, auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let (mut payment_intent, mut payment_attempt, currency): (_, _, storage_enums::Currency); diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 1ddaf0b6abf9..e36c2e7aba23 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -48,6 +48,7 @@ impl key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse< 'a, diff --git a/crates/router/src/core/payments/operations/tax_calculation.rs b/crates/router/src/core/payments/operations/tax_calculation.rs index 2d4054a60bed..00f84e1d61da 100644 --- a/crates/router/src/core/payments/operations/tax_calculation.rs +++ b/crates/router/src/core/payments/operations/tax_calculation.rs @@ -49,6 +49,7 @@ impl GetTracker, api::PaymentsDynamicTaxCalcu key_store: &domain::MerchantKeyStore, _auth_flow: services::AuthFlow, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult< operations::GetTrackerResponse< 'a, diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 3532f1e3fd72..cb00c561aac4 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -568,6 +568,7 @@ async fn payments_incoming_webhook_flow( consume_or_trigger_flow.clone(), None, HeaderPayload::default(), + None, //Platform merchant account )) .await; // When mandate details are present in successful webhooks, and consuming webhooks are skipped during payment sync if the payment status is already updated to charged, this function is used to update the connector mandate details. @@ -1108,6 +1109,7 @@ async fn external_authentication_incoming_webhook_flow( payments::CallConnectorAction::Trigger, None, HeaderPayload::with_source(enums::PaymentSource::ExternalAuthenticator), + None, // Platform merchant account )) .await?; match payments_response { @@ -1305,6 +1307,7 @@ async fn frm_incoming_webhook_flow( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + None, // Platform merchant account )) .await? } @@ -1334,6 +1337,7 @@ async fn frm_incoming_webhook_flow( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + None, // Platform merchant account )) .await? } @@ -1492,6 +1496,7 @@ async fn bank_transfer_webhook_flow( payments::CallConnectorAction::Trigger, None, HeaderPayload::with_source(common_enums::PaymentSource::Webhook), + None, //Platform merchant account )) .await } else { diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 48b15c3b2047..dd8c755d5828 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -85,6 +85,7 @@ pub async fn payments_create( header_payload.clone(), req, api::AuthFlow::Merchant, + auth.platform_merchant_account, ) }, match env::which() { @@ -254,6 +255,7 @@ pub async fn payments_start( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + None, ) }, &auth::MerchantIdAuth(merchant_id), @@ -328,6 +330,7 @@ pub async fn payments_retrieve( payments::CallConnectorAction::Trigger, None, header_payload.clone(), + auth.platform_merchant_account, ) }, auth::auth_type( @@ -398,6 +401,7 @@ pub async fn payments_retrieve_with_gateway_creds( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, &*auth_type, @@ -450,6 +454,7 @@ pub async fn payments_update( HeaderPayload::default(), req, auth_flow, + auth.platform_merchant_account, ) }, &*auth_type, @@ -508,6 +513,7 @@ pub async fn payments_post_session_tokens( payments::CallConnectorAction::Trigger, None, header_payload.clone(), + None, ) }, &auth::PublishableKeyAuth, @@ -570,6 +576,7 @@ pub async fn payments_confirm( header_payload.clone(), req, auth_flow, + auth.platform_merchant_account, ) }, &*auth_type, @@ -622,6 +629,7 @@ pub async fn payments_capture( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -681,6 +689,7 @@ pub async fn payments_dynamic_tax_calculation( payments::CallConnectorAction::Trigger, None, header_payload.clone(), + None, ) }, &auth::PublishableKeyAuth, @@ -749,6 +758,7 @@ pub async fn payments_connector_session( payments::CallConnectorAction::Trigger, None, header_payload.clone(), + None, ) }, &auth::HeaderAuth(auth::PublishableKeyAuth), @@ -798,7 +808,7 @@ pub async fn payments_redirect_response( auth.merchant_account, auth.key_store, req, - + auth.platform_merchant_account, ) }, &auth::MerchantIdAuth(merchant_id), @@ -848,7 +858,7 @@ pub async fn payments_redirect_response_with_creds_identifier( auth.merchant_account, auth.key_store, req, - + auth.platform_merchant_account, ) }, &auth::MerchantIdAuth(merchant_id), @@ -899,7 +909,7 @@ pub async fn payments_complete_authorize_redirect( auth.merchant_account, auth.key_store, req, - + auth.platform_merchant_account, ) }, &auth::MerchantIdAuth(merchant_id), @@ -965,6 +975,7 @@ pub async fn payments_complete_authorize( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + None, ) }, &*auth_type, @@ -1014,6 +1025,7 @@ pub async fn payments_cancel( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -1294,6 +1306,7 @@ pub async fn payments_approve( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, match env::which() { @@ -1358,6 +1371,7 @@ pub async fn payments_reject( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, match env::which() { @@ -1387,6 +1401,7 @@ async fn authorize_verify_select( header_payload: HeaderPayload, req: api_models::payments::PaymentsRequest, auth_flow: api::AuthFlow, + platform_merchant_account: Option, ) -> errors::RouterResponse where Op: Sync @@ -1436,6 +1451,7 @@ where auth_flow, payments::CallConnectorAction::Trigger, header_payload, + platform_merchant_account, ) .await } else { @@ -1463,6 +1479,7 @@ where payments::CallConnectorAction::Trigger, eligible_connectors, header_payload, + platform_merchant_account, ) .await } @@ -1486,6 +1503,7 @@ where payments::CallConnectorAction::Trigger, eligible_connectors, header_payload, + platform_merchant_account, ) .await } @@ -1534,6 +1552,7 @@ pub async fn payments_incremental_authorization( payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -1619,6 +1638,7 @@ pub async fn post_3ds_payments_authorize( auth.merchant_account, auth.key_store, req, + auth.platform_merchant_account, ) }, &auth::MerchantIdAuth(merchant_id), diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 8ffb0f2001fb..b54bcdf8b1af 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -274,6 +274,7 @@ pub async fn generate_sample_data( shipping_cost: None, tax_details: None, skip_external_tax_calculation: None, + platform_merchant_id: None, }; let (connector_transaction_id, connector_transaction_data) = ConnectorTransactionId::form_id_and_data(attempt_id.clone()); diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index 1bfcc8ebe7bb..c7df4aeff0cd 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -400,6 +400,7 @@ async fn get_outgoing_webhook_content_and_event_type( CallConnectorAction::Avoid, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, //Platform merchant account )) .await? { diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index b4fcf69241b8..05a433790e2d 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -91,6 +91,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { services::AuthFlow::Client, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, //Platform merchant account )) .await?; diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/down.sql b/migrations/2024-11-13-114939_platorm_merchant_account/down.sql index a84cba91cac1..342380e09333 100644 --- a/migrations/2024-11-13-114939_platorm_merchant_account/down.sql +++ b/migrations/2024-11-13-114939_platorm_merchant_account/down.sql @@ -1,2 +1,4 @@ -- This file should undo anything in `up.sql` ALTER TABLE merchant_account DROP COLUMN IF EXISTS is_platform_account; + +ALTER TABLE payment_intent DROP COLUMN IF EXISTS platform_merchant_id; diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/up.sql b/migrations/2024-11-13-114939_platorm_merchant_account/up.sql index 8934a93128e7..cd07e163d7c8 100644 --- a/migrations/2024-11-13-114939_platorm_merchant_account/up.sql +++ b/migrations/2024-11-13-114939_platorm_merchant_account/up.sql @@ -1,3 +1,4 @@ -- Your SQL goes here ALTER TABLE merchant_account ADD COLUMN IF NOT EXISTS is_platform_account BOOL NOT NULL DEFAULT FALSE; +ALTER TABLE payment_intent ADD COLUMN IF NOT EXISTS platform_merchant_id VARCHAR(64); From 5af6299209eca841144c387a6c4cd2ec2c256964 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 28 Nov 2024 18:31:22 +0530 Subject: [PATCH 06/24] happy flow fix --- crates/router/src/services/authentication.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index c109a1747970..310205b2611a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -486,8 +486,6 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - throw_error_if_platform_merchant_authentication_required(request_headers)?; - let api_key = get_api_key(request_headers) .change_context(errors::ApiErrorResponse::Unauthorized)? .trim(); @@ -3297,7 +3295,7 @@ where .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized) .attach_printable("Failed to fetch merchant account for the merchant id")?; - + //TODO CHANGE ALL ERROR TYPES if platform_org_id != connected_merchant_account.organization_id { return Err(errors::ApiErrorResponse::Unauthorized) .attach_printable("Access for merchant id Unauthorized"); @@ -3318,7 +3316,14 @@ where .get_id_type_from_header_if_present::( headers::X_CONNECTED_MERCHANT_ID, )? - .filter(|_| merchant_account.is_platform_account) + .map(|merchant_id| { + merchant_account + .is_platform_account + .then(|| merchant_id) + .ok_or(errors::ApiErrorResponse::Unauthorized) + }) + .transpose() + .attach_printable("Non platform_merchant_account using X_CONNECTED_MERCHANT_ID header")? .async_map(|merchant_id| { get_connected_merchant_account( state, From 587205e851c9c9c419e6663c6c5ea9dfcfbe68e4 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Fri, 29 Nov 2024 14:42:17 +0530 Subject: [PATCH 07/24] added invalid platform error --- .../src/errors/api_error_response.rs | 13 +++++++++---- crates/router/src/compatibility/stripe/errors.rs | 9 ++++++--- crates/router/src/services/authentication.rs | 10 +++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index 23fd6561d862..f28fad7bb81f 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -293,8 +293,10 @@ pub enum ApiErrorResponse { field_names: String, connector_transaction_id: Option, }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_42", message = "This API does not support platfrom account")] - PlatfromAccountAuthNotSupported, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_42", message = "API does not support platform account operation")] + PlatformAccountAuthNotSupported, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_43", message = "Invalid platform account operation")] + InvalidPlatformOperation, } #[derive(Clone)] @@ -661,8 +663,11 @@ impl ErrorSwitch for ApiErrorRespon ..Default::default() }) )), - Self::PlatfromAccountAuthNotSupported => { - AER::BadRequest(ApiError::new("IR", 42, "API does not support platform merchant account", None)) + Self::PlatformAccountAuthNotSupported => { + AER::BadRequest(ApiError::new("IR", 42, "API does not support platform operation", None)) + } + Self::InvalidPlatformOperation => { + AER::Unauthorized(ApiError::new("IR", 43, "Invalid platform account operation", None)) } } } diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index 50efe1b67f42..569da0788c47 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -280,6 +280,8 @@ pub enum StripeErrorCode { AmountConversionFailed { amount_type: &'static str }, #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Platform Bad Request")] PlatformBadRequest, + #[error(error_type = StripeErrorType::HyperswitchError, code = "", message = "Platform Unauthorized Request")] + PlatformUnauthorizedRequest, // [#216]: https://github.com/juspay/hyperswitch/issues/216 // Implement the remaining stripe error codes @@ -678,8 +680,9 @@ impl From for StripeErrorCode { | errors::ApiErrorResponse::MissingTenantId => Self::InvalidTenant, errors::ApiErrorResponse::AmountConversionFailed { amount_type } => { Self::AmountConversionFailed { amount_type } - } - errors::ApiErrorResponse::PlatfromAccountAuthNotSupported => Self::PlatformBadRequest, + }, + errors::ApiErrorResponse::PlatformAccountAuthNotSupported => Self::PlatformBadRequest, + errors::ApiErrorResponse::InvalidPlatformOperation => Self::PlatformUnauthorizedRequest, } } } @@ -689,7 +692,7 @@ impl actix_web::ResponseError for StripeErrorCode { use reqwest::StatusCode; match self { - Self::Unauthorized => StatusCode::UNAUTHORIZED, + Self::Unauthorized | Self::PlatformUnauthorizedRequest => StatusCode::UNAUTHORIZED, Self::InvalidRequestUrl | Self::GenericNotFoundError { .. } => StatusCode::NOT_FOUND, Self::ParameterUnknown { .. } | Self::HyperswitchUnprocessableEntity { .. } => { StatusCode::UNPROCESSABLE_ENTITY diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 1d9b2f06f477..3e225b9d61f2 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -3337,18 +3337,18 @@ where &state.store().get_master_key().to_vec().into(), ) .await - .to_not_found_response(errors::ApiErrorResponse::Unauthorized) + .to_not_found_response(errors::ApiErrorResponse::InvalidPlatformOperation) .attach_printable("Failed to fetch merchant key store for the merchant id")?; let connected_merchant_account = state .store() .find_merchant_account_by_merchant_id(key_manager_state, &connected_merchant_id, &key_store) .await - .to_not_found_response(errors::ApiErrorResponse::Unauthorized) + .to_not_found_response(errors::ApiErrorResponse::InvalidPlatformOperation) .attach_printable("Failed to fetch merchant account for the merchant id")?; //TODO CHANGE ALL ERROR TYPES if platform_org_id != connected_merchant_account.organization_id { - return Err(errors::ApiErrorResponse::Unauthorized) + return Err(errors::ApiErrorResponse::InvalidPlatformOperation) .attach_printable("Access for merchant id Unauthorized"); } @@ -3371,7 +3371,7 @@ where merchant_account .is_platform_account .then(|| merchant_id) - .ok_or(errors::ApiErrorResponse::Unauthorized) + .ok_or(errors::ApiErrorResponse::InvalidPlatformOperation) }) .transpose() .attach_printable("Non platform_merchant_account using X_CONNECTED_MERCHANT_ID header")? @@ -3400,6 +3400,6 @@ fn throw_error_if_platform_merchant_authentication_required( headers::X_CONNECTED_MERCHANT_ID, )? .map_or(Ok(()), |_| { - Err(errors::ApiErrorResponse::PlatfromAccountAuthNotSupported.into()) + Err(errors::ApiErrorResponse::PlatformAccountAuthNotSupported.into()) }) } From 9915ca046451977a9625f585aa4a7988615ef815 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 2 Dec 2024 16:50:24 +0530 Subject: [PATCH 08/24] platform checks in header auth --- crates/router/src/compatibility/stripe/errors.rs | 2 +- crates/router/src/services/authentication.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index 569da0788c47..53b15837538c 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -680,7 +680,7 @@ impl From for StripeErrorCode { | errors::ApiErrorResponse::MissingTenantId => Self::InvalidTenant, errors::ApiErrorResponse::AmountConversionFailed { amount_type } => { Self::AmountConversionFailed { amount_type } - }, + } errors::ApiErrorResponse::PlatformAccountAuthNotSupported => Self::PlatformBadRequest, errors::ApiErrorResponse::InvalidPlatformOperation => Self::PlatformUnauthorizedRequest, } diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 3e225b9d61f2..143636654489 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -623,7 +623,7 @@ where merchant_id: Some(merchant_id), key_id: Some(key_id), } => { - let auth = construct_authentication_data(state, &merchant_id).await?; + let auth = construct_authentication_data(state, &merchant_id, request_headers).await?; Ok(( auth.clone(), AuthenticationType::ApiKey { @@ -637,7 +637,7 @@ where merchant_id: Some(merchant_id), key_id: None, } => { - let auth = construct_authentication_data(state, &merchant_id).await?; + let auth = construct_authentication_data(state, &merchant_id, request_headers).await?; Ok(( auth.clone(), AuthenticationType::PublishableKey { @@ -711,9 +711,10 @@ where async fn construct_authentication_data( state: &A, merchant_id: &id_type::MerchantId, + request_headers: &HeaderMap, ) -> RouterResult where - A: SessionStateInfo, + A: SessionStateInfo + Sync, { let key_store = state .store() @@ -736,9 +737,13 @@ where .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account + let (merchant, platform_merchant_account) = + get_platform_merchant_account(state, request_headers, merchant).await?; + let auth = AuthenticationData { merchant_account: merchant, - platform_merchant_account: None, + platform_merchant_account, key_store, profile_id: None, }; From bbc907ce6cea631b507ddb95e6387f556560556d Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 9 Dec 2024 14:47:13 +0530 Subject: [PATCH 09/24] v2 compiles --- crates/diesel_models/src/merchant_account.rs | 7 +++ crates/diesel_models/src/payment_intent.rs | 2 + crates/diesel_models/src/schema_v2.rs | 3 ++ .../src/merchant_account.rs | 24 ++++++++++ .../hyperswitch_domain_models/src/payments.rs | 4 ++ .../src/payments/payment_intent.rs | 4 ++ crates/router/src/core/admin.rs | 1 + .../fraud_check/operation/fraud_check_post.rs | 1 + crates/router/src/core/payments.rs | 13 +++++- crates/router/src/core/payments/operations.rs | 1 + .../operations/payment_confirm_intent.rs | 1 + .../operations/payment_create_intent.rs | 2 + .../core/payments/operations/payment_get.rs | 3 +- .../payments/operations/payment_get_intent.rs | 1 + .../operations/payment_session_intent.rs | 1 + .../src/core/payments/session_operation.rs | 4 ++ crates/router/src/routes/payments.rs | 4 ++ crates/router/src/services/authentication.rs | 44 ++++++++++++++++++- .../down.sql | 0 .../up.sql | 0 20 files changed, 116 insertions(+), 4 deletions(-) rename migrations/{2024-11-13-114939_platorm_merchant_account => 2024-12-03-072318_platform_merchant_account}/down.sql (100%) rename migrations/{2024-11-13-114939_platorm_merchant_account => 2024-12-03-072318_platform_merchant_account}/up.sql (100%) diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index ce5624a2c28c..fc42c940ef51 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -151,6 +151,7 @@ pub struct MerchantAccount { pub recon_status: storage_enums::ReconStatus, pub version: common_enums::ApiVersion, pub id: common_utils::id_type::MerchantId, + pub is_platform_account: bool, } #[cfg(feature = "v2")] @@ -168,6 +169,7 @@ impl From for MerchantAccount { organization_id: item.organization_id, recon_status: item.recon_status, version: item.version, + is_platform_account: item.is_platform_account, } } } @@ -185,6 +187,7 @@ pub struct MerchantAccountSetter { pub organization_id: common_utils::id_type::OrganizationId, pub recon_status: storage_enums::ReconStatus, pub version: common_enums::ApiVersion, + pub is_platform_account: bool, } impl MerchantAccount { @@ -248,6 +251,7 @@ pub struct MerchantAccountNew { pub recon_status: storage_enums::ReconStatus, pub id: common_utils::id_type::MerchantId, pub version: common_enums::ApiVersion, + pub is_platform_account: bool, } #[cfg(feature = "v2")] @@ -262,6 +266,7 @@ pub struct MerchantAccountUpdateInternal { pub modified_at: time::PrimitiveDateTime, pub organization_id: Option, pub recon_status: Option, + pub is_platform_account: Option, } #[cfg(feature = "v2")] @@ -276,6 +281,7 @@ impl MerchantAccountUpdateInternal { modified_at, organization_id, recon_status, + is_platform_account } = self; MerchantAccount { @@ -290,6 +296,7 @@ impl MerchantAccountUpdateInternal { recon_status: recon_status.unwrap_or(source.recon_status), version: source.version, id: source.id, + is_platform_account: is_platform_account.unwrap_or(source.is_platform_account) } } } diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 712ec4f9b0f8..ecfffa438a63 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -73,6 +73,7 @@ pub struct PaymentIntent { pub payment_link_config: Option, pub id: common_utils::id_type::GlobalPaymentId, pub psd2_sca_exemption_type: Option, + pub platform_merchant_id: Option } #[cfg(feature = "v1")] @@ -292,6 +293,7 @@ pub struct PaymentIntentNew { pub enable_payment_link: Option, pub apply_mit_exemption: Option, pub id: common_utils::id_type::GlobalPaymentId, + pub platform_merchant_id: Option } #[cfg(feature = "v1")] diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index f6bab9071cd0..6710cc436ce2 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -672,6 +672,7 @@ diesel::table! { version -> ApiVersion, #[max_length = 64] id -> Varchar, + is_platform_account -> Bool, } } @@ -896,6 +897,8 @@ diesel::table! { #[max_length = 64] id -> Varchar, psd2_sca_exemption_type -> Nullable, + #[max_length = 64] + platform_merchant_id -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 61227c546c69..aa1ad6a81605 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -136,6 +136,7 @@ pub struct MerchantAccountSetter { pub modified_at: time::PrimitiveDateTime, pub organization_id: common_utils::id_type::OrganizationId, pub recon_status: diesel_models::enums::ReconStatus, + pub is_platform_account: bool } #[cfg(feature = "v2")] @@ -152,6 +153,7 @@ impl From for MerchantAccount { modified_at, organization_id, recon_status, + is_platform_account } = item; Self { id, @@ -164,6 +166,7 @@ impl From for MerchantAccount { modified_at, organization_id, recon_status, + is_platform_account } } } @@ -181,6 +184,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub organization_id: common_utils::id_type::OrganizationId, pub recon_status: diesel_models::enums::ReconStatus, + pub is_platform_account: bool } impl MerchantAccount { @@ -256,6 +260,7 @@ pub enum MerchantAccountUpdate { recon_status: diesel_models::enums::ReconStatus, }, ModifiedAtUpdate, + ToPlatformAccount } #[cfg(feature = "v1")] @@ -426,6 +431,7 @@ impl From for MerchantAccountUpdateInternal { pm_collect_link_config: None, is_platform_account: None, }, + // TODO: Change it to toggle MerchantAccountUpdate::ToPlatformAccount => Self { modified_at: now, merchant_name: None, @@ -478,6 +484,7 @@ impl From for MerchantAccountUpdateInternal { storage_scheme: None, organization_id: None, recon_status: None, + is_platform_account: None }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), @@ -488,6 +495,7 @@ impl From for MerchantAccountUpdateInternal { metadata: None, organization_id: None, recon_status: None, + is_platform_account: None }, MerchantAccountUpdate::ReconUpdate { recon_status } => Self { recon_status: Some(recon_status), @@ -498,6 +506,7 @@ impl From for MerchantAccountUpdateInternal { storage_scheme: None, metadata: None, organization_id: None, + is_platform_account: None }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -508,7 +517,19 @@ impl From for MerchantAccountUpdateInternal { metadata: None, organization_id: None, recon_status: None, + is_platform_account: None }, + MerchantAccountUpdate::ToPlatformAccount => Self { + modified_at: now, + merchant_name: None, + merchant_details: None, + publishable_key: None, + storage_scheme: None, + metadata: None, + organization_id: None, + recon_status: None, + is_platform_account: Some(true) + } } } } @@ -533,6 +554,7 @@ impl super::behaviour::Conversion for MerchantAccount { organization_id: self.organization_id, recon_status: self.recon_status, version: crate::consts::API_VERSION, + is_platform_account: self.is_platform_account }; Ok(diesel_models::MerchantAccount::from(setter)) @@ -592,6 +614,7 @@ impl super::behaviour::Conversion for MerchantAccount { modified_at: item.modified_at, organization_id: item.organization_id, recon_status: item.recon_status, + is_platform_account: item.is_platform_account }) } .await @@ -613,6 +636,7 @@ impl super::behaviour::Conversion for MerchantAccount { organization_id: self.organization_id, recon_status: self.recon_status, version: crate::consts::API_VERSION, + is_platform_account: self.is_platform_account }) } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 9a8ce29afb93..f7451594fc03 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -376,6 +376,8 @@ pub struct PaymentIntent { pub payment_link_config: Option, /// The straight through routing algorithm id that is used for this payment. This overrides the default routing algorithm that is configured in business profile. pub routing_algorithm_id: Option, + /// Identifier for the platform merchant. + pub platform_merchant_id: Option } #[cfg(feature = "v2")] @@ -422,6 +424,7 @@ impl PaymentIntent { profile: &business_profile::Profile, request: api_models::payments::PaymentsCreateIntentRequest, decrypted_payment_intent: DecryptedPaymentIntent, + platform_merchant_id: Option<&merchant_account::MerchantAccount> ) -> CustomResult { let connector_metadata = request .get_connector_metadata_as_value() @@ -515,6 +518,7 @@ impl PaymentIntent { .payment_link_config .map(ApiModelToDieselModelConvertor::convert_from), routing_algorithm_id: request.routing_algorithm_id, + platform_merchant_id: platform_merchant_id.map(|merchant_account| merchant_account.get_id().to_owned()) }) } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 8c2dc376becf..a9f5df10b460 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1219,6 +1219,7 @@ impl behaviour::Conversion for PaymentIntent { customer_present, routing_algorithm_id, payment_link_config, + platform_merchant_id } = self; Ok(DieselPaymentIntent { skip_external_tax_calculation: Some(amount_details.get_external_tax_action_as_bool()), @@ -1289,6 +1290,7 @@ impl behaviour::Conversion for PaymentIntent { payment_link_config, routing_algorithm_id, psd2_sca_exemption_type: None, + platform_merchant_id }) } async fn convert_back( @@ -1421,6 +1423,7 @@ impl behaviour::Conversion for PaymentIntent { ), payment_link_config: storage_model.payment_link_config, routing_algorithm_id: storage_model.routing_algorithm_id, + platform_merchant_id: storage_model.platform_merchant_id }) } .await @@ -1493,6 +1496,7 @@ impl behaviour::Conversion for PaymentIntent { tax_details: amount_details.tax_details, enable_payment_link: Some(self.enable_payment_link.as_bool()), apply_mit_exemption: Some(self.apply_mit_exemption.as_bool()), + platform_merchant_id: self.platform_merchant_id }) } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index b2938125361b..f00979186814 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -671,6 +671,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { modified_at: date_time::now(), organization_id: organization.get_organization_id(), recon_status: diesel_models::enums::ReconStatus::NotRequested, + is_platform_account: false }), ) } diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index ef769e5dbefd..cc17d3af5151 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -226,6 +226,7 @@ where _payment_data: &mut D, _customer: &Option, _should_continue_capture: &mut bool, + _platform_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult> { todo!() } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 7fa368beab3f..1e1d60a13396 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1008,6 +1008,7 @@ pub async fn payments_intent_operation_core( operation: Op, req: Req, header_payload: HeaderPayload, + platform_merchant_account: Option ) -> RouterResult<(D, Req, Option)> where F: Send + Clone + Sync, @@ -1037,6 +1038,7 @@ where &profile, &key_store, &header_payload, + platform_merchant_account.as_ref() ) .await?; @@ -1441,6 +1443,7 @@ pub async fn payments_intent_core( operation: Op, req: Req, header_payload: HeaderPayload, + platform_merchant_account: Option ) -> RouterResponse where F: Send + Clone + Sync, @@ -1458,6 +1461,7 @@ where operation.clone(), req, header_payload.clone(), + platform_merchant_account ) .await?; @@ -1526,6 +1530,7 @@ where &profile, &key_store, &header_payload, + None ) .await?; @@ -1602,6 +1607,7 @@ pub trait PaymentRedirectFlow: Sync { ) -> RouterResult; #[cfg(feature = "v2")] + #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, state: &SessionState, @@ -1610,6 +1616,7 @@ pub trait PaymentRedirectFlow: Sync { merchant_key_store: domain::MerchantKeyStore, profile: domain::Profile, req: PaymentsRedirectResponseData, + platform_merchant_account: Option, ) -> RouterResult; fn get_payment_action(&self) -> services::PaymentAction; @@ -1703,6 +1710,7 @@ pub trait PaymentRedirectFlow: Sync { } #[cfg(feature = "v2")] + #[allow(clippy::too_many_arguments)] async fn handle_payments_redirect_response( &self, state: SessionState, @@ -1711,6 +1719,7 @@ pub trait PaymentRedirectFlow: Sync { key_store: domain::MerchantKeyStore, profile: domain::Profile, request: PaymentsRedirectResponseData, + platform_merchant_account: Option ) -> RouterResponse { metrics::REDIRECTION_TRIGGERED.add( &metrics::CONTEXT, @@ -1729,7 +1738,7 @@ pub trait PaymentRedirectFlow: Sync { key_store, profile, request, - platform_merchant_account, + platform_merchant_account ) .await?; @@ -2020,6 +2029,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { merchant_key_store: domain::MerchantKeyStore, profile: domain::Profile, req: PaymentsRedirectResponseData, + platform_merchant_account: Option ) -> RouterResult { let payment_id = req.payment_id.clone(); @@ -2046,6 +2056,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { &profile, &merchant_key_store, &HeaderPayload::default(), + platform_merchant_account.as_ref() ) .await?; diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 1df1f7dbf0a2..4da07c73c5ae 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -197,6 +197,7 @@ pub trait GetTracker: Send { profile: &domain::Profile, mechant_key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>; } diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index 5965bdc88503..aa1f9b2aaceb 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -158,6 +158,7 @@ impl GetTracker, PaymentsConfirmIntent profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 988e040f3769..64309d9d3494 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -98,6 +98,7 @@ impl GetTracker, PaymentsCrea profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + platfom_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -136,6 +137,7 @@ impl GetTracker, PaymentsCrea profile, request.clone(), encrypted_data, + platfom_merchant_account ) .await?; diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs index a69c3187c781..1c4fc35f3f8e 100644 --- a/crates/router/src/core/payments/operations/payment_get.rs +++ b/crates/router/src/core/payments/operations/payment_get.rs @@ -131,7 +131,8 @@ impl GetTracker, PaymentsRetrieveReques 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, + _platform_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 344f10434bdd..ba11e2c8c2cd 100644 --- a/crates/router/src/core/payments/operations/payment_get_intent.rs +++ b/crates/router/src/core/payments/operations/payment_get_intent.rs @@ -89,6 +89,7 @@ impl GetTracker, PaymentsGetI _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 ee490408cb11..48452da35de5 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -101,6 +101,7 @@ impl GetTracker, PaymentsSess _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount> ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index d7b6ad0d345b..4b432dc2b5d1 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -42,6 +42,7 @@ pub async fn payments_session_core( payment_id: id_type::GlobalPaymentId, call_connector_action: CallConnectorAction, header_payload: HeaderPayload, + platform_merchant_account: Option ) -> RouterResponse where F: Send + Clone + Sync, @@ -71,6 +72,7 @@ where payment_id, call_connector_action, header_payload.clone(), + platform_merchant_account ) .await?; @@ -100,6 +102,7 @@ pub async fn payments_session_operation_core( payment_id: id_type::GlobalPaymentId, _call_connector_action: CallConnectorAction, header_payload: HeaderPayload, + platform_merchant_account: Option ) -> RouterResult<(D, Req, Option, Option, Option)> where F: Send + Clone + Sync, @@ -132,6 +135,7 @@ where &profile, &key_store, &header_payload, + platform_merchant_account.as_ref() ) .await?; diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index f046c6845802..2da7ee2581ad 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -140,6 +140,7 @@ pub async fn payments_create_intent( payments::operations::PaymentIntentCreate, req, header_payload.clone(), + auth.platform_merchant_account ) }, match env::which() { @@ -200,6 +201,7 @@ pub async fn payments_get_intent( payments::operations::PaymentGetIntent, req, header_payload.clone(), + auth.platform_merchant_account ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -753,6 +755,7 @@ pub async fn payments_connector_session( payment_id, payments::CallConnectorAction::Trigger, header_payload.clone(), + auth.platform_merchant_account ) }, &auth::HeaderAuth(auth::PublishableKeyAuth), @@ -2395,6 +2398,7 @@ pub async fn payments_finish_redirection( auth.key_store, auth.profile, req, + auth.platform_merchant_account ) }, &auth::PublishableKeyAndProfileIdAuth { diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 4cb3e5528358..836c870f5e53 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -68,6 +68,7 @@ pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, pub key_store: domain::MerchantKeyStore, pub profile: domain::Profile, + pub platform_merchant_account: Option } #[derive(Clone, Debug)] @@ -455,6 +456,27 @@ where .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; + // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account + let (merchant, platform_merchant_account) = + get_platform_merchant_account(state, request_headers, merchant).await?; + + let key_store = { + if platform_merchant_account.is_some() { + state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant.get_id(), + &state.store().get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant key store for the merchant id")? + } else { + key_store + } + }; + let profile = state .store() .find_business_profile_by_profile_id(key_manager_state, &key_store, &profile_id) @@ -463,6 +485,7 @@ where let auth = AuthenticationData { merchant_account: merchant, + platform_merchant_account, key_store, profile, }; @@ -701,6 +724,7 @@ where let auth_data_v2 = AuthenticationData { merchant_account: auth_data.merchant_account, + platform_merchant_account: auth_data.platform_merchant_account, key_store: auth_data.key_store, profile, }; @@ -1017,6 +1041,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1055,6 +1080,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None, }; Ok(( @@ -1076,6 +1102,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1267,6 +1294,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1307,6 +1335,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( auth, @@ -1327,6 +1356,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1447,6 +1477,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; 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)? @@ -1481,6 +1512,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None, }; Ok(( auth.clone(), @@ -1509,6 +1541,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + throw_error_if_platform_merchant_authentication_required(request_headers)?; let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() @@ -1540,6 +1573,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( auth.clone(), @@ -1599,6 +1633,7 @@ where merchant_account, key_store, profile, + platform_merchant_account: None, }, AuthenticationType::PublishableKey { merchant_id }, )) @@ -1690,6 +1725,7 @@ where merchant_account, key_store, profile, + platform_merchant_account: None }, AuthenticationType::PublishableKey { merchant_id }, )) @@ -2061,6 +2097,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( @@ -2303,6 +2340,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( auth.clone(), @@ -2583,6 +2621,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( auth.clone(), @@ -2748,6 +2787,7 @@ where merchant_account: merchant, key_store, profile, + platform_merchant_account: None }; Ok(( auth, @@ -3310,7 +3350,7 @@ where .await .to_not_found_response(errors::ApiErrorResponse::InvalidPlatformOperation) .attach_printable("Failed to fetch merchant account for the merchant id")?; - //TODO CHANGE ALL ERROR TYPES + if platform_org_id != connected_merchant_account.organization_id { return Err(errors::ApiErrorResponse::InvalidPlatformOperation) .attach_printable("Access for merchant id Unauthorized"); @@ -3334,7 +3374,7 @@ where .map(|merchant_id| { merchant_account .is_platform_account - .then(|| merchant_id) + .then_some(merchant_id) .ok_or(errors::ApiErrorResponse::InvalidPlatformOperation) }) .transpose() diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/down.sql b/migrations/2024-12-03-072318_platform_merchant_account/down.sql similarity index 100% rename from migrations/2024-11-13-114939_platorm_merchant_account/down.sql rename to migrations/2024-12-03-072318_platform_merchant_account/down.sql diff --git a/migrations/2024-11-13-114939_platorm_merchant_account/up.sql b/migrations/2024-12-03-072318_platform_merchant_account/up.sql similarity index 100% rename from migrations/2024-11-13-114939_platorm_merchant_account/up.sql rename to migrations/2024-12-03-072318_platform_merchant_account/up.sql From cd8b2c6cd595c38767bfef3ef56e18732207b7aa Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 16 Dec 2024 16:51:39 +0530 Subject: [PATCH 10/24] tests and fmt --- crates/diesel_models/src/merchant_account.rs | 4 +-- crates/diesel_models/src/payment_intent.rs | 4 +-- .../src/merchant_account.rs | 28 +++++++++---------- .../hyperswitch_domain_models/src/payments.rs | 7 +++-- .../src/payments/payment_intent.rs | 8 +++--- crates/router/src/core/admin.rs | 2 +- .../fraud_check/operation/fraud_check_post.rs | 6 ++-- crates/router/src/core/payments.rs | 18 ++++++------ crates/router/src/core/payments/helpers.rs | 3 ++ .../operations/payment_confirm_intent.rs | 2 +- .../operations/payment_create_intent.rs | 4 +-- .../core/payments/operations/payment_get.rs | 2 +- .../payments/operations/payment_get_intent.rs | 2 +- .../operations/payment_session_intent.rs | 2 +- .../src/core/payments/session_operation.rs | 8 +++--- crates/router/src/routes/payments.rs | 6 ++-- crates/router/src/services/authentication.rs | 26 +++++++++-------- crates/router/tests/payments.rs | 2 ++ crates/router/tests/payments2.rs | 2 ++ 19 files changed, 73 insertions(+), 63 deletions(-) diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index fc42c940ef51..b5c2bc285724 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -281,7 +281,7 @@ impl MerchantAccountUpdateInternal { modified_at, organization_id, recon_status, - is_platform_account + is_platform_account, } = self; MerchantAccount { @@ -296,7 +296,7 @@ impl MerchantAccountUpdateInternal { recon_status: recon_status.unwrap_or(source.recon_status), version: source.version, id: source.id, - is_platform_account: is_platform_account.unwrap_or(source.is_platform_account) + is_platform_account: is_platform_account.unwrap_or(source.is_platform_account), } } } diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index ecfffa438a63..f267e988de68 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -73,7 +73,7 @@ pub struct PaymentIntent { pub payment_link_config: Option, pub id: common_utils::id_type::GlobalPaymentId, pub psd2_sca_exemption_type: Option, - pub platform_merchant_id: Option + pub platform_merchant_id: Option, } #[cfg(feature = "v1")] @@ -293,7 +293,7 @@ pub struct PaymentIntentNew { pub enable_payment_link: Option, pub apply_mit_exemption: Option, pub id: common_utils::id_type::GlobalPaymentId, - pub platform_merchant_id: Option + pub platform_merchant_id: Option, } #[cfg(feature = "v1")] diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index aa1ad6a81605..9e51f0aba7fe 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -136,7 +136,7 @@ pub struct MerchantAccountSetter { pub modified_at: time::PrimitiveDateTime, pub organization_id: common_utils::id_type::OrganizationId, pub recon_status: diesel_models::enums::ReconStatus, - pub is_platform_account: bool + pub is_platform_account: bool, } #[cfg(feature = "v2")] @@ -153,7 +153,7 @@ impl From for MerchantAccount { modified_at, organization_id, recon_status, - is_platform_account + is_platform_account, } = item; Self { id, @@ -166,7 +166,7 @@ impl From for MerchantAccount { modified_at, organization_id, recon_status, - is_platform_account + is_platform_account, } } } @@ -184,7 +184,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub organization_id: common_utils::id_type::OrganizationId, pub recon_status: diesel_models::enums::ReconStatus, - pub is_platform_account: bool + pub is_platform_account: bool, } impl MerchantAccount { @@ -260,7 +260,7 @@ pub enum MerchantAccountUpdate { recon_status: diesel_models::enums::ReconStatus, }, ModifiedAtUpdate, - ToPlatformAccount + ToPlatformAccount, } #[cfg(feature = "v1")] @@ -484,7 +484,7 @@ impl From for MerchantAccountUpdateInternal { storage_scheme: None, organization_id: None, recon_status: None, - is_platform_account: None + is_platform_account: None, }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), @@ -495,7 +495,7 @@ impl From for MerchantAccountUpdateInternal { metadata: None, organization_id: None, recon_status: None, - is_platform_account: None + is_platform_account: None, }, MerchantAccountUpdate::ReconUpdate { recon_status } => Self { recon_status: Some(recon_status), @@ -506,7 +506,7 @@ impl From for MerchantAccountUpdateInternal { storage_scheme: None, metadata: None, organization_id: None, - is_platform_account: None + is_platform_account: None, }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -517,7 +517,7 @@ impl From for MerchantAccountUpdateInternal { metadata: None, organization_id: None, recon_status: None, - is_platform_account: None + is_platform_account: None, }, MerchantAccountUpdate::ToPlatformAccount => Self { modified_at: now, @@ -528,8 +528,8 @@ impl From for MerchantAccountUpdateInternal { metadata: None, organization_id: None, recon_status: None, - is_platform_account: Some(true) - } + is_platform_account: Some(true), + }, } } } @@ -554,7 +554,7 @@ impl super::behaviour::Conversion for MerchantAccount { organization_id: self.organization_id, recon_status: self.recon_status, version: crate::consts::API_VERSION, - is_platform_account: self.is_platform_account + is_platform_account: self.is_platform_account, }; Ok(diesel_models::MerchantAccount::from(setter)) @@ -614,7 +614,7 @@ impl super::behaviour::Conversion for MerchantAccount { modified_at: item.modified_at, organization_id: item.organization_id, recon_status: item.recon_status, - is_platform_account: item.is_platform_account + is_platform_account: item.is_platform_account, }) } .await @@ -636,7 +636,7 @@ impl super::behaviour::Conversion for MerchantAccount { organization_id: self.organization_id, recon_status: self.recon_status, version: crate::consts::API_VERSION, - is_platform_account: self.is_platform_account + is_platform_account: self.is_platform_account, }) } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index f7451594fc03..58c0f5a4c87d 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -377,7 +377,7 @@ pub struct PaymentIntent { /// The straight through routing algorithm id that is used for this payment. This overrides the default routing algorithm that is configured in business profile. pub routing_algorithm_id: Option, /// Identifier for the platform merchant. - pub platform_merchant_id: Option + pub platform_merchant_id: Option, } #[cfg(feature = "v2")] @@ -424,7 +424,7 @@ impl PaymentIntent { profile: &business_profile::Profile, request: api_models::payments::PaymentsCreateIntentRequest, decrypted_payment_intent: DecryptedPaymentIntent, - platform_merchant_id: Option<&merchant_account::MerchantAccount> + platform_merchant_id: Option<&merchant_account::MerchantAccount>, ) -> CustomResult { let connector_metadata = request .get_connector_metadata_as_value() @@ -518,7 +518,8 @@ impl PaymentIntent { .payment_link_config .map(ApiModelToDieselModelConvertor::convert_from), routing_algorithm_id: request.routing_algorithm_id, - platform_merchant_id: platform_merchant_id.map(|merchant_account| merchant_account.get_id().to_owned()) + platform_merchant_id: platform_merchant_id + .map(|merchant_account| merchant_account.get_id().to_owned()), }) } } diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index a9f5df10b460..d8c433fd1c3a 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1219,7 +1219,7 @@ impl behaviour::Conversion for PaymentIntent { customer_present, routing_algorithm_id, payment_link_config, - platform_merchant_id + platform_merchant_id, } = self; Ok(DieselPaymentIntent { skip_external_tax_calculation: Some(amount_details.get_external_tax_action_as_bool()), @@ -1290,7 +1290,7 @@ impl behaviour::Conversion for PaymentIntent { payment_link_config, routing_algorithm_id, psd2_sca_exemption_type: None, - platform_merchant_id + platform_merchant_id, }) } async fn convert_back( @@ -1423,7 +1423,7 @@ impl behaviour::Conversion for PaymentIntent { ), payment_link_config: storage_model.payment_link_config, routing_algorithm_id: storage_model.routing_algorithm_id, - platform_merchant_id: storage_model.platform_merchant_id + platform_merchant_id: storage_model.platform_merchant_id, }) } .await @@ -1496,7 +1496,7 @@ impl behaviour::Conversion for PaymentIntent { tax_details: amount_details.tax_details, enable_payment_link: Some(self.enable_payment_link.as_bool()), apply_mit_exemption: Some(self.apply_mit_exemption.as_bool()), - platform_merchant_id: self.platform_merchant_id + platform_merchant_id: self.platform_merchant_id, }) } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index f00979186814..ea09401799a3 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -671,7 +671,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { modified_at: date_time::now(), organization_id: organization.get_organization_id(), recon_status: diesel_models::enums::ReconStatus::NotRequested, - is_platform_account: false + is_platform_account: false, }), ) } diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index cc17d3af5151..b88e6c0f0298 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -226,7 +226,7 @@ where _payment_data: &mut D, _customer: &Option, _should_continue_capture: &mut bool, - _platform_merchant_account: Option<&domain::MerchantAccount> + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult> { todo!() } @@ -279,7 +279,7 @@ where payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), - platform_merchant_account.map(|account| account.clone()), + platform_merchant_account.cloned(), )) .await?; logger::debug!("payment_id : {:?} has been cancelled since it has been found fraudulent by configured frm connector",payment_data.get_payment_attempt().payment_id); @@ -337,7 +337,7 @@ where payments::CallConnectorAction::Trigger, None, HeaderPayload::default(), - platform_merchant_account.map(|account| account.clone()), + platform_merchant_account.cloned(), )) .await?; logger::debug!("payment_id : {:?} has been captured since it has been found legit by configured frm connector",payment_data.get_payment_attempt().payment_id); diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 1e1d60a13396..a922139b0909 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1008,7 +1008,7 @@ pub async fn payments_intent_operation_core( operation: Op, req: Req, header_payload: HeaderPayload, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResult<(D, Req, Option)> where F: Send + Clone + Sync, @@ -1038,7 +1038,7 @@ where &profile, &key_store, &header_payload, - platform_merchant_account.as_ref() + platform_merchant_account.as_ref(), ) .await?; @@ -1443,7 +1443,7 @@ pub async fn payments_intent_core( operation: Op, req: Req, header_payload: HeaderPayload, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResponse where F: Send + Clone + Sync, @@ -1461,7 +1461,7 @@ where operation.clone(), req, header_payload.clone(), - platform_merchant_account + platform_merchant_account, ) .await?; @@ -1530,7 +1530,7 @@ where &profile, &key_store, &header_payload, - None + None, ) .await?; @@ -1719,7 +1719,7 @@ pub trait PaymentRedirectFlow: Sync { key_store: domain::MerchantKeyStore, profile: domain::Profile, request: PaymentsRedirectResponseData, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResponse { metrics::REDIRECTION_TRIGGERED.add( &metrics::CONTEXT, @@ -1738,7 +1738,7 @@ pub trait PaymentRedirectFlow: Sync { key_store, profile, request, - platform_merchant_account + platform_merchant_account, ) .await?; @@ -2029,7 +2029,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { merchant_key_store: domain::MerchantKeyStore, profile: domain::Profile, req: PaymentsRedirectResponseData, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResult { let payment_id = req.payment_id.clone(); @@ -2056,7 +2056,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { &profile, &merchant_key_store, &HeaderPayload::default(), - platform_merchant_account.as_ref() + platform_merchant_account.as_ref(), ) .await?; diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 219a4d90519b..451b63aa715f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3477,6 +3477,7 @@ mod tests { tax_details: None, skip_external_tax_calculation: None, psd2_sca_exemption_type: None, + platform_merchant_id: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_ok()); @@ -3547,6 +3548,7 @@ mod tests { tax_details: None, skip_external_tax_calculation: None, psd2_sca_exemption_type: None, + platform_merchant_id: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent,).is_err()) @@ -3615,6 +3617,7 @@ mod tests { tax_details: None, skip_external_tax_calculation: None, psd2_sca_exemption_type: None, + platform_merchant_id: None, }; let req_cs = Some("1".to_string()); assert!(authenticate_client_secret(req_cs.as_ref(), &payment_intent).is_err()) diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index aa1f9b2aaceb..8ebaa3606b7e 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -158,7 +158,7 @@ impl GetTracker, PaymentsConfirmIntent profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - _platform_merchant_account: Option<&domain::MerchantAccount> + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 64309d9d3494..38b8ccd7bbfe 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -98,7 +98,7 @@ impl GetTracker, PaymentsCrea profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - platfom_merchant_account: Option<&domain::MerchantAccount> + platfom_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -137,7 +137,7 @@ impl GetTracker, PaymentsCrea profile, request.clone(), encrypted_data, - platfom_merchant_account + platfom_merchant_account, ) .await?; diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs index 1c4fc35f3f8e..f1cab083f7d2 100644 --- a/crates/router/src/core/payments/operations/payment_get.rs +++ b/crates/router/src/core/payments/operations/payment_get.rs @@ -132,7 +132,7 @@ impl GetTracker, PaymentsRetrieveReques _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - _platform_merchant_account: Option<&domain::MerchantAccount> + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 ba11e2c8c2cd..e0ef04ae414a 100644 --- a/crates/router/src/core/payments/operations/payment_get_intent.rs +++ b/crates/router/src/core/payments/operations/payment_get_intent.rs @@ -89,7 +89,7 @@ impl GetTracker, PaymentsGetI _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - _platform_merchant_account: Option<&domain::MerchantAccount> + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); 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 48452da35de5..db3a8caa43d8 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -101,7 +101,7 @@ impl GetTracker, PaymentsSess _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - _platform_merchant_account: Option<&domain::MerchantAccount> + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index 4b432dc2b5d1..40a2717fea72 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -42,7 +42,7 @@ pub async fn payments_session_core( payment_id: id_type::GlobalPaymentId, call_connector_action: CallConnectorAction, header_payload: HeaderPayload, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResponse where F: Send + Clone + Sync, @@ -72,7 +72,7 @@ where payment_id, call_connector_action, header_payload.clone(), - platform_merchant_account + platform_merchant_account, ) .await?; @@ -102,7 +102,7 @@ pub async fn payments_session_operation_core( payment_id: id_type::GlobalPaymentId, _call_connector_action: CallConnectorAction, header_payload: HeaderPayload, - platform_merchant_account: Option + platform_merchant_account: Option, ) -> RouterResult<(D, Req, Option, Option, Option)> where F: Send + Clone + Sync, @@ -135,7 +135,7 @@ where &profile, &key_store, &header_payload, - platform_merchant_account.as_ref() + platform_merchant_account.as_ref(), ) .await?; diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 2da7ee2581ad..b8236f68e537 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -140,7 +140,7 @@ pub async fn payments_create_intent( payments::operations::PaymentIntentCreate, req, header_payload.clone(), - auth.platform_merchant_account + auth.platform_merchant_account, ) }, match env::which() { @@ -201,7 +201,7 @@ pub async fn payments_get_intent( payments::operations::PaymentGetIntent, req, header_payload.clone(), - auth.platform_merchant_account + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -755,7 +755,7 @@ pub async fn payments_connector_session( payment_id, payments::CallConnectorAction::Trigger, header_payload.clone(), - auth.platform_merchant_account + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::PublishableKeyAuth), diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 836c870f5e53..d69480b7529c 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -68,7 +68,7 @@ pub struct AuthenticationData { pub merchant_account: domain::MerchantAccount, pub key_store: domain::MerchantKeyStore, pub profile: domain::Profile, - pub platform_merchant_account: Option + pub platform_merchant_account: Option, } #[derive(Clone, Debug)] @@ -474,7 +474,7 @@ where .attach_printable("Failed to fetch merchant key store for the merchant id")? } else { key_store - } + } }; let profile = state @@ -647,7 +647,8 @@ where merchant_id: Some(merchant_id), key_id: Some(key_id), } => { - let auth = construct_authentication_data(state, &merchant_id, request_headers).await?; + let auth = + construct_authentication_data(state, &merchant_id, request_headers).await?; Ok(( auth.clone(), AuthenticationType::ApiKey { @@ -661,7 +662,8 @@ where merchant_id: Some(merchant_id), key_id: None, } => { - let auth = construct_authentication_data(state, &merchant_id, request_headers).await?; + let auth = + construct_authentication_data(state, &merchant_id, request_headers).await?; Ok(( auth.clone(), AuthenticationType::PublishableKey { @@ -764,7 +766,7 @@ where // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + get_platform_merchant_account(state, request_headers, merchant).await?; let auth = AuthenticationData { merchant_account: merchant, @@ -1335,7 +1337,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( auth, @@ -1573,7 +1575,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( auth.clone(), @@ -1725,7 +1727,7 @@ where merchant_account, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }, AuthenticationType::PublishableKey { merchant_id }, )) @@ -2097,7 +2099,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( @@ -2340,7 +2342,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( auth.clone(), @@ -2621,7 +2623,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( auth.clone(), @@ -2787,7 +2789,7 @@ where merchant_account: merchant, key_store, profile, - platform_merchant_account: None + platform_merchant_account: None, }; Ok(( auth, diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 68ca08c8bd3c..97d3b6b217c4 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -472,6 +472,7 @@ async fn payments_create_core() { payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, )) .await .unwrap(); @@ -734,6 +735,7 @@ async fn payments_create_core_adyen_no_redirect() { payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, )) .await .unwrap(); diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 90fe3a1f8477..13f0ae751b3a 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -234,6 +234,7 @@ async fn payments_create_core() { payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, )) .await .unwrap(); @@ -504,6 +505,7 @@ async fn payments_create_core_adyen_no_redirect() { payments::CallConnectorAction::Trigger, None, hyperswitch_domain_models::payments::HeaderPayload::default(), + None, )) .await .unwrap(); From a84a4dcd6d4eaf35766b9cba9cc2fa4c22599ba5 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 16 Dec 2024 17:43:03 +0530 Subject: [PATCH 11/24] post merge cleanup --- crates/diesel_models/src/payment_intent.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 75359e6b5e1e..1243abe98556 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -368,6 +368,7 @@ pub struct PaymentIntentNew { pub skip_external_tax_calculation: Option, pub psd2_sca_exemption_type: Option, pub platform_merchant_id: Option, + pub split_payments: Option, } #[cfg(feature = "v2")] @@ -384,9 +385,6 @@ pub enum PaymentIntentUpdate { status: storage_enums::IntentStatus, updated_by: String, }, -======= - pub split_payments: Option, ->>>>>>> main } #[cfg(feature = "v1")] From 01deb60bc7ec586e717180a55d4fbe97a0b9dd40 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Tue, 17 Dec 2024 12:48:39 +0530 Subject: [PATCH 12/24] post merge v2 cleanup --- .../router/src/core/payments/operations/payment_update_intent.rs | 1 + crates/router/src/routes/payments.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/router/src/core/payments/operations/payment_update_intent.rs b/crates/router/src/core/payments/operations/payment_update_intent.rs index f8ce03d558e9..4782d237e211 100644 --- a/crates/router/src/core/payments/operations/payment_update_intent.rs +++ b/crates/router/src/core/payments/operations/payment_update_intent.rs @@ -135,6 +135,7 @@ impl GetTracker, PaymentsUpda _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 3b714a5e28b8..7ef632539d3e 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -208,6 +208,7 @@ pub async fn payments_get_intent( req, global_payment_id.clone(), header_payload.clone(), + auth.platform_merchant_account, ) }, &auth::HeaderAuth(auth::ApiKeyAuth), From 00a063fa5291b32fc139c94dfe9eeb4275af1db3 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Tue, 17 Dec 2024 23:00:42 +0530 Subject: [PATCH 13/24] added route to enable platform account --- crates/router/src/core/admin.rs | 32 ++++++++++++++++++++++++++ crates/router/src/routes/admin.rs | 23 ++++++++++++++++++ crates/router/src/routes/app.rs | 1 + crates/router/src/routes/lock_utils.rs | 3 ++- crates/router_env/src/logger/types.rs | 2 ++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 5f0f581cb93b..fd5693293132 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -4766,3 +4766,35 @@ async fn locker_recipient_create_call( Ok(store_resp.card_reference) } + +pub async fn enable_platform_account( + state: SessionState, + merchant_id: id_type::MerchantId, +) -> RouterResponse<()> { + let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); + let key_store = db + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + + let merchant_account = db + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + + db.update_merchant( + key_manager_state, + merchant_account, + storage::MerchantAccountUpdate::ToPlatformAccount, + &key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error while enabling platform merchant account") + .map(|_| services::ApplicationResponse::StatusOk) +} diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index a57fd56e6c84..c9f292f8a499 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -910,3 +910,26 @@ pub async fn merchant_account_transfer_keys( )) .await } + +/// Merchant Account - Platform Account +/// +/// Enable platform account +#[instrument(skip_all)] +pub async fn merchant_account_enable_platform_account( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::EnablePlatformAccount; + let merchant_id = path.into_inner(); + Box::pin(api::server_wrap( + flow, + state, + &req, + merchant_id, + |state, _, req, _| enable_platform_account(state, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 5f04581fbb39..1048c7929364 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1324,6 +1324,7 @@ impl MerchantAccount { .route(web::post().to(admin::merchant_account_toggle_kv)) .route(web::get().to(admin::merchant_account_kv_status)), ) + .service(web::resource("/{id}/platform").route(web::post().to(admin::merchant_account_enable_platform_account))) .service( web::resource("/transfer") .route(web::post().to(admin::merchant_account_transfer_keys)), diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index b904cb343a53..cde953064f59 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -47,7 +47,8 @@ impl From for ApiIdentifier { | Flow::MerchantsAccountUpdate | Flow::MerchantsAccountDelete | Flow::MerchantTransferKey - | Flow::MerchantAccountList => Self::MerchantAccount, + | Flow::MerchantAccountList + | Flow::EnablePlatformAccount=> Self::MerchantAccount, Flow::OrganizationCreate | Flow::OrganizationRetrieve | Flow::OrganizationUpdate => { Self::Organization diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 84e322872a9c..d8d1b0f1cb3f 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -88,6 +88,8 @@ pub enum Flow { ConfigKeyCreate, /// ConfigKey fetch flow. ConfigKeyFetch, + /// Enable platform account flow. + EnablePlatformAccount, /// ConfigKey Update flow. ConfigKeyUpdate, /// ConfigKey Delete flow. From 16243838b3749ca22eb260466e41893cdb5c46a5 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Wed, 18 Dec 2024 23:49:48 +0530 Subject: [PATCH 14/24] feature flagged --- crates/router/Cargo.toml | 3 +- crates/router/src/core/admin.rs | 1 + crates/router/src/routes/admin.rs | 3 +- crates/router/src/routes/app.rs | 12 +++- crates/router/src/routes/lock_utils.rs | 2 +- crates/router/src/services/authentication.rs | 61 +++++++++++++++++--- 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 619d2fb19375..5c08323dfd00 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -10,7 +10,7 @@ license.workspace = true [features] default = ["common_default", "v1"] -common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"] +common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id","platform"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] tls = ["actix-web/rustls-0_22"] email = ["external_services/email", "scheduler/email", "olap"] @@ -38,6 +38,7 @@ v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"] payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"] dynamic_routing = ["external_services/dynamic_routing", "storage_impl/dynamic_routing", "api_models/dynamic_routing"] +platform = [] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index fd5693293132..57bee9b60b8e 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -4767,6 +4767,7 @@ async fn locker_recipient_create_call( Ok(store_resp.card_reference) } +#[cfg(feature = "platform")] pub async fn enable_platform_account( state: SessionState, merchant_id: id_type::MerchantId, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index c9f292f8a499..789fa602b413 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -914,6 +914,7 @@ pub async fn merchant_account_transfer_keys( /// Merchant Account - Platform Account /// /// Enable platform account +#[cfg(feature = "platform")] #[instrument(skip_all)] pub async fn merchant_account_enable_platform_account( state: web::Data, @@ -927,7 +928,7 @@ pub async fn merchant_account_enable_platform_account( state, &req, merchant_id, - |state, _, req, _| enable_platform_account(state, req), + |state, _, req, _| enable_platform_account(state, req), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1048c7929364..a03e9e586854 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1315,7 +1315,8 @@ impl MerchantAccount { #[cfg(all(feature = "olap", feature = "v1"))] impl MerchantAccount { pub fn server(state: AppState) -> Scope { - web::scope("/accounts") + #[allow(unused_mut)] + let mut routes = web::scope("/accounts") .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(admin::merchant_account_create))) .service(web::resource("/list").route(web::get().to(admin::merchant_account_list))) @@ -1324,7 +1325,6 @@ impl MerchantAccount { .route(web::post().to(admin::merchant_account_toggle_kv)) .route(web::get().to(admin::merchant_account_kv_status)), ) - .service(web::resource("/{id}/platform").route(web::post().to(admin::merchant_account_enable_platform_account))) .service( web::resource("/transfer") .route(web::post().to(admin::merchant_account_transfer_keys)), @@ -1337,7 +1337,15 @@ impl MerchantAccount { .route(web::get().to(admin::retrieve_merchant_account)) .route(web::post().to(admin::update_merchant_account)) .route(web::delete().to(admin::delete_merchant_account)), + ); + #[cfg(feature = "platform")] + { + routes = routes.service( + web::resource("/{id}/platform") + .route(web::post().to(admin::merchant_account_enable_platform_account)), ) + } + routes } } diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index cde953064f59..6bd69b9d2845 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -48,7 +48,7 @@ impl From for ApiIdentifier { | Flow::MerchantsAccountDelete | Flow::MerchantTransferKey | Flow::MerchantAccountList - | Flow::EnablePlatformAccount=> Self::MerchantAccount, + | Flow::EnablePlatformAccount => Self::MerchantAccount, Flow::OrganizationCreate | Flow::OrganizationRetrieve | Flow::OrganizationUpdate => { Self::Organization diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 56c0e63966b2..2fe84ccd4435 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,7 +11,9 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, ext_traits::AsyncExt, id_type}; +#[cfg(feature = "platform")] +use common_utils::ext_traits::AsyncExt; +use common_utils::{date_time, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -463,8 +465,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let key_store = { if platform_merchant_account.is_some() { @@ -573,8 +583,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let auth = AuthenticationData { merchant_account: merchant, @@ -741,6 +759,7 @@ where } #[cfg(all(feature = "partial-auth", feature = "v1"))] +#[allow(unused)] async fn construct_authentication_data( state: &A, merchant_id: &id_type::MerchantId, @@ -771,8 +790,16 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = - get_platform_merchant_account(state, request_headers, merchant).await?; + let (merchant, platform_merchant_account) = { + #[cfg(feature = "platform")] + { + get_platform_merchant_account(state, request_headers, merchant).await? + } + #[cfg(not(feature = "platform"))] + { + (merchant, None) + } + }; let auth = AuthenticationData { merchant_account: merchant, @@ -1049,7 +1076,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1110,7 +1139,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1210,6 +1241,8 @@ impl<'a> HeaderMapStruct<'a> { }) } + // Remove feature flag if required. + #[cfg(feature = "platform")] pub fn get_id_type_from_header_if_present(&self, key: &str) -> RouterResult> where T: TryFrom< @@ -1302,7 +1335,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1364,7 +1399,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + AdminApiAuth .authenticate_and_fetch(request_headers, state) .await?; @@ -1431,6 +1468,7 @@ pub struct MerchantIdAuth(pub id_type::MerchantId); #[cfg(feature = "v1")] #[async_trait] +#[allow(unused)] impl AuthenticateAndFetch for MerchantIdAuth where A: SessionStateInfo + Sync, @@ -1440,6 +1478,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; let key_manager_state = &(&state.session_state()).into(); @@ -1485,7 +1524,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + 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)? @@ -1549,7 +1590,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() @@ -1669,6 +1712,7 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + #[cfg(feature = "platform")] throw_error_if_platform_merchant_authentication_required(request_headers)?; let publishable_key = @@ -3332,6 +3376,7 @@ where } } +#[cfg(feature = "platform")] async fn get_connected_merchant_account( state: &A, connected_merchant_id: id_type::MerchantId, @@ -3367,6 +3412,7 @@ where Ok(connected_merchant_account) } +#[cfg(feature = "platform")] async fn get_platform_merchant_account( state: &A, request_headers: &HeaderMap, @@ -3404,6 +3450,7 @@ where } } +#[cfg(feature = "platform")] fn throw_error_if_platform_merchant_authentication_required( request_headers: &HeaderMap, ) -> RouterResult<()> { From d8086724a2d557f56333c0c0f492579f750fd2a5 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Wed, 18 Dec 2024 23:56:06 +0530 Subject: [PATCH 15/24] cleanup --- crates/diesel_models/src/payment_intent.rs | 16 ---------------- .../src/merchant_account.rs | 1 - .../payments/operations/payment_create_intent.rs | 4 ++-- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 1243abe98556..5f61ec452919 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -371,22 +371,6 @@ pub struct PaymentIntentNew { pub split_payments: Option, } -#[cfg(feature = "v2")] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum PaymentIntentUpdate { - /// Update the payment intent details on payment intent confirmation, before calling the connector - ConfirmIntent { - status: storage_enums::IntentStatus, - active_attempt_id: common_utils::id_type::GlobalAttemptId, - updated_by: String, - }, - /// Update the payment intent details on payment intent confirmation, after calling the connector - ConfirmIntentPostUpdate { - status: storage_enums::IntentStatus, - updated_by: String, - }, -} - #[cfg(feature = "v1")] #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PaymentIntentUpdate { diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 0efbd432db14..c42b0005513f 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -430,7 +430,6 @@ impl From for MerchantAccountUpdateInternal { pm_collect_link_config: None, is_platform_account: None, }, - // TODO: Change it to toggle MerchantAccountUpdate::ToPlatformAccount => Self { modified_at: now, merchant_name: None, 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 d6ed4ba05a61..b414f9aa70b7 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -99,7 +99,7 @@ impl profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - platfom_merchant_account: Option<&domain::MerchantAccount>, + platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -138,7 +138,7 @@ impl profile, request.clone(), encrypted_data, - platfom_merchant_account, + platform_merchant_account, ) .await?; From 5c7a963db9acb31277db22f5ce5c174f80876706 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 19 Dec 2024 13:08:45 +0530 Subject: [PATCH 16/24] schema fix --- crates/diesel_models/src/payment_intent.rs | 4 ++-- crates/diesel_models/src/schema.rs | 2 +- crates/diesel_models/src/schema_v2.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 5f61ec452919..f5139e98fc12 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -73,8 +73,8 @@ pub struct PaymentIntent { pub payment_link_config: Option, pub id: common_utils::id_type::GlobalPaymentId, pub psd2_sca_exemption_type: Option, - pub platform_merchant_id: Option, pub split_payments: Option, + pub platform_merchant_id: Option, } #[cfg(feature = "v1")] @@ -140,8 +140,8 @@ pub struct PaymentIntent { pub tax_details: Option, pub skip_external_tax_calculation: Option, pub psd2_sca_exemption_type: Option, - pub platform_merchant_id: Option, pub split_payments: Option, + pub platform_merchant_id: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, diesel::AsExpression, PartialEq)] diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 33fa5099f9be..ac447148b0a5 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -968,9 +968,9 @@ diesel::table! { tax_details -> Nullable, skip_external_tax_calculation -> Nullable, psd2_sca_exemption_type -> Nullable, + split_payments -> Nullable, #[max_length = 64] platform_merchant_id -> Nullable, - split_payments -> Nullable, } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index bf523e4c53e6..4cc97f46b880 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -931,9 +931,9 @@ diesel::table! { #[max_length = 64] id -> Varchar, psd2_sca_exemption_type -> Nullable, + split_payments -> Nullable, #[max_length = 64] platform_merchant_id -> Nullable, - split_payments -> Nullable, } } From a7385e590d40a1ad79219770282dc78902425c41 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 19 Dec 2024 16:11:25 +0530 Subject: [PATCH 17/24] feature flag into runtime checks --- crates/router/Cargo.toml | 3 +- .../src/configs/secrets_transformers.rs | 1 + crates/router/src/configs/settings.rs | 6 + crates/router/src/core/admin.rs | 1 - crates/router/src/routes/admin.rs | 1 - crates/router/src/routes/app.rs | 7 +- crates/router/src/services/authentication.rs | 145 ++++++++++-------- 7 files changed, 90 insertions(+), 74 deletions(-) diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 5c08323dfd00..619d2fb19375 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -10,7 +10,7 @@ license.workspace = true [features] default = ["common_default", "v1"] -common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id","platform"] +common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] tls = ["actix-web/rustls-0_22"] email = ["external_services/email", "scheduler/email", "olap"] @@ -38,7 +38,6 @@ v1 = ["common_default", "api_models/v1", "diesel_models/v1", "hyperswitch_domain customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2", "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"] payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2", "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"] dynamic_routing = ["external_services/dynamic_routing", "storage_impl/dynamic_routing", "api_models/dynamic_routing"] -platform = [] # Partial Auth # The feature reduces the overhead of the router authenticating the merchant for every request, and trusts on `x-merchant-id` header to be present in the request. diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 3862f70536fc..7b74aca76472 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -546,5 +546,6 @@ pub(crate) async fn fetch_raw_secrets( network_tokenization_service, network_tokenization_supported_connectors: conf.network_tokenization_supported_connectors, theme: conf.theme, + platform: conf.platform, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 4ab2a8fd5c77..194657b22cee 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -129,6 +129,12 @@ pub struct Settings { pub network_tokenization_service: Option>, pub network_tokenization_supported_connectors: NetworkTokenizationSupportedConnectors, pub theme: ThemeSettings, + pub platform: Platform, +} + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Platform { + pub enabled: bool, } #[derive(Debug, Deserialize, Clone, Default)] diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 57bee9b60b8e..fd5693293132 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -4767,7 +4767,6 @@ async fn locker_recipient_create_call( Ok(store_resp.card_reference) } -#[cfg(feature = "platform")] pub async fn enable_platform_account( state: SessionState, merchant_id: id_type::MerchantId, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index 789fa602b413..9557aabbe81d 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -914,7 +914,6 @@ pub async fn merchant_account_transfer_keys( /// Merchant Account - Platform Account /// /// Enable platform account -#[cfg(feature = "platform")] #[instrument(skip_all)] pub async fn merchant_account_enable_platform_account( state: web::Data, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index a03e9e586854..64e4d1d00058 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1315,9 +1315,7 @@ impl MerchantAccount { #[cfg(all(feature = "olap", feature = "v1"))] impl MerchantAccount { pub fn server(state: AppState) -> Scope { - #[allow(unused_mut)] let mut routes = web::scope("/accounts") - .app_data(web::Data::new(state)) .service(web::resource("").route(web::post().to(admin::merchant_account_create))) .service(web::resource("/list").route(web::get().to(admin::merchant_account_list))) .service( @@ -1338,14 +1336,13 @@ impl MerchantAccount { .route(web::post().to(admin::update_merchant_account)) .route(web::delete().to(admin::delete_merchant_account)), ); - #[cfg(feature = "platform")] - { + if state.conf.platform.enabled { routes = routes.service( web::resource("/{id}/platform") .route(web::post().to(admin::merchant_account_enable_platform_account)), ) } - routes + routes.app_data(web::Data::new(state)) } } diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 2fe84ccd4435..f91695967125 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,9 +11,7 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -#[cfg(feature = "platform")] -use common_utils::ext_traits::AsyncExt; -use common_utils::{date_time, id_type}; +use common_utils::{date_time, ext_traits::AsyncExt, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -465,32 +463,25 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = { - #[cfg(feature = "platform")] - { - get_platform_merchant_account(state, request_headers, merchant).await? - } - #[cfg(not(feature = "platform"))] - { - (merchant, None) - } + let (merchant, platform_merchant_account) = if state.conf().platform.enabled { + get_platform_merchant_account(state, request_headers, merchant).await? + } else { + (merchant, None) }; - let key_store = { - if platform_merchant_account.is_some() { - state - .store() - .get_merchant_key_store_by_merchant_id( - key_manager_state, - merchant.get_id(), - &state.store().get_master_key().to_vec().into(), - ) - .await - .change_context(errors::ApiErrorResponse::Unauthorized) - .attach_printable("Failed to fetch merchant key store for the merchant id")? - } else { - key_store - } + let key_store = if platform_merchant_account.is_some() { + state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant.get_id(), + &state.store().get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant key store for the merchant id")? + } else { + key_store }; let profile = state @@ -583,15 +574,25 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = { - #[cfg(feature = "platform")] - { - get_platform_merchant_account(state, request_headers, merchant).await? - } - #[cfg(not(feature = "platform"))] - { - (merchant, None) - } + let (merchant, platform_merchant_account) = if state.conf().platform.enabled { + get_platform_merchant_account(state, request_headers, merchant).await? + } else { + (merchant, None) + }; + + let key_store = if platform_merchant_account.is_some() { + state + .store() + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant.get_id(), + &state.store().get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant key store for the merchant id")? + } else { + key_store }; let auth = AuthenticationData { @@ -790,15 +791,25 @@ where .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; // Get connected merchant account if API call is done by Platform merchant account on behalf of connected merchant account - let (merchant, platform_merchant_account) = { - #[cfg(feature = "platform")] - { - get_platform_merchant_account(state, request_headers, merchant).await? - } - #[cfg(not(feature = "platform"))] - { - (merchant, None) - } + let (merchant, platform_merchant_account) = if state.conf().platform.enabled { + get_platform_merchant_account(state, request_headers, merchant).await? + } else { + (merchant, None) + }; + + let key_store = if platform_merchant_account.is_some() { + state + .store() + .get_merchant_key_store_by_merchant_id( + &(&state.session_state()).into(), + merchant.get_id(), + &state.store().get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::Unauthorized) + .attach_printable("Failed to fetch merchant key store for the merchant id")? + } else { + key_store }; let auth = AuthenticationData { @@ -1076,8 +1087,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } AdminApiAuth .authenticate_and_fetch(request_headers, state) @@ -1139,8 +1151,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } AdminApiAuth .authenticate_and_fetch(request_headers, state) @@ -1242,7 +1255,6 @@ impl<'a> HeaderMapStruct<'a> { } // Remove feature flag if required. - #[cfg(feature = "platform")] pub fn get_id_type_from_header_if_present(&self, key: &str) -> RouterResult> where T: TryFrom< @@ -1335,8 +1347,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } AdminApiAuth .authenticate_and_fetch(request_headers, state) @@ -1399,8 +1412,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationDataWithoutProfile, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } AdminApiAuth .authenticate_and_fetch(request_headers, state) @@ -1478,8 +1492,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } let key_manager_state = &(&state.session_state()).into(); let key_store = state @@ -1524,8 +1539,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } let key_manager_state = &(&state.session_state()).into(); let profile_id = @@ -1590,8 +1606,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } let key_manager_state = &(&state.session_state()).into(); let key_store = state @@ -1712,8 +1729,9 @@ where request_headers: &HeaderMap, state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { - #[cfg(feature = "platform")] - throw_error_if_platform_merchant_authentication_required(request_headers)?; + if state.conf().platform.enabled { + throw_error_if_platform_merchant_authentication_required(request_headers)?; + } let publishable_key = get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; @@ -3376,7 +3394,6 @@ where } } -#[cfg(feature = "platform")] async fn get_connected_merchant_account( state: &A, connected_merchant_id: id_type::MerchantId, @@ -3412,7 +3429,6 @@ where Ok(connected_merchant_account) } -#[cfg(feature = "platform")] async fn get_platform_merchant_account( state: &A, request_headers: &HeaderMap, @@ -3450,7 +3466,6 @@ where } } -#[cfg(feature = "platform")] fn throw_error_if_platform_merchant_authentication_required( request_headers: &HeaderMap, ) -> RouterResult<()> { From d609bff5a66417d331172c2b262cc537fa54ef2e Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 19 Dec 2024 18:40:23 +0530 Subject: [PATCH 18/24] env flags --- config/deployments/integration_test.toml | 3 +++ config/deployments/production.toml | 3 +++ config/deployments/sandbox.toml | 3 +++ crates/router/Cargo.toml | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index fbf80ced5f17..61528c803c75 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -411,3 +411,6 @@ card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" + +[platform] +enabled = true diff --git a/config/deployments/production.toml b/config/deployments/production.toml index a22b618bfa21..2dc587813cb3 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -427,3 +427,6 @@ card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" + +[platform] +enabled = false diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 2defc5729cf2..3a348e9a3e54 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -429,3 +429,6 @@ card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" + +[platform] +enabled = false diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 619d2fb19375..ae6c0f4952b9 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" license.workspace = true [features] -default = ["common_default", "v1"] +default = ["common_default", "v2"] common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] tls = ["actix-web/rustls-0_22"] From 1ec12447caf28c7445c40d4d09a006593d003450 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Thu, 19 Dec 2024 19:37:20 +0530 Subject: [PATCH 19/24] v2 disbaled --- crates/router/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index ae6c0f4952b9..619d2fb19375 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" license.workspace = true [features] -default = ["common_default", "v2"] +default = ["common_default", "v1"] common_default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls", "partial-auth", "km_forward_x_request_id"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] tls = ["actix-web/rustls-0_22"] From d1f58800a5c78b214010e1412e5d7a47e4fdc8b8 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Fri, 20 Dec 2024 16:36:39 +0530 Subject: [PATCH 20/24] restructure getter function --- crates/router/src/services/authentication.rs | 42 +++++++++++--------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index f91695967125..51be1a6f82ec 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,7 +11,7 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, ext_traits::AsyncExt, id_type}; +use common_utils::{date_time, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -3437,7 +3437,28 @@ async fn get_platform_merchant_account( where A: SessionStateInfo + Sync, { - let connected_merchant_account = HeaderMapStruct::new(request_headers) + let connected_merchant_id = + get_and_validate_connected_merchant_id(request_headers, &merchant_account)?; + + match connected_merchant_id { + Some(merchant_id) => { + let connected_merchant_account = get_connected_merchant_account( + state, + merchant_id, + merchant_account.organization_id.clone(), + ) + .await?; + Ok((connected_merchant_account, Some(merchant_account))) + } + None => Ok((merchant_account, None)), + } +} + +fn get_and_validate_connected_merchant_id( + request_headers: &HeaderMap, + merchant_account: &domain::MerchantAccount, +) -> RouterResult> { + HeaderMapStruct::new(request_headers) .get_id_type_from_header_if_present::( headers::X_CONNECTED_MERCHANT_ID, )? @@ -3448,22 +3469,7 @@ where .ok_or(errors::ApiErrorResponse::InvalidPlatformOperation) }) .transpose() - .attach_printable("Non platform_merchant_account using X_CONNECTED_MERCHANT_ID header")? - .async_map(|merchant_id| { - get_connected_merchant_account( - state, - merchant_id, - merchant_account.organization_id.clone(), - ) - }) - .await - .transpose()?; - - if let Some(connected_merchant_account) = connected_merchant_account { - Ok((connected_merchant_account, Some(merchant_account))) - } else { - Ok((merchant_account, None)) - } + .attach_printable("Non platform_merchant_account using X_CONNECTED_MERCHANT_ID header") } fn throw_error_if_platform_merchant_authentication_required( From 13fbda22e1fbf5cdf0d677de85c4685a2e81e31e Mon Sep 17 00:00:00 2001 From: Narayanbhat166 Date: Sat, 21 Dec 2024 09:53:32 +0530 Subject: [PATCH 21/24] chore: increase min stack size in debug builds to solve postman errors --- crates/router/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/build.rs b/crates/router/build.rs index b33c168833d2..5475e0011d77 100644 --- a/crates/router/build.rs +++ b/crates/router/build.rs @@ -2,7 +2,7 @@ fn main() { // Set thread stack size to 8 MiB for debug builds // Reference: https://doc.rust-lang.org/std/thread/#stack-size #[cfg(debug_assertions)] - println!("cargo:rustc-env=RUST_MIN_STACK=8388608"); // 8 * 1024 * 1024 = 8 MiB + println!("cargo:rustc-env=RUST_MIN_STACK=12582912"); // 12 * 1024 * 1024 = 12 MiB #[cfg(feature = "vergen")] router_env::vergen::generate_cargo_instructions(); From 7102076d3fe8ea884b1b139481762ff3ea0ee488 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Sun, 22 Dec 2024 00:14:04 +0530 Subject: [PATCH 22/24] stack size and v2 --- crates/router/build.rs | 2 +- .../router/src/core/payments/operations/payment_capture_v2.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/router/build.rs b/crates/router/build.rs index 5475e0011d77..aa6c6eda681e 100644 --- a/crates/router/build.rs +++ b/crates/router/build.rs @@ -2,7 +2,7 @@ fn main() { // Set thread stack size to 8 MiB for debug builds // Reference: https://doc.rust-lang.org/std/thread/#stack-size #[cfg(debug_assertions)] - println!("cargo:rustc-env=RUST_MIN_STACK=12582912"); // 12 * 1024 * 1024 = 12 MiB + println!("cargo:rustc-env=RUST_MIN_STACK=14680064"); // 14 * 1024 * 1024 = 12 MiB #[cfg(feature = "vergen")] router_env::vergen::generate_cargo_instructions(); diff --git a/crates/router/src/core/payments/operations/payment_capture_v2.rs b/crates/router/src/core/payments/operations/payment_capture_v2.rs index 0ee62ea73c1f..81ffa8e992fd 100644 --- a/crates/router/src/core/payments/operations/payment_capture_v2.rs +++ b/crates/router/src/core/payments/operations/payment_capture_v2.rs @@ -142,6 +142,7 @@ impl GetTracker, PaymentsCaptureReques _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, + _platform_merchant_account: Option<&domain::MerchantAccount>, ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); From 009e6929890f6f1c4e20b30c8064ba4924c66850 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 23 Dec 2024 14:19:12 +0530 Subject: [PATCH 23/24] min stack to 10mb --- .github/workflows/postman-collection-runner.yml | 2 +- crates/router/build.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/postman-collection-runner.yml b/.github/workflows/postman-collection-runner.yml index de8235c14ea4..b8ce65f4b6c8 100644 --- a/.github/workflows/postman-collection-runner.yml +++ b/.github/workflows/postman-collection-runner.yml @@ -17,7 +17,7 @@ env: CONNECTORS: stripe RUST_BACKTRACE: short RUSTUP_MAX_RETRIES: 10 - RUST_MIN_STACK: 8388608 + RUST_MIN_STACK: 10485760 jobs: runner: diff --git a/crates/router/build.rs b/crates/router/build.rs index aa6c6eda681e..7c8043c48ade 100644 --- a/crates/router/build.rs +++ b/crates/router/build.rs @@ -1,8 +1,8 @@ fn main() { - // Set thread stack size to 8 MiB for debug builds + // Set thread stack size to 10 MiB for debug builds // Reference: https://doc.rust-lang.org/std/thread/#stack-size #[cfg(debug_assertions)] - println!("cargo:rustc-env=RUST_MIN_STACK=14680064"); // 14 * 1024 * 1024 = 12 MiB + println!("cargo:rustc-env=RUST_MIN_STACK=10485760"); // 10 * 1024 * 1024 = 10 MiB #[cfg(feature = "vergen")] router_env::vergen::generate_cargo_instructions(); From 4049c6ee6135503b05ed547ff5c35b4b58eaf528 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 23 Dec 2024 15:59:39 +0530 Subject: [PATCH 24/24] clean up --- config/development.toml | 3 +++ config/docker_compose.toml | 3 +++ .../src/errors/api_error_response.rs | 13 ++++++------- crates/router/src/services/authentication.rs | 3 --- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/config/development.toml b/config/development.toml index 133823d6cb37..7e3c3c8ba070 100644 --- a/config/development.toml +++ b/config/development.toml @@ -830,3 +830,6 @@ entity_logo_url = "https://example.com/logo.svg" # Logo URL of the entity to be foreground_color = "#000000" # Foreground color of email text primary_color = "#006DF9" # Primary color of email body background_color = "#FFFFFF" # Background color of email body + +[platform] +enabled = true diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 1ad2ef91336c..09b27d6bd7f4 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -710,3 +710,6 @@ entity_logo_url = "https://example.com/logo.svg" # Logo URL of the entity to be foreground_color = "#000000" # Foreground color of email text primary_color = "#006DF9" # Primary color of email body background_color = "#FFFFFF" # Background color of email body + +[platform] +enabled = true diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index f1888824c652..6fa9f9450c85 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -279,7 +279,10 @@ pub enum ApiErrorResponse { message = "Cookies are not found in the request" )] CookieNotFound, - + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_43", message = "API does not support platform account operation")] + PlatformAccountAuthNotSupported, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_44", message = "Invalid platform account operation")] + InvalidPlatformOperation, #[error(error_type = ErrorType::InvalidRequestError, code = "WE_01", message = "Failed to authenticate the webhook")] WebhookAuthenticationFailed, #[error(error_type = ErrorType::InvalidRequestError, code = "WE_02", message = "Bad request received in webhook")] @@ -298,10 +301,6 @@ pub enum ApiErrorResponse { field_names: String, connector_transaction_id: Option, }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_42", message = "API does not support platform account operation")] - PlatformAccountAuthNotSupported, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_43", message = "Invalid platform account operation")] - InvalidPlatformOperation, } #[derive(Clone)] @@ -672,10 +671,10 @@ impl ErrorSwitch for ApiErrorRespon }) )), Self::PlatformAccountAuthNotSupported => { - AER::BadRequest(ApiError::new("IR", 42, "API does not support platform operation", None)) + AER::BadRequest(ApiError::new("IR", 43, "API does not support platform operation", None)) } Self::InvalidPlatformOperation => { - AER::Unauthorized(ApiError::new("IR", 43, "Invalid platform account operation", None)) + AER::Unauthorized(ApiError::new("IR", 44, "Invalid platform account operation", None)) } } } diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 0e65a67a28f5..d35e321a7bea 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -775,7 +775,6 @@ where } #[cfg(all(feature = "partial-auth", feature = "v1"))] -#[allow(unused)] async fn construct_authentication_data( state: &A, merchant_id: &id_type::MerchantId, @@ -1269,7 +1268,6 @@ impl<'a> HeaderMapStruct<'a> { }) } - // Remove feature flag if required. pub fn get_id_type_from_header_if_present(&self, key: &str) -> RouterResult> where T: TryFrom< @@ -1537,7 +1535,6 @@ pub struct MerchantIdAuth(pub id_type::MerchantId); #[cfg(feature = "v1")] #[async_trait] -#[allow(unused)] impl AuthenticateAndFetch for MerchantIdAuth where A: SessionStateInfo + Sync,