diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index e637e70b8112..48739ac77f93 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -9024,6 +9024,14 @@ "example": "y3oqhf46pyzuxjbcn2giaqnb44", "maxLength": 64, "minLength": 1 + }, + "additional_merchant_data": { + "allOf": [ + { + "$ref": "#/components/schemas/AdditionalMerchantData" + } + ], + "nullable": true } }, "additionalProperties": false diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index a23e7093a2ca..e08644d778f8 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -13201,6 +13201,14 @@ }, "status": { "$ref": "#/components/schemas/ConnectorStatus" + }, + "additional_merchant_data": { + "allOf": [ + { + "$ref": "#/components/schemas/AdditionalMerchantData" + } + ], + "nullable": true } }, "additionalProperties": false diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 8ef5d371aa1b..55a2444973da 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1556,6 +1556,10 @@ pub struct MerchantConnectorUpdate { #[schema(value_type = ConnectorStatus, example = "inactive")] pub status: Option, + + /// In case the merchant needs to store any additional sensitive data + #[schema(value_type = Option)] + pub additional_merchant_data: Option, } /// 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." @@ -1637,6 +1641,10 @@ pub struct MerchantConnectorUpdate { /// The identifier for the Merchant Account #[schema(value_type = String, max_length = 64, min_length = 1, example = "y3oqhf46pyzuxjbcn2giaqnb44")] pub merchant_id: id_type::MerchantId, + + /// In case the merchant needs to store any additional sensitive data + #[schema(value_type = Option)] + pub additional_merchant_data: Option, } #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index 61f2af238ed7..e674b9b75871 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -203,6 +203,7 @@ pub struct MerchantConnectorAccountUpdateInternal { pub pm_auth_config: Option, pub status: Option, pub connector_wallets_details: Option, + pub additional_merchant_data: Option, } #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] @@ -224,6 +225,7 @@ pub struct MerchantConnectorAccountUpdateInternal { pub pm_auth_config: Option, pub status: Option, pub connector_wallets_details: Option, + pub additional_merchant_data: Option, } #[cfg(all( diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 76b7cd4a85af..a841e2d1507d 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -109,6 +109,7 @@ pub enum MerchantConnectorAccountUpdate { connector_label: Option, status: Option, connector_wallets_details: Option>, + additional_merchant_data: Option>, }, ConnectorWalletDetailsUpdate { connector_wallets_details: Encryptable, @@ -131,6 +132,7 @@ pub enum MerchantConnectorAccountUpdate { connector_label: Option, status: Option, connector_wallets_details: Option>, + additional_merchant_data: Option>, }, ConnectorWalletDetailsUpdate { connector_wallets_details: Encryptable, @@ -453,6 +455,7 @@ impl From for MerchantConnectorAccountUpdateInte connector_label, status, connector_wallets_details, + additional_merchant_data, } => Self { connector_type, connector_name, @@ -471,6 +474,7 @@ impl From for MerchantConnectorAccountUpdateInte connector_label, status, connector_wallets_details: connector_wallets_details.map(Encryption::from), + additional_merchant_data: additional_merchant_data.map(Encryption::from), }, MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate { connector_wallets_details, @@ -492,6 +496,7 @@ impl From for MerchantConnectorAccountUpdateInte applepay_verified_domains: None, pm_auth_config: None, status: None, + additional_merchant_data: None, }, } } @@ -514,6 +519,7 @@ impl From for MerchantConnectorAccountUpdateInte connector_label, status, connector_wallets_details, + additional_merchant_data, } => Self { connector_type, connector_account_details: connector_account_details.map(Encryption::from), @@ -528,6 +534,7 @@ impl From for MerchantConnectorAccountUpdateInte connector_label, status, connector_wallets_details: connector_wallets_details.map(Encryption::from), + additional_merchant_data: additional_merchant_data.map(Encryption::from), }, MerchantConnectorAccountUpdate::ConnectorWalletDetailsUpdate { connector_wallets_details, @@ -545,6 +552,7 @@ impl From for MerchantConnectorAccountUpdateInte applepay_verified_domains: None, pm_auth_config: None, status: None, + additional_merchant_data: None, }, } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index f78d5dca2e0d..553de8c56082 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2080,6 +2080,30 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect pm_auth_config_validation.validate_pm_auth_config().await?; + let merchant_recipient_data = if let Some(data) = &self.additional_merchant_data { + Some( + process_open_banking_connectors( + state, + merchant_account.get_id(), + &auth, + &self.connector_type, + &connector_enum, + types::AdditionalMerchantData::foreign_from(data.clone()), + ) + .await?, + ) + } else { + None + } + .map(|data| { + serde_json::to_value(types::AdditionalMerchantData::OpenBankingRecipientData( + data, + )) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to serialize MerchantRecipientData")?; + Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), connector_label: self.connector_label.clone(), @@ -2114,6 +2138,23 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect applepay_verified_domains: None, pm_auth_config: self.pm_auth_config, status: Some(connector_status), + additional_merchant_data: if let Some(mcd) = merchant_recipient_data { + Some( + domain_types::crypto_operation( + key_manager_state, + type_name!(domain::MerchantConnectorAccount), + domain_types::CryptoOperation::Encrypt(Secret::new(mcd)), + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.peek(), + ) + .await + .and_then(|val| val.try_into_operation()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt additional_merchant_data")?, + ) + } else { + None + }, connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( state, &key_store, &metadata, ) @@ -2216,6 +2257,30 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect } } + let merchant_recipient_data = if let Some(data) = &self.additional_merchant_data { + Some( + process_open_banking_connectors( + state, + merchant_account.get_id(), + &auth, + &self.connector_type, + &connector_enum, + types::AdditionalMerchantData::foreign_from(data.clone()), + ) + .await?, + ) + } else { + None + } + .map(|data| { + serde_json::to_value(types::AdditionalMerchantData::OpenBankingRecipientData( + data, + )) + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to serialize MerchantRecipientData")?; + Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), connector_name: None, @@ -2253,6 +2318,23 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect applepay_verified_domains: None, pm_auth_config: self.pm_auth_config, status: Some(connector_status), + additional_merchant_data: if let Some(mcd) = merchant_recipient_data { + Some( + domain_types::crypto_operation( + key_manager_state, + type_name!(domain::MerchantConnectorAccount), + domain_types::CryptoOperation::Encrypt(Secret::new(mcd)), + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.peek(), + ) + .await + .and_then(|val| val.try_into_operation()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt additional_merchant_data")?, + ) + } else { + None + }, connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( state, &key_store, &metadata, ) @@ -2349,7 +2431,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get MerchantRecipientData")?; + .attach_printable("Failed to serialize MerchantRecipientData")?; Ok(domain::MerchantConnectorAccount { merchant_id: business_profile.merchant_id.clone(), connector_type: self.connector_type, @@ -2398,7 +2480,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { key_manager_state, type_name!(domain::MerchantConnectorAccount), domain_types::CryptoOperation::Encrypt(Secret::new(mcd)), - identifier, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.peek(), ) .await @@ -2518,7 +2600,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get MerchantRecipientData")?; + .attach_printable("Failed to serialize MerchantRecipientData")?; Ok(domain::MerchantConnectorAccount { merchant_id: business_profile.merchant_id.clone(), connector_type: self.connector_type, diff --git a/crates/router/src/core/connector_onboarding/paypal.rs b/crates/router/src/core/connector_onboarding/paypal.rs index 13b2bf16cdb5..f967f2aece3f 100644 --- a/crates/router/src/core/connector_onboarding/paypal.rs +++ b/crates/router/src/core/connector_onboarding/paypal.rs @@ -164,6 +164,7 @@ pub async fn update_mca( connector_webhook_details: None, pm_auth_config: None, test_mode: None, + additional_merchant_data: None, }; #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] let request = MerchantConnectorUpdate { @@ -178,6 +179,7 @@ pub async fn update_mca( connector_webhook_details: None, pm_auth_config: None, merchant_id: merchant_id.clone(), + additional_merchant_data: None, }; let mca_response = admin::update_connector(state.clone(), &merchant_id, None, &connector_id, request).await?; diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index 4b8e0ac0748c..c4fbf7ab0813 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -87,6 +87,7 @@ pub async fn check_existence_and_add_domain_to_db( connector_label: None, status: None, connector_wallets_details: None, + additional_merchant_data: None, }; #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] let updated_mca = storage::MerchantConnectorAccountUpdate::Update { @@ -102,6 +103,7 @@ pub async fn check_existence_and_add_domain_to_db( connector_label: None, status: None, connector_wallets_details: None, + additional_merchant_data: None, }; state .store