Bug: [FEATURE] extend dynamic fields support for payout use cases



### Feature Description

Dynamic fields helps SDK decide what fields are to be collected from the user. Dynamic fields are defined for a given pm<>pmt<>connector combination. These are attached in the pm list response. These fields are mapped in SDK and collected and sent in the `POST :/confirm` API.

### Possible Implementation

#### Define defaults
A default implementation for RequiredFields is added in Settings. Same types can be extended to define PayoutRequiredFields for a given pm<>pmt<>connector combination. These can be fetched from state config at any given instance.

#### Consumption
This list is fetched during payout link render request. Any data that was provided during `POST :/create` request are populated in the dynamic fields and embedded in the payout link.

### 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/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json
index 6b9ba4afd01..256832c376d 100644
--- a/api-reference-v2/openapi_spec.json
+++ b/api-reference-v2/openapi_spec.json
@@ -3534,6 +3534,14 @@
           {
             "type": "object",
             "properties": {
+              "form_layout": {
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/UIWidgetFormLayout"
+                  }
+                ],
+                "nullable": true
+              },
               "payout_test_mode": {
                 "type": "boolean",
                 "description": "Allows for removing any validations / pre-requisites which are necessary in a production environment",
@@ -6576,6 +6584,12 @@
             "enum": [
               "user_cnpj"
             ]
+          },
+          {
+            "type": "string",
+            "enum": [
+              "user_iban"
+            ]
           }
         ],
         "description": "Possible field type of required fields in payment_method_data"
@@ -14484,6 +14498,14 @@
                 "example": "[{\"payment_method\": \"bank_transfer\", \"payment_method_types\": [\"ach\", \"bacs\"]}]",
                 "nullable": true
               },
+              "form_layout": {
+                "allOf": [
+                  {
+                    "$ref": "#/components/schemas/UIWidgetFormLayout"
+                  }
+                ],
+                "nullable": true
+              },
               "test_mode": {
                 "type": "boolean",
                 "description": "`test_mode` allows for opening payout links without any restrictions. This removes\n- domain name validations\n- check for making sure link is accessed within an iframe",
@@ -14787,8 +14809,11 @@
             "nullable": true
           },
           "billing": {
-            "type": "object",
-            "description": "The billing address for the payout",
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/Address"
+              }
+            ],
             "nullable": true
           },
           "auto_fulfill": {
@@ -17376,6 +17401,13 @@
           "payout"
         ]
       },
+      "UIWidgetFormLayout": {
+        "type": "string",
+        "enum": [
+          "tabs",
+          "journey"
+        ]
+      },
       "UpdateApiKeyRequest": {
         "type": "object",
         "description": "The request body for updating an API Key.",
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index 0b77fd15a30..2b185d202e1 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -2473,6 +2473,10 @@ pub struct BusinessPayoutLinkConfig {
     #[serde(flatten)]
     pub config: BusinessGenericLinkConfig,
 
+    /// Form layout of the payout link
+    #[schema(value_type = Option<UIWidgetFormLayout>, max_length = 255, example = "tabs")]
+    pub form_layout: Option<api_enums::UIWidgetFormLayout>,
+
     /// Allows for removing any validations / pre-requisites which are necessary in a production environment
     #[schema(value_type = Option<bool>, default = false)]
     pub payout_test_mode: Option<bool>,
diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs
index 5db816305bd..4f6a327b28f 100644
--- a/crates/api_models/src/enums.rs
+++ b/crates/api_models/src/enums.rs
@@ -512,6 +512,7 @@ pub enum FieldType {
     UserPixKey,
     UserCpf,
     UserCnpj,
+    UserIban,
 }
 
 impl FieldType {
diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs
index aad0cfcf303..01bb63642be 100644
--- a/crates/api_models/src/payouts.rs
+++ b/crates/api_models/src/payouts.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
 use cards::CardNumber;
 use common_utils::{
     consts::default_payouts_list_limit,
@@ -5,11 +7,12 @@ use common_utils::{
     pii::{self, Email},
 };
 use masking::Secret;
+use router_derive::FlatStruct;
 use serde::{Deserialize, Serialize};
 use time::PrimitiveDateTime;
 use utoipa::ToSchema;
 
-use crate::{enums as api_enums, payments};
+use crate::{enums as api_enums, payment_methods::RequiredFieldInfo, payments};
 
 #[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
 pub enum PayoutRequest {
@@ -204,6 +207,10 @@ pub struct PayoutCreatePayoutLinkConfig {
     #[schema(value_type = Option<Vec<EnabledPaymentMethod>>, example = r#"[{"payment_method": "bank_transfer", "payment_method_types": ["ach", "bacs"]}]"#)]
     pub enabled_payment_methods: Option<Vec<link_utils::EnabledPaymentMethod>>,
 
+    /// Form layout of the payout link
+    #[schema(value_type = Option<UIWidgetFormLayout>, max_length = 255, example = "tabs")]
+    pub form_layout: Option<api_enums::UIWidgetFormLayout>,
+
     /// `test_mode` allows for opening payout links without any restrictions. This removes
     /// - domain name validations
     /// - check for making sure link is accessed within an iframe
@@ -411,7 +418,7 @@ pub struct PayoutCreateResponse {
     pub payout_type: Option<api_enums::PayoutType>,
 
     /// The billing address for the payout
-    #[schema(value_type = Option<Object>, example = json!(r#"{
+    #[schema(value_type = Option<Address>, example = json!(r#"{
         "address": {
             "line1": "1467",
             "line2": "Harrison Street",
@@ -778,12 +785,31 @@ pub struct PayoutLinkDetails {
     #[serde(flatten)]
     pub ui_config: link_utils::GenericLinkUiConfigFormData,
     pub enabled_payment_methods: Vec<link_utils::EnabledPaymentMethod>,
+    pub enabled_payment_methods_with_required_fields: Vec<PayoutEnabledPaymentMethodsInfo>,
     pub amount: common_utils::types::StringMajorUnit,
     pub currency: common_enums::Currency,
     pub locale: String,
+    pub form_layout: Option<common_enums::UIWidgetFormLayout>,
     pub test_mode: bool,
 }
 
+#[derive(Clone, Debug, serde::Serialize)]
+pub struct PayoutEnabledPaymentMethodsInfo {
+    pub payment_method: common_enums::PaymentMethod,
+    pub payment_method_types_info: Vec<PaymentMethodTypeInfo>,
+}
+
+#[derive(Clone, Debug, serde::Serialize)]
+pub struct PaymentMethodTypeInfo {
+    pub payment_method_type: common_enums::PaymentMethodType,
+    pub required_fields: Option<HashMap<String, RequiredFieldInfo>>,
+}
+
+#[derive(Clone, Debug, serde::Serialize, FlatStruct)]
+pub struct RequiredFieldsOverrideRequest {
+    pub billing: Option<payments::Address>,
+}
+
 #[derive(Clone, Debug, serde::Serialize)]
 pub struct PayoutLinkStatusDetails {
     pub payout_link_id: String,
diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index eef23ff5b89..de4ef498793 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -3159,6 +3159,27 @@ pub enum OrderFulfillmentTimeOrigin {
     Confirm,
 }
 
+#[derive(
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    PartialEq,
+    serde::Deserialize,
+    serde::Serialize,
+    strum::Display,
+    strum::EnumString,
+    ToSchema,
+    Hash,
+)]
+#[router_derive::diesel_enum(storage_type = "db_enum")]
+#[serde(rename_all = "snake_case")]
+#[strum(serialize_all = "snake_case")]
+pub enum UIWidgetFormLayout {
+    Tabs,
+    Journey,
+}
+
 #[derive(
     Clone,
     Copy,
diff --git a/crates/common_utils/src/link_utils.rs b/crates/common_utils/src/link_utils.rs
index dc7153f2c5b..2201000d062 100644
--- a/crates/common_utils/src/link_utils.rs
+++ b/crates/common_utils/src/link_utils.rs
@@ -2,7 +2,7 @@
 
 use std::{collections::HashSet, primitive::i64};
 
-use common_enums::enums;
+use common_enums::{enums, UIWidgetFormLayout};
 use diesel::{
     backend::Backend,
     deserialize,
@@ -167,6 +167,8 @@ pub struct PayoutLinkData {
     pub currency: enums::Currency,
     /// A list of allowed domains (glob patterns) where this link can be embedded / opened from
     pub allowed_domains: HashSet<String>,
+    /// Form layout of the payout link
+    pub form_layout: Option<UIWidgetFormLayout>,
     /// `test_mode` can be used for testing payout links without any restrictions
     pub test_mode: Option<bool>,
 }
diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs
index 549ad47609f..029add42ced 100644
--- a/crates/diesel_models/src/business_profile.rs
+++ b/crates/diesel_models/src/business_profile.rs
@@ -1,6 +1,6 @@
 use std::collections::{HashMap, HashSet};
 
-use common_enums::AuthenticationConnectors;
+use common_enums::{AuthenticationConnectors, UIWidgetFormLayout};
 use common_utils::{encryption::Encryption, pii};
 use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
 use masking::Secret;
@@ -556,6 +556,7 @@ common_utils::impl_to_sql_from_sql_json!(BusinessPaymentLinkConfig);
 pub struct BusinessPayoutLinkConfig {
     #[serde(flatten)]
     pub config: BusinessGenericLinkConfig,
+    pub form_layout: Option<UIWidgetFormLayout>,
     pub payout_test_mode: Option<bool>,
 }
 
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index f12539cc232..291b883561d 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -264,6 +264,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::enums::ConnectorStatus,
         api_models::enums::AuthorizationStatus,
         api_models::enums::PaymentMethodStatus,
+        api_models::enums::UIWidgetFormLayout,
         api_models::admin::MerchantConnectorCreate,
         api_models::admin::AdditionalMerchantData,
         api_models::admin::MerchantRecipientData,
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index c58e4c5183b..26765a29c3b 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -182,6 +182,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::enums::AuthorizationStatus,
         api_models::enums::PaymentMethodStatus,
         api_models::enums::OrderFulfillmentTimeOrigin,
+        api_models::enums::UIWidgetFormLayout,
         api_models::admin::MerchantConnectorCreate,
         api_models::admin::AdditionalMerchantData,
         api_models::admin::MerchantRecipientData,
diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs
index 6898a9e002f..8814e9156b7 100644
--- a/crates/router/src/configs/defaults.rs
+++ b/crates/router/src/configs/defaults.rs
@@ -4,6 +4,9 @@ use api_models::{enums, payment_methods::RequiredFieldInfo};
 
 use super::settings::{ConnectorFields, PaymentMethodType, RequiredFieldFinal};
 
+#[cfg(feature = "payouts")]
+pub mod payout_required_fields;
+
 impl Default for super::settings::Server {
     fn default() -> Self {
         Self {
diff --git a/crates/router/src/configs/defaults/payout_required_fields.rs b/crates/router/src/configs/defaults/payout_required_fields.rs
new file mode 100644
index 00000000000..0b8d380f7a3
--- /dev/null
+++ b/crates/router/src/configs/defaults/payout_required_fields.rs
@@ -0,0 +1,603 @@
+use std::collections::HashMap;
+
+use api_models::{
+    enums::{
+        CountryAlpha2, FieldType,
+        PaymentMethod::{BankTransfer, Card, Wallet},
+        PaymentMethodType, PayoutConnectors,
+    },
+    payment_methods::RequiredFieldInfo,
+};
+
+use crate::settings::{
+    ConnectorFields, PaymentMethodType as PaymentMethodTypeInfo, PayoutRequiredFields,
+    RequiredFieldFinal,
+};
+
+impl Default for PayoutRequiredFields {
+    fn default() -> Self {
+        Self(HashMap::from([
+            (
+                Card,
+                PaymentMethodTypeInfo(HashMap::from([
+                    // Adyen
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Adyenplatform,
+                        PaymentMethodType::Debit,
+                    ),
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Adyenplatform,
+                        PaymentMethodType::Credit,
+                    ),
+                ])),
+            ),
+            (
+                BankTransfer,
+                PaymentMethodTypeInfo(HashMap::from([
+                    // Adyen
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Adyenplatform,
+                        PaymentMethodType::Sepa,
+                    ),
+                    // Ebanx
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Ebanx,
+                        PaymentMethodType::Pix,
+                    ),
+                    // Wise
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Wise,
+                        PaymentMethodType::Bacs,
+                    ),
+                ])),
+            ),
+            (
+                Wallet,
+                PaymentMethodTypeInfo(HashMap::from([
+                    // Adyen
+                    get_connector_payment_method_type_fields(
+                        PayoutConnectors::Adyenplatform,
+                        PaymentMethodType::Paypal,
+                    ),
+                ])),
+            ),
+        ]))
+    }
+}
+
+fn get_connector_payment_method_type_fields(
+    connector: PayoutConnectors,
+    payment_method_type: PaymentMethodType,
+) -> (PaymentMethodType, ConnectorFields) {
+    let mut common_fields = get_billing_details(connector);
+    match payment_method_type {
+        // Card
+        PaymentMethodType::Debit => {
+            common_fields.extend(get_card_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+        PaymentMethodType::Credit => {
+            common_fields.extend(get_card_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+
+        // Banks
+        PaymentMethodType::Bacs => {
+            common_fields.extend(get_bacs_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+        PaymentMethodType::Pix => {
+            common_fields.extend(get_pix_bank_transfer_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+        PaymentMethodType::Sepa => {
+            common_fields.extend(get_sepa_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+
+        // Wallets
+        PaymentMethodType::Paypal => {
+            common_fields.extend(get_paypal_fields());
+            (
+                payment_method_type,
+                ConnectorFields {
+                    fields: HashMap::from([(
+                        connector.into(),
+                        RequiredFieldFinal {
+                            mandate: HashMap::new(),
+                            non_mandate: HashMap::new(),
+                            common: common_fields,
+                        },
+                    )]),
+                },
+            )
+        }
+
+        _ => (
+            payment_method_type,
+            ConnectorFields {
+                fields: HashMap::new(),
+            },
+        ),
+    }
+}
+
+fn get_card_fields() -> HashMap<String, RequiredFieldInfo> {
+    HashMap::from([
+        (
+            "payout_method_data.card.card_number".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.card.card_number".to_string(),
+                display_name: "card_number".to_string(),
+                field_type: FieldType::UserCardNumber,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.card.expiry_month".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.card.expiry_month".to_string(),
+                display_name: "exp_month".to_string(),
+                field_type: FieldType::UserCardExpiryMonth,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.card.expiry_year".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.card.expiry_year".to_string(),
+                display_name: "exp_year".to_string(),
+                field_type: FieldType::UserCardExpiryYear,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.card.card_holder_name".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.card.card_holder_name".to_string(),
+                display_name: "card_holder_name".to_string(),
+                field_type: FieldType::UserFullName,
+                value: None,
+            },
+        ),
+    ])
+}
+
+fn get_bacs_fields() -> HashMap<String, RequiredFieldInfo> {
+    HashMap::from([
+        (
+            "payout_method_data.bank.bank_sort_code".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.bank_sort_code".to_string(),
+                display_name: "bank_sort_code".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.bank.bank_account_number".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.bank_account_number".to_string(),
+                display_name: "bank_account_number".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+    ])
+}
+
+fn get_pix_bank_transfer_fields() -> HashMap<String, RequiredFieldInfo> {
+    HashMap::from([
+        (
+            "payout_method_data.bank.bank_account_number".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.bank_account_number".to_string(),
+                display_name: "bank_account_number".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.bank.pix_key".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.pix_key".to_string(),
+                display_name: "pix_key".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+    ])
+}
+
+fn get_sepa_fields() -> HashMap<String, RequiredFieldInfo> {
+    HashMap::from([
+        (
+            "payout_method_data.bank.iban".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.iban".to_string(),
+                display_name: "iban".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+        (
+            "payout_method_data.bank.bic".to_string(),
+            RequiredFieldInfo {
+                required_field: "payout_method_data.bank.bic".to_string(),
+                display_name: "bic".to_string(),
+                field_type: FieldType::Text,
+                value: None,
+            },
+        ),
+    ])
+}
+
+fn get_paypal_fields() -> HashMap<String, RequiredFieldInfo> {
+    HashMap::from([(
+        "payout_method_data.wallet.telephone_number".to_string(),
+        RequiredFieldInfo {
+            required_field: "payout_method_data.wallet.telephone_number".to_string(),
+            display_name: "telephone_number".to_string(),
+            field_type: FieldType::Text,
+            value: None,
+        },
+    )])
+}
+
+fn get_countries_for_connector(connector: PayoutConnectors) -> Vec<CountryAlpha2> {
+    match connector {
+        PayoutConnectors::Adyenplatform => vec![
+            CountryAlpha2::ES,
+            CountryAlpha2::SK,
+            CountryAlpha2::AT,
+            CountryAlpha2::NL,
+            CountryAlpha2::DE,
+            CountryAlpha2::BE,
+            CountryAlpha2::FR,
+            CountryAlpha2::FI,
+            CountryAlpha2::PT,
+            CountryAlpha2::IE,
+            CountryAlpha2::EE,
+            CountryAlpha2::LT,
+            CountryAlpha2::LV,
+            CountryAlpha2::IT,
+            CountryAlpha2::CZ,
+            CountryAlpha2::DE,
+            CountryAlpha2::HU,
+            CountryAlpha2::NO,
+            CountryAlpha2::PL,
+            CountryAlpha2::SE,
+            CountryAlpha2::GB,
+            CountryAlpha2::CH,
+        ],
+        PayoutConnectors::Stripe => vec![CountryAlpha2::US],
+        _ => vec![],
+    }
+}
+
+fn get_billing_details(connector: PayoutConnectors) -> HashMap<String, RequiredFieldInfo> {
+    match connector {
+        PayoutConnectors::Adyen => HashMap::from([
+            (
+                "billing.address.line1".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line1".to_string(),
+                    display_name: "billing_address_line1".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.line2".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line2".to_string(),
+                    display_name: "billing_address_line2".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.city".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.city".to_string(),
+                    display_name: "billing_address_city".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.zip".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.zip".to_string(),
+                    display_name: "billing_address_zip".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.country".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.country".to_string(),
+                    display_name: "billing_address_country".to_string(),
+                    field_type: FieldType::UserAddressCountry {
+                        options: get_countries_for_connector(connector)
+                            .iter()
+                            .map(|country| country.to_string())
+                            .collect::<Vec<String>>(),
+                    },
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.first_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.first_name".to_string(),
+                    display_name: "billing_address_first_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.last_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.last_name".to_string(),
+                    display_name: "billing_address_last_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+        ]),
+        PayoutConnectors::Adyenplatform => HashMap::from([
+            (
+                "billing.address.line1".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line1".to_string(),
+                    display_name: "billing_address_line1".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.line2".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line2".to_string(),
+                    display_name: "billing_address_line2".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.city".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.city".to_string(),
+                    display_name: "billing_address_city".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.zip".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.zip".to_string(),
+                    display_name: "billing_address_zip".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.country".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.country".to_string(),
+                    display_name: "billing_address_country".to_string(),
+                    field_type: FieldType::UserAddressCountry {
+                        options: get_countries_for_connector(connector)
+                            .iter()
+                            .map(|country| country.to_string())
+                            .collect::<Vec<String>>(),
+                    },
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.first_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.first_name".to_string(),
+                    display_name: "billing_address_first_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+        ]),
+        PayoutConnectors::Wise => HashMap::from([
+            (
+                "billing.address.line1".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line1".to_string(),
+                    display_name: "billing_address_line1".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.city".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.city".to_string(),
+                    display_name: "billing_address_city".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.state".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.state".to_string(),
+                    display_name: "billing_address_state".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.zip".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.zip".to_string(),
+                    display_name: "billing_address_zip".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.country".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.country".to_string(),
+                    display_name: "billing_address_country".to_string(),
+                    field_type: FieldType::UserAddressCountry {
+                        options: get_countries_for_connector(connector)
+                            .iter()
+                            .map(|country| country.to_string())
+                            .collect::<Vec<String>>(),
+                    },
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.first_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.first_name".to_string(),
+                    display_name: "billing_address_first_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+        ]),
+        _ => HashMap::from([
+            (
+                "billing.address.line1".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line1".to_string(),
+                    display_name: "billing_address_line1".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.line2".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.line2".to_string(),
+                    display_name: "billing_address_line2".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.city".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.city".to_string(),
+                    display_name: "billing_address_city".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.zip".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.zip".to_string(),
+                    display_name: "billing_address_zip".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.country".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.country".to_string(),
+                    display_name: "billing_address_country".to_string(),
+                    field_type: FieldType::UserAddressCountry {
+                        options: get_countries_for_connector(connector)
+                            .iter()
+                            .map(|country| country.to_string())
+                            .collect::<Vec<String>>(),
+                    },
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.first_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.first_name".to_string(),
+                    display_name: "billing_address_first_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+            (
+                "billing.address.last_name".to_string(),
+                RequiredFieldInfo {
+                    required_field: "billing.address.last_name".to_string(),
+                    display_name: "billing_address_last_name".to_string(),
+                    field_type: FieldType::Text,
+                    value: None,
+                },
+            ),
+        ]),
+    }
+}
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index 2c27b0cfc72..0a65d935f50 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -491,6 +491,10 @@ pub struct NotAvailableFlows {
     pub capture_method: Option<enums::CaptureMethod>,
 }
 
+#[cfg(feature = "payouts")]
+#[derive(Debug, Deserialize, Clone)]
+pub struct PayoutRequiredFields(pub HashMap<enums::PaymentMethod, PaymentMethodType>);
+
 #[derive(Debug, Deserialize, Clone)]
 pub struct RequiredFields(pub HashMap<enums::PaymentMethod, PaymentMethodType>);
 
@@ -841,6 +845,8 @@ impl Settings<RawSecret> {
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct Payouts {
     pub payout_eligibility: bool,
+    #[serde(default)]
+    pub required_fields: PayoutRequiredFields,
 }
 
 #[derive(Debug, Clone, Default)]
diff --git a/crates/router/src/core/generic_link/payout_link/initiate/script.js b/crates/router/src/core/generic_link/payout_link/initiate/script.js
index abbfbb896ac..2d3592256e2 100644
--- a/crates/router/src/core/generic_link/payout_link/initiate/script.js
+++ b/crates/router/src/core/generic_link/payout_link/initiate/script.js
@@ -160,12 +160,13 @@ if (!isTestMode && !isFramed) {
       theme: payoutDetails.theme,
       collectorName: payoutDetails.merchant_name,
       logo: payoutDetails.logo,
-      enabledPaymentMethods: payoutDetails.enabled_payment_methods,
+      enabledPaymentMethods: payoutDetails.enabled_payment_methods_with_required_fields,
       returnUrl: payoutDetails.return_url,
       sessionExpiry,
       amount: payoutDetails.amount,
       currency: payoutDetails.currency,
       flow: "PayoutLinkInitiate",
+      formLayout: payoutDetails.form_layout,
     };
     payoutWidget = widgets.create("paymentMethodCollect", payoutOptions);
 
diff --git a/crates/router/src/core/generic_link/payout_link/status/script.js b/crates/router/src/core/generic_link/payout_link/status/script.js
index 828ea7f7fc4..aa75676153a 100644
--- a/crates/router/src/core/generic_link/payout_link/status/script.js
+++ b/crates/router/src/core/generic_link/payout_link/status/script.js
@@ -121,10 +121,10 @@ function renderStatusDetails(payoutDetails) {
     "{{i18n_ref_id_text}}": payoutDetails.payout_id,
   };
   if (typeof payoutDetails.error_code === "string") {
-    resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code;
+    // resourceInfo["{{i18n_error_code_text}}"] = payoutDetails.error_code;
   }
   if (typeof payoutDetails.error_message === "string") {
-    resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message;
+    // resourceInfo["{{i18n_error_message}}"] = payoutDetails.error_message;
   }
   var resourceNode = document.createElement("div");
   resourceNode.id = "resource-info-container";
diff --git a/crates/router/src/core/generic_link/payout_link/status/styles.css b/crates/router/src/core/generic_link/payout_link/status/styles.css
index ff922298fad..a10bfe7e3c0 100644
--- a/crates/router/src/core/generic_link/payout_link/status/styles.css
+++ b/crates/router/src/core/generic_link/payout_link/status/styles.css
@@ -80,7 +80,7 @@ body {
 #resource-info-container {
   width: 100%;
   border-top: 1px solid rgb(231, 234, 241);
-  padding: 20px 0;
+  padding: 20px 10px;
 }
 #resource-info {
   display: flex;
diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs
index 210d0e6d38b..a153d071088 100644
--- a/crates/router/src/core/payout_link.rs
+++ b/crates/router/src/core/payout_link.rs
@@ -21,7 +21,7 @@ use crate::{
     errors,
     routes::{app::StorageInterface, SessionState},
     services,
-    types::domain,
+    types::{api, domain, transformers::ForeignFrom},
 };
 
 #[cfg(all(feature = "v2", feature = "customer_v2"))]
@@ -156,9 +156,31 @@ pub async fn initiate_payout_link(
                 .attach_printable_lazy(|| {
                     format!("customer [{}] not found", payout_link.primary_reference)
                 })?;
+            let address = payout
+                .address_id
+                .as_ref()
+                .async_map(|address_id| async {
+                    db.find_address_by_address_id(&(&state).into(), address_id, &key_store)
+                        .await
+                })
+                .await
+                .transpose()
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable_lazy(|| {
+                    format!(
+                        "Failed while fetching address [id - {:?}] for payout [id - {}]",
+                        payout.address_id, payout.payout_id
+                    )
+                })?;
 
-            let enabled_payout_methods =
-                filter_payout_methods(&state, &merchant_account, &key_store, &payout).await?;
+            let enabled_payout_methods = filter_payout_methods(
+                &state,
+                &merchant_account,
+                &key_store,
+                &payout,
+                address.as_ref(),
+            )
+            .await?;
             // Fetch default enabled_payout_methods
             let mut default_enabled_payout_methods: Vec<link_utils::EnabledPaymentMethod> = vec![];
             for (payment_method, payment_method_types) in
@@ -188,6 +210,16 @@ pub async fn initiate_payout_link(
                 _ => Ordering::Equal,
             });
 
+            let required_field_override = api::RequiredFieldsOverrideRequest {
+                billing: address.as_ref().map(From::from),
+            };
+
+            let enabled_payment_methods_with_required_fields = ForeignFrom::foreign_from((
+                &state.conf.payouts.required_fields,
+                enabled_payment_methods.clone(),
+                required_field_override,
+            ));
+
             let js_data = payouts::PayoutLinkDetails {
                 publishable_key: masking::Secret::new(merchant_account.publishable_key),
                 client_secret: link_data.client_secret.clone(),
@@ -204,9 +236,11 @@ pub async fn initiate_payout_link(
                     .attach_printable("Failed to parse payout status link's return URL")?,
                 ui_config: ui_config_data,
                 enabled_payment_methods,
+                enabled_payment_methods_with_required_fields,
                 amount,
                 currency: payout.destination_currency,
                 locale: locale.clone(),
+                form_layout: link_data.form_layout,
                 test_mode: link_data.test_mode.unwrap_or(false),
             };
 
@@ -287,6 +321,7 @@ pub async fn filter_payout_methods(
     merchant_account: &domain::MerchantAccount,
     key_store: &domain::MerchantKeyStore,
     payout: &hyperswitch_domain_models::payouts::payouts::Payouts,
+    address: Option<&domain::Address>,
 ) -> errors::RouterResult<Vec<link_utils::EnabledPaymentMethod>> {
     use masking::ExposeInterface;
 
@@ -308,22 +343,6 @@ pub async fn filter_payout_methods(
         &payout.profile_id,
         common_enums::ConnectorType::PayoutProcessor,
     );
-    let address = payout
-        .address_id
-        .as_ref()
-        .async_map(|address_id| async {
-            db.find_address_by_address_id(key_manager_state, address_id, key_store)
-                .await
-        })
-        .await
-        .transpose()
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable_lazy(|| {
-            format!(
-                "Failed while fetching address [id - {:?}] for payout [id - {}]",
-                payout.address_id, payout.payout_id
-            )
-        })?;
 
     let mut response: Vec<link_utils::EnabledPaymentMethod> = vec![];
     let mut payment_method_list_hm: HashMap<
diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs
index 0e9df5284da..82128f98a52 100644
--- a/crates/router/src/core/payouts.rs
+++ b/crates/router/src/core/payouts.rs
@@ -11,11 +11,9 @@ use api_models::{self, enums as api_enums, payouts::PayoutLinkResponse};
 use common_enums::PayoutRetryType;
 use common_utils::{
     consts,
-    crypto::Encryptable,
     ext_traits::{AsyncExt, ValueExt},
     id_type::CustomerId,
     link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus},
-    pii,
     types::MinorUnit,
 };
 use diesel_models::{
@@ -2142,29 +2140,7 @@ pub async fn response_handler(
     let billing_address = payout_data.billing_address.to_owned();
     let customer_details = payout_data.customer_details.to_owned();
     let customer_id = payouts.customer_id;
-
-    let address = billing_address.as_ref().map(|a| {
-        let phone_details = payment_api_types::PhoneDetails {
-            number: a.phone_number.to_owned().map(Encryptable::into_inner),
-            country_code: a.country_code.to_owned(),
-        };
-        let address_details = payment_api_types::AddressDetails {
-            city: a.city.to_owned(),
-            country: a.country.to_owned(),
-            line1: a.line1.to_owned().map(Encryptable::into_inner),
-            line2: a.line2.to_owned().map(Encryptable::into_inner),
-            line3: a.line3.to_owned().map(Encryptable::into_inner),
-            zip: a.zip.to_owned().map(Encryptable::into_inner),
-            first_name: a.first_name.to_owned().map(Encryptable::into_inner),
-            last_name: a.last_name.to_owned().map(Encryptable::into_inner),
-            state: a.state.to_owned().map(Encryptable::into_inner),
-        };
-        api::payments::Address {
-            phone: Some(phone_details),
-            address: Some(address_details),
-            email: a.email.to_owned().map(pii::Email::from),
-        }
-    });
+    let billing = billing_address.as_ref().map(From::from);
 
     let response = api::PayoutCreateResponse {
         payout_id: payouts.payout_id.to_owned(),
@@ -2173,7 +2149,7 @@ pub async fn response_handler(
         currency: payouts.destination_currency.to_owned(),
         connector: payout_attempt.connector.to_owned(),
         payout_type: payouts.payout_type.to_owned(),
-        billing: address,
+        billing,
         auto_fulfill: payouts.auto_fulfill,
         customer_id,
         email: customer_details.as_ref().and_then(|c| c.email.clone()),
@@ -2746,6 +2722,14 @@ pub async fn create_payout_link(
             .and_then(|config| config.payout_link_id.clone()),
         "payout_link",
     )?;
+    let form_layout = payout_link_config_req
+        .as_ref()
+        .and_then(|config| config.form_layout.to_owned())
+        .or_else(|| {
+            profile_config
+                .as_ref()
+                .and_then(|config| config.form_layout.to_owned())
+        });
 
     let data = PayoutLinkData {
         payout_link_id: payout_link_id.clone(),
@@ -2759,6 +2743,7 @@ pub async fn create_payout_link(
         amount: MinorUnit::from(*amount),
         currency: *currency,
         allowed_domains,
+        form_layout,
         test_mode: test_mode_in_config,
     };
 
diff --git a/crates/router/src/core/payouts/transformers.rs b/crates/router/src/core/payouts/transformers.rs
index d10e56cff43..35919abb1f0 100644
--- a/crates/router/src/core/payouts/transformers.rs
+++ b/crates/router/src/core/payouts/transformers.rs
@@ -1,3 +1,7 @@
+use std::collections::HashMap;
+
+use common_utils::link_utils::EnabledPaymentMethod;
+
 #[cfg(all(
     any(feature = "v1", feature = "v2"),
     not(feature = "customer_v2"),
@@ -5,7 +9,11 @@
 ))]
 use crate::types::transformers::ForeignInto;
 #[cfg(feature = "olap")]
-use crate::types::{api, domain, storage, transformers::ForeignFrom};
+use crate::types::{domain, storage};
+use crate::{
+    settings::PayoutRequiredFields,
+    types::{api, transformers::ForeignFrom},
+};
 
 #[cfg(all(feature = "v2", feature = "customer_v2", feature = "olap"))]
 impl
@@ -103,3 +111,72 @@ impl
         }
     }
 }
+
+impl
+    ForeignFrom<(
+        &PayoutRequiredFields,
+        Vec<EnabledPaymentMethod>,
+        api::RequiredFieldsOverrideRequest,
+    )> for Vec<api::PayoutEnabledPaymentMethodsInfo>
+{
+    fn foreign_from(
+        (payout_required_fields, enabled_payout_methods, value_overrides): (
+            &PayoutRequiredFields,
+            Vec<EnabledPaymentMethod>,
+            api::RequiredFieldsOverrideRequest,
+        ),
+    ) -> Self {
+        let value_overrides = value_overrides.flat_struct();
+
+        enabled_payout_methods
+            .into_iter()
+            .map(|enabled_payout_method| {
+                let payment_method = enabled_payout_method.payment_method;
+                let payment_method_types_info = enabled_payout_method
+                    .payment_method_types
+                    .into_iter()
+                    .filter_map(|pmt| {
+                        payout_required_fields
+                            .0
+                            .get(&payment_method)
+                            .and_then(|pmt_info| {
+                                pmt_info.0.get(&pmt).map(|connector_fields| {
+                                    let mut required_fields = HashMap::new();
+
+                                    for required_field_final in connector_fields.fields.values() {
+                                        required_fields.extend(required_field_final.common.clone());
+                                    }
+
+                                    for (key, value) in &value_overrides {
+                                        required_fields.entry(key.to_string()).and_modify(
+                                            |required_field| {
+                                                required_field.value =
+                                                    Some(masking::Secret::new(value.to_string()));
+                                            },
+                                        );
+                                    }
+                                    api::PaymentMethodTypeInfo {
+                                        payment_method_type: pmt,
+                                        required_fields: if required_fields.is_empty() {
+                                            None
+                                        } else {
+                                            Some(required_fields)
+                                        },
+                                    }
+                                })
+                            })
+                            .or(Some(api::PaymentMethodTypeInfo {
+                                payment_method_type: pmt,
+                                required_fields: None,
+                            }))
+                    })
+                    .collect();
+
+                api::PayoutEnabledPaymentMethodsInfo {
+                    payment_method,
+                    payment_method_types_info,
+                }
+            })
+            .collect()
+    }
+}
diff --git a/crates/router/src/types/api/payouts.rs b/crates/router/src/types/api/payouts.rs
index 84b7d93d68a..984fba1c8db 100644
--- a/crates/router/src/types/api/payouts.rs
+++ b/crates/router/src/types/api/payouts.rs
@@ -1,9 +1,10 @@
 pub use api_models::payouts::{
-    AchBankTransfer, BacsBankTransfer, Bank as BankPayout, CardPayout, PayoutActionRequest,
-    PayoutAttemptResponse, PayoutCreateRequest, PayoutCreateResponse, PayoutLinkResponse,
-    PayoutListConstraints, PayoutListFilterConstraints, PayoutListFilters, PayoutListResponse,
-    PayoutMethodData, PayoutRequest, PayoutRetrieveBody, PayoutRetrieveRequest, PixBankTransfer,
-    SepaBankTransfer, Wallet as WalletPayout,
+    AchBankTransfer, BacsBankTransfer, Bank as BankPayout, CardPayout, PaymentMethodTypeInfo,
+    PayoutActionRequest, PayoutAttemptResponse, PayoutCreateRequest, PayoutCreateResponse,
+    PayoutEnabledPaymentMethodsInfo, PayoutLinkResponse, PayoutListConstraints,
+    PayoutListFilterConstraints, PayoutListFilters, PayoutListResponse, PayoutMethodData,
+    PayoutRequest, PayoutRetrieveBody, PayoutRetrieveRequest, PixBankTransfer,
+    RequiredFieldsOverrideRequest, SepaBankTransfer, Wallet as WalletPayout,
 };
 pub use hyperswitch_domain_models::router_flow_types::payouts::{
     PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount, PoSync,
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index de897c816dd..d8f3ce38d27 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -1797,6 +1797,7 @@ impl ForeignFrom<api_models::admin::BusinessPayoutLinkConfig>
     fn foreign_from(item: api_models::admin::BusinessPayoutLinkConfig) -> Self {
         Self {
             config: item.config.foreign_into(),
+            form_layout: item.form_layout,
             payout_test_mode: item.payout_test_mode,
         }
     }
@@ -1808,6 +1809,7 @@ impl ForeignFrom<diesel_models::business_profile::BusinessPayoutLinkConfig>
     fn foreign_from(item: diesel_models::business_profile::BusinessPayoutLinkConfig) -> Self {
         Self {
             config: item.config.foreign_into(),
+            form_layout: item.form_layout,
             payout_test_mode: item.payout_test_mode,
         }
     }
diff --git a/crates/router/tests/macros.rs b/crates/router/tests/macros.rs
new file mode 100644
index 00000000000..3a5301efa5f
--- /dev/null
+++ b/crates/router/tests/macros.rs
@@ -0,0 +1,42 @@
+#[cfg(test)]
+mod flat_struct_test {
+    #![allow(clippy::unwrap_used)]
+    use std::collections::HashMap;
+
+    use router_derive::FlatStruct;
+    use serde::Serialize;
+
+    #[test]
+    fn test_flat_struct() {
+        #[derive(FlatStruct, Serialize)]
+        struct User {
+            address: Address,
+        }
+
+        #[derive(Serialize)]
+        struct Address {
+            line1: String,
+            zip: String,
+            city: String,
+        }
+
+        let line1 = "1397".to_string();
+        let zip = "Some street".to_string();
+        let city = "941222".to_string();
+
+        let address = Address {
+            line1: line1.clone(),
+            zip: zip.clone(),
+            city: city.clone(),
+        };
+        let user = User { address };
+        let flat_user_map = user.flat_struct();
+
+        let mut required_map = HashMap::new();
+        required_map.insert("address.line1".to_string(), line1);
+        required_map.insert("address.zip".to_string(), zip);
+        required_map.insert("address.city".to_string(), city);
+
+        assert_eq!(flat_user_map, required_map);
+    }
+}
diff --git a/crates/router_derive/Cargo.toml b/crates/router_derive/Cargo.toml
index 5a17c32cd01..72cf9605f9b 100644
--- a/crates/router_derive/Cargo.toml
+++ b/crates/router_derive/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
 indexmap = "2.2.6"
 proc-macro2 = "1.0.79"
 quote = "1.0.35"
+serde_json = "1.0.115"
 strum = { version = "0.26.2", features = ["derive"] }
 syn = { version = "2.0.57", features = ["full", "extra-traits"] } # the full feature does not seem to encompass all the features
 
diff --git a/crates/router_derive/src/lib.rs b/crates/router_derive/src/lib.rs
index 2931d9bdca2..02179934e38 100644
--- a/crates/router_derive/src/lib.rs
+++ b/crates/router_derive/src/lib.rs
@@ -1,10 +1,8 @@
 //! Utility macros for the `router` crate.
 #![warn(missing_docs)]
-
 use syn::parse_macro_input;
 
 use crate::macros::diesel::DieselEnumMeta;
-
 mod macros;
 
 /// Uses the [`Debug`][Debug] implementation of a type to derive its [`Display`][Display]
@@ -615,3 +613,84 @@ pub fn try_get_enum_variant(input: proc_macro::TokenStream) -> proc_macro::Token
         .unwrap_or_else(|error| error.into_compile_error())
         .into()
 }
+
+/// Uses the [`Serialize`] implementation of a type to derive a function implementation
+/// for converting nested keys structure into a HashMap of key, value where key is in
+/// the flattened form.
+///
+/// Example
+///
+/// #[derive(Default, Serialize, FlatStruct)]
+/// pub struct User {
+///     name: String,
+///     address: Address,
+///     email: String,
+/// }
+///
+/// #[derive(Default, Serialize)]
+/// pub struct Address {
+///     line1: String,
+///     line2: String,
+///     zip: String,
+/// }
+///
+/// let user = User::default();
+/// let flat_struct_map = user.flat_struct();
+///
+/// [
+///     ("name", "Test"),
+///     ("address.line1", "1397"),
+///     ("address.line2", "Some street"),
+///     ("address.zip", "941222"),
+///     ("email", "test@example.com"),
+/// ]
+#[proc_macro_derive(FlatStruct)]
+pub fn flat_struct_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let input = parse_macro_input!(input as syn::DeriveInput);
+    let name = &input.ident;
+
+    let expanded = quote::quote! {
+        impl #name {
+            pub fn flat_struct(&self) -> std::collections::HashMap<String, String> {
+                use serde_json::Value;
+                use std::collections::HashMap;
+
+                fn flatten_value(
+                    value: &Value,
+                    prefix: &str,
+                    result: &mut HashMap<String, String>
+                ) {
+                    match value {
+                        Value::Object(map) => {
+                            for (key, val) in map {
+                                let new_key = if prefix.is_empty() {
+                                    key.to_string()
+                                } else {
+                                    format!("{}.{}", prefix, key)
+                                };
+                                flatten_value(val, &new_key, result);
+                            }
+                        }
+                        Value::String(s) => {
+                            result.insert(prefix.to_string(), s.clone());
+                        }
+                        Value::Number(n) => {
+                            result.insert(prefix.to_string(), n.to_string());
+                        }
+                        Value::Bool(b) => {
+                            result.insert(prefix.to_string(), b.to_string());
+                        }
+                        _ => {}
+                    }
+                }
+
+                let mut result = HashMap::new();
+                let value = serde_json::to_value(self).unwrap();
+                flatten_value(&value, "", &mut result);
+                result
+            }
+        }
+    };
+
+    proc_macro::TokenStream::from(expanded)
+}

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

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

## Description
This PR includes below changes
- add dynamic fields support for payout widget (#5765)
- expose `formLayout` option in payout link config - journey or tabs

### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [x] 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 for collecting data dynamically based on the selected payout method type.


## How did you test it?
Tested locally

<details>
    <summary>1. Create a payout widget which uses journey layout</summary>

    curl --location 'http://localhost:8080/payouts/create' \
    --header 'Content-Type: application/json' \
    --header 'api-key: dev_6lYcIjzguBUudkK9bpoM3LZDod7RdE1wDJWzxwZhGLWWL6mlQIcfbUkXQgTAgj7e' \
    --data '{
        "amount": 1,
        "currency": "EUR",
        "customer_id": "cus_s5yZDiR680XOcGjcZrQ4",
        "description": "Its my first payout request",
        "entity_type": "Individual",
        "confirm": false,
        "auto_fulfill": true,
        "session_expiry": 1000000,
        "priority": "instant",
        "profile_id": "pro_zFe41ju6ZVZMtuuUHPji",
        "payout_link": true,
        "return_url": "https://www.google.com",
        "payout_link_config": {
            "formLayout": "journey",
            "theme": "#0066ff"
        }
    }'

![image](https://github.com/user-attachments/assets/603b1d6e-0fa3-4046-a27a-1e4566823032)

</details>

<details>
    <summary>2. Create a payout widget which uses tabs layout</summary>

    curl --location 'http://localhost:8080/payouts/create' \
    --header 'Content-Type: application/json' \
    --header 'api-key: dev_6lYcIjzguBUudkK9bpoM3LZDod7RdE1wDJWzxwZhGLWWL6mlQIcfbUkXQgTAgj7e' \
    --data '{
        "amount": 1,
        "currency": "EUR",
        "customer_id": "cus_s5yZDiR680XOcGjcZrQ4",
        "description": "Its my first payout request",
        "entity_type": "Individual",
        "confirm": false,
        "auto_fulfill": true,
        "session_expiry": 1000000,
        "priority": "instant",
        "profile_id": "pro_zFe41ju6ZVZMtuuUHPji",
        "payout_link": true,
        "return_url": "https://www.google.com",
        "payout_link_config": {
            "formLayout": "tabs",
            "theme": "#0066ff"
        }
    }'

![image](https://github.com/user-attachments/assets/1eb65fdb-62b7-4784-b83e-554143610352)

</details>

<details>
    <summary>3. Create a payout without billing address</summary>

    curl --location 'http://localhost:8080/payouts/create' \
    --header 'Content-Type: application/json' \
    --header 'api-key: dev_6lYcIjzguBUudkK9bpoM3LZDod7RdE1wDJWzxwZhGLWWL6mlQIcfbUkXQgTAgj7e' \
    --data '{
        "amount": 1,
        "currency": "EUR",
        "customer_id": "cus_s5yZDiR680XOcGjcZrQ4",
        "description": "Its my first payout request",
        "entity_type": "Individual",
        "confirm": false,
        "auto_fulfill": true,
        "session_expiry": 1000000,
        "priority": "instant",
        "profile_id": "pro_zFe41ju6ZVZMtuuUHPji",
        "payout_link": true,
        "return_url": "https://www.google.com",
        "payout_link_config": {
            "formLayout": "tabs",
            "theme": "#0066ff"
        }
    }'

![image](https://github.com/user-attachments/assets/1eb65fdb-62b7-4784-b83e-554143610352)

</details>

<details>
    <summary>4. Create a payout with partial billing address</summary>

    curl --location 'http://localhost:8080/payouts/create' \
    --header 'Content-Type: application/json' \
    --header 'api-key: dev_6lYcIjzguBUudkK9bpoM3LZDod7RdE1wDJWzxwZhGLWWL6mlQIcfbUkXQgTAgj7e' \
    --data '{
        "amount": 1,
        "currency": "EUR",
        "customer_id": "cus_s5yZDiR680XOcGjcZrQ4",
        "description": "Its my first payout request",
        "entity_type": "Individual",
        "confirm": false,
        "auto_fulfill": true,
        "session_expiry": 1000000,
        "priority": "instant",
        "profile_id": "pro_zFe41ju6ZVZMtuuUHPji",
        "payout_link": true,
        "return_url": "https://www.google.com",
        "payout_link_config": {
            "formLayout": "tabs",
            "theme": "#0066ff"
        }
    }'

![image](https://github.com/user-attachments/assets/4abd4aea-eb9e-49ab-8cbd-901ae562c9e1)

</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
