Bug: [FEATURE] allow configuring outgoing webhooks for different status as a part of payment and refund flows



### Feature Description

This feature allows merchants to configure different states for triggered the outgoing webhooks during payment and refund flows.

Currently, outgoing webhooks are triggered for predefined status for both payments and refunds - which are common for all the merchants.

### Possible Implementation

Allow defining the different payment and refund states which are eligible for triggering outgoing webhooks during the payment and refund flows.

This config can be set at business profile level.

For payment and refund flows (different from the incoming webhook flow)
- This config will be used for checking if the current payment status is eligible for triggering an outgoing webhook
- By default, the current states will be kept as is (in code) for triggering the outgoing webhook

### Have you spent some time checking if this feature request has been raised before?

- [x] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [x] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index f12e6d5bd4a..00fccd4ed45 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -702,6 +702,58 @@ pub struct WebhookDetails {
     /// If this property is true, a webhook message is posted whenever a payment fails
     #[schema(example = true)]
     pub payment_failed_enabled: Option<bool>,
+
+    /// List of payment statuses that triggers a webhook for payment intents
+    #[schema(value_type = Vec<IntentStatus>, example = json!(["succeeded", "failed", "partially_captured", "requires_merchant_action"]))]
+    pub payment_statuses_enabled: Option<Vec<api_enums::IntentStatus>>,
+
+    /// List of refund statuses that triggers a webhook for refunds
+    #[schema(value_type = Vec<IntentStatus>, example = json!(["success", "failure"]))]
+    pub refund_statuses_enabled: Option<Vec<api_enums::RefundStatus>>,
+
+    /// List of payout statuses that triggers a webhook for payouts
+    #[cfg(feature = "payouts")]
+    #[schema(value_type = Option<Vec<PayoutStatus>>, example = json!(["success", "failed"]))]
+    pub payout_statuses_enabled: Option<Vec<api_enums::PayoutStatus>>,
+}
+
+impl WebhookDetails {
+    fn validate_statuses<T>(statuses: &[T], status_type_name: &str) -> Result<(), String>
+    where
+        T: strum::IntoEnumIterator + Copy + PartialEq + std::fmt::Debug,
+        T: Into<Option<api_enums::EventType>>,
+    {
+        let valid_statuses: Vec<T> = T::iter().filter(|s| (*s).into().is_some()).collect();
+
+        for status in statuses {
+            if !valid_statuses.contains(status) {
+                return Err(format!(
+                    "Invalid {} webhook status provided: {:?}",
+                    status_type_name, status
+                ));
+            }
+        }
+        Ok(())
+    }
+
+    pub fn validate(&self) -> Result<(), String> {
+        if let Some(payment_statuses) = &self.payment_statuses_enabled {
+            Self::validate_statuses(payment_statuses, "payment")?;
+        }
+
+        if let Some(refund_statuses) = &self.refund_statuses_enabled {
+            Self::validate_statuses(refund_statuses, "refund")?;
+        }
+
+        #[cfg(feature = "payouts")]
+        {
+            if let Some(payout_statuses) = &self.payout_statuses_enabled {
+                Self::validate_statuses(payout_statuses, "payout")?;
+            }
+        }
+
+        Ok(())
+    }
 }
 
 #[derive(Debug, Serialize, ToSchema)]
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index 641dc83291c..e33e7b0a376 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -2292,6 +2292,7 @@ pub enum FrmTransactionType {
     serde::Deserialize,
     serde::Serialize,
     strum::Display,
+    strum::EnumIter,
     strum::EnumString,
     ToSchema,
 )]
@@ -6959,6 +6960,7 @@ pub enum BrazilStatesAbbreviation {
     serde::Deserialize,
     serde::Serialize,
     strum::Display,
+    strum::EnumIter,
     strum::EnumString,
 )]
 #[router_derive::diesel_enum(storage_type = "db_enum")]
diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs
index 0ea21f5943c..fb4555dea92 100644
--- a/crates/common_enums/src/transformers.rs
+++ b/crates/common_enums/src/transformers.rs
@@ -2,9 +2,11 @@ use std::fmt::{Display, Formatter};
 
 use serde::{Deserialize, Serialize};
 
+#[cfg(feature = "payouts")]
+use crate::enums::PayoutStatus;
 use crate::enums::{
-    AttemptStatus, Country, CountryAlpha2, CountryAlpha3, IntentStatus, PaymentMethod,
-    PaymentMethodType,
+    AttemptStatus, Country, CountryAlpha2, CountryAlpha3, DisputeStatus, EventType, IntentStatus,
+    MandateStatus, PaymentMethod, PaymentMethodType, RefundStatus,
 };
 
 impl Display for NumericCountryCodeParseError {
@@ -2119,6 +2121,82 @@ impl From<AttemptStatus> for IntentStatus {
     }
 }
 
+impl From<IntentStatus> for Option<EventType> {
+    fn from(value: IntentStatus) -> Self {
+        match value {
+            IntentStatus::Succeeded => Some(EventType::PaymentSucceeded),
+            IntentStatus::Failed => Some(EventType::PaymentFailed),
+            IntentStatus::Processing => Some(EventType::PaymentProcessing),
+            IntentStatus::RequiresMerchantAction
+            | IntentStatus::RequiresCustomerAction
+            | IntentStatus::Conflicted => Some(EventType::ActionRequired),
+            IntentStatus::Cancelled => Some(EventType::PaymentCancelled),
+            IntentStatus::PartiallyCaptured | IntentStatus::PartiallyCapturedAndCapturable => {
+                Some(EventType::PaymentCaptured)
+            }
+            IntentStatus::RequiresCapture => Some(EventType::PaymentAuthorized),
+            IntentStatus::RequiresPaymentMethod | IntentStatus::RequiresConfirmation => None,
+        }
+    }
+}
+
+impl From<RefundStatus> for Option<EventType> {
+    fn from(value: RefundStatus) -> Self {
+        match value {
+            RefundStatus::Success => Some(EventType::RefundSucceeded),
+            RefundStatus::Failure => Some(EventType::RefundFailed),
+            RefundStatus::ManualReview
+            | RefundStatus::Pending
+            | RefundStatus::TransactionFailure => None,
+        }
+    }
+}
+
+#[cfg(feature = "payouts")]
+impl From<PayoutStatus> for Option<EventType> {
+    fn from(value: PayoutStatus) -> Self {
+        match value {
+            PayoutStatus::Success => Some(EventType::PayoutSuccess),
+            PayoutStatus::Failed => Some(EventType::PayoutFailed),
+            PayoutStatus::Cancelled => Some(EventType::PayoutCancelled),
+            PayoutStatus::Initiated => Some(EventType::PayoutInitiated),
+            PayoutStatus::Expired => Some(EventType::PayoutExpired),
+            PayoutStatus::Reversed => Some(EventType::PayoutReversed),
+            PayoutStatus::Ineligible
+            | PayoutStatus::Pending
+            | PayoutStatus::RequiresCreation
+            | PayoutStatus::RequiresFulfillment
+            | PayoutStatus::RequiresPayoutMethodData
+            | PayoutStatus::RequiresVendorAccountCreation
+            | PayoutStatus::RequiresConfirmation => None,
+        }
+    }
+}
+
+impl From<DisputeStatus> for EventType {
+    fn from(value: DisputeStatus) -> Self {
+        match value {
+            DisputeStatus::DisputeOpened => Self::DisputeOpened,
+            DisputeStatus::DisputeExpired => Self::DisputeExpired,
+            DisputeStatus::DisputeAccepted => Self::DisputeAccepted,
+            DisputeStatus::DisputeCancelled => Self::DisputeCancelled,
+            DisputeStatus::DisputeChallenged => Self::DisputeChallenged,
+            DisputeStatus::DisputeWon => Self::DisputeWon,
+            DisputeStatus::DisputeLost => Self::DisputeLost,
+        }
+    }
+}
+
+impl From<MandateStatus> for Option<EventType> {
+    fn from(value: MandateStatus) -> Self {
+        match value {
+            MandateStatus::Active => Some(EventType::MandateActive),
+            MandateStatus::Revoked => Some(EventType::MandateRevoked),
+            MandateStatus::Inactive | MandateStatus::Pending => None,
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     #![allow(clippy::unwrap_used)]
diff --git a/crates/common_types/src/consts.rs b/crates/common_types/src/consts.rs
index 3550333443f..1ceb9999086 100644
--- a/crates/common_types/src/consts.rs
+++ b/crates/common_types/src/consts.rs
@@ -7,3 +7,26 @@ pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V1;
 /// API version
 #[cfg(feature = "v2")]
 pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V2;
+
+/// Default payment intent statuses that trigger a webhook
+pub const DEFAULT_PAYMENT_WEBHOOK_TRIGGER_STATUSES: &[common_enums::IntentStatus] = &[
+    common_enums::IntentStatus::Succeeded,
+    common_enums::IntentStatus::Failed,
+    common_enums::IntentStatus::PartiallyCaptured,
+    common_enums::IntentStatus::RequiresMerchantAction,
+];
+
+/// Default refund statuses that trigger a webhook
+pub const DEFAULT_REFUND_WEBHOOK_TRIGGER_STATUSES: &[common_enums::RefundStatus] = &[
+    common_enums::RefundStatus::Success,
+    common_enums::RefundStatus::Failure,
+    common_enums::RefundStatus::TransactionFailure,
+];
+
+/// Default payout statuses that trigger a webhook
+pub const DEFAULT_PAYOUT_WEBHOOK_TRIGGER_STATUSES: &[common_enums::PayoutStatus] = &[
+    common_enums::PayoutStatus::Success,
+    common_enums::PayoutStatus::Failed,
+    common_enums::PayoutStatus::Initiated,
+    common_enums::PayoutStatus::Pending,
+];
diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs
index e2543e3b431..2c887e7f7cb 100644
--- a/crates/diesel_models/src/business_profile.rs
+++ b/crates/diesel_models/src/business_profile.rs
@@ -747,6 +747,9 @@ pub struct WebhookDetails {
     pub payment_created_enabled: Option<bool>,
     pub payment_succeeded_enabled: Option<bool>,
     pub payment_failed_enabled: Option<bool>,
+    pub payment_statuses_enabled: Option<Vec<common_enums::IntentStatus>>,
+    pub refund_statuses_enabled: Option<Vec<common_enums::RefundStatus>>,
+    pub payout_statuses_enabled: Option<Vec<common_enums::PayoutStatus>>,
 }
 
 common_utils::impl_to_sql_from_sql_json!(WebhookDetails);
diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml
index bf530143006..f8c9bbce62b 100644
--- a/crates/euclid_wasm/Cargo.toml
+++ b/crates/euclid_wasm/Cargo.toml
@@ -15,8 +15,8 @@ release = ["payouts"]
 dummy_connector = ["kgraph_utils/dummy_connector", "connector_configs/dummy_connector"]
 production = ["connector_configs/production"]
 sandbox = ["connector_configs/sandbox"]
-payouts = ["api_models/payouts", "euclid/payouts"]
-v1 = ["api_models/v1", "kgraph_utils/v1"]
+payouts = ["api_models/payouts", "common_enums/payouts", "euclid/payouts"]
+v1 = ["api_models/v1", "kgraph_utils/v1", "payouts"]
 v2 = []
 
 [dependencies]
diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs
index 25d1da385ad..c0e58520c71 100644
--- a/crates/euclid_wasm/src/lib.rs
+++ b/crates/euclid_wasm/src/lib.rs
@@ -34,7 +34,12 @@ use wasm_bindgen::prelude::*;
 use crate::utils::JsResultExt;
 type JsResult = Result<JsValue, JsValue>;
 use api_models::payment_methods::CountryCodeWithName;
-use common_enums::{CountryAlpha2, MerchantCategoryCode, MerchantCategoryCodeWithName};
+#[cfg(feature = "payouts")]
+use common_enums::PayoutStatus;
+use common_enums::{
+    CountryAlpha2, DisputeStatus, EventClass, EventType, IntentStatus, MandateStatus,
+    MerchantCategoryCode, MerchantCategoryCodeWithName, RefundStatus,
+};
 use strum::IntoEnumIterator;
 
 struct SeedData {
@@ -472,3 +477,42 @@ pub fn get_payout_description_category() -> JsResult {
 
     Ok(serde_wasm_bindgen::to_value(&category)?)
 }
+
+#[wasm_bindgen(js_name = getValidWebhookStatus)]
+pub fn get_valid_webhook_status(key: &str) -> JsResult {
+    let event_class = EventClass::from_str(key)
+        .map_err(|_| "Invalid webhook event type received".to_string())
+        .err_to_js()?;
+
+    match event_class {
+        EventClass::Payments => {
+            let statuses: Vec<IntentStatus> = IntentStatus::iter()
+                .filter(|intent_status| Into::<Option<EventType>>::into(*intent_status).is_some())
+                .collect();
+            Ok(serde_wasm_bindgen::to_value(&statuses)?)
+        }
+        EventClass::Refunds => {
+            let statuses: Vec<RefundStatus> = RefundStatus::iter()
+                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
+                .collect();
+            Ok(serde_wasm_bindgen::to_value(&statuses)?)
+        }
+        EventClass::Disputes => {
+            let statuses: Vec<DisputeStatus> = DisputeStatus::iter().collect();
+            Ok(serde_wasm_bindgen::to_value(&statuses)?)
+        }
+        EventClass::Mandates => {
+            let statuses: Vec<MandateStatus> = MandateStatus::iter()
+                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
+                .collect();
+            Ok(serde_wasm_bindgen::to_value(&statuses)?)
+        }
+        #[cfg(feature = "payouts")]
+        EventClass::Payouts => {
+            let statuses: Vec<PayoutStatus> = PayoutStatus::iter()
+                .filter(|status| Into::<Option<EventType>>::into(*status).is_some())
+                .collect();
+            Ok(serde_wasm_bindgen::to_value(&statuses)?)
+        }
+    }
+}
diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs
index 8f239ef62e0..c386693a222 100644
--- a/crates/hyperswitch_domain_models/src/business_profile.rs
+++ b/crates/hyperswitch_domain_models/src/business_profile.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use common_enums::enums as api_enums;
 use common_types::primitive_wrappers;
 use common_utils::{
@@ -1251,6 +1253,39 @@ impl Profile {
                 "unable to deserialize frm routing algorithm ref from merchant account",
             )
     }
+
+    pub fn get_payment_webhook_statuses(&self) -> Cow<'_, [common_enums::IntentStatus]> {
+        self.webhook_details
+            .as_ref()
+            .and_then(|details| details.payment_statuses_enabled.as_ref())
+            .filter(|statuses_vec| !statuses_vec.is_empty())
+            .map(|statuses_vec| Cow::Borrowed(statuses_vec.as_slice()))
+            .unwrap_or_else(|| {
+                Cow::Borrowed(common_types::consts::DEFAULT_PAYMENT_WEBHOOK_TRIGGER_STATUSES)
+            })
+    }
+
+    pub fn get_refund_webhook_statuses(&self) -> Cow<'_, [common_enums::RefundStatus]> {
+        self.webhook_details
+            .as_ref()
+            .and_then(|details| details.refund_statuses_enabled.as_ref())
+            .filter(|statuses_vec| !statuses_vec.is_empty())
+            .map(|statuses_vec| Cow::Borrowed(statuses_vec.as_slice()))
+            .unwrap_or_else(|| {
+                Cow::Borrowed(common_types::consts::DEFAULT_REFUND_WEBHOOK_TRIGGER_STATUSES)
+            })
+    }
+
+    pub fn get_payout_webhook_statuses(&self) -> Cow<'_, [common_enums::PayoutStatus]> {
+        self.webhook_details
+            .as_ref()
+            .and_then(|details| details.payout_statuses_enabled.as_ref())
+            .filter(|statuses_vec| !statuses_vec.is_empty())
+            .map(|statuses_vec| Cow::Borrowed(statuses_vec.as_slice()))
+            .unwrap_or_else(|| {
+                Cow::Borrowed(common_types::consts::DEFAULT_PAYOUT_WEBHOOK_TRIGGER_STATUSES)
+            })
+    }
 }
 
 #[cfg(feature = "v2")]
diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs
index cbb9957115b..495bea84eac 100644
--- a/crates/router/src/core/payouts.rs
+++ b/crates/router/src/core/payouts.rs
@@ -363,7 +363,7 @@ pub async fn payouts_create_core(
         .await?
     };
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
 }
 
 #[instrument(skip_all)]
@@ -426,7 +426,7 @@ pub async fn payouts_confirm_core(
     )
     .await?;
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
 }
 
 pub async fn payouts_update_core(
@@ -498,7 +498,7 @@ pub async fn payouts_update_core(
         .await?;
     }
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
 }
 
 #[cfg(all(feature = "payouts", feature = "v1"))]
@@ -541,7 +541,9 @@ pub async fn payouts_retrieve_core(
         .await?;
     }
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    Ok(services::ApplicationResponse::Json(
+        response_handler(&state, &merchant_context, &payout_data).await?,
+    ))
 }
 
 #[instrument(skip_all)]
@@ -632,7 +634,9 @@ pub async fn payouts_cancel_core(
             .attach_printable("Payout cancellation failed for given Payout request")?;
     }
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    Ok(services::ApplicationResponse::Json(
+        response_handler(&state, &merchant_context, &payout_data).await?,
+    ))
 }
 
 #[instrument(skip_all)]
@@ -722,7 +726,7 @@ pub async fn payouts_fulfill_core(
         }));
     }
 
-    response_handler(&state, &merchant_context, &payout_data).await
+    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
 }
 
 #[cfg(all(feature = "olap", feature = "v2"))]
@@ -2481,11 +2485,21 @@ pub async fn fulfill_payout(
     Ok(())
 }
 
-pub async fn response_handler(
+pub async fn trigger_webhook_and_handle_response(
     state: &SessionState,
     merchant_context: &domain::MerchantContext,
     payout_data: &PayoutData,
 ) -> RouterResponse<payouts::PayoutCreateResponse> {
+    let response = response_handler(state, merchant_context, payout_data).await?;
+    utils::trigger_payouts_webhook(state, merchant_context, &response).await?;
+    Ok(services::ApplicationResponse::Json(response))
+}
+
+pub async fn response_handler(
+    state: &SessionState,
+    merchant_context: &domain::MerchantContext,
+    payout_data: &PayoutData,
+) -> RouterResult<payouts::PayoutCreateResponse> {
     let payout_attempt = payout_data.payout_attempt.to_owned();
     let payouts = payout_data.payouts.to_owned();
 
@@ -2574,7 +2588,8 @@ pub async fn response_handler(
             .attach_printable("Failed to parse payout link's URL")?,
         payout_method_id,
     };
-    Ok(services::ApplicationResponse::Json(response))
+
+    Ok(response)
 }
 
 #[cfg(feature = "v2")]
diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs
index 3f95d61bfb7..cf841e98b5d 100644
--- a/crates/router/src/core/webhooks/incoming.rs
+++ b/crates/router/src/core/webhooks/incoming.rs
@@ -727,7 +727,7 @@ async fn payments_incoming_webhook_flow(
 
             let status = payments_response.status;
 
-            let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
+            let event_type: Option<enums::EventType> = payments_response.status.into();
 
             // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
             if let Some(outgoing_event_type) = event_type {
@@ -847,20 +847,13 @@ async fn payouts_incoming_webhook_flow(
                 )
             })?;
 
-        let event_type: Option<enums::EventType> = updated_payout_attempt.status.foreign_into();
+        let event_type: Option<enums::EventType> = updated_payout_attempt.status.into();
 
         // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
         if let Some(outgoing_event_type) = event_type {
-            let router_response =
+            let payout_create_response =
                 payouts::response_handler(&state, &merchant_context, &payout_data).await?;
 
-            let payout_create_response: payout_models::PayoutCreateResponse = match router_response
-            {
-                services::ApplicationResponse::Json(response) => response,
-                _ => Err(errors::ApiErrorResponse::WebhookResourceNotFound)
-                    .attach_printable("Failed to fetch the payout create response")?,
-            };
-
             Box::pin(super::create_event_and_trigger_outgoing_webhook(
                 state,
                 merchant_context,
@@ -1055,7 +1048,7 @@ async fn refunds_incoming_webhook_flow(
         .await
         .attach_printable_lazy(|| format!("Failed while updating refund: refund_id: {refund_id}"))?
     };
-    let event_type: Option<enums::EventType> = updated_refund.refund_status.foreign_into();
+    let event_type: Option<enums::EventType> = updated_refund.refund_status.into();
 
     // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
     if let Some(outgoing_event_type) = event_type {
@@ -1367,8 +1360,7 @@ async fn external_authentication_incoming_webhook_flow(
                         let payment_id = payments_response.payment_id.clone();
 
                         let status = payments_response.status;
-                        let event_type: Option<enums::EventType> =
-                            payments_response.status.foreign_into();
+                        let event_type: Option<enums::EventType> = payments_response.status.into();
                         // Set poll_id as completed in redis to allow the fetch status of poll through retrieve_poll_status api from client
                         let poll_id = core_utils::get_poll_id(
                             merchant_context.get_merchant_account().get_id(),
@@ -1486,7 +1478,7 @@ async fn mandates_incoming_webhook_flow(
             )
             .await?,
         );
-        let event_type: Option<enums::EventType> = updated_mandate.mandate_status.foreign_into();
+        let event_type: Option<enums::EventType> = updated_mandate.mandate_status.into();
         if let Some(outgoing_event_type) = event_type {
             Box::pin(super::create_event_and_trigger_outgoing_webhook(
                 state,
@@ -1589,7 +1581,7 @@ async fn frm_incoming_webhook_flow(
             services::ApplicationResponse::JsonWithHeaders((payments_response, _)) => {
                 let payment_id = payments_response.payment_id.clone();
                 let status = payments_response.status;
-                let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
+                let event_type: Option<enums::EventType> = payments_response.status.into();
                 if let Some(outgoing_event_type) = event_type {
                     let primary_object_created_at = payments_response.created;
                     Box::pin(super::create_event_and_trigger_outgoing_webhook(
@@ -1663,7 +1655,7 @@ async fn disputes_incoming_webhook_flow(
         )
         .await?;
         let disputes_response = Box::new(dispute_object.clone().foreign_into());
-        let event_type: enums::EventType = dispute_object.dispute_status.foreign_into();
+        let event_type: enums::EventType = dispute_object.dispute_status.into();
 
         Box::pin(super::create_event_and_trigger_outgoing_webhook(
             state,
@@ -1745,7 +1737,7 @@ async fn bank_transfer_webhook_flow(
         services::ApplicationResponse::JsonWithHeaders((payments_response, _)) => {
             let payment_id = payments_response.payment_id.clone();
 
-            let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
+            let event_type: Option<enums::EventType> = payments_response.status.into();
             let status = payments_response.status;
 
             // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
diff --git a/crates/router/src/core/webhooks/incoming_v2.rs b/crates/router/src/core/webhooks/incoming_v2.rs
index c6ff1e73abc..0cf8b2bfe7b 100644
--- a/crates/router/src/core/webhooks/incoming_v2.rs
+++ b/crates/router/src/core/webhooks/incoming_v2.rs
@@ -535,7 +535,7 @@ async fn payments_incoming_webhook_flow(
 
             let status = payments_response.status;
 
-            let event_type: Option<enums::EventType> = payments_response.status.foreign_into();
+            let event_type: Option<enums::EventType> = payments_response.status.into();
 
             // If event is NOT an UnsupportedEvent, trigger Outgoing Webhook
             if let Some(outgoing_event_type) = event_type {
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index d3ffb715b73..89fe95268ee 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -3,7 +3,7 @@ use router_env::{instrument, tracing, Flow};
 
 use super::app::AppState;
 use crate::{
-    core::{admin::*, api_locking},
+    core::{admin::*, api_locking, errors},
     services::{api, authentication as auth, authorization::permissions::Permission},
     types::{api::admin, domain},
 };
@@ -186,11 +186,25 @@ pub async fn merchant_account_create(
     json_payload: web::Json<admin::MerchantAccountCreate>,
 ) -> HttpResponse {
     let flow = Flow::MerchantsAccountCreate;
+    let payload = json_payload.into_inner();
+    if let Err(api_error) = payload
+        .webhook_details
+        .as_ref()
+        .map(|details| {
+            details
+                .validate()
+                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
+        })
+        .transpose()
+    {
+        return api::log_and_return_error_response(api_error.into());
+    }
+
     Box::pin(api::server_wrap(
         flow,
         state,
         &req,
-        json_payload.into_inner(),
+        payload,
         |state, auth, req, _| create_merchant_account(state, req, auth),
         &auth::PlatformOrgAdminAuth {
             is_admin_auth_allowed: true,
diff --git a/crates/router/src/routes/profiles.rs b/crates/router/src/routes/profiles.rs
index caa06c97b74..a854a68a2a3 100644
--- a/crates/router/src/routes/profiles.rs
+++ b/crates/router/src/routes/profiles.rs
@@ -3,7 +3,7 @@ use router_env::{instrument, tracing, Flow};
 
 use super::app::AppState;
 use crate::{
-    core::{admin::*, api_locking},
+    core::{admin::*, api_locking, errors},
     services::{api, authentication as auth, authorization::permissions},
     types::{api::admin, domain},
 };
@@ -19,6 +19,18 @@ pub async fn profile_create(
     let flow = Flow::ProfileCreate;
     let payload = json_payload.into_inner();
     let merchant_id = path.into_inner();
+    if let Err(api_error) = payload
+        .webhook_details
+        .as_ref()
+        .map(|details| {
+            details
+                .validate()
+                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
+        })
+        .transpose()
+    {
+        return api::log_and_return_error_response(api_error.into());
+    }
 
     Box::pin(api::server_wrap(
         flow,
@@ -53,6 +65,18 @@ pub async fn profile_create(
 ) -> HttpResponse {
     let flow = Flow::ProfileCreate;
     let payload = json_payload.into_inner();
+    if let Err(api_error) = payload
+        .webhook_details
+        .as_ref()
+        .map(|details| {
+            details
+                .validate()
+                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
+        })
+        .transpose()
+    {
+        return api::log_and_return_error_response(api_error.into());
+    }
 
     Box::pin(api::server_wrap(
         flow,
@@ -158,12 +182,25 @@ pub async fn profile_update(
 ) -> HttpResponse {
     let flow = Flow::ProfileUpdate;
     let (merchant_id, profile_id) = path.into_inner();
+    let payload = json_payload.into_inner();
+    if let Err(api_error) = payload
+        .webhook_details
+        .as_ref()
+        .map(|details| {
+            details
+                .validate()
+                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
+        })
+        .transpose()
+    {
+        return api::log_and_return_error_response(api_error.into());
+    }
 
     Box::pin(api::server_wrap(
         flow,
         state,
         &req,
-        json_payload.into_inner(),
+        payload,
         |state, auth_data, req, _| update_profile(state, &profile_id, auth_data.key_store, req),
         auth::auth_type(
             &auth::HeaderAuth(auth::ApiKeyAuthWithMerchantIdFromRoute(merchant_id.clone())),
@@ -189,12 +226,25 @@ pub async fn profile_update(
 ) -> HttpResponse {
     let flow = Flow::ProfileUpdate;
     let profile_id = path.into_inner();
+    let payload = json_payload.into_inner();
+    if let Err(api_error) = payload
+        .webhook_details
+        .as_ref()
+        .map(|details| {
+            details
+                .validate()
+                .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
+        })
+        .transpose()
+    {
+        return api::log_and_return_error_response(api_error.into());
+    }
 
     Box::pin(api::server_wrap(
         flow,
         state,
         &req,
-        json_payload.into_inner(),
+        payload,
         |state, auth::AuthenticationDataWithoutProfile { key_store, .. }, req, _| {
             update_profile(state, &profile_id, key_store, req)
         },
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 2e64b10c284..ea7f0990649 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -461,31 +461,6 @@ impl ForeignFrom<payments::MandateAmountData> for storage_enums::MandateAmountDa
     }
 }
 
-impl ForeignFrom<api_enums::IntentStatus> for Option<storage_enums::EventType> {
-    fn foreign_from(value: api_enums::IntentStatus) -> Self {
-        match value {
-            api_enums::IntentStatus::Succeeded => Some(storage_enums::EventType::PaymentSucceeded),
-            api_enums::IntentStatus::Failed => Some(storage_enums::EventType::PaymentFailed),
-            api_enums::IntentStatus::Processing => {
-                Some(storage_enums::EventType::PaymentProcessing)
-            }
-            api_enums::IntentStatus::RequiresMerchantAction
-            | api_enums::IntentStatus::RequiresCustomerAction
-            | api_enums::IntentStatus::Conflicted => Some(storage_enums::EventType::ActionRequired),
-            api_enums::IntentStatus::Cancelled => Some(storage_enums::EventType::PaymentCancelled),
-            api_enums::IntentStatus::PartiallyCaptured
-            | api_enums::IntentStatus::PartiallyCapturedAndCapturable => {
-                Some(storage_enums::EventType::PaymentCaptured)
-            }
-            api_enums::IntentStatus::RequiresCapture => {
-                Some(storage_enums::EventType::PaymentAuthorized)
-            }
-            api_enums::IntentStatus::RequiresPaymentMethod
-            | api_enums::IntentStatus::RequiresConfirmation => None,
-        }
-    }
-}
-
 impl ForeignFrom<api_enums::PaymentMethodType> for api_enums::PaymentMethod {
     fn foreign_from(payment_method_type: api_enums::PaymentMethodType) -> Self {
         match payment_method_type {
@@ -632,66 +607,6 @@ impl ForeignTryFrom<payments::PaymentMethodData> for api_enums::PaymentMethod {
     }
 }
 
-impl ForeignFrom<storage_enums::RefundStatus> for Option<storage_enums::EventType> {
-    fn foreign_from(value: storage_enums::RefundStatus) -> Self {
-        match value {
-            storage_enums::RefundStatus::Success => Some(storage_enums::EventType::RefundSucceeded),
-            storage_enums::RefundStatus::Failure => Some(storage_enums::EventType::RefundFailed),
-            api_enums::RefundStatus::ManualReview
-            | api_enums::RefundStatus::Pending
-            | api_enums::RefundStatus::TransactionFailure => None,
-        }
-    }
-}
-
-impl ForeignFrom<storage_enums::PayoutStatus> for Option<storage_enums::EventType> {
-    fn foreign_from(value: storage_enums::PayoutStatus) -> Self {
-        match value {
-            storage_enums::PayoutStatus::Success => Some(storage_enums::EventType::PayoutSuccess),
-            storage_enums::PayoutStatus::Failed => Some(storage_enums::EventType::PayoutFailed),
-            storage_enums::PayoutStatus::Cancelled => {
-                Some(storage_enums::EventType::PayoutCancelled)
-            }
-            storage_enums::PayoutStatus::Initiated => {
-                Some(storage_enums::EventType::PayoutInitiated)
-            }
-            storage_enums::PayoutStatus::Expired => Some(storage_enums::EventType::PayoutExpired),
-            storage_enums::PayoutStatus::Reversed => Some(storage_enums::EventType::PayoutReversed),
-            storage_enums::PayoutStatus::Ineligible
-            | storage_enums::PayoutStatus::Pending
-            | storage_enums::PayoutStatus::RequiresCreation
-            | storage_enums::PayoutStatus::RequiresFulfillment
-            | storage_enums::PayoutStatus::RequiresPayoutMethodData
-            | storage_enums::PayoutStatus::RequiresVendorAccountCreation
-            | storage_enums::PayoutStatus::RequiresConfirmation => None,
-        }
-    }
-}
-
-impl ForeignFrom<storage_enums::DisputeStatus> for storage_enums::EventType {
-    fn foreign_from(value: storage_enums::DisputeStatus) -> Self {
-        match value {
-            storage_enums::DisputeStatus::DisputeOpened => Self::DisputeOpened,
-            storage_enums::DisputeStatus::DisputeExpired => Self::DisputeExpired,
-            storage_enums::DisputeStatus::DisputeAccepted => Self::DisputeAccepted,
-            storage_enums::DisputeStatus::DisputeCancelled => Self::DisputeCancelled,
-            storage_enums::DisputeStatus::DisputeChallenged => Self::DisputeChallenged,
-            storage_enums::DisputeStatus::DisputeWon => Self::DisputeWon,
-            storage_enums::DisputeStatus::DisputeLost => Self::DisputeLost,
-        }
-    }
-}
-
-impl ForeignFrom<storage_enums::MandateStatus> for Option<storage_enums::EventType> {
-    fn foreign_from(value: storage_enums::MandateStatus) -> Self {
-        match value {
-            storage_enums::MandateStatus::Active => Some(storage_enums::EventType::MandateActive),
-            storage_enums::MandateStatus::Revoked => Some(storage_enums::EventType::MandateRevoked),
-            storage_enums::MandateStatus::Inactive | storage_enums::MandateStatus::Pending => None,
-        }
-    }
-}
-
 impl ForeignTryFrom<api_models::webhooks::IncomingWebhookEvent> for storage_enums::RefundStatus {
     type Error = errors::ValidationError;
 
@@ -2153,8 +2068,11 @@ impl ForeignFrom<api_models::admin::WebhookDetails>
             webhook_password: item.webhook_password,
             webhook_url: item.webhook_url,
             payment_created_enabled: item.payment_created_enabled,
-            payment_succeeded_enabled: item.payment_succeeded_enabled,
             payment_failed_enabled: item.payment_failed_enabled,
+            payment_succeeded_enabled: item.payment_succeeded_enabled,
+            payment_statuses_enabled: item.payment_statuses_enabled,
+            refund_statuses_enabled: item.refund_statuses_enabled,
+            payout_statuses_enabled: item.payout_statuses_enabled,
         }
     }
 }
@@ -2169,8 +2087,11 @@ impl ForeignFrom<diesel_models::business_profile::WebhookDetails>
             webhook_password: item.webhook_password,
             webhook_url: item.webhook_url,
             payment_created_enabled: item.payment_created_enabled,
-            payment_succeeded_enabled: item.payment_succeeded_enabled,
             payment_failed_enabled: item.payment_failed_enabled,
+            payment_succeeded_enabled: item.payment_succeeded_enabled,
+            payment_statuses_enabled: item.payment_statuses_enabled,
+            refund_statuses_enabled: item.refund_statuses_enabled,
+            payout_statuses_enabled: item.payout_statuses_enabled,
         }
     }
 }
diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs
index 6861e4d1df3..7da042a4882 100644
--- a/crates/router/src/utils.rs
+++ b/crates/router/src/utils.rs
@@ -55,10 +55,7 @@ use crate::{
     logger,
     routes::{metrics, SessionState},
     services::{self, authentication::get_header_value_by_key},
-    types::{
-        self, domain,
-        transformers::{ForeignFrom, ForeignInto},
-    },
+    types::{self, domain, transformers::ForeignInto},
 };
 #[cfg(feature = "v1")]
 use crate::{core::webhooks as webhooks_core, types::storage};
@@ -1154,25 +1151,21 @@ where
     D: payments_core::OperationSessionGetters<F>,
 {
     let status = payment_data.get_payment_intent().status;
-    let payment_id = payment_data.get_payment_intent().get_id().to_owned();
-
-    let captures = payment_data
-        .get_multiple_capture_data()
-        .map(|multiple_capture_data| {
-            multiple_capture_data
-                .get_all_captures()
-                .into_iter()
-                .cloned()
-                .collect()
-        });
-
-    if matches!(
-        status,
-        enums::IntentStatus::Succeeded
-            | enums::IntentStatus::Failed
-            | enums::IntentStatus::PartiallyCaptured
-            | enums::IntentStatus::RequiresMerchantAction
-    ) {
+    let should_trigger_webhook = business_profile
+        .get_payment_webhook_statuses()
+        .contains(&status);
+
+    if should_trigger_webhook {
+        let captures = payment_data
+            .get_multiple_capture_data()
+            .map(|multiple_capture_data| {
+                multiple_capture_data
+                    .get_all_captures()
+                    .into_iter()
+                    .cloned()
+                    .collect()
+            });
+        let payment_id = payment_data.get_payment_intent().get_id().to_owned();
         let payments_response = crate::core::payments::transformers::payments_to_payments_response(
             payment_data,
             captures,
@@ -1186,7 +1179,7 @@ where
             None,
         )?;
 
-        let event_type = ForeignFrom::foreign_from(status);
+        let event_type = status.into();
 
         if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) =
             payments_response
@@ -1248,27 +1241,28 @@ pub async fn trigger_refund_outgoing_webhook(
     profile_id: id_type::ProfileId,
 ) -> RouterResult<()> {
     let refund_status = refund.refund_status;
-    if matches!(
-        refund_status,
-        enums::RefundStatus::Success
-            | enums::RefundStatus::Failure
-            | enums::RefundStatus::TransactionFailure
-    ) {
-        let event_type = ForeignFrom::foreign_from(refund_status);
+
+    let key_manager_state = &(state).into();
+    let business_profile = state
+        .store
+        .find_business_profile_by_profile_id(
+            key_manager_state,
+            merchant_context.get_merchant_key_store(),
+            &profile_id,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
+            id: profile_id.get_string_repr().to_owned(),
+        })?;
+
+    let should_trigger_webhook = business_profile
+        .get_refund_webhook_statuses()
+        .contains(&refund_status);
+
+    if should_trigger_webhook {
+        let event_type = refund_status.into();
         let refund_response: api_models::refunds::RefundResponse = refund.clone().foreign_into();
-        let key_manager_state = &(state).into();
         let refund_id = refund_response.refund_id.clone();
-        let business_profile = state
-            .store
-            .find_business_profile_by_profile_id(
-                key_manager_state,
-                merchant_context.get_merchant_key_store(),
-                &profile_id,
-            )
-            .await
-            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
-                id: profile_id.get_string_repr().to_owned(),
-            })?;
         let cloned_state = state.clone();
         let cloned_merchant_context = merchant_context.clone();
         let primary_object_created_at = refund_response.created_at;
@@ -1315,3 +1309,72 @@ pub fn get_locale_from_header(headers: &actix_web::http::header::HeaderMap) -> S
         .map(|val| val.to_string())
         .unwrap_or(common_utils::consts::DEFAULT_LOCALE.to_string())
 }
+
+#[cfg(all(feature = "payouts", feature = "v1"))]
+pub async fn trigger_payouts_webhook(
+    state: &SessionState,
+    merchant_context: &domain::MerchantContext,
+    payout_response: &api_models::payouts::PayoutCreateResponse,
+) -> RouterResult<()> {
+    let key_manager_state = &(state).into();
+    let profile_id = &payout_response.profile_id;
+    let business_profile = state
+        .store
+        .find_business_profile_by_profile_id(
+            key_manager_state,
+            merchant_context.get_merchant_key_store(),
+            profile_id,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
+            id: profile_id.get_string_repr().to_owned(),
+        })?;
+
+    let status = &payout_response.status;
+    let should_trigger_webhook = business_profile
+        .get_payout_webhook_statuses()
+        .contains(status);
+
+    if should_trigger_webhook {
+        let event_type = (*status).into();
+        if let Some(event_type) = event_type {
+            let cloned_merchant_context = merchant_context.clone();
+            let cloned_state = state.clone();
+            let cloned_response = payout_response.clone();
+
+            // This spawns this futures in a background thread, the exception inside this future won't affect
+            // the current thread and the lifecycle of spawn thread is not handled by runtime.
+            // So when server shutdown won't wait for this thread's completion.
+            tokio::spawn(
+                async move {
+                    let primary_object_created_at = cloned_response.created;
+                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
+                        cloned_state,
+                        cloned_merchant_context,
+                        business_profile,
+                        event_type,
+                        diesel_models::enums::EventClass::Payouts,
+                        cloned_response.payout_id.clone(),
+                        diesel_models::enums::EventObjectType::PayoutDetails,
+                        webhooks::OutgoingWebhookContent::PayoutDetails(Box::new(cloned_response)),
+                        primary_object_created_at,
+                    ))
+                    .await
+                }
+                .in_current_span(),
+            );
+        } else {
+            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
+        }
+    }
+    Ok(())
+}
+
+#[cfg(all(feature = "payouts", feature = "v2"))]
+pub async fn trigger_payouts_webhook(
+    state: &SessionState,
+    merchant_context: &domain::MerchantContext,
+    payout_response: &api_models::payouts::PayoutCreateResponse,
+) -> RouterResult<()> {
+    todo!()
+}
diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs
index d525763529e..8a04094f31d 100644
--- a/crates/router/src/workflows/outgoing_webhook_retry.rs
+++ b/crates/router/src/workflows/outgoing_webhook_retry.rs
@@ -438,7 +438,7 @@ async fn get_outgoing_webhook_content_and_event_type(
                     })
                 }
             }?;
-            let event_type = Option::<EventType>::foreign_from(payments_response.status);
+            let event_type: Option<EventType> = payments_response.status.into();
             logger::debug!(current_resource_status=%payments_response.status);
 
             Ok((
@@ -462,7 +462,7 @@ async fn get_outgoing_webhook_content_and_event_type(
                 request,
             ))
             .await?;
-            let event_type = Option::<EventType>::foreign_from(refund.refund_status);
+            let event_type: Option<EventType> = refund.refund_status.into();
             logger::debug!(current_resource_status=%refund.refund_status);
             let refund_response = RefundResponse::foreign_from(refund);
 
@@ -495,7 +495,7 @@ async fn get_outgoing_webhook_content_and_event_type(
                     }
                 }
                 .map(Box::new)?;
-            let event_type = Some(EventType::foreign_from(dispute_response.dispute_status));
+            let event_type = Some(EventType::from(dispute_response.dispute_status));
             logger::debug!(current_resource_status=%dispute_response.dispute_status);
 
             Ok((
@@ -527,7 +527,7 @@ async fn get_outgoing_webhook_content_and_event_type(
                     }
                 }
                 .map(Box::new)?;
-            let event_type = Option::<EventType>::foreign_from(mandate_response.status);
+            let event_type: Option<EventType> = mandate_response.status.into();
             logger::debug!(current_resource_status=%mandate_response.status);
 
             Ok((
@@ -551,17 +551,10 @@ async fn get_outgoing_webhook_content_and_event_type(
             ))
             .await?;
 
-            let router_response =
+            let payout_create_response =
                 payouts::response_handler(&state, &merchant_context, &payout_data).await?;
 
-            let payout_create_response: payout_models::PayoutCreateResponse = match router_response
-            {
-                ApplicationResponse::Json(response) => response,
-                _ => Err(errors::ApiErrorResponse::WebhookResourceNotFound)
-                    .attach_printable("Failed to fetch the payout create response")?,
-            };
-
-            let event_type = Option::<EventType>::foreign_from(payout_data.payout_attempt.status);
+            let event_type: Option<EventType> = payout_data.payout_attempt.status.into();
             logger::debug!(current_resource_status=%payout_data.payout_attempt.status);
 
             Ok((
diff --git a/crates/router/tests/utils.rs b/crates/router/tests/utils.rs
index eb8ed7090df..20b83f47483 100644
--- a/crates/router/tests/utils.rs
+++ b/crates/router/tests/utils.rs
@@ -220,9 +220,6 @@ fn mk_merchant_account(merchant_id: Option<String>) -> Value {
         "webhook_version": "1.0.1",
         "webhook_username": "ekart_retail",
         "webhook_password": "password_ekart@123",
-        "payment_created_enabled": true,
-        "payment_succeeded_enabled": true,
-        "payment_failed_enabled": true
       },
       "routing_algorithm": {
         "type": "single",
diff --git a/loadtest/k6/helper/setup.js b/loadtest/k6/helper/setup.js
index 79e0146329e..18f10242a60 100644
--- a/loadtest/k6/helper/setup.js
+++ b/loadtest/k6/helper/setup.js
@@ -34,10 +34,7 @@ export function setup_merchant_apikey() {
         "webhook_details":{
             "webhook_version":"1.0.1",
             "webhook_username":"wh_store",
-            "webhook_password":"pwd_wh@101",
-            "payment_created_enabled":true,
-            "payment_succeeded_enabled":true,
-            "payment_failed_enabled":true
+            "webhook_password":"pwd_wh@101"
         },
         "routing_algorithm": {
             "type": "single",
@@ -126,10 +123,7 @@ export function setup_merchant_apikey() {
         "webhook_details":{
             "webhook_version":"1.0.1",
             "webhook_username":"wh_store",
-            "webhook_password":"pwd_wh@101",
-            "payment_created_enabled":true,
-            "payment_succeeded_enabled":true,
-            "payment_failed_enabled":true
+            "webhook_password":"pwd_wh@101"
         },
         "routing_algorithm": {
             "type": "single",

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
This pull request introduces validation for webhook status configurations at the business profile level.

Specifically, the following changes have been made:

1.  **WebhookDetails Validation:**
    *   Added a `validate()` method to the `WebhookDetails` struct in `crates/api_models/src/admin.rs`.
    *   This method ensures that any statuses provided in `payment_statuses_enabled`, `refund_statuses_enabled`, and `payout_statuses_enabled` are valid and correspond to triggerable event types.

2.  **API Handler Integration:**
    *   Updated the API route handlers in `crates/router/src/routes/profiles.rs` for merchant account creation, merchant account updates, profile creation and profile updates.
    *   These handlers now invoke the `WebhookDetails::validate()` method if `webhook_details` are present in the request payload.
    *   If validation fails (i.e., an invalid status is provided), the API will return an `ApiErrorResponse::InvalidRequestData` with a descriptive message.

3. **Core flows - payments, refunds and payouts**
    * Added logic for checking if the resource status is eligible for triggering an outgoing webhook in the payment, refund and payout flows.
    * If statuses configured are empty in profile, it uses a default list for checking the eligibility.

4. **WASM functionalities**
    * Added functionality in WASM crate for fetching a list of valid webhook statuses which can be used to render on HyperSwitch dashboard for configurations.

This ensures that only valid webhook configurations are accepted and stored, improving the robustness of the outgoing webhook system.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
Allows merchants to configure different statuses for triggering outgoing webhooks.

## How did you test it?

I. New behavior
<details>
    <summary>1. Create Merchant Account with custom `webhook_details`</summary>

cURL

    curl --location --request POST 'http://localhost:8080/accounts' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: test_admin' \
        --data-raw '{"merchant_id":"merchant_1750673206","locker_id":"m0010","merchant_name":"NewAge Retailer","merchant_details":{"primary_contact_person":"John Test","primary_email":"JohnTest@test.com","primary_phone":"sunt laborum","secondary_contact_person":"John Test2","secondary_email":"JohnTest2@test.com","secondary_phone":"cillum do dolor id","website":"https://www.example.com","about_business":"Online Retail with a wide selection of organic products for North America","address":{"line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"California","zip":"94122","country":"US"}},"return_url":"https://google.com/success","webhook_details":{"webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass","payment_statuses_enabled":["requires_customer_action","failed"],"payout_statuses_enabled":["failed"],"refund_statuses_enabled":["success"]},"sub_merchants_enabled":false,"metadata":{"city":"NY","unit":"245"},"primary_business_details":[{"country":"IN","business":"default"}]}'

Response

    {"merchant_id":"merchant_1750673203","merchant_name":"NewAge Retailer","return_url":"https://google.com/success","enable_payment_response_hash":true,"payment_response_hash_key":"pv3MWoTzwpOnCUWo8rkeBATn5QiaJ2MKtpKe9anxnqC2dt53I0pK6lH0yy50j1zT","redirect_to_merchant_with_http_post":false,"merchant_details":{"primary_contact_person":"John Test","primary_phone":"sunt laborum","primary_email":"JohnTest@test.com","secondary_contact_person":"John Test2","secondary_phone":"cillum do dolor id","secondary_email":"JohnTest2@test.com","website":"https://www.example.com","about_business":"Online Retail with a wide selection of organic products for North America","address":{"city":"San Fransico","country":"US","line1":null,"line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":null,"last_name":null}},"webhook_details":{"webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass","webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","payment_statuses_enabled":["requires_customer_action","failed"],"refund_statuses_enabled":["success"],"payout_statuses_enabled":["failed"]},"payout_routing_algorithm":null,"sub_merchants_enabled":false,"parent_merchant_id":null,"publishable_key":"pk_dev_fdfb25c3c2294cf49aa1bb361afb1100","metadata":{"city":"NY","unit":"245","compatible_connector":null},"locker_id":"m0010","primary_business_details":[{"country":"IN","business":"default"}],"frm_routing_algorithm":null,"organization_id":"org_II9RJV4U9ZcIloWWrNbR","is_recon_enabled":false,"default_profile":"pro_65qra6NQYjw2UP9vperw","recon_status":"not_requested","pm_collect_link_config":null,"product_type":"orchestration","merchant_account_type":"standard"}

Expectation
- Only valid status can be specified for triggering webhooks across payments, refunds and payouts

</details>

<details>
    <summary>2. Create a payment</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_1MDGBqiM9KD4GY0mu6QfjecN40bJrnJWFDyGxxTdaGKKXt6xH3Jbrr59B0Ua4NPK' \
        --data-raw '{"amount":4500,"currency":"EUR","confirm":true,"profile_id":"pro_pNXZoICIkiLUZEYSrKfX","capture_method":"manual","capture_on":"2022-09-10T10:11:12Z","authentication_type":"three_ds","setup_future_usage":"off_session","connector":["adyen"],"customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","email":"abc@example.com","return_url":"https://google.com","payment_method":"card","payment_method_type":"debit","payment_method_data":{"card":{"card_number":"4111112014267661","card_exp_month":"12","card_exp_year":"2030","card_cvc":"737"}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"California","zip":"94122","country":"IT","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"metadata":{"Force-PSP":"Adyen","udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"browser_info":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","language":"en-US","color_depth":32,"screen_height":1117,"screen_width":1728,"time_zone":-330,"java_enabled":true,"java_script_enabled":true,"ip_address":"127.0.0.1"},"customer_acceptance":{"acceptance_type":"online","accepted_at":"1963-05-03T04:07:52.723Z","online":{"ip_address":"127.0.0.1","user_agent":"amet irure esse"}},"connector_metadata":{"adyen":{"testing":{}}},"session_expiry":60}'

Response

    {"payment_id":"pay_iApiXN3tqc2yW1XBP0Ua","merchant_id":"merchant_1750673324","status":"requires_capture","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":4500,"amount_received":null,"connector":"adyen","client_secret":"pay_iApiXN3tqc2yW1XBP0Ua_secret_AgBcvgc6zoSu2Qr2sq96","created":"2025-06-23T10:08:56.548Z","currency":"EUR","customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","customer":{"id":"cus_zhY6PdgnyRlXLVGzqkvM","name":"John Nether","email":"abc@example.com","phone":"6168205362","phone_country_code":"+1"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":"off_session","off_session":null,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"7661","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"IT","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"order_details":null,"email":"abc@example.com","name":"John Nether","phone":"6168205362","return_url":"https://google.com/","authentication_type":"three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":null,"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"debit","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":{"customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","created_at":1750673336,"expires":1750676936,"secret":"epk_0e045ee6e8b3409da23d14233314639d"},"manual_retry_allowed":false,"connector_transaction_id":"GZD3LFQ2BDDHL975","frm_message":null,"metadata":{"udf1":"value1","Force-PSP":"Adyen","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":{"apple_pay":null,"airwallex":null,"noon":null,"braintree":null,"adyen":{"testing":{"holder_name":null}}},"feature_metadata":null,"reference_id":"pay_iApiXN3tqc2yW1XBP0Ua_1","payment_link":null,"profile_id":"pro_pNXZoICIkiLUZEYSrKfX","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_9dLJtXqFTDdVt4fwwLL4","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-06-23T10:09:56.548Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_method_id":"pm_4eqrT6kM50j4XdLyX6YK","payment_method_status":"active","updated":"2025-06-23T10:08:57.185Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":"JVZJSSRZPLSMC375","card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null}

Expectation
- A payment webhook must be triggered since the status reaches `requires_capture` which was configured for triggering webhooks

</details>

<details>
    <summary>3. Update an existing profile</summary>

cURL

    curl --location --request POST 'http://localhost:8080/account/merchant_1750673423/business_profile/pro_vjiSMmVIZdLWNbPBnGXP' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_WWRksDrZIVkWppHEPz4MWFccxq9ZMEoGHTWRw1euMjstZC3XD51mGBc5NjMLVAwx' \
        --data '{"webhook_details":{"webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass","payment_statuses_enabled":["requires_capture","failed"],"payout_statuses_enabled":["failed"],"refund_statuses_enabled":["success"]}}'

Response

    {"merchant_id":"merchant_1750673423","profile_id":"pro_vjiSMmVIZdLWNbPBnGXP","profile_name":"IN_default","return_url":"https://google.com/success","enable_payment_response_hash":true,"payment_response_hash_key":"EPy4DzeilG41Sz0g2krFOfbz9jI49AnrsljEPFkIqRhHn96Hh70NcvvrsXMsuiR9","redirect_to_merchant_with_http_post":false,"webhook_details":{"webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass","webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","payment_statuses_enabled":["requires_capture","failed"],"refund_statuses_enabled":["success"],"payout_statuses_enabled":["failed"]},"metadata":null,"routing_algorithm":null,"intent_fulfillment_time":900,"frm_routing_algorithm":null,"payout_routing_algorithm":null,"applepay_verified_domains":["gdpp-dev.zurich.com"],"session_expiry":900,"payment_link_config":{"domain_name":null,"theme":"#E1E1E1","logo":"https://hyperswitch.io/favicon.ico","seller_name":null,"sdk_layout":null,"display_sdk_only":null,"enabled_saved_payment_method":true,"hide_card_nickname_field":null,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":null,"custom_message_for_card_terms":"","payment_button_colour":null,"skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":null,"payment_link_ui_rules":null,"enable_button_only_on_form_ready":null,"payment_form_header_text":null,"payment_form_label_type":null,"show_card_terms":"always","is_setup_mandate_flow":null,"color_icon_card_cvc_error":null,"business_specific_configs":{"mendix":{"theme":null,"logo":"https://hyperswitch.io/favicon.ico","seller_name":null,"sdk_layout":null,"display_sdk_only":true,"enabled_saved_payment_method":null,"hide_card_nickname_field":null,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":"Validate Card","custom_message_for_card_terms":null,"payment_button_colour":null,"skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":{".Input--invalid":{"color":"#545454"},".w-full.relative.flex.flex-row.my-4":{"display":"none"},".Tab":{"display":"none"},".InputLogo":{"color":"#545454"},".Error":{"paddingLeft":"0px","opacity":"1","color":"#CB4B40","display":"block","fontSize":"12px","marginTop":"4px"},".Label":{"left":"0px","fontWeight":"300","color":"#2167AE","fontSize":"12px","bottom":"5px","marginLeft":"0px","opacity":"1","fontFamily":"'zurich-sans-Medium', sans-serif"},".InputLogo--empty":{"color":"#979797"},".flex.flex-col.gap-2.h-auto.w-full":{"display":"none"},".Input":{"paddingBottom":"2px","fontWeight":"400","boxSizing":"border-box","height":"56px","width":"100%","paddingLeft":"0px","backgroundColor":"white","display":"block","border":"none","fontSize":"18px","color":"#23366F","borderRadius":"0px","boxShadow":"none","borderBottom":"1px solid #2167AE","fontFamily":"'zurich-sans-Medium', sans-serif"},".Input:focus":{"outline":"none","borderBottom":"1px solid #2167AE","backgroundColor":"white"},".Label--floating":{"opacity":"1"},".Input:focus + .Label":{"opacity":"0 !important"},".PaymentLabel":{"color":"#545454","fontSize":"24px"},".Input--empty":{"backgroundColor":"white","boxShadow":"none","borderBottom":"1px solid #2167AE"},".TabHeader":{"display":"none"},".InputLogo--invalid":{"color":"#CB4B40"},".Input::placeholder":{"fontFamily":"'zurich-sans-light', sans-serif","fontSize":"18px","paddingLeft":"0px","color":"#C0C0C0"}},"payment_link_ui_rules":{"#submit.processing":{"paddingLeft":"50px !important","paddingRight":"50px !important"},"#submit":{"padding":"13px 20px","marginTop":"24px","width":"max-content !important","borderRadius":"50px !important","backgroundColor":"#2167AE !important","height":"max-content !important","fontSize":"14px !important"}},"enable_button_only_on_form_ready":true,"payment_form_header_text":null,"payment_form_label_type":null,"show_card_terms":null,"is_setup_mandate_flow":null,"color_icon_card_cvc_error":"#CB4B40"},"omnium":{"theme":null,"logo":"https://hyperswitch.io/favicon.ico","seller_name":null,"sdk_layout":null,"display_sdk_only":true,"enabled_saved_payment_method":true,"hide_card_nickname_field":null,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":"Save Card","custom_message_for_card_terms":null,"payment_button_colour":"#c2c2c2","skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":{".Input:not(.Input--empty) + .Label":{"opacity":"0 !important"},".Error":{"marginTop":"4px","display":"block","opacity":"1","fontSize":"12px","paddingLeft":"0px","color":"#CB4B40"},".Label--invalid":{"color":"#CB4B40 !important","opacity":"1 !important"},".InputLogo--empty":{"color":"#979797"},".Tab":{"display":"none"},".TabHeader":{"display":"none"},".InputLogo":{"color":"#545454"},".w-full.relative.flex.flex-row.my-4":{"display":"none"},".Input:focus":{"border":"none !important","outline":"none","borderBottom":"1px solid #C2C2C2 !important","backgroundColor":"white","color":"#C2C2C2"},".Label":{"marginLeft":"0px","bottom":"5px","pointerEvents":"none","left":"0px","color":"#C2C2C2","opacity":"1","fontSize":"14px"},".Input--invalid":{"color":"#CB4B40"},".Input::placeholder":{"color":"#C2C2C2","paddingLeft":"0px"},".Input":{"width":"100%","fontSize":"14px","fontFamily":"'Open Sans', sans-serif","padding":"0","boxSizing":"border-box","borderRadius":"0px","backgroundColor":"white","display":"block","borderBottom":"1px solid #C2C2C2 !important","border":"none !important","boxShadow":"none","color":"#C2C2C2"},".PaymentLabel":{"fontSize":"24px","color":"#545454"},".InputLogo--invalid":{"color":"#CB4B40"},".flex.flex-col.gap-2.h-auto.w-full":{"display":"none"},".Input--empty":{"color":"#C2C2C2","backgroundColor":"white"},".Input:focus + .Label":{"color":"transparent"}},"payment_link_ui_rules":{"#submit":{"height":"25px !important","width":"max-content !important","backgroundColour":"#c2c2c2 !important","padding":"0 8px","color":"#ffffff !important","margin":"0 0 4px auto","fontSize":"13px !important","border":"1px solid #c2c2c2 !important","borderRadius":"0 !important"},"#submit.not-ready":{"border":"1px solid #c2c2c2 !important","backgroundColor":"#ffffff !important","color":"#c2c2c2 !important"},"#submit-spinner":{"borderBottomColor":"#c2c2c2 !important","width":"20px !important","border":"2px solid #fff !important","height":"20px !important"}},"enable_button_only_on_form_ready":true,"payment_form_header_text":null,"payment_form_label_type":"floating","show_card_terms":null,"is_setup_mandate_flow":null,"color_icon_card_cvc_error":"#CB4B40"},"zproject":{"theme":null,"logo":"https://hyperswitch.io/favicon.ico","seller_name":null,"sdk_layout":null,"display_sdk_only":true,"enabled_saved_payment_method":null,"hide_card_nickname_field":null,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":"Validate Card","custom_message_for_card_terms":null,"payment_button_colour":null,"skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":{".Label":{"marginLeft":"0px","left":"0px","color":"#5495CF !important","fontFamily":"'zurich-sans-light', sans-serif","fontWeight":"400","fontSize":"14px","bottom":"5px","opacity":"1 !important"},".InputLogo":{"color":"#545454"},".PaymentLabel":{"fontSize":"24px","color":"#545454 !important"},".w-full.relative.flex.flex-row.my-4":{"display":"none"},".Input, .Input:focus, .Input--invalid, .Input--empty":{"height":"56px","fontSize":"18px","paddingLeft":"0px !important","borderBottom":"1px solid #5495CF","boxShadow":"none","width":"100%","display":"block","borderRadius":"0px","border":"none","boxSizing":"border-box","color":"#000000","paddingBottom":"2px !important","backgroundColor":"white !important","fontFamily":"'zurich-sans-Medium', sans-serif","fontWeight":"400"},".Input::placeholder":{"color":"#C0C0C0","fontSize":"14px","paddingLeft":"0px !important","fontFamily":"'zurich-sans-light', sans-serif"},".Tab":{"display":"none"},".InputLogo--invalid":{"color":"#CB4B40"},".Input--invalid":{"color":"#545454 !important"},".InputLogo--empty":{"color":"#979797"},".Error":{"fontSize":"12px","color":"#CB4B40 !important","marginTop":"4px","opacity":"1 !important","paddingLeft":"0px","display":"block"},".TabHeader":{"display":"none"},".Input:focus + .Label":{"opacity":"0 !important"},".flex.flex-col.gap-2.h-auto.w-full":{"display":"none"},".Label--floating":{"opacity":"1 !important"}},"payment_link_ui_rules":{"#submit":{"padding":"13px 20px","fontSize":"14px !important","borderRadius":"50px !important","height":"max-content !important","backgroundColor":"#5495CF !important","marginTop":"24px","width":"max-content !important"},"#submit.processing":{"paddingRight":"50px !important","paddingLeft":"50px !important"}},"enable_button_only_on_form_ready":true,"payment_form_header_text":null,"payment_form_label_type":null,"show_card_terms":null,"is_setup_mandate_flow":null,"color_icon_card_cvc_error":"#CB4B40"},"illustrator":{"theme":null,"logo":"https://hyperswitch.io/favicon.ico","seller_name":null,"sdk_layout":null,"display_sdk_only":true,"enabled_saved_payment_method":null,"hide_card_nickname_field":null,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":"Save Card","custom_message_for_card_terms":null,"payment_button_colour":null,"skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":{".Label--floating,":{"opacity":"1 !important"},".Label--invalid, .Error":{"opacity":"1 !important","color":"#f00 !important"},".InputLogo--empty":{"color":"#979797"},".TabHeader":{"display":"none"},".Input, .Input:focus, .Input--invalid, .Input--empty":{"width":"100%","fontFamily":"'Open Sans', sans-serif","paddingBottom":"2px !important","paddingLeft":"0px !important","height":"56px","fontWeight":"400","boxSizing":"border-box","color":"#494949","borderBottom":"1px solid #C2C2C2","borderRadius":"0px","backgroundColor":"white !important","display":"block","border":"none","fontSize":"14px","boxShadow":"none"},".InputLogo":{"color":"#545454"},".flex.flex-col.gap-2.h-auto.w-full":{"display":"none"},".Input::placeholder":{"color":"#979797","paddingLeft":"0px !important"},".Input--invalid":{"color":"#545454 !important","borderBottom":"1px solid #f00 !important"},".PaymentLabel":{"color":"#545454 !important","fontSize":"24px"},".w-full.relative.flex.flex-row.my-4":{"display":"none"},".Input:focus + .Label":{"opacity":"0 !important"},".Error":{"marginTop":"4px","fontSize":"12px","paddingLeft":"0px","color":"#f00 !important","display":"block","opacity":"1 !important"},".InputLogo--invalid":{"color":"#CB4B40"},".Tab":{"display":"none"},".Label":{"color":"#FF0022","left":"0px","opacity":"1 !important","marginLeft":"0px","bottom":"5px"}},"payment_link_ui_rules":{"#submit":{"margin":"0 4px 0 auto","fontSize":"13px !important","width":"max-content !important","borderRadius":"0 !important","border":"1px solid #b2b2b2 !important","backgroundColor":"#b2b2b2 !important","height":"25px !important","padding":"0 8px","color":"#ffffff !important"},"#submit.not-ready":{"color":"#b2b2b2 !important","border":"1px solid #b2b2b2 !important","backgroundColor":"#ffffff !important"},"#submit-spinner":{"height":"20px !important","width":"20px !important","borderBottomColor":"#c2c2c2 !important","border":"2px solid #fff !important"}},"enable_button_only_on_form_ready":true,"payment_form_header_text":null,"payment_form_label_type":null,"show_card_terms":null,"is_setup_mandate_flow":null,"color_icon_card_cvc_error":"#CB4B40"},"spain":{"theme":"#2167AE","logo":null,"seller_name":null,"sdk_layout":null,"display_sdk_only":true,"enabled_saved_payment_method":null,"hide_card_nickname_field":true,"show_card_form_by_default":null,"transaction_details":null,"background_image":null,"details_layout":null,"payment_button_text":null,"custom_message_for_card_terms":null,"payment_button_colour":null,"skip_status_screen":null,"payment_button_text_colour":null,"background_colour":null,"sdk_ui_rules":{".InputLogo":{"color":"#23366F"},".InputLogo--invalid":{"color":"#CB4B40"},".Input, .Input:focus, .Input--empty":{"color":"#23366F"},".Label--floating":{"opacity":"1 !important"},".Input, .Input:focus, .Input--invalid, .Input--empty":{"backgroundColor":"rgb(231, 234, 235) !important","paddingLeft":"22px !important","border":"none","boxShadow":"none","borderRadius":"28px","fontSize":"20px","height":"56px","paddingBottom":"2px !important"},".Label":{"color":"#2167AE","opacity":"1 !important","bottom":"5px","marginLeft":"22px"},".Label--invalid, .Input--invalid, .Error":{"color":"#CB4B40 !important","opacity":"1 !important"},".PaymentLabel":{"color":"#23366F !important","fontSize":"24px"},".InputLogo--empty":{"color":"#979797"},".Input--invalid":{"border":"2px solid #CB4B40 !important"}},"payment_link_ui_rules":{"#submit":{"height":"max-content","padding":"14px 20px","fontSize":"20px","marginTop":"48px","borderRadius":"50px"},"#submit.not-ready":{"backgroundColor":"#A6ADAF !important"},"#submit.processing":{"paddingRight":"50px !important","paddingLeft":"50px !important"}},"enable_button_only_on_form_ready":true,"payment_form_header_text":null,"payment_form_label_type":"floating","show_card_terms":"never","is_setup_mandate_flow":null,"color_icon_card_cvc_error":null}},"allowed_domains":["http://localhost:5500","localhost:5500","*","localhost:5501","http://localhost:5501"],"branding_visibility":false},"authentication_connector_details":null,"use_billing_as_payment_method_billing":true,"extended_card_info_config":null,"collect_shipping_details_from_wallet_connector":false,"collect_billing_details_from_wallet_connector":false,"always_collect_shipping_details_from_wallet_connector":false,"always_collect_billing_details_from_wallet_connector":false,"is_connector_agnostic_mit_enabled":true,"payout_link_config":null,"outgoing_webhook_custom_http_headers":null,"tax_connector_id":null,"is_tax_connector_enabled":false,"is_network_tokenization_enabled":true,"is_auto_retries_enabled":false,"max_auto_retries_enabled":null,"always_request_extended_authorization":null,"is_click_to_pay_enabled":false,"authentication_product_ids":null,"card_testing_guard_config":{"card_ip_blocking_status":"disabled","card_ip_blocking_threshold":3,"guest_user_card_blocking_status":"disabled","guest_user_card_blocking_threshold":10,"customer_id_blocking_status":"disabled","customer_id_blocking_threshold":5,"card_testing_guard_expiry":3600},"is_clear_pan_retries_enabled":false,"force_3ds_challenge":false,"is_debit_routing_enabled":false,"merchant_business_country":null,"is_pre_network_tokenization_enabled":false,"acquirer_configs":null,"is_iframe_redirection_enabled":null,"merchant_category_code":null}

Expectation
- Only valid status can be specified for triggering webhooks across payments, refunds and payouts

</details>

II. Existing behavior
<details>
    <summary>1. Create Merchant account without specifying custom status list for triggering webhooks</summary>

cURL

    curl --location --request POST 'http://localhost:8080/accounts' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: test_admin' \
        --data-raw '{"merchant_id":"merchant_1750673427","locker_id":"m0010","merchant_name":"NewAge Retailer","merchant_details":{"primary_contact_person":"John Test","primary_email":"JohnTest@test.com","primary_phone":"sunt laborum","secondary_contact_person":"John Test2","secondary_email":"JohnTest2@test.com","secondary_phone":"cillum do dolor id","website":"https://www.example.com","about_business":"Online Retail with a wide selection of organic products for North America","address":{"line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"California","zip":"94122","country":"US"}},"return_url":"https://google.com/success","webhook_details":{"webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass"},"sub_merchants_enabled":false,"metadata":{"city":"NY","unit":"245"},"primary_business_details":[{"country":"IN","business":"default"}]}'

Response

    {"merchant_id":"merchant_1750673423","merchant_name":"NewAge Retailer","return_url":"https://google.com/success","enable_payment_response_hash":true,"payment_response_hash_key":"EPy4DzeilG41Sz0g2krFOfbz9jI49AnrsljEPFkIqRhHn96Hh70NcvvrsXMsuiR9","redirect_to_merchant_with_http_post":false,"merchant_details":{"primary_contact_person":"John Test","primary_phone":"sunt laborum","primary_email":"JohnTest@test.com","secondary_contact_person":"John Test2","secondary_phone":"cillum do dolor id","secondary_email":"JohnTest2@test.com","website":"https://www.example.com","about_business":"Online Retail with a wide selection of organic products for North America","address":{"city":"San Fransico","country":"US","line1":null,"line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":null,"last_name":null}},"webhook_details":{"webhook_version":"1.0.1","webhook_username":"random","webhook_password":"pass","webhook_url":"https://webhook.site/ede0ddc6-5583-4a92-80e3-d513028c4fe2","payment_statuses_enabled":null,"refund_statuses_enabled":null,"payout_statuses_enabled":null},"payout_routing_algorithm":null,"sub_merchants_enabled":false,"parent_merchant_id":null,"publishable_key":"pk_dev_429522d9b95049bb9f077fdbbf270d2c","metadata":{"city":"NY","unit":"245","compatible_connector":null},"locker_id":"m0010","primary_business_details":[{"country":"IN","business":"default"}],"frm_routing_algorithm":null,"organization_id":"org_wpDh2xmA3WgSWCi8Lzik","is_recon_enabled":false,"default_profile":"pro_vjiSMmVIZdLWNbPBnGXP","recon_status":"not_requested","pm_collect_link_config":null,"product_type":"orchestration","merchant_account_type":"standard"}

</details>

<details>
    <summary>2. Create a payment</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_WWRksDrZIVkWppHEPz4MWFccxq9ZMEoGHTWRw1euMjstZC3XD51mGBc5NjMLVAwx' \
        --data-raw '{"amount":4500,"currency":"EUR","confirm":true,"profile_id":"pro_vjiSMmVIZdLWNbPBnGXP","capture_method":"manual","capture_on":"2022-09-10T10:11:12Z","authentication_type":"three_ds","setup_future_usage":"off_session","connector":["adyen"],"customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","email":"abc@example.com","return_url":"https://google.com","payment_method":"card","payment_method_type":"debit","payment_method_data":{"card":{"card_number":"4111112014267661","card_exp_month":"12","card_exp_year":"2030","card_cvc":"737"}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"California","zip":"94122","country":"IT","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"metadata":{"Force-PSP":"Adyen","udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"browser_info":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","language":"en-US","color_depth":32,"screen_height":1117,"screen_width":1728,"time_zone":-330,"java_enabled":true,"java_script_enabled":true,"ip_address":"127.0.0.1"},"customer_acceptance":{"acceptance_type":"online","accepted_at":"1963-05-03T04:07:52.723Z","online":{"ip_address":"127.0.0.1","user_agent":"amet irure esse"}},"connector_metadata":{"adyen":{"testing":{}}},"session_expiry":60}'

Response

    {"payment_id":"pay_g0OJdggJBGyoEzizQvs5","merchant_id":"merchant_1750673423","status":"requires_capture","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":4500,"amount_received":null,"connector":"adyen","client_secret":"pay_g0OJdggJBGyoEzizQvs5_secret_hfNsAK9g7dE6py1TIyMV","created":"2025-06-23T10:11:45.123Z","currency":"EUR","customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","customer":{"id":"cus_zhY6PdgnyRlXLVGzqkvM","name":null,"email":"abc@example.com","phone":null,"phone_country_code":null},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":"off_session","off_session":null,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"7661","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"IT","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"California","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"order_details":null,"email":"abc@example.com","name":null,"phone":null,"return_url":"https://google.com/","authentication_type":"three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":null,"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"debit","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":{"customer_id":"cus_zhY6PdgnyRlXLVGzqkvM","created_at":1750673505,"expires":1750677105,"secret":"epk_f3de069957be49b9b927d52fd2c01192"},"manual_retry_allowed":false,"connector_transaction_id":"NJHLXGQ2BDDHL975","frm_message":null,"metadata":{"udf1":"value1","Force-PSP":"Adyen","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":{"apple_pay":null,"airwallex":null,"noon":null,"braintree":null,"adyen":{"testing":{"holder_name":null}}},"feature_metadata":null,"reference_id":"pay_g0OJdggJBGyoEzizQvs5_1","payment_link":null,"profile_id":"pro_vjiSMmVIZdLWNbPBnGXP","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_q5cJJsbcZBqqwUZURC49","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-06-23T10:12:45.123Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_method_id":"pm_kWp3mS4JxBegRLBnls36","payment_method_status":"active","updated":"2025-06-23T10:11:46.750Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":"WCLS4TMLJZ545G75","card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null}

Expectation
- Webhook must not be triggered as payment status `requires_capture` is not configured for triggering webhooks by default

</details>


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
