From d1f9f85697ada317cff09d4a71ea1e317de56b55 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 21 Aug 2024 14:55:14 +0530 Subject: [PATCH 1/4] fix(open_banking): Added merchant data update in mca update --- crates/api_models/src/admin.rs | 8 ++ .../src/merchant_connector_account.rs | 2 + .../src/merchant_connector_account.rs | 8 ++ crates/router/src/core/admin.rs | 84 ++++++++++++++++++- .../src/core/connector_onboarding/paypal.rs | 2 + crates/router/src/core/verification/utils.rs | 2 + 6 files changed, 105 insertions(+), 1 deletion(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index b073c2195178..e4f61fc2e412 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1555,6 +1555,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." @@ -1636,6 +1640,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 876240f44a3d..19fe880f3aa3 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 9e2f6918b43f..d763531f3bd6 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 1f5fb0684084..3a68607625da 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2027,6 +2027,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 get MerchantRecipientData")?; + Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), connector_label: self.connector_label.clone(), @@ -2061,6 +2085,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)), + identifier, + 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, ) @@ -2163,6 +2204,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 get MerchantRecipientData")?; + Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), connector_name: None, @@ -2200,6 +2265,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, ) @@ -2346,7 +2428,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 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 dd0aa3edef42..012f04299dac 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 From f4e63ac5257c8112938324ceea57b17c0f5613ea Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:08:02 +0000 Subject: [PATCH 2/4] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 8 ++++++++ api-reference/openapi_spec.json | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 458e1ae9ff0a..b68a5c91c8b8 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -7766,6 +7766,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 8b082caa3733..14f4953c5b40 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -12598,6 +12598,14 @@ }, "status": { "$ref": "#/components/schemas/ConnectorStatus" + }, + "additional_merchant_data": { + "allOf": [ + { + "$ref": "#/components/schemas/AdditionalMerchantData" + } + ], + "nullable": true } }, "additionalProperties": false From 52de237f1236ed1dd228d9141101f531dcb7e39d Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 22 Aug 2024 16:29:13 +0530 Subject: [PATCH 3/4] fix(open_banking): Resolved comments --- crates/router/src/core/admin.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 3a68607625da..a5a75ac82e5f 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2049,7 +2049,7 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get MerchantRecipientData")?; + .attach_printable("Failed to serialize MerchantRecipientData")?; Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), @@ -2226,7 +2226,7 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get MerchantRecipientData")?; + .attach_printable("Failed to serialize MerchantRecipientData")?; Ok(storage::MerchantConnectorAccountUpdate::Update { connector_type: Some(self.connector_type), @@ -2379,7 +2379,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, @@ -2545,7 +2545,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, From 5d8ac7b1bdd03f2b2429c456dd98cb7ba1339971 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 22 Aug 2024 17:19:57 +0530 Subject: [PATCH 4/4] fix(open_banking): Fixed errors --- crates/router/src/core/admin.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index a5a75ac82e5f..da497c77f16c 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2091,7 +2091,7 @@ impl MerchantConnectorAccountUpdateBridge for api_models::admin::MerchantConnect 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