feat(webhooks): add support for custom outgoing webhook http headers (#5275)

Co-authored-by: Chikke Srujan <chikke.srujan@Chikke-Srujan-N7WRTY72X7.local>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
chikke srujan
2024-07-16 21:43:44 +05:30
committed by GitHub
parent 29f8732d30
commit 101b21f52d
14 changed files with 365 additions and 224 deletions

View File

@ -1,3 +1,5 @@
use std::collections::HashMap;
use api_models::{
webhook_events::{OutgoingWebhookRequestContent, OutgoingWebhookResponseContent},
webhooks,
@ -5,6 +7,7 @@ use api_models::{
use common_utils::{ext_traits::Encode, request::RequestContent};
use diesel_models::process_tracker::business_status;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::type_encryption::decrypt;
use masking::{ExposeInterface, Mask, PeekInterface, Secret};
use router_env::{
instrument,
@ -86,8 +89,10 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook(
let request_content = get_outgoing_webhook_request(
&merchant_account,
outgoing_webhook,
business_profile.payment_response_hash_key.as_deref(),
&business_profile,
merchant_key_store,
)
.await
.change_context(errors::ApiErrorResponse::WebhookProcessingFailure)
.attach_printable("Failed to construct outgoing webhook request content")?;
@ -546,15 +551,17 @@ fn get_webhook_url_from_business_profile(
.map(ExposeInterface::expose)
}
pub(crate) fn get_outgoing_webhook_request(
pub(crate) async fn get_outgoing_webhook_request(
merchant_account: &domain::MerchantAccount,
outgoing_webhook: api::OutgoingWebhook,
payment_response_hash_key: Option<&str>,
business_profile: &diesel_models::business_profile::BusinessProfile,
key_store: &domain::MerchantKeyStore,
) -> CustomResult<OutgoingWebhookRequestContent, errors::WebhooksFlowError> {
#[inline]
fn get_outgoing_webhook_request_inner<WebhookType: types::OutgoingWebhookType>(
async fn get_outgoing_webhook_request_inner<WebhookType: types::OutgoingWebhookType>(
outgoing_webhook: api::OutgoingWebhook,
payment_response_hash_key: Option<&str>,
business_profile: &diesel_models::business_profile::BusinessProfile,
key_store: &domain::MerchantKeyStore,
) -> CustomResult<OutgoingWebhookRequestContent, errors::WebhooksFlowError> {
let mut headers = vec![(
reqwest::header::CONTENT_TYPE.to_string(),
@ -562,7 +569,31 @@ pub(crate) fn get_outgoing_webhook_request(
)];
let transformed_outgoing_webhook = WebhookType::from(outgoing_webhook);
let payment_response_hash_key = business_profile.payment_response_hash_key.clone();
let custom_headers = decrypt::<serde_json::Value, masking::WithType>(
business_profile
.outgoing_webhook_custom_http_headers
.clone(),
key_store.key.get_inner().peek(),
)
.await
.change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed)
.attach_printable("Failed to decrypt outgoing webhook custom HTTP headers")?
.map(|decrypted_value| {
decrypted_value
.into_inner()
.expose()
.parse_value::<HashMap<String, String>>("HashMap<String,String>")
.change_context(errors::WebhooksFlowError::OutgoingWebhookEncodingFailed)
.attach_printable("Failed to deserialize outgoing webhook custom HTTP headers")
})
.transpose()?;
if let Some(ref map) = custom_headers {
headers.extend(
map.iter()
.map(|(key, value)| (key.clone(), value.clone().into_masked())),
);
};
let outgoing_webhooks_signature = transformed_outgoing_webhook
.get_outgoing_webhooks_signature(payment_response_hash_key)?;
@ -581,15 +612,22 @@ pub(crate) fn get_outgoing_webhook_request(
match merchant_account.get_compatible_connector() {
#[cfg(feature = "stripe")]
Some(api_models::enums::Connector::Stripe) => get_outgoing_webhook_request_inner::<
stripe_webhooks::StripeOutgoingWebhook,
>(
outgoing_webhook, payment_response_hash_key
),
_ => get_outgoing_webhook_request_inner::<webhooks::OutgoingWebhook>(
outgoing_webhook,
payment_response_hash_key,
),
Some(api_models::enums::Connector::Stripe) => {
get_outgoing_webhook_request_inner::<stripe_webhooks::StripeOutgoingWebhook>(
outgoing_webhook,
business_profile,
key_store,
)
.await
}
_ => {
get_outgoing_webhook_request_inner::<webhooks::OutgoingWebhook>(
outgoing_webhook,
business_profile,
key_store,
)
.await
}
}
}