mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	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:
		| @ -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 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 chikke srujan
					chikke srujan