mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-31 01:57:45 +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