mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	refactor: Move trait IncomingWebhook to hyperswitch_interfaces (#5191)
This commit is contained in:
		| @ -7,7 +7,7 @@ use base64::Engine; | ||||
| use common_utils::request::RequestContent; | ||||
| use diesel_models::{enums as storage_enums, enums}; | ||||
| use error_stack::{report, ResultExt}; | ||||
| use masking::ExposeInterface; | ||||
| use masking::{ExposeInterface, Secret}; | ||||
| use ring::hmac; | ||||
| use router_env::{instrument, tracing}; | ||||
|  | ||||
| @ -1754,15 +1754,16 @@ impl api::IncomingWebhook for Adyen { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -1774,7 +1775,7 @@ impl api::IncomingWebhook for Adyen { | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -7,7 +7,7 @@ use base64::Engine; | ||||
| use common_utils::{crypto, ext_traits::XmlExt, request::RequestContent}; | ||||
| use diesel_models::enums; | ||||
| use error_stack::{report, Report, ResultExt}; | ||||
| use masking::{ExposeInterface, PeekInterface}; | ||||
| use masking::{ExposeInterface, PeekInterface, Secret}; | ||||
| use ring::hmac; | ||||
| use sha1::{Digest, Sha1}; | ||||
|  | ||||
| @ -31,7 +31,6 @@ use crate::{ | ||||
|     types::{ | ||||
|         self, | ||||
|         api::{self, ConnectorCommon, ConnectorCommonExt}, | ||||
|         domain, | ||||
|         transformers::ForeignFrom, | ||||
|         ErrorResponse, | ||||
|     }, | ||||
| @ -180,7 +179,7 @@ impl ConnectorValidation for Braintree { | ||||
|     fn validate_mandate_payment( | ||||
|         &self, | ||||
|         pm_type: Option<types::storage::enums::PaymentMethodType>, | ||||
|         pm_data: domain::payments::PaymentMethodData, | ||||
|         pm_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData, | ||||
|     ) -> CustomResult<(), errors::ConnectorError> { | ||||
|         let mandate_supported_pmd = std::collections::HashSet::from([PaymentMethodDataType::Card]); | ||||
|         connector_utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id()) | ||||
| @ -1377,15 +1376,16 @@ impl api::IncomingWebhook for Braintree { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -1397,7 +1397,7 @@ impl api::IncomingWebhook for Braintree { | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -24,7 +24,7 @@ use crate::{ | ||||
|     types::{ | ||||
|         self, | ||||
|         api::{self, ConnectorCommon, ConnectorCommonExt}, | ||||
|         domain, storage, ErrorResponse, Response, | ||||
|         storage, ErrorResponse, Response, | ||||
|     }, | ||||
|     utils::{ByteSliceExt, BytesExt}, | ||||
| }; | ||||
| @ -383,15 +383,16 @@ impl api::IncomingWebhook for Cashtocode { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -5,6 +5,7 @@ use std::fmt::Debug; | ||||
|  | ||||
| use common_utils::{ext_traits::ByteSliceExt, request::RequestContent}; | ||||
| use error_stack::ResultExt; | ||||
| use hyperswitch_interfaces::authentication::ExternalAuthenticationPayload; | ||||
| use transformers as netcetera; | ||||
|  | ||||
| use crate::{ | ||||
| @ -203,12 +204,12 @@ impl api::IncomingWebhook for Netcetera { | ||||
|     fn get_external_authentication_details( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<api::ExternalAuthenticationPayload, errors::ConnectorError> { | ||||
|     ) -> CustomResult<ExternalAuthenticationPayload, errors::ConnectorError> { | ||||
|         let webhook_body: netcetera::ResultsResponseData = request | ||||
|             .body | ||||
|             .parse_struct("netcetera ResultsResponseData") | ||||
|             .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; | ||||
|         Ok(api::ExternalAuthenticationPayload { | ||||
|         Ok(ExternalAuthenticationPayload { | ||||
|             trans_status: webhook_body | ||||
|                 .trans_status | ||||
|                 .unwrap_or(common_enums::TransactionStatus::InformationOnly), | ||||
|  | ||||
| @ -11,7 +11,7 @@ use common_utils::{ | ||||
| }; | ||||
| use diesel_models::enums; | ||||
| use error_stack::{Report, ResultExt}; | ||||
| use masking::ExposeInterface; | ||||
| use masking::{ExposeInterface, Secret}; | ||||
| use transformers as payme; | ||||
|  | ||||
| use crate::{ | ||||
| @ -1160,8 +1160,9 @@ impl api::IncomingWebhook for Payme { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let algorithm = self | ||||
| @ -1170,9 +1171,9 @@ impl api::IncomingWebhook for Payme { | ||||
|  | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -1184,7 +1185,7 @@ impl api::IncomingWebhook for Payme { | ||||
|         let mut message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -9,7 +9,7 @@ use common_utils::{ | ||||
| }; | ||||
| use diesel_models::enums; | ||||
| use error_stack::{Report, ResultExt}; | ||||
| use masking::{ExposeInterface, PeekInterface}; | ||||
| use masking::{ExposeInterface, PeekInterface, Secret}; | ||||
| use rand::distributions::{Alphanumeric, DistString}; | ||||
| use ring::hmac; | ||||
| use transformers as rapyd; | ||||
| @ -29,7 +29,7 @@ use crate::{ | ||||
|     types::{ | ||||
|         self, | ||||
|         api::{self, ConnectorCommon}, | ||||
|         domain, ErrorResponse, | ||||
|         ErrorResponse, | ||||
|     }, | ||||
|     utils::{self, crypto, ByteSliceExt, BytesExt}, | ||||
| }; | ||||
| @ -819,15 +819,16 @@ impl api::IncomingWebhook for Rapyd { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -837,7 +838,7 @@ impl api::IncomingWebhook for Rapyd { | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -7,7 +7,6 @@ use common_utils::{ | ||||
| use error_stack::{Report, ResultExt}; | ||||
| use masking::ExposeInterface; | ||||
| use transformers as razorpay; | ||||
| use types::domain; | ||||
|  | ||||
| use super::utils::{self as connector_utils}; | ||||
| use crate::{ | ||||
| @ -644,8 +643,11 @@ impl api::IncomingWebhook for Razorpay { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         _request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         _merchant_account: &domain::MerchantAccount, | ||||
|         _merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         _merchant_id: &str, | ||||
|         _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: common_utils::crypto::Encryptable< | ||||
|             masking::Secret<serde_json::Value>, | ||||
|         >, | ||||
|         _connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         Ok(false) | ||||
|  | ||||
| @ -8,7 +8,7 @@ use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; | ||||
| #[cfg(feature = "frm")] | ||||
| use error_stack::ResultExt; | ||||
| #[cfg(feature = "frm")] | ||||
| use masking::{ExposeInterface, PeekInterface}; | ||||
| use masking::{ExposeInterface, PeekInterface, Secret}; | ||||
| #[cfg(feature = "frm")] | ||||
| use ring::hmac; | ||||
| #[cfg(feature = "frm")] | ||||
| @ -31,9 +31,7 @@ use crate::{ | ||||
|     events::connector_api_logs::ConnectorEvent, | ||||
|     headers, | ||||
|     services::request, | ||||
|     types::{ | ||||
|         api::fraud_check as frm_api, domain, fraud_check as frm_types, ErrorResponse, Response, | ||||
|     }, | ||||
|     types::{api::fraud_check as frm_api, fraud_check as frm_types, ErrorResponse, Response}, | ||||
|     utils::BytesExt, | ||||
| }; | ||||
|  | ||||
| @ -572,15 +570,16 @@ impl api::IncomingWebhook for Riskified { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -592,7 +591,7 @@ impl api::IncomingWebhook for Riskified { | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -8,7 +8,7 @@ use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; | ||||
| #[cfg(feature = "frm")] | ||||
| use error_stack::ResultExt; | ||||
| #[cfg(feature = "frm")] | ||||
| use masking::PeekInterface; | ||||
| use masking::{PeekInterface, Secret}; | ||||
| #[cfg(feature = "frm")] | ||||
| use ring::hmac; | ||||
| #[cfg(feature = "frm")] | ||||
| @ -30,9 +30,7 @@ use crate::{ | ||||
| use crate::{ | ||||
|     consts, | ||||
|     events::connector_api_logs::ConnectorEvent, | ||||
|     types::{ | ||||
|         api::fraud_check as frm_api, domain, fraud_check as frm_types, ErrorResponse, Response, | ||||
|     }, | ||||
|     types::{api::fraud_check as frm_api, fraud_check as frm_types, ErrorResponse, Response}, | ||||
|     utils::BytesExt, | ||||
| }; | ||||
|  | ||||
| @ -689,15 +687,16 @@ impl api::IncomingWebhook for Signifyd { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
| @ -709,7 +708,7 @@ impl api::IncomingWebhook for Signifyd { | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
| @ -5,7 +5,7 @@ use std::fmt::Debug; | ||||
| use common_utils::{ext_traits::ByteSliceExt, request::RequestContent}; | ||||
| use diesel_models::enums; | ||||
| use error_stack::ResultExt; | ||||
| use masking::PeekInterface; | ||||
| use masking::{PeekInterface, Secret}; | ||||
| use transformers as stax; | ||||
|  | ||||
| use self::stax::StaxWebhookEventType; | ||||
| @ -24,7 +24,7 @@ use crate::{ | ||||
|     types::{ | ||||
|         self, | ||||
|         api::{self, ConnectorCommon, ConnectorCommonExt}, | ||||
|         domain, ErrorResponse, Response, | ||||
|         ErrorResponse, Response, | ||||
|     }, | ||||
|     utils::BytesExt, | ||||
| }; | ||||
| @ -852,8 +852,9 @@ impl api::IncomingWebhook for Stax { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         _request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         _merchant_account: &domain::MerchantAccount, | ||||
|         _merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         _merchant_id: &str, | ||||
|         _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         _connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         Ok(false) | ||||
|  | ||||
| @ -4,7 +4,7 @@ use std::fmt::Debug; | ||||
|  | ||||
| use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; | ||||
| use error_stack::ResultExt; | ||||
| use masking::PeekInterface; | ||||
| use masking::{PeekInterface, Secret}; | ||||
| use transformers as zen; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| @ -608,16 +608,17 @@ impl api::IncomingWebhook for Zen { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         _connector_account_details: crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let algorithm = self.get_webhook_source_verification_algorithm(request)?; | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 merchant_id, | ||||
|                 connector_label, | ||||
|                 merchant_connector_account, | ||||
|                 connector_webhook_details, | ||||
|             ) | ||||
|             .await?; | ||||
|         let signature = | ||||
| @ -625,7 +626,7 @@ impl api::IncomingWebhook for Zen { | ||||
|  | ||||
|         let mut message = self.get_webhook_source_verification_message( | ||||
|             request, | ||||
|             &merchant_account.merchant_id, | ||||
|             merchant_id, | ||||
|             &connector_webhook_secrets, | ||||
|         )?; | ||||
|         let mut secret = connector_webhook_secrets.secret; | ||||
|  | ||||
| @ -5,7 +5,7 @@ use std::fmt::Debug; | ||||
| use common_utils::ext_traits::ValueExt; | ||||
| use diesel_models::enums; | ||||
| use error_stack::ResultExt; | ||||
| use masking::ExposeInterface; | ||||
| use masking::{ExposeInterface, Secret}; | ||||
| use transformers as zsl; | ||||
|  | ||||
| use crate::{ | ||||
| @ -418,12 +418,12 @@ impl api::IncomingWebhook for Zsl { | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &api::IncomingWebhookRequestDetails<'_>, | ||||
|         _merchant_account: &types::domain::MerchantAccount, | ||||
|         merchant_connector_account: types::domain::MerchantConnectorAccount, | ||||
|         _merchant_id: &str, | ||||
|         _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>, | ||||
|         _connector_label: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_account_details = merchant_connector_account | ||||
|             .connector_account_details | ||||
|         let connector_account_details = connector_account_details | ||||
|             .parse_value::<types::ConnectorAuthType>("ConnectorAuthType") | ||||
|             .change_context_lazy(|| errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|         let auth_type = zsl::ZslAuthType::try_from(&connector_account_details)?; | ||||
|  | ||||
| @ -9,6 +9,11 @@ use api_models::{ | ||||
| }; | ||||
| use common_utils::{errors::ReportSwitchExt, events::ApiEventsType}; | ||||
| use error_stack::{report, ResultExt}; | ||||
| use hyperswitch_domain_models::{ | ||||
|     router_request_types::VerifyWebhookSourceRequestData, | ||||
|     router_response_types::{VerifyWebhookSourceResponseData, VerifyWebhookStatus}, | ||||
| }; | ||||
| use hyperswitch_interfaces::webhooks::IncomingWebhookRequestDetails; | ||||
| use masking::ExposeInterface; | ||||
| use router_env::{instrument, metrics::add_attributes, tracing, tracing_actix_web::RequestId}; | ||||
|  | ||||
| @ -19,6 +24,7 @@ use crate::{ | ||||
|         api_locking, | ||||
|         errors::{self, ConnectorErrorExt, CustomResult, RouterResponse, StorageErrorExt}, | ||||
|         metrics, payments, refunds, utils as core_utils, | ||||
|         webhooks::utils::construct_webhook_router_data, | ||||
|     }, | ||||
|     db::StorageInterface, | ||||
|     events::api_logs::ApiEvent, | ||||
| @ -32,7 +38,10 @@ use crate::{ | ||||
|         ConnectorValidation, | ||||
|     }, | ||||
|     types::{ | ||||
|         api::{self, mandates::MandateResponseExt, ConnectorCommon, IncomingWebhook}, | ||||
|         api::{ | ||||
|             self, mandates::MandateResponseExt, ConnectorCommon, ConnectorData, GetToken, | ||||
|             IncomingWebhook, | ||||
|         }, | ||||
|         domain, | ||||
|         storage::{self, enums}, | ||||
|         transformers::{ForeignFrom, ForeignInto, ForeignTryFrom}, | ||||
| @ -129,7 +138,7 @@ async fn incoming_webhooks_core<W: types::OutgoingWebhookType>( | ||||
|             merchant_account.merchant_id.clone(), | ||||
|         )], | ||||
|     ); | ||||
|     let mut request_details = api::IncomingWebhookRequestDetails { | ||||
|     let mut request_details = IncomingWebhookRequestDetails { | ||||
|         method: req.method().clone(), | ||||
|         uri: req.uri().clone(), | ||||
|         headers: req.headers(), | ||||
| @ -150,9 +159,14 @@ async fn incoming_webhooks_core<W: types::OutgoingWebhookType>( | ||||
|  | ||||
|     let decoded_body = connector | ||||
|         .decode_webhook_body( | ||||
|             &*state.clone().store, | ||||
|             &request_details, | ||||
|             &merchant_account.merchant_id, | ||||
|             merchant_connector_account | ||||
|                 .clone() | ||||
|                 .and_then(|merchant_connector_account| { | ||||
|                     merchant_connector_account.connector_webhook_details | ||||
|                 }), | ||||
|             connector_name.as_str(), | ||||
|         ) | ||||
|         .await | ||||
|         .switch() | ||||
| @ -258,30 +272,32 @@ async fn incoming_webhooks_core<W: types::OutgoingWebhookType>( | ||||
|             .connectors_with_webhook_source_verification_call | ||||
|             .contains(&connector_enum) | ||||
|         { | ||||
|             connector | ||||
|                 .verify_webhook_source_verification_call( | ||||
|                     &state, | ||||
|                     &merchant_account, | ||||
|                     merchant_connector_account.clone(), | ||||
|                     &connector_name, | ||||
|                     &request_details, | ||||
|                 ) | ||||
|                 .await | ||||
|                 .or_else(|error| match error.current_context() { | ||||
|                     errors::ConnectorError::WebhookSourceVerificationFailed => { | ||||
|                         logger::error!(?error, "Source Verification Failed"); | ||||
|                         Ok(false) | ||||
|                     } | ||||
|                     _ => Err(error), | ||||
|                 }) | ||||
|                 .switch() | ||||
|                 .attach_printable("There was an issue in incoming webhook source verification")? | ||||
|             verify_webhook_source_verification_call( | ||||
|                 connector.clone(), | ||||
|                 &state, | ||||
|                 &merchant_account, | ||||
|                 merchant_connector_account.clone(), | ||||
|                 &connector_name, | ||||
|                 &request_details, | ||||
|             ) | ||||
|             .await | ||||
|             .or_else(|error| match error.current_context() { | ||||
|                 errors::ConnectorError::WebhookSourceVerificationFailed => { | ||||
|                     logger::error!(?error, "Source Verification Failed"); | ||||
|                     Ok(false) | ||||
|                 } | ||||
|                 _ => Err(error), | ||||
|             }) | ||||
|             .switch() | ||||
|             .attach_printable("There was an issue in incoming webhook source verification")? | ||||
|         } else { | ||||
|             connector | ||||
|                 .clone() | ||||
|                 .verify_webhook_source( | ||||
|                     &request_details, | ||||
|                     &merchant_account, | ||||
|                     merchant_connector_account.clone(), | ||||
|                     &merchant_account.merchant_id, | ||||
|                     merchant_connector_account.connector_webhook_details.clone(), | ||||
|                     merchant_connector_account.connector_account_details.clone(), | ||||
|                     connector_name.as_str(), | ||||
|                 ) | ||||
|                 .await | ||||
| @ -970,7 +986,7 @@ async fn external_authentication_incoming_webhook_flow( | ||||
|     key_store: domain::MerchantKeyStore, | ||||
|     source_verified: bool, | ||||
|     event_type: webhooks::IncomingWebhookEvent, | ||||
|     request_details: &api::IncomingWebhookRequestDetails<'_>, | ||||
|     request_details: &IncomingWebhookRequestDetails<'_>, | ||||
|     connector: &ConnectorEnum, | ||||
|     object_ref_id: api::ObjectReferenceId, | ||||
|     business_profile: diesel_models::business_profile::BusinessProfile, | ||||
| @ -1346,7 +1362,7 @@ async fn disputes_incoming_webhook_flow( | ||||
|     webhook_details: api::IncomingWebhookDetails, | ||||
|     source_verified: bool, | ||||
|     connector: &ConnectorEnum, | ||||
|     request_details: &api::IncomingWebhookRequestDetails<'_>, | ||||
|     request_details: &IncomingWebhookRequestDetails<'_>, | ||||
|     event_type: webhooks::IncomingWebhookEvent, | ||||
| ) -> CustomResult<WebhookResponseTracker, errors::ApiErrorResponse> { | ||||
|     metrics::INCOMING_DISPUTE_WEBHOOK_METRIC.add(&metrics::CONTEXT, 1, &[]); | ||||
| @ -1534,6 +1550,66 @@ async fn get_payment_id( | ||||
|         .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound) | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| async fn verify_webhook_source_verification_call( | ||||
|     connector: ConnectorEnum, | ||||
|     state: &SessionState, | ||||
|     merchant_account: &domain::MerchantAccount, | ||||
|     merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|     connector_name: &str, | ||||
|     request_details: &IncomingWebhookRequestDetails<'_>, | ||||
| ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|     let connector_data = ConnectorData::get_connector_by_name( | ||||
|         &state.conf.connectors, | ||||
|         connector_name, | ||||
|         GetToken::Connector, | ||||
|         None, | ||||
|     ) | ||||
|     .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|     .attach_printable("invalid connector name received in payment attempt")?; | ||||
|     let connector_integration: services::BoxedWebhookSourceVerificationConnectorIntegrationInterface< | ||||
|         hyperswitch_domain_models::router_flow_types::VerifyWebhookSource, | ||||
|         VerifyWebhookSourceRequestData, | ||||
|         VerifyWebhookSourceResponseData, | ||||
|     > = connector_data.connector.get_connector_integration(); | ||||
|     let connector_webhook_secrets = connector | ||||
|         .get_webhook_source_verification_merchant_secret( | ||||
|             &merchant_account.merchant_id, | ||||
|             connector_name, | ||||
|             merchant_connector_account.connector_webhook_details.clone(), | ||||
|         ) | ||||
|         .await | ||||
|         .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|     let router_data = construct_webhook_router_data( | ||||
|         connector_name, | ||||
|         merchant_connector_account, | ||||
|         merchant_account, | ||||
|         &connector_webhook_secrets, | ||||
|         request_details, | ||||
|     ) | ||||
|     .await | ||||
|     .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|     .attach_printable("Failed while constructing webhook router data")?; | ||||
|  | ||||
|     let response = services::execute_connector_processing_step( | ||||
|         state, | ||||
|         connector_integration, | ||||
|         &router_data, | ||||
|         payments::CallConnectorAction::Trigger, | ||||
|         None, | ||||
|     ) | ||||
|     .await?; | ||||
|  | ||||
|     let verification_result = response | ||||
|         .response | ||||
|         .map(|response| response.verify_webhook_status); | ||||
|     match verification_result { | ||||
|         Ok(VerifyWebhookStatus::SourceVerified) => Ok(true), | ||||
|         _ => Ok(false), | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn get_connector_by_connector_name( | ||||
|     state: &SessionState, | ||||
|     connector_name: &str, | ||||
| @ -1562,10 +1638,10 @@ fn get_connector_by_connector_name( | ||||
|             authentication_connector_data.connector_name.to_string(), | ||||
|         ) | ||||
|     } else { | ||||
|         let connector_data = api::ConnectorData::get_connector_by_name( | ||||
|         let connector_data = ConnectorData::get_connector_by_name( | ||||
|             &state.conf.connectors, | ||||
|             connector_name, | ||||
|             api::GetToken::Connector, | ||||
|             GetToken::Connector, | ||||
|             merchant_connector_id, | ||||
|         ) | ||||
|         .change_context(errors::ApiErrorResponse::InvalidRequestData { | ||||
|  | ||||
| @ -15,10 +15,7 @@ use crate::routes::dummy_connector::types::{ | ||||
| }; | ||||
| use crate::{ | ||||
|     core::payments::PaymentsRedirectResponseData, | ||||
|     services::{ | ||||
|         authentication::AuthenticationType, kafka::KafkaMessage, ApplicationResponse, | ||||
|         GenericLinkFormData, PaymentLinkFormData, | ||||
|     }, | ||||
|     services::{authentication::AuthenticationType, kafka::KafkaMessage}, | ||||
|     types::api::{ | ||||
|         AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId, PollId, | ||||
|     }, | ||||
| @ -101,22 +98,11 @@ impl KafkaMessage for ApiEvent { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T: ApiEventMetric> ApiEventMetric for ApplicationResponse<T> { | ||||
|     fn get_api_event_type(&self) -> Option<ApiEventsType> { | ||||
|         match self { | ||||
|             Self::Json(r) => r.get_api_event_type(), | ||||
|             Self::JsonWithHeaders((r, _)) => r.get_api_event_type(), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl_misc_api_event_type!( | ||||
|     Config, | ||||
|     CreateFileRequest, | ||||
|     FileId, | ||||
|     AttachEvidenceRequest, | ||||
|     PaymentLinkFormData, | ||||
|     GenericLinkFormData, | ||||
|     ConfigUpdate | ||||
| ); | ||||
|  | ||||
|  | ||||
| @ -4,7 +4,7 @@ pub mod request; | ||||
| use std::{ | ||||
|     collections::{HashMap, HashSet}, | ||||
|     error::Error, | ||||
|     fmt::{Debug, Display}, | ||||
|     fmt::Debug, | ||||
|     future::Future, | ||||
|     str, | ||||
|     sync::Arc, | ||||
| @ -26,7 +26,14 @@ use common_utils::{ | ||||
| }; | ||||
| use error_stack::{report, Report, ResultExt}; | ||||
| use hyperswitch_domain_models::router_data_v2::flow_common_types as common_types; | ||||
| pub use hyperswitch_domain_models::router_response_types::RedirectForm; | ||||
| pub use hyperswitch_domain_models::{ | ||||
|     api::{ | ||||
|         ApplicationResponse, GenericExpiredLinkData, GenericLinkFormData, GenericLinkStatusData, | ||||
|         GenericLinks, PaymentLinkAction, PaymentLinkFormData, PaymentLinkStatusData, | ||||
|         RedirectionFormData, | ||||
|     }, | ||||
|     router_response_types::RedirectForm, | ||||
| }; | ||||
| pub use hyperswitch_interfaces::{ | ||||
|     api::{ | ||||
|         BoxedConnectorIntegration, CaptureSyncMethod, ConnectorIntegration, ConnectorIntegrationAny, | ||||
| @ -713,93 +720,6 @@ async fn handle_response( | ||||
|         .await | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq)] | ||||
| pub enum ApplicationResponse<R> { | ||||
|     Json(R), | ||||
|     StatusOk, | ||||
|     TextPlain(String), | ||||
|     JsonForRedirection(api::RedirectionResponse), | ||||
|     Form(Box<RedirectionFormData>), | ||||
|     PaymentLinkForm(Box<PaymentLinkAction>), | ||||
|     FileData((Vec<u8>, mime::Mime)), | ||||
|     JsonWithHeaders((R, Vec<(String, Maskable<String>)>)), | ||||
|     GenericLinkForm(Box<GenericLinks>), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq)] | ||||
| pub enum GenericLinks { | ||||
|     ExpiredLink(GenericExpiredLinkData), | ||||
|     PaymentMethodCollect(GenericLinkFormData), | ||||
|     PayoutLink(GenericLinkFormData), | ||||
|     PayoutLinkStatus(GenericLinkStatusData), | ||||
|     PaymentMethodCollectStatus(GenericLinkStatusData), | ||||
| } | ||||
|  | ||||
| impl Display for Box<GenericLinks> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         write!( | ||||
|             f, | ||||
|             "{}", | ||||
|             match **self { | ||||
|                 GenericLinks::ExpiredLink(_) => "ExpiredLink", | ||||
|                 GenericLinks::PaymentMethodCollect(_) => "PaymentMethodCollect", | ||||
|                 GenericLinks::PayoutLink(_) => "PayoutLink", | ||||
|                 GenericLinks::PayoutLinkStatus(_) => "PayoutLinkStatus", | ||||
|                 GenericLinks::PaymentMethodCollectStatus(_) => "PaymentMethodCollectStatus", | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct GenericLinkFormData { | ||||
|     pub js_data: String, | ||||
|     pub css_data: String, | ||||
|     pub sdk_url: String, | ||||
|     pub html_meta_tags: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct GenericExpiredLinkData { | ||||
|     pub title: String, | ||||
|     pub message: String, | ||||
|     pub theme: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct GenericLinkStatusData { | ||||
|     pub js_data: String, | ||||
|     pub css_data: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq)] | ||||
| pub enum PaymentLinkAction { | ||||
|     PaymentLinkFormData(PaymentLinkFormData), | ||||
|     PaymentLinkStatus(PaymentLinkStatusData), | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct PaymentLinkFormData { | ||||
|     pub js_script: String, | ||||
|     pub css_script: String, | ||||
|     pub sdk_url: String, | ||||
|     pub html_meta_tags: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)] | ||||
| pub struct PaymentLinkStatusData { | ||||
|     pub js_script: String, | ||||
|     pub css_script: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq)] | ||||
| pub struct RedirectionFormData { | ||||
|     pub redirect_form: RedirectForm, | ||||
|     pub payment_method_data: Option<api::PaymentMethodData>, | ||||
|     pub amount: String, | ||||
|     pub currency: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Eq, PartialEq)] | ||||
| pub enum PaymentAction { | ||||
|     PSync, | ||||
|  | ||||
| @ -1,13 +1,14 @@ | ||||
| use common_utils::{crypto, errors::CustomResult, request::Request}; | ||||
| use hyperswitch_domain_models::{router_data::RouterData, router_data_v2::RouterDataV2}; | ||||
| use hyperswitch_interfaces::connector_integration_v2::ConnectorIntegrationV2; | ||||
| use hyperswitch_interfaces::{ | ||||
|     authentication::ExternalAuthenticationPayload, connector_integration_v2::ConnectorIntegrationV2, | ||||
| }; | ||||
|  | ||||
| use super::{BoxedConnectorIntegrationV2, ConnectorValidation}; | ||||
| use crate::{ | ||||
|     core::payments, | ||||
|     errors, | ||||
|     events::connector_api_logs::ConnectorEvent, | ||||
|     routes::app::StorageInterface, | ||||
|     services::{ | ||||
|         api as services_api, BoxedConnectorIntegration, CaptureSyncMethod, ConnectorIntegration, | ||||
|         ConnectorRedirectResponse, PaymentAction, | ||||
| @ -16,8 +17,8 @@ use crate::{ | ||||
|     types::{ | ||||
|         self, | ||||
|         api::{ | ||||
|             self, disputes, Connector, ConnectorV2, CurrencyUnit, ExternalAuthenticationPayload, | ||||
|             IncomingWebhookEvent, IncomingWebhookRequestDetails, ObjectReferenceId, | ||||
|             self, disputes, Connector, ConnectorV2, CurrencyUnit, IncomingWebhookEvent, | ||||
|             IncomingWebhookRequestDetails, ObjectReferenceId, | ||||
|         }, | ||||
|         domain, | ||||
|     }, | ||||
| @ -99,25 +100,6 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn get_webhook_body_decoding_merchant_secret( | ||||
|         &self, | ||||
|         db: &dyn StorageInterface, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         match self { | ||||
|             Self::Old(connector) => { | ||||
|                 connector | ||||
|                     .get_webhook_body_decoding_merchant_secret(db, merchant_id) | ||||
|                     .await | ||||
|             } | ||||
|             Self::New(connector) => { | ||||
|                 connector | ||||
|                     .get_webhook_body_decoding_merchant_secret(db, merchant_id) | ||||
|                     .await | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_body_decoding_message( | ||||
|         &self, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
| @ -130,19 +112,30 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|  | ||||
|     async fn decode_webhook_body( | ||||
|         &self, | ||||
|         db: &dyn StorageInterface, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         connector_name: &str, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         match self { | ||||
|             Self::Old(connector) => { | ||||
|                 connector | ||||
|                     .decode_webhook_body(db, request, merchant_id) | ||||
|                     .decode_webhook_body( | ||||
|                         request, | ||||
|                         merchant_id, | ||||
|                         connector_webhook_details, | ||||
|                         connector_name, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
|             Self::New(connector) => { | ||||
|                 connector | ||||
|                     .decode_webhook_body(db, request, merchant_id) | ||||
|                     .decode_webhook_body( | ||||
|                         request, | ||||
|                         merchant_id, | ||||
|                         connector_webhook_details, | ||||
|                         connector_name, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
|         } | ||||
| @ -160,26 +153,26 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|  | ||||
|     async fn get_webhook_source_verification_merchant_secret( | ||||
|         &self, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_name: &str, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|     ) -> CustomResult<api_models::webhooks::ConnectorWebhookSecrets, errors::ConnectorError> { | ||||
|         match self { | ||||
|             Self::Old(connector) => { | ||||
|                 connector | ||||
|                     .get_webhook_source_verification_merchant_secret( | ||||
|                         merchant_account, | ||||
|                         merchant_id, | ||||
|                         connector_name, | ||||
|                         merchant_connector_account, | ||||
|                         connector_webhook_details, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
|             Self::New(connector) => { | ||||
|                 connector | ||||
|                     .get_webhook_source_verification_merchant_secret( | ||||
|                         merchant_account, | ||||
|                         merchant_id, | ||||
|                         connector_name, | ||||
|                         merchant_connector_account, | ||||
|                         connector_webhook_details, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
| @ -219,45 +212,12 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn verify_webhook_source_verification_call( | ||||
|         &self, | ||||
|         state: &crate::routes::SessionState, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         connector_name: &str, | ||||
|         request_details: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         match self { | ||||
|             Self::Old(connector) => { | ||||
|                 connector | ||||
|                     .verify_webhook_source_verification_call( | ||||
|                         state, | ||||
|                         merchant_account, | ||||
|                         merchant_connector_account, | ||||
|                         connector_name, | ||||
|                         request_details, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
|             Self::New(connector) => { | ||||
|                 connector | ||||
|                     .verify_webhook_source_verification_call( | ||||
|                         state, | ||||
|                         merchant_account, | ||||
|                         merchant_connector_account, | ||||
|                         connector_name, | ||||
|                         request_details, | ||||
|                     ) | ||||
|                     .await | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         merchant_id: &str, | ||||
|         connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>, | ||||
|         connector_account_details: crypto::Encryptable<masking::Secret<serde_json::Value>>, | ||||
|         connector_name: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         match self { | ||||
| @ -265,8 +225,9 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|                 connector | ||||
|                     .verify_webhook_source( | ||||
|                         request, | ||||
|                         merchant_account, | ||||
|                         merchant_connector_account, | ||||
|                         merchant_id, | ||||
|                         connector_webhook_details, | ||||
|                         connector_account_details, | ||||
|                         connector_name, | ||||
|                     ) | ||||
|                     .await | ||||
| @ -275,8 +236,9 @@ impl api::IncomingWebhook for ConnectorEnum { | ||||
|                 connector | ||||
|                     .verify_webhook_source( | ||||
|                         request, | ||||
|                         merchant_account, | ||||
|                         merchant_connector_account, | ||||
|                         merchant_id, | ||||
|                         connector_webhook_details, | ||||
|                         connector_account_details, | ||||
|                         connector_name, | ||||
|                     ) | ||||
|                     .await | ||||
|  | ||||
| @ -74,13 +74,6 @@ pub struct PostAuthenticationResponse { | ||||
|     pub eci: Option<String>, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, serde::Deserialize, Debug, serde::Serialize, PartialEq, Eq)] | ||||
| pub struct ExternalAuthenticationPayload { | ||||
|     pub trans_status: common_enums::TransactionStatus, | ||||
|     pub authentication_value: Option<String>, | ||||
|     pub eci: Option<String>, | ||||
| } | ||||
|  | ||||
| pub trait ConnectorAuthentication: | ||||
|     services::ConnectorIntegration< | ||||
|     Authentication, | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| pub use hyperswitch_interfaces::disputes::DisputePayload; | ||||
| use masking::{Deserialize, Serialize}; | ||||
| use time::PrimitiveDateTime; | ||||
|  | ||||
| use crate::{services, types}; | ||||
|  | ||||
| @ -12,20 +12,6 @@ pub use hyperswitch_domain_models::router_flow_types::dispute::{Accept, Defend, | ||||
|  | ||||
| pub use super::disputes_v2::{AcceptDisputeV2, DefendDisputeV2, DisputeV2, SubmitEvidenceV2}; | ||||
|  | ||||
| #[derive(Default, Debug)] | ||||
| pub struct DisputePayload { | ||||
|     pub amount: String, | ||||
|     pub currency: String, | ||||
|     pub dispute_stage: api_models::enums::DisputeStage, | ||||
|     pub connector_status: String, | ||||
|     pub connector_dispute_id: String, | ||||
|     pub connector_reason: Option<String>, | ||||
|     pub connector_reason_code: Option<String>, | ||||
|     pub challenge_required_by: Option<PrimitiveDateTime>, | ||||
|     pub created_at: Option<PrimitiveDateTime>, | ||||
|     pub updated_at: Option<PrimitiveDateTime>, | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Deserialize, Serialize)] | ||||
| pub struct DisputeEvidence { | ||||
|     pub cancellation_policy: Option<String>, | ||||
|  | ||||
| @ -1,282 +1,5 @@ | ||||
| use api_models::admin::MerchantConnectorWebhookDetails; | ||||
| pub use api_models::webhooks::{ | ||||
|     AuthenticationIdType, IncomingWebhookDetails, IncomingWebhookEvent, MerchantWebhookConfig, | ||||
|     ObjectReferenceId, OutgoingWebhook, OutgoingWebhookContent, WebhookFlow, | ||||
| }; | ||||
| use common_utils::ext_traits::ValueExt; | ||||
| use error_stack::ResultExt; | ||||
| use masking::ExposeInterface; | ||||
|  | ||||
| use super::ConnectorCommon; | ||||
| use crate::{ | ||||
|     core::{ | ||||
|         errors::{self, CustomResult}, | ||||
|         payments, | ||||
|         webhooks::utils::construct_webhook_router_data, | ||||
|     }, | ||||
|     db::StorageInterface, | ||||
|     services::{self}, | ||||
|     types::{self, domain}, | ||||
|     utils::crypto, | ||||
| }; | ||||
|  | ||||
| pub struct IncomingWebhookRequestDetails<'a> { | ||||
|     pub method: actix_web::http::Method, | ||||
|     pub uri: actix_web::http::Uri, | ||||
|     pub headers: &'a actix_web::http::header::HeaderMap, | ||||
|     pub body: &'a [u8], | ||||
|     pub query_params: String, | ||||
| } | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| pub trait IncomingWebhook: ConnectorCommon + Sync { | ||||
|     fn get_webhook_body_decoding_algorithm( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<Box<dyn crypto::DecodeMessage + Send>, errors::ConnectorError> { | ||||
|         Ok(Box::new(crypto::NoAlgorithm)) | ||||
|     } | ||||
|  | ||||
|     async fn get_webhook_body_decoding_merchant_secret( | ||||
|         &self, | ||||
|         _db: &dyn StorageInterface, | ||||
|         _merchant_id: &str, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         Ok(Vec::new()) | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_body_decoding_message( | ||||
|         &self, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         Ok(request.body.to_vec()) | ||||
|     } | ||||
|  | ||||
|     async fn decode_webhook_body( | ||||
|         &self, | ||||
|         db: &dyn StorageInterface, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         let algorithm = self.get_webhook_body_decoding_algorithm(request)?; | ||||
|  | ||||
|         let message = self | ||||
|             .get_webhook_body_decoding_message(request) | ||||
|             .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; | ||||
|         let secret = self | ||||
|             .get_webhook_body_decoding_merchant_secret(db, merchant_id) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; | ||||
|  | ||||
|         algorithm | ||||
|             .decode_message(&secret, message.into()) | ||||
|             .change_context(errors::ConnectorError::WebhookBodyDecodingFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_source_verification_algorithm( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> { | ||||
|         Ok(Box::new(crypto::NoAlgorithm)) | ||||
|     } | ||||
|  | ||||
|     async fn get_webhook_source_verification_merchant_secret( | ||||
|         &self, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         connector_name: &str, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|     ) -> CustomResult<api_models::webhooks::ConnectorWebhookSecrets, errors::ConnectorError> { | ||||
|         let merchant_id = merchant_account.merchant_id.as_str(); | ||||
|         let debug_suffix = format!( | ||||
|             "For merchant_id: {}, and connector_name: {}", | ||||
|             merchant_id, connector_name | ||||
|         ); | ||||
|         let default_secret = "default_secret".to_string(); | ||||
|         let merchant_secret = match merchant_connector_account.connector_webhook_details { | ||||
|             Some(merchant_connector_webhook_details) => { | ||||
|                 let connector_webhook_details = merchant_connector_webhook_details | ||||
|                     .parse_value::<MerchantConnectorWebhookDetails>( | ||||
|                         "MerchantConnectorWebhookDetails", | ||||
|                     ) | ||||
|                     .change_context_lazy(|| errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|                     .attach_printable_lazy(|| { | ||||
|                         format!( | ||||
|                             "Deserializing MerchantConnectorWebhookDetails failed {}", | ||||
|                             debug_suffix | ||||
|                         ) | ||||
|                     })?; | ||||
|                 api_models::webhooks::ConnectorWebhookSecrets { | ||||
|                     secret: connector_webhook_details | ||||
|                         .merchant_secret | ||||
|                         .expose() | ||||
|                         .into_bytes(), | ||||
|                     additional_secret: connector_webhook_details.additional_secret, | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             None => api_models::webhooks::ConnectorWebhookSecrets { | ||||
|                 secret: default_secret.into_bytes(), | ||||
|                 additional_secret: None, | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         //need to fetch merchant secret from config table with caching in future for enhanced performance | ||||
|  | ||||
|         //If merchant has not set the secret for webhook source verification, "default_secret" is returned. | ||||
|         //So it will fail during verification step and goes to psync flow. | ||||
|         Ok(merchant_secret) | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_source_verification_signature( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|         _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         Ok(Vec::new()) | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_source_verification_message( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|         _merchant_id: &str, | ||||
|         _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, | ||||
|     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||
|         Ok(Vec::new()) | ||||
|     } | ||||
|  | ||||
|     async fn verify_webhook_source_verification_call( | ||||
|         &self, | ||||
|         state: &crate::routes::SessionState, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         connector_name: &str, | ||||
|         request_details: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let connector_data = types::api::ConnectorData::get_connector_by_name( | ||||
|             &state.conf.connectors, | ||||
|             connector_name, | ||||
|             types::api::GetToken::Connector, | ||||
|             None, | ||||
|         ) | ||||
|         .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|         .attach_printable("invalid connector name received in payment attempt")?; | ||||
|         let connector_integration: services::BoxedWebhookSourceVerificationConnectorIntegrationInterface< | ||||
|             types::api::VerifyWebhookSource, | ||||
|             types::VerifyWebhookSourceRequestData, | ||||
|             types::VerifyWebhookSourceResponseData, | ||||
|         > = connector_data.connector.get_connector_integration(); | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 connector_name, | ||||
|                 merchant_connector_account.clone(), | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|         let router_data = construct_webhook_router_data( | ||||
|             connector_name, | ||||
|             merchant_connector_account, | ||||
|             merchant_account, | ||||
|             &connector_webhook_secrets, | ||||
|             request_details, | ||||
|         ) | ||||
|         .await | ||||
|         .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|         .attach_printable("Failed while constructing webhook router data")?; | ||||
|  | ||||
|         let response = services::execute_connector_processing_step( | ||||
|             state, | ||||
|             connector_integration, | ||||
|             &router_data, | ||||
|             payments::CallConnectorAction::Trigger, | ||||
|             None, | ||||
|         ) | ||||
|         .await?; | ||||
|  | ||||
|         let verification_result = response | ||||
|             .response | ||||
|             .map(|response| response.verify_webhook_status); | ||||
|         match verification_result { | ||||
|             Ok(types::VerifyWebhookStatus::SourceVerified) => Ok(true), | ||||
|             _ => Ok(false), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn verify_webhook_source( | ||||
|         &self, | ||||
|         request: &IncomingWebhookRequestDetails<'_>, | ||||
|         merchant_account: &domain::MerchantAccount, | ||||
|         merchant_connector_account: domain::MerchantConnectorAccount, | ||||
|         connector_name: &str, | ||||
|     ) -> CustomResult<bool, errors::ConnectorError> { | ||||
|         let algorithm = self | ||||
|             .get_webhook_source_verification_algorithm(request) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|         let connector_webhook_secrets = self | ||||
|             .get_webhook_source_verification_merchant_secret( | ||||
|                 merchant_account, | ||||
|                 connector_name, | ||||
|                 merchant_connector_account, | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|         let signature = self | ||||
|             .get_webhook_source_verification_signature(request, &connector_webhook_secrets) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|         let message = self | ||||
|             .get_webhook_source_verification_message( | ||||
|                 request, | ||||
|                 &merchant_account.merchant_id, | ||||
|                 &connector_webhook_secrets, | ||||
|             ) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||
|  | ||||
|         algorithm | ||||
|             .verify_signature(&connector_webhook_secrets.secret, &signature, &message) | ||||
|             .change_context(errors::ConnectorError::WebhookSourceVerificationFailed) | ||||
|     } | ||||
|  | ||||
|     fn get_webhook_object_reference_id( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<ObjectReferenceId, errors::ConnectorError>; | ||||
|  | ||||
|     fn get_webhook_event_type( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError>; | ||||
|  | ||||
|     fn get_webhook_resource_object( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError>; | ||||
|  | ||||
|     fn get_webhook_api_response( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<services::api::ApplicationResponse<serde_json::Value>, errors::ConnectorError> | ||||
|     { | ||||
|         Ok(services::api::ApplicationResponse::StatusOk) | ||||
|     } | ||||
|  | ||||
|     fn get_dispute_details( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<super::disputes::DisputePayload, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented("get_dispute_details method".to_string()).into()) | ||||
|     } | ||||
|  | ||||
|     fn get_external_authentication_details( | ||||
|         &self, | ||||
|         _request: &IncomingWebhookRequestDetails<'_>, | ||||
|     ) -> CustomResult<super::ExternalAuthenticationPayload, errors::ConnectorError> { | ||||
|         Err(errors::ConnectorError::NotImplemented( | ||||
|             "get_external_authentication_details method".to_string(), | ||||
|         ) | ||||
|         .into()) | ||||
|     } | ||||
| } | ||||
| pub use hyperswitch_interfaces::webhooks::{IncomingWebhook, IncomingWebhookRequestDetails}; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 DEEPANSHU BANSAL
					DEEPANSHU BANSAL