diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 6b9928734cef..efde4a048323 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -609,6 +609,9 @@ pub struct MerchantConnectorCreate { pub profile_id: Option, pub pm_auth_config: Option, + + #[schema(value_type = ConnectorStatus, example = "inactive")] + pub status: Option, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] @@ -714,6 +717,9 @@ pub struct MerchantConnectorResponse { pub applepay_verified_domains: Option>, pub pm_auth_config: Option, + + #[schema(value_type = ConnectorStatus, example = "inactive")] + pub status: api_enums::ConnectorStatus, } /// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc." @@ -788,6 +794,9 @@ pub struct MerchantConnectorUpdate { pub connector_webhook_details: Option, pub pm_auth_config: Option, + + #[schema(value_type = ConnectorStatus, example = "inactive")] + pub status: Option, } ///Details of FrmConfigs are mentioned here... it should be passed in payment connector create api call, and stored in merchant_connector_table diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 8b1437fa8926..cf3c398f8f48 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1857,3 +1857,25 @@ pub enum ApplePayFlow { Simplified, Manual, } + +#[derive( + Clone, + Copy, + Debug, + Eq, + PartialEq, + strum::Display, + strum::EnumString, + serde::Deserialize, + serde::Serialize, + ToSchema, + Default, +)] +#[router_derive::diesel_enum(storage_type = "pg_enum")] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum ConnectorStatus { + #[default] + Inactive, + Active, +} diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index ec021f0f51a5..817fee633190 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -3,9 +3,10 @@ pub mod diesel_exports { pub use super::{ DbAttemptStatus as AttemptStatus, DbAuthenticationType as AuthenticationType, DbCaptureMethod as CaptureMethod, DbCaptureStatus as CaptureStatus, - DbConnectorType as ConnectorType, DbCountryAlpha2 as CountryAlpha2, DbCurrency as Currency, - DbDisputeStage as DisputeStage, DbDisputeStatus as DisputeStatus, - DbEventClass as EventClass, DbEventObjectType as EventObjectType, DbEventType as EventType, + DbConnectorStatus as ConnectorStatus, DbConnectorType as ConnectorType, + DbCountryAlpha2 as CountryAlpha2, DbCurrency as Currency, DbDisputeStage as DisputeStage, + DbDisputeStatus as DisputeStatus, DbEventClass as EventClass, + DbEventObjectType as EventObjectType, DbEventType as EventType, DbFraudCheckStatus as FraudCheckStatus, DbFraudCheckType as FraudCheckType, DbFutureUsage as FutureUsage, DbIntentStatus as IntentStatus, DbMandateStatus as MandateStatus, DbMandateType as MandateType, diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index a4faa45ce4bc..e45ef0026261 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -42,6 +42,7 @@ pub struct MerchantConnectorAccount { #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, + pub status: storage_enums::ConnectorStatus, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] @@ -70,6 +71,7 @@ pub struct MerchantConnectorAccountNew { #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, + pub status: storage_enums::ConnectorStatus, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] @@ -93,6 +95,7 @@ pub struct MerchantConnectorAccountUpdateInternal { #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, + pub status: Option, } impl MerchantConnectorAccountUpdateInternal { @@ -115,6 +118,7 @@ impl MerchantConnectorAccountUpdateInternal { frm_config: self.frm_config, modified_at: self.modified_at.unwrap_or(source.modified_at), pm_auth_config: self.pm_auth_config, + status: self.status.unwrap_or(source.status), ..source } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index e9db5714bed8..190a123185e4 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -492,6 +492,7 @@ diesel::table! { profile_id -> Nullable, applepay_verified_domains -> Nullable>>, pm_auth_config -> Nullable, + status -> ConnectorStatus, } } diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index ecea12203f8a..6105dc85d7e6 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -65,6 +65,7 @@ fn build_test_data<'a>(total_enabled: usize, total_pm_types: usize) -> graph::Kn profile_id: None, applepay_verified_domains: None, pm_auth_config: None, + status: api_enums::ConnectorStatus::Inactive, }; kgraph_utils::mca::make_mca_graph(vec![stripe_account]).expect("Failed graph construction") diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 34babd7a02bd..deea51bd8808 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -410,6 +410,7 @@ mod tests { profile_id: None, applepay_verified_domains: None, pm_auth_config: None, + status: api_enums::ConnectorStatus::Inactive, }; make_mca_graph(vec![stripe_account]).expect("Failed graph construction") diff --git a/crates/router/src/connector/fiserv/transformers.rs b/crates/router/src/connector/fiserv/transformers.rs index 2d07da7f47a4..f8d88d08c6ba 100644 --- a/crates/router/src/connector/fiserv/transformers.rs +++ b/crates/router/src/connector/fiserv/transformers.rs @@ -1,4 +1,4 @@ -use common_utils::ext_traits::ValueExt; +use common_utils::{ext_traits::ValueExt, pii}; use error_stack::ResultExt; use serde::{Deserialize, Serialize}; @@ -150,9 +150,11 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP merchant_transaction_id: item.router_data.connector_request_reference_id.clone(), }; let metadata = item.router_data.get_connector_meta()?; - let session: SessionObject = metadata - .parse_value("SessionObject") - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let session: FiservSessionObject = metadata + .parse_value("FiservSessionObject") + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "Merchant connector account metadata", + })?; let merchant_details = MerchantDetails { merchant_id: auth.merchant_account, @@ -230,9 +232,11 @@ impl TryFrom<&types::PaymentsCancelRouterData> for FiservCancelRequest { fn try_from(item: &types::PaymentsCancelRouterData) -> Result { let auth: FiservAuthType = FiservAuthType::try_from(&item.connector_auth_type)?; let metadata = item.get_connector_meta()?; - let session: SessionObject = metadata - .parse_value("SessionObject") - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let session: FiservSessionObject = metadata + .parse_value("FiservSessionObject") + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "Merchant connector account metadata", + })?; Ok(Self { merchant_details: MerchantDetails { merchant_id: auth.merchant_account, @@ -418,11 +422,21 @@ pub struct ReferenceTransactionDetails { } #[derive(Debug, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SessionObject { +pub struct FiservSessionObject { pub terminal_id: String, } +impl TryFrom<&Option> for FiservSessionObject { + type Error = error_stack::Report; + fn try_from(meta_data: &Option) -> Result { + let metadata: Self = utils::to_connector_meta_from_secret::(meta_data.clone()) + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "metadata", + })?; + Ok(metadata) + } +} + impl TryFrom<&FiservRouterData<&types::PaymentsCaptureRouterData>> for FiservCaptureRequest { type Error = error_stack::Report; fn try_from( @@ -434,9 +448,11 @@ impl TryFrom<&FiservRouterData<&types::PaymentsCaptureRouterData>> for FiservCap .connector_meta_data .clone() .ok_or(errors::ConnectorError::RequestEncodingFailed)?; - let session: SessionObject = metadata - .parse_value("SessionObject") - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let session: FiservSessionObject = metadata + .parse_value("FiservSessionObject") + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "Merchant connector account metadata", + })?; Ok(Self { amount: Amount { total: item.amount.clone(), @@ -527,9 +543,11 @@ impl TryFrom<&FiservRouterData<&types::RefundsRouterData>> for FiservRefun .connector_meta_data .clone() .ok_or(errors::ConnectorError::RequestEncodingFailed)?; - let session: SessionObject = metadata - .parse_value("SessionObject") - .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let session: FiservSessionObject = metadata + .parse_value("FiservSessionObject") + .change_context(errors::ConnectorError::InvalidConnectorConfig { + config: "Merchant connector account metadata", + })?; Ok(Self { amount: Amount { total: item.amount.clone(), diff --git a/crates/router/src/connector/square/transformers.rs b/crates/router/src/connector/square/transformers.rs index 54a7c461dbfc..dfb49e8e6775 100644 --- a/crates/router/src/connector/square/transformers.rs +++ b/crates/router/src/connector/square/transformers.rs @@ -334,6 +334,7 @@ impl TryFrom<&types::ConnectorAuthType> for SquareAuthType { | types::ConnectorAuthType::SignatureKey { .. } | types::ConnectorAuthType::MultiAuthKey { .. } | types::ConnectorAuthType::CurrencyAuthKey { .. } + | types::ConnectorAuthType::TemporaryAuth { .. } | types::ConnectorAuthType::NoKey { .. } => { Err(errors::ConnectorError::FailedToObtainAuthType.into()) } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 39b4749535b7..3a0c938c32b4 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -868,6 +868,15 @@ pub async fn create_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("error updating the merchant account when creating payment connector")?; + let (connector_status, disabled) = validate_status_and_disabled( + req.status, + req.disabled, + auth, + // The validate_status_and_disabled function will use this value only + // when the status can be active. So we are passing this as fallback. + api_enums::ConnectorStatus::Active, + )?; + let merchant_connector_account = domain::MerchantConnectorAccount { merchant_id: merchant_id.to_string(), connector_type: req.connector_type, @@ -886,7 +895,7 @@ pub async fn create_payment_connector( .attach_printable("Unable to encrypt connector account details")?, payment_methods_enabled, test_mode: req.test_mode, - disabled: req.disabled, + disabled, metadata: req.metadata, frm_configs, connector_label: Some(connector_label), @@ -911,6 +920,7 @@ pub async fn create_payment_connector( profile_id: Some(profile_id.clone()), applepay_verified_domains: None, pm_auth_config: req.pm_auth_config.clone(), + status: connector_status, }; let mut default_routing_config = @@ -1083,6 +1093,19 @@ pub async fn update_payment_connector( let frm_configs = get_frm_config_as_secret(req.frm_configs); + let auth: types::ConnectorAuthType = req + .connector_account_details + .clone() + .unwrap_or(mca.connector_account_details.clone().into_inner()) + .parse_value("ConnectorAuthType") + .change_context(errors::ApiErrorResponse::InvalidDataFormat { + field_name: "connector_account_details".to_string(), + expected_format: "auth_type and api_key".to_string(), + })?; + + let (connector_status, disabled) = + validate_status_and_disabled(req.status, req.disabled, auth, mca.status)?; + let payment_connector = storage::MerchantConnectorAccountUpdate::Update { merchant_id: None, connector_type: Some(req.connector_type), @@ -1098,7 +1121,7 @@ pub async fn update_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting data")?, test_mode: req.test_mode, - disabled: req.disabled, + disabled, payment_methods_enabled, metadata: req.metadata, frm_configs, @@ -1115,6 +1138,7 @@ pub async fn update_payment_connector( }, applepay_verified_domains: None, pm_auth_config: req.pm_auth_config, + status: Some(connector_status), }; let updated_mca = db @@ -1565,6 +1589,7 @@ pub(crate) fn validate_auth_and_metadata_type( } api_enums::Connector::Fiserv => { fiserv::transformers::FiservAuthType::try_from(val)?; + fiserv::transformers::FiservSessionObject::try_from(connector_meta_data)?; Ok(()) } api_enums::Connector::Forte => { @@ -1722,3 +1747,37 @@ pub async fn validate_dummy_connector_enabled( Ok(()) } } + +pub fn validate_status_and_disabled( + status: Option, + disabled: Option, + auth: types::ConnectorAuthType, + current_status: api_enums::ConnectorStatus, +) -> RouterResult<(api_enums::ConnectorStatus, Option)> { + let connector_status = match (status, auth) { + (Some(common_enums::ConnectorStatus::Active), types::ConnectorAuthType::TemporaryAuth) => { + return Err(errors::ApiErrorResponse::InvalidRequestData { + message: "Connector status cannot be active when using TemporaryAuth".to_string(), + } + .into()); + } + (Some(status), _) => status, + (None, types::ConnectorAuthType::TemporaryAuth) => common_enums::ConnectorStatus::Inactive, + (None, _) => current_status, + }; + + let disabled = match (disabled, connector_status) { + (Some(true), common_enums::ConnectorStatus::Inactive) => { + return Err(errors::ApiErrorResponse::InvalidRequestData { + message: "Connector cannot be enabled when connector_status is inactive or when using TemporaryAuth" + .to_string(), + } + .into()); + } + (Some(disabled), _) => Some(disabled), + (None, common_enums::ConnectorStatus::Inactive) => Some(true), + (None, _) => None, + }; + + Ok((connector_status, disabled)) +} diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index 433430507fb1..56960d3cb480 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -60,6 +60,7 @@ pub async fn check_existence_and_add_domain_to_db( applepay_verified_domains: Some(already_verified_domains.clone()), pm_auth_config: None, connector_label: None, + status: None, }; state .store diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index ecf52531f28a..4fbb8f19ccff 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -643,6 +643,7 @@ impl MerchantConnectorAccountInterface for MockDb { profile_id: t.profile_id, applepay_verified_domains: t.applepay_verified_domains, pm_auth_config: t.pm_auth_config, + status: t.status, }; accounts.push(account.clone()); account @@ -839,6 +840,7 @@ mod merchant_connector_account_cache_tests { profile_id: Some(profile_id.to_string()), applepay_verified_domains: None, pm_auth_config: None, + status: common_enums::ConnectorStatus::Inactive, }; db.insert_merchant_connector_account(mca.clone(), &merchant_key) diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs index 095e1f45f93f..04ef90546cfa 100644 --- a/crates/router/src/openapi.rs +++ b/crates/router/src/openapi.rs @@ -174,6 +174,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::enums::AttemptStatus, api_models::enums::CaptureStatus, api_models::enums::ReconStatus, + api_models::enums::ConnectorStatus, api_models::admin::MerchantConnectorCreate, api_models::admin::MerchantConnectorUpdate, api_models::admin::PrimaryBusinessDetails, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 7cf8f6b71fa5..ceeb93f69763 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -900,6 +900,7 @@ pub struct ResponseRouterData { #[derive(Default, Debug, Clone, serde::Deserialize)] #[serde(tag = "auth_type")] pub enum ConnectorAuthType { + TemporaryAuth, HeaderKey { api_key: Secret, }, diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index 58c2e018316c..c84abbefc381 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -35,6 +35,7 @@ pub struct MerchantConnectorAccount { pub profile_id: Option, pub applepay_verified_domains: Option>, pub pm_auth_config: Option, + pub status: enums::ConnectorStatus, } #[derive(Debug)] @@ -54,6 +55,7 @@ pub enum MerchantConnectorAccountUpdate { applepay_verified_domains: Option>, pm_auth_config: Option, connector_label: Option, + status: Option, }, } @@ -89,6 +91,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { profile_id: self.profile_id, applepay_verified_domains: self.applepay_verified_domains, pm_auth_config: self.pm_auth_config, + status: self.status, }, ) } @@ -128,6 +131,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { profile_id: other.profile_id, applepay_verified_domains: other.applepay_verified_domains, pm_auth_config: other.pm_auth_config, + status: other.status, }) } @@ -155,6 +159,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { profile_id: self.profile_id, applepay_verified_domains: self.applepay_verified_domains, pm_auth_config: self.pm_auth_config, + status: self.status, }) } } @@ -177,6 +182,7 @@ impl From for MerchantConnectorAccountUpdateInte applepay_verified_domains, pm_auth_config, connector_label, + status, } => Self { merchant_id, connector_type, @@ -194,6 +200,7 @@ impl From for MerchantConnectorAccountUpdateInte applepay_verified_domains, pm_auth_config, connector_label, + status, }, } } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 3ffba5aff50a..2b7ea86cf51d 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -852,6 +852,7 @@ impl TryFrom for api_models::admin::MerchantCo profile_id: item.profile_id, applepay_verified_domains: item.applepay_verified_domains, pm_auth_config: item.pm_auth_config, + status: item.status, }) } } diff --git a/migrations/2023-11-12-131143_connector-status-column/down.sql b/migrations/2023-11-12-131143_connector-status-column/down.sql new file mode 100644 index 000000000000..9463f4d77135 --- /dev/null +++ b/migrations/2023-11-12-131143_connector-status-column/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS status; +DROP TYPE IF EXISTS "ConnectorStatus"; diff --git a/migrations/2023-11-12-131143_connector-status-column/up.sql b/migrations/2023-11-12-131143_connector-status-column/up.sql new file mode 100644 index 000000000000..7a992d142d6f --- /dev/null +++ b/migrations/2023-11-12-131143_connector-status-column/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here +CREATE TYPE "ConnectorStatus" AS ENUM ('active', 'inactive'); + +ALTER TABLE merchant_connector_account +ADD COLUMN status "ConnectorStatus"; + +UPDATE merchant_connector_account SET status='active'; + +ALTER TABLE merchant_connector_account +ALTER COLUMN status SET NOT NULL, +ALTER COLUMN status SET DEFAULT 'inactive'; diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index be66a1bff92c..7d94f13dd125 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -4147,6 +4147,13 @@ } } }, + "ConnectorStatus": { + "type": "string", + "enum": [ + "inactive", + "active" + ] + }, "ConnectorType": { "type": "string", "enum": [ @@ -6871,7 +6878,8 @@ "description": "Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc.\"", "required": [ "connector_type", - "connector_name" + "connector_name", + "status" ], "properties": { "connector_type": { @@ -7002,6 +7010,9 @@ }, "pm_auth_config": { "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ConnectorStatus" } } }, @@ -7087,7 +7098,8 @@ "required": [ "connector_type", "connector_name", - "merchant_connector_id" + "merchant_connector_id", + "status" ], "properties": { "connector_type": { @@ -7230,6 +7242,9 @@ }, "pm_auth_config": { "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ConnectorStatus" } } }, @@ -7237,7 +7252,8 @@ "type": "object", "description": "Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc.\"", "required": [ - "connector_type" + "connector_type", + "status" ], "properties": { "connector_type": { @@ -7335,6 +7351,9 @@ }, "pm_auth_config": { "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ConnectorStatus" } } },