Bug: [FEATURE] connector's sandbox testing via magic values



### Feature Description

Connectors provide sending magic values for certain fields in their API requests. These values can be used to simulate different error scenarios. For most of the cases across all the connectors, these can be set in the already present fields. 

For a few cases, extra fields are needed to be passed to the connector (eg: payment method holder name for MIT payments for Adyen). For such cases, the fields are not necessary in the real environment but necessary for simulating certain scenarios.

This feature allows the end merchants to simulate different scenarios in the sandbox environment.

### Possible Implementation

There are two parts to this for the implementation

1. Provision sending these fields by the merchant in the payments API request (structured metadata)
2. Consume these fields on a connector to connector basis as per the requirement

Step 1
- Expose a field in `connector_metadata` for Adyen
- With below fields for now
    - holder_name: Option<String>

Sample body in the payments request
```
{
   ...
    "connector_metadata": {
        "adyen": {
            "testing": {
                "holder_name": "CARD_EXPIRED"
            }
        }
    }
   ...
}
```

Step 2
- In non prod environment, pass this to connector integration for below actions
    - Authorize 
    - SetupMandate
    - Refund

Step 3
- Consume these fields in connector integration as per requirement

Step 4
- Consume `holder_name` in MIT requests for Adyen integration

### 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 10682866ab7..e068f12f4be 100644
--- a/api-reference-v2/openapi_spec.json
+++ b/api-reference-v2/openapi_spec.json
@@ -3775,6 +3775,17 @@
         },
         "additionalProperties": false
       },
+      "AdyenConnectorMetadata": {
+        "type": "object",
+        "required": [
+          "testing"
+        ],
+        "properties": {
+          "testing": {
+            "$ref": "#/components/schemas/AdyenTestingData"
+          }
+        }
+      },
       "AdyenSplitData": {
         "type": "object",
         "description": "Fee information for Split Payments to be charged on the payment being collected for Adyen",
@@ -3848,6 +3859,18 @@
           "Vat"
         ]
       },
+      "AdyenTestingData": {
+        "type": "object",
+        "required": [
+          "holder_name"
+        ],
+        "properties": {
+          "holder_name": {
+            "type": "string",
+            "description": "Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions."
+          }
+        }
+      },
       "AirwallexData": {
         "type": "object",
         "properties": {
@@ -7960,6 +7983,14 @@
               }
             ],
             "nullable": true
+          },
+          "adyen": {
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/AdyenConnectorMetadata"
+              }
+            ],
+            "nullable": true
           }
         }
       },
diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index 011fd7e4431..52b3b893dde 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -6720,6 +6720,7 @@ pub struct ConnectorMetadata {
     pub airwallex: Option<AirwallexData>,
     pub noon: Option<NoonData>,
     pub braintree: Option<BraintreeData>,
+    pub adyen: Option<AdyenConnectorMetadata>,
 }
 
 impl ConnectorMetadata {
@@ -6770,6 +6771,18 @@ pub struct BraintreeData {
     pub merchant_config_currency: Option<api_enums::Currency>,
 }
 
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct AdyenConnectorMetadata {
+    pub testing: AdyenTestingData,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
+pub struct AdyenTestingData {
+    /// Holder name to be sent to Adyen for a card payment(CIT) or a generic payment(MIT). This value overrides the values for card.card_holder_name and applies during both CIT and MIT payment transactions.
+    #[schema(value_type = String)]
+    pub holder_name: Option<Secret<String>>,
+}
+
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
 pub struct ApplepayConnectorMetadataRequest {
     pub session_token_data: Option<SessionTokenInfo>,
diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
index be6543d8b1c..c15affcc8aa 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
@@ -10,7 +10,7 @@ use common_enums::enums as storage_enums;
 use common_utils::ext_traits::OptionExt;
 use common_utils::{
     errors::{CustomResult, ParsingError},
-    ext_traits::Encode,
+    ext_traits::{Encode, ValueExt},
     pii::Email,
     request::Method,
     types::MinorUnit,
@@ -1222,6 +1222,7 @@ pub struct AdyenMandate {
     #[serde(rename = "type")]
     payment_type: PaymentType,
     stored_payment_method_id: Secret<String>,
+    holder_name: Option<Secret<String>>,
 }
 
 #[serde_with::skip_serializing_none]
@@ -2008,13 +2009,21 @@ impl TryFrom<(&BankDebitData, &PaymentsAuthorizeRouterData)> for AdyenPaymentMet
                 account_number,
                 sort_code,
                 ..
-            } => Ok(AdyenPaymentMethod::BacsDirectDebit(Box::new(
-                BacsDirectDebitData {
-                    bank_account_number: account_number.clone(),
-                    bank_location_id: sort_code.clone(),
-                    holder_name: item.get_billing_full_name()?,
-                },
-            ))),
+            } => {
+                let testing_data = item
+                    .request
+                    .get_connector_testing_data()
+                    .map(AdyenTestingData::try_from)
+                    .transpose()?;
+                let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
+                Ok(AdyenPaymentMethod::BacsDirectDebit(Box::new(
+                    BacsDirectDebitData {
+                        bank_account_number: account_number.clone(),
+                        bank_location_id: sort_code.clone(),
+                        holder_name: test_holder_name.unwrap_or(item.get_billing_full_name()?),
+                    },
+                )))
+            }
 
             BankDebitData::BecsBankDebit { .. } => Err(errors::ConnectorError::NotImplemented(
                 utils::get_unimplemented_payment_method_error_message("Adyen"),
@@ -2396,30 +2405,38 @@ impl TryFrom<(&BankRedirectData, &PaymentsAuthorizeRouterData)> for AdyenPayment
                 card_exp_month,
                 card_exp_year,
                 ..
-            } => Ok(AdyenPaymentMethod::BancontactCard(Box::new(AdyenCard {
-                brand: Some(CardBrand::Bcmc),
-                number: card_number
-                    .as_ref()
-                    .ok_or(errors::ConnectorError::MissingRequiredField {
-                        field_name: "bancontact_card.card_number",
-                    })?
-                    .clone(),
-                expiry_month: card_exp_month
-                    .as_ref()
-                    .ok_or(errors::ConnectorError::MissingRequiredField {
-                        field_name: "bancontact_card.card_exp_month",
-                    })?
-                    .clone(),
-                expiry_year: card_exp_year
-                    .as_ref()
-                    .ok_or(errors::ConnectorError::MissingRequiredField {
-                        field_name: "bancontact_card.card_exp_year",
-                    })?
-                    .clone(),
-                holder_name: Some(item.get_billing_full_name()?),
-                cvc: None,
-                network_payment_reference: None,
-            }))),
+            } => {
+                let testing_data = item
+                    .request
+                    .get_connector_testing_data()
+                    .map(AdyenTestingData::try_from)
+                    .transpose()?;
+                let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
+                Ok(AdyenPaymentMethod::BancontactCard(Box::new(AdyenCard {
+                    brand: Some(CardBrand::Bcmc),
+                    number: card_number
+                        .as_ref()
+                        .ok_or(errors::ConnectorError::MissingRequiredField {
+                            field_name: "bancontact_card.card_number",
+                        })?
+                        .clone(),
+                    expiry_month: card_exp_month
+                        .as_ref()
+                        .ok_or(errors::ConnectorError::MissingRequiredField {
+                            field_name: "bancontact_card.card_exp_month",
+                        })?
+                        .clone(),
+                    expiry_year: card_exp_year
+                        .as_ref()
+                        .ok_or(errors::ConnectorError::MissingRequiredField {
+                            field_name: "bancontact_card.card_exp_year",
+                        })?
+                        .clone(),
+                    holder_name: test_holder_name.or(Some(item.get_billing_full_name()?)),
+                    cvc: None,
+                    network_payment_reference: None,
+                })))
+            }
             BankRedirectData::Bizum { .. } => Ok(AdyenPaymentMethod::Bizum),
             BankRedirectData::Blik { blik_code } => {
                 Ok(AdyenPaymentMethod::Blik(Box::new(BlikRedirectionData {
@@ -2684,6 +2701,13 @@ impl
         let additional_data = get_additional_data(item.router_data);
         let return_url = item.router_data.request.get_router_return_url()?;
         let payment_method_type = item.router_data.request.payment_method_type;
+        let testing_data = item
+            .router_data
+            .request
+            .get_connector_testing_data()
+            .map(AdyenTestingData::try_from)
+            .transpose()?;
+        let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
         let payment_method = match mandate_ref_id {
             payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids) => {
                 let adyen_mandate = AdyenMandate {
@@ -2696,6 +2720,7 @@ impl
                             .get_connector_mandate_id()
                             .ok_or_else(missing_field_err("mandate_id"))?,
                     ),
+                    holder_name: test_holder_name,
                 };
                 Ok::<PaymentMethod<'_>, Self::Error>(PaymentMethod::AdyenMandatePaymentMethod(
                     Box::new(adyen_mandate),
@@ -2727,7 +2752,7 @@ impl
                                 .get_expiry_year_4_digit()
                                 .clone(),
                             cvc: None,
-                            holder_name: card_holder_name,
+                            holder_name: test_holder_name.or(card_holder_name),
                             brand: Some(brand),
                             network_payment_reference: Some(Secret::new(network_mandate_id)),
                         };
@@ -2768,7 +2793,7 @@ impl
                             number: token_data.get_network_token(),
                             expiry_month: token_data.get_network_token_expiry_month(),
                             expiry_year: token_data.get_expiry_year_4_digit(),
-                            holder_name: card_holder_name,
+                            holder_name: test_holder_name.or(card_holder_name),
                             brand: Some(brand), // FIXME: Remove hardcoding
                             network_payment_reference: Some(Secret::new(
                                 network_mandate_id.network_transaction_id,
@@ -2879,7 +2904,15 @@ impl TryFrom<(&AdyenRouterData<&PaymentsAuthorizeRouterData>, &Card)> for AdyenP
         let country_code = get_country_code(item.router_data.get_optional_billing());
         let additional_data = get_additional_data(item.router_data);
         let return_url = item.router_data.request.get_router_return_url()?;
-        let card_holder_name = item.router_data.get_optional_billing_full_name();
+        let testing_data = item
+            .router_data
+            .request
+            .get_connector_testing_data()
+            .map(AdyenTestingData::try_from)
+            .transpose()?;
+        let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
+        let card_holder_name =
+            test_holder_name.or(item.router_data.get_optional_billing_full_name());
         let payment_method = PaymentMethod::AdyenPaymentMethod(Box::new(
             AdyenPaymentMethod::try_from((card_data, card_holder_name))?,
         ));
@@ -5505,6 +5538,24 @@ pub struct DefenseDocuments {
     defense_document_type_code: String,
 }
 
+#[derive(Debug, Deserialize)]
+pub struct AdyenTestingData {
+    holder_name: Option<Secret<String>>,
+}
+
+impl TryFrom<common_utils::pii::SecretSerdeValue> for AdyenTestingData {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(testing_data: common_utils::pii::SecretSerdeValue) -> Result<Self, Self::Error> {
+        let testing_data = testing_data
+            .expose()
+            .parse_value::<Self>("AdyenTestingData")
+            .change_context(errors::ConnectorError::InvalidDataFormat {
+                field_name: "connector_metadata.adyen.testing",
+            })?;
+        Ok(testing_data)
+    }
+}
+
 impl TryFrom<&SubmitEvidenceRouterData> for Evidence {
     type Error = error_stack::Report<errors::ConnectorError>;
     fn try_from(item: &SubmitEvidenceRouterData) -> Result<Self, Self::Error> {
@@ -5782,7 +5833,15 @@ impl
         let country_code = get_country_code(item.router_data.get_optional_billing());
         let additional_data = get_additional_data(item.router_data);
         let return_url = item.router_data.request.get_router_return_url()?;
-        let card_holder_name = item.router_data.get_optional_billing_full_name();
+        let testing_data = item
+            .router_data
+            .request
+            .get_connector_testing_data()
+            .map(AdyenTestingData::try_from)
+            .transpose()?;
+        let test_holder_name = testing_data.and_then(|test_data| test_data.holder_name);
+        let card_holder_name =
+            test_holder_name.or(item.router_data.get_optional_billing_full_name());
         let payment_method = PaymentMethod::AdyenPaymentMethod(Box::new(
             AdyenPaymentMethod::try_from((token_data, card_holder_name))?,
         ));
diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs
index 60cb878d9a3..c693d4789ca 100644
--- a/crates/hyperswitch_connectors/src/utils.rs
+++ b/crates/hyperswitch_connectors/src/utils.rs
@@ -1685,6 +1685,7 @@ pub trait PaymentsAuthorizeRequestData {
     fn get_card_network_from_additional_payment_method_data(
         &self,
     ) -> Result<enums::CardNetwork, Error>;
+    fn get_connector_testing_data(&self) -> Option<pii::SecretSerdeValue>;
 }
 
 impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData {
@@ -1905,6 +1906,9 @@ impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData {
             .into()),
         }
     }
+    fn get_connector_testing_data(&self) -> Option<pii::SecretSerdeValue> {
+        self.connector_testing_data.clone()
+    }
 }
 
 pub trait PaymentsCaptureRequestData {
@@ -5998,6 +6002,7 @@ pub(crate) fn convert_setup_mandate_router_data_to_authorize_router_data(
         shipping_cost: data.request.shipping_cost,
         merchant_account_id: None,
         merchant_config_currency: None,
+        connector_testing_data: data.request.connector_testing_data.clone(),
     }
 }
 
diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs
index 7a91f82f306..1dc88ed95ae 100644
--- a/crates/hyperswitch_domain_models/src/router_request_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_request_types.rs
@@ -76,6 +76,7 @@ pub struct PaymentsAuthorizeData {
     pub additional_payment_method_data: Option<AdditionalPaymentData>,
     pub merchant_account_id: Option<Secret<String>>,
     pub merchant_config_currency: Option<storage_enums::Currency>,
+    pub connector_testing_data: Option<pii::SecretSerdeValue>,
 }
 #[derive(Debug, Clone)]
 pub struct PaymentsPostSessionTokensData {
@@ -935,4 +936,5 @@ pub struct SetupMandateRequestData {
     // MinorUnit for amount framework
     pub minor_amount: Option<MinorUnit>,
     pub shipping_cost: Option<MinorUnit>,
+    pub connector_testing_data: Option<pii::SecretSerdeValue>,
 }
diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs
index 5d9ccfab2a4..6dc61d4bf4a 100644
--- a/crates/openapi/src/openapi.rs
+++ b/crates/openapi/src/openapi.rs
@@ -734,6 +734,8 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::payments::PaymentsUpdateMetadataRequest,
         api_models::payments::PaymentsUpdateMetadataResponse,
         api_models::payments::CtpServiceDetails,
+        api_models::payments::AdyenConnectorMetadata,
+        api_models::payments::AdyenTestingData,
         api_models::feature_matrix::FeatureMatrixListResponse,
         api_models::feature_matrix::FeatureMatrixRequest,
         api_models::feature_matrix::ConnectorFeatureMatrixResponse,
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index 2629b1b8b7d..b7f88345484 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -701,6 +701,8 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::payments::DisplayAmountOnSdk,
         api_models::payments::ErrorDetails,
         api_models::payments::CtpServiceDetails,
+        api_models::payments::AdyenConnectorMetadata,
+        api_models::payments::AdyenTestingData,
         api_models::feature_matrix::FeatureMatrixListResponse,
         api_models::feature_matrix::FeatureMatrixRequest,
         api_models::feature_matrix::ConnectorFeatureMatrixResponse,
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 0628a7281f7..6ae4cf4ec72 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -307,6 +307,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>(
         additional_payment_method_data: None,
         merchant_account_id: None,
         merchant_config_currency: None,
+        connector_testing_data: None,
     };
     let connector_mandate_request_reference_id = payment_data
         .payment_attempt
@@ -960,6 +961,7 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>(
         shipping_cost: payment_data.payment_intent.amount_details.shipping_cost,
         capture_method: Some(payment_data.payment_intent.capture_method),
         complete_authorize_url,
+        connector_testing_data: None,
     };
     let connector_mandate_request_reference_id = payment_data
         .payment_attempt
@@ -3183,7 +3185,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
                 field_name: "browser_info",
             })?;
 
-        let order_category = additional_data
+        let connector_metadata = additional_data
             .payment_data
             .payment_intent
             .connector_metadata
@@ -3193,21 +3195,16 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
                     .change_context(errors::ApiErrorResponse::InternalServerError)
                     .attach_printable("Failed parsing ConnectorMetadata")
             })
-            .transpose()?
-            .and_then(|cm| cm.noon.and_then(|noon| noon.order_category));
+            .transpose()?;
 
-        let braintree_metadata = additional_data
-            .payment_data
-            .payment_intent
-            .connector_metadata
-            .clone()
-            .map(|cm| {
-                cm.parse_value::<api_models::payments::ConnectorMetadata>("ConnectorMetadata")
-                    .change_context(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Failed parsing ConnectorMetadata")
-            })
-            .transpose()?
-            .and_then(|cm| cm.braintree);
+        let order_category = connector_metadata.as_ref().and_then(|cm| {
+            cm.noon
+                .as_ref()
+                .and_then(|noon| noon.order_category.clone())
+        });
+        let braintree_metadata = connector_metadata
+            .as_ref()
+            .and_then(|cm| cm.braintree.clone());
 
         let merchant_account_id = braintree_metadata
             .as_ref()
@@ -3301,6 +3298,30 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
             .clone();
         let shipping_cost = payment_data.payment_intent.shipping_cost;
 
+        let connector = api_models::enums::Connector::from_str(connector_name)
+            .change_context(errors::ConnectorError::InvalidConnectorName)
+            .change_context(errors::ApiErrorResponse::InvalidDataValue {
+                field_name: "connector",
+            })
+            .attach_printable_lazy(|| {
+                format!("unable to parse connector name {connector_name:?}")
+            })?;
+
+        let connector_testing_data = connector_metadata
+            .and_then(|cm| match connector {
+                api_models::enums::Connector::Adyen => cm
+                    .adyen
+                    .map(|adyen_cm| adyen_cm.testing)
+                    .map(|testing_data| {
+                        serde_json::to_value(testing_data)
+                            .change_context(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable("Failed to parse Adyen testing data")
+                    }),
+                _ => None,
+            })
+            .transpose()?
+            .map(pii::SecretSerdeValue::new);
+
         Ok(Self {
             payment_method_data: (payment_method_data.get_required_value("payment_method_data")?),
             setup_future_usage: payment_data.payment_attempt.setup_future_usage_applied,
@@ -3355,6 +3376,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsAuthoriz
             shipping_cost,
             merchant_account_id,
             merchant_config_currency,
+            connector_testing_data,
         })
     }
 }
@@ -4106,6 +4128,40 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::SetupMandateRequ
             payment_data.creds_identifier.as_deref(),
         ));
 
+        let connector = api_models::enums::Connector::from_str(connector_name)
+            .change_context(errors::ConnectorError::InvalidConnectorName)
+            .change_context(errors::ApiErrorResponse::InvalidDataValue {
+                field_name: "connector",
+            })
+            .attach_printable_lazy(|| {
+                format!("unable to parse connector name {connector_name:?}")
+            })?;
+
+        let connector_testing_data = payment_data
+            .payment_intent
+            .connector_metadata
+            .as_ref()
+            .map(|cm| {
+                cm.clone()
+                    .parse_value::<api_models::payments::ConnectorMetadata>("ConnectorMetadata")
+                    .change_context(errors::ApiErrorResponse::InternalServerError)
+                    .attach_printable("Failed parsing ConnectorMetadata")
+            })
+            .transpose()?
+            .and_then(|cm| match connector {
+                api_models::enums::Connector::Adyen => cm
+                    .adyen
+                    .map(|adyen_cm| adyen_cm.testing)
+                    .map(|testing_data| {
+                        serde_json::to_value(testing_data)
+                            .change_context(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable("Failed to parse Adyen testing data")
+                    }),
+                _ => None,
+            })
+            .transpose()?
+            .map(pii::SecretSerdeValue::new);
+
         Ok(Self {
             currency: payment_data.currency,
             confirm: true,
@@ -4138,6 +4194,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::SetupMandateRequ
             webhook_url,
             complete_authorize_url,
             capture_method: payment_data.payment_attempt.capture_method,
+            connector_testing_data,
         })
     }
 }
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index 2927bc4c8fa..057543d9632 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -923,6 +923,7 @@ impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData {
             shipping_cost: data.request.shipping_cost,
             merchant_account_id: None,
             merchant_config_currency: None,
+            connector_testing_data: data.request.connector_testing_data.clone(),
         }
     }
 }
diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs
index a2a4cf88f65..223c3e5638d 100644
--- a/crates/router/src/types/api/verify_connector.rs
+++ b/crates/router/src/types/api/verify_connector.rs
@@ -63,6 +63,7 @@ impl VerifyConnectorData {
             shipping_cost: None,
             merchant_account_id: None,
             merchant_config_currency: None,
+            connector_testing_data: None,
         }
     }
 
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index 6a7239b5b46..2bc4c9f24eb 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -987,6 +987,7 @@ impl Default for PaymentAuthorizeType {
             shipping_cost: None,
             merchant_account_id: None,
             merchant_config_currency: None,
+            connector_testing_data: None,
         };
         Self(data)
     }

## Type of Change
<!-- Put an `x` in the boxes that apply -->
- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
This PR adds support for connector testing data in payment requests, specifically for Adyen connector. It allows merchants to send testing values via `connector_metadata` to simulate different scenarios in the sandbox environment.

The implementation includes:
1. Added a new structure `AdyenConnectorMetadata` and `AdyenTestingData` in the API models
2. Modified the Adyen connector payment flows (both CIT and MIT) across different payment methods to accept and process the testing data

The holder_name in testing data will override card_holder_name in the connector's payment request, allowing merchants to test specific scenarios without affecting their production implementation.

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

## Motivation and Context
Connectors like Adyen provide special values for certain fields that can be used to simulate different error scenarios in their sandbox environments. These special values help merchants test error handling without requiring them to modify their actual implementation.

For example, sending a specific holder name value like "CARD_EXPIRED" can simulate a card expiration error from Adyen.

This feature enables merchants to test various error scenarios in a systematic way through HyperSwitch API without changing their integration code.

## How did you test it?
<details>
    <summary>1. Simulate Adyen's CARD_EXPIRED error through testing data in connector_metadata for CIT</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_UEgACZ01bXHeFIrvN93I7h3sWWs4V0LTiJwPQRdI9OQzV4lZcVzLQ1mnfAprp5nx' \
        --data-raw '{"amount":10,"currency":"EUR","confirm":true,"profile_id":"pro_HPqNgWlKGAgwQms0jwIF","capture_method":"automatic","capture_on":"2022-09-10T10:11:12Z","authentication_type":"three_ds","setup_future_usage":"off_session","connector":["adyen"],"customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","email":"abc@example.com","return_url":"https://google.com","payment_method":"card","payment_method_type":"debit","payment_method_data":{"card":{"card_number":"4111111111111111","card_exp_month":"03","card_exp_year":"2030","card_holder_name":"Test User","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"},"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":{"holder_name":"CARD_EXPIRED x This is my error message"}}},"session_expiry":60}'

Expectations
1. Payment fails with the specified string in testing data's holder name
2. Testing data's holder name takes precedence over the value passed in payment_method_data card_holder_name

Response

    {"payment_id":"pay_YAtEbtMJOeLlpgy6Qo1q","merchant_id":"merchant_1745378456","status":"failed","amount":10,"net_amount":10,"shipping_cost":null,"amount_capturable":0,"amount_received":null,"connector":"adyen","client_secret":"pay_YAtEbtMJOeLlpgy6Qo1q_secret_wDnisrGKMPVJ1yITYAgB","created":"2025-04-23T04:31:43.728Z","currency":"EUR","customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","customer":{"id":"cus_xPhu4Ugm5fIPkjIUQNaH","name":"John Doe","email":"abc@example.com","phone":"999999999","phone_country_code":"+65"},"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":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1111","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"Test User","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 Doe","phone":"999999999","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":"6","error_message":"Expired Card","unified_code":"UE_9000","unified_message":"Something went wrong","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_xPhu4Ugm5fIPkjIUQNaH","created_at":1745382703,"expires":1745386303,"secret":"epk_deacab31d1e6413291eada3764b2986c"},"manual_retry_allowed":true,"connector_transaction_id":"LW79V9ZRB72FMNT5","frm_message":null,"metadata":null,"connector_metadata":{"apple_pay":null,"airwallex":null,"noon":null,"braintree":null,"adyen":{"testing":{"holder_name":"CARD_EXPIRED x This is my error message"}}},"feature_metadata":null,"reference_id":null,"payment_link":null,"profile_id":"pro_HPqNgWlKGAgwQms0jwIF","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_OSy4xc1t36A2rIA8IJcn","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-04-23T04:32:43.728Z","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":null,"payment_method_status":null,"updated":"2025-04-23T04:31:45.103Z","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":null,"card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":"This is my error message"}

</details>

<details>
    <summary>2. Simulate Adyen's CARD_EXPIRED error through testing data in connector_metadata for MIT<summary>

cURL (for creating mandates)

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_UEgACZ01bXHeFIrvN93I7h3sWWs4V0LTiJwPQRdI9OQzV4lZcVzLQ1mnfAprp5nx' \
        --data-raw '{"amount":10,"currency":"EUR","confirm":true,"profile_id":"pro_HPqNgWlKGAgwQms0jwIF","capture_method":"automatic","capture_on":"2022-09-10T10:11:12Z","authentication_type":"three_ds","setup_future_usage":"off_session","connector":["adyen"],"customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","email":"abc@example.com","return_url":"https://google.com","payment_method":"card","payment_method_type":"debit","payment_method_data":{"card":{"card_number":"4111111111111111","card_exp_month":"03","card_exp_year":"2030","card_holder_name":"Test User","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"},"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"}},"session_expiry":60}'

Expectations
1. Payment succeeds
2. payment_method_id is retrieved

Response

    {"payment_id":"pay_Bc8m48deiQPthw9eNKB2","merchant_id":"merchant_1745378456","status":"succeeded","amount":10,"net_amount":10,"shipping_cost":null,"amount_capturable":0,"amount_received":10,"connector":"adyen","client_secret":"pay_Bc8m48deiQPthw9eNKB2_secret_x78ITiMF1X73mI6bc4st","created":"2025-04-23T04:35:53.717Z","currency":"EUR","customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","customer":{"id":"cus_xPhu4Ugm5fIPkjIUQNaH","name":"John Doe","email":"abc@example.com","phone":"999999999","phone_country_code":"+65"},"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":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1111","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"Test User","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 Doe","phone":"999999999","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_xPhu4Ugm5fIPkjIUQNaH","created_at":1745382953,"expires":1745386553,"secret":"epk_ee929d43a2f3400d84b614ba30219d53"},"manual_retry_allowed":false,"connector_transaction_id":"D2XCLCJRGB9VWM75","frm_message":null,"metadata":null,"connector_metadata":{"apple_pay":null,"airwallex":null,"noon":null,"braintree":null,"adyen":{"testing":{"holder_name":null}}},"feature_metadata":null,"reference_id":"pay_Bc8m48deiQPthw9eNKB2_1","payment_link":null,"profile_id":"pro_HPqNgWlKGAgwQms0jwIF","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_OSy4xc1t36A2rIA8IJcn","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-04-23T04:36:53.717Z","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_EJSd2QoapABA7TLpNrjp","payment_method_status":"active","updated":"2025-04-23T04:35:55.388Z","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":"LQR35D9R636NHJ65","card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null}

cURL (Simulating error in MIT)

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_UEgACZ01bXHeFIrvN93I7h3sWWs4V0LTiJwPQRdI9OQzV4lZcVzLQ1mnfAprp5nx' \
        --data-raw '{"amount":6500,"currency":"EUR","confirm":true,"capture_method":"automatic","capture_on":"2022-09-10T10:11:12Z","customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","email":"guest@example.com","name":"John Doe","phone":"999999999","profile_id":"pro_HPqNgWlKGAgwQms0jwIF","phone_country_code":"+65","description":"Its my first payment request","return_url":"https://hyperswitch.io","off_session":true,"recurring_details":{"type":"payment_method_id","data":"pm_EJSd2QoapABA7TLpNrjp"},"connector":["adyen"],"statement_descriptor_name":"joseph","statement_descriptor_suffix":"JS","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"browser_info":{"ip_address":"129.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","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},"connector_metadata":{"adyen":{"testing":{"holder_name":"CARD_EXPIRED x MIT error message"}}}}'

Expectations
1. MIT payment fails with the specified error in testing data's holder name field

Response

    {"payment_id":"pay_fRuTbjd8yFPgBk9NHn1U","merchant_id":"merchant_1745378456","status":"failed","amount":6500,"net_amount":6500,"shipping_cost":null,"amount_capturable":0,"amount_received":null,"connector":"adyen","client_secret":"pay_fRuTbjd8yFPgBk9NHn1U_secret_AZm2NYex5skj3qtRYmiy","created":"2025-04-23T04:37:46.301Z","currency":"EUR","customer_id":"cus_xPhu4Ugm5fIPkjIUQNaH","customer":{"id":"cus_xPhu4Ugm5fIPkjIUQNaH","name":"John Doe","email":"guest@example.com","phone":"999999999","phone_country_code":"+65"},"description":"Its my first payment request","refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1111","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"Test User","payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":null,"order_details":null,"email":"guest@example.com","name":"John Doe","phone":"999999999","return_url":"https://hyperswitch.io/","authentication_type":"no_three_ds","statement_descriptor_name":"joseph","statement_descriptor_suffix":"JS","next_action":null,"cancellation_reason":null,"error_code":"6","error_message":"Expired Card","unified_code":"UE_9000","unified_message":"Something went wrong","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_xPhu4Ugm5fIPkjIUQNaH","created_at":1745383066,"expires":1745386666,"secret":"epk_99ac0c08e6d843ae9d15e6f280822f2f"},"manual_retry_allowed":true,"connector_transaction_id":"JKS4HVG55L4VWM75","frm_message":null,"metadata":{"udf1":"value1","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":"CARD_EXPIRED x MIT error message"}}},"feature_metadata":null,"reference_id":null,"payment_link":null,"profile_id":"pro_HPqNgWlKGAgwQms0jwIF","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_OSy4xc1t36A2rIA8IJcn","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-04-23T04:52:46.301Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"129.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_EJSd2QoapABA7TLpNrjp","payment_method_status":"active","updated":"2025-04-23T04:37:47.572Z","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":"LQR35D9R636NHJ65","card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":"MIT error message"}

</details>

<details>
    <summary>Automated cypress tests for Adyen (Ideal bank redirection fails as expected)</summary>

![Screenshot 2025-04-24 at 8 28 21 PM](https://github.com/user-attachments/assets/d9119546-f8b5-4852-a141-7456e490935d)

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