Bug: [BUG] Fix Refund Reason Type in Adyen



### Bug Description

The reason field in AdyenRefundRequest is currently of type Option<String>, which allows any arbitrary string to be passed as the refund reason. Adyen supports only a fixed set of values (FRAUD, CUSTOMER REQUEST, RETURN, DUPLICATE, OTHER). If an unsupported value is passed, the connector throws an error at runtime. This results in a failure that could be avoided earlier in the flow.

### Expected Behavior

The reason field should be strongly typed as Option<AdyenRefundRequestReason>, an enum with all supported values. Any value outside the allowed set should be mapped to OTHER internally, ensuring the connector does not throw unexpected errors.

### Actual Behavior

Currently, if the refund reason is anything other than the accepted Adyen values, the connector fails and returns an error response. There’s no early validation or fallback mechanism to handle unsupported values.

### Steps To Reproduce

1. Set reason = Some("INVALID_REASON") in a refund request.
2. The system accepts the value since it's an Option<String>.
3. Connector forwards the request to Adyen.
4. Adyen rejects it due to unsupported reason.
5. The system returns an error response from the connector.

### Context For The Bug

_No response_

### Environment

Are you using hyperswitch hosted version? Yes/No
If yes, please provide the value of the `x-request-id` response header to help us debug your issue.

If not (or if building/running locally), please provide the following details:
1. Operating System or Linux distribution: macOS
2. Rust version (output of `rustc --version`): ``
3. App version (output of `cargo r --features vergen -- --version`): ``


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

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

### Have you read the Contributing Guidelines?

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

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml
index 0b378a05718..3bba4ca57d0 100644
--- a/crates/connector_configs/toml/production.toml
+++ b/crates/connector_configs/toml/production.toml
@@ -114,6 +114,8 @@ payment_method_type = "apple_pay"
 payment_method_type = "google_pay"
 [[adyen.wallet]]
 payment_method_type = "paypal"
+[[adyen.voucher]]
+  payment_method_type = "boleto"
 [adyen.connector_auth.BodyKey]
 api_key = "Adyen API Key"
 key1 = "Adyen Account Id"
diff --git a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
index 3d04375b106..1625ee900e7 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyen/transformers.rs
@@ -1,3 +1,5 @@
+use std::str::FromStr;
+
 #[cfg(feature = "payouts")]
 use api_models::payouts::{self, PayoutMethodData};
 use api_models::{
@@ -1391,12 +1393,37 @@ pub struct DokuBankData {
 pub struct AdyenRefundRequest {
     merchant_account: Secret<String>,
     amount: Amount,
-    merchant_refund_reason: Option<String>,
+    merchant_refund_reason: Option<AdyenRefundRequestReason>,
     reference: String,
     splits: Option<Vec<AdyenSplitData>>,
     store: Option<String>,
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+pub enum AdyenRefundRequestReason {
+    FRAUD,
+    #[serde(rename = "CUSTOMER REQUEST")]
+    CUSTOMERREQUEST,
+    RETURN,
+    DUPLICATE,
+    OTHER,
+}
+
+impl FromStr for AdyenRefundRequestReason {
+    type Err = error_stack::Report<errors::ConnectorError>;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_uppercase().as_str() {
+            "FRAUD" => Ok(Self::FRAUD),
+            "CUSTOMER REQUEST" => Ok(Self::CUSTOMERREQUEST),
+            "RETURN" => Ok(Self::RETURN),
+            "DUPLICATE" => Ok(Self::DUPLICATE),
+            "OTHER" => Ok(Self::OTHER),
+            _ => Ok(Self::OTHER),
+        }
+    }
+}
+
 #[derive(Default, Debug, Clone, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct AdyenRefundResponse {
@@ -4693,7 +4720,13 @@ impl<F> TryFrom<&AdyenRouterData<&RefundsRouterData<F>>> for AdyenRefundRequest
                 currency: item.router_data.request.currency,
                 value: item.amount,
             },
-            merchant_refund_reason: item.router_data.request.reason.clone(),
+            merchant_refund_reason: item
+                .router_data
+                .request
+                .reason
+                .as_ref()
+                .map(|reason| AdyenRefundRequestReason::from_str(reason))
+                .transpose()?,
             reference: item.router_data.request.refund_id.clone(),
             store,
             splits,

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

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

Closes this [issue](https://github.com/juspay/hyperswitch/issues/8845)

## Description
<!-- Describe your changes in detail -->

This PR updates the reason field in AdyenRefundRequest from Option<String> to Option<AdyenRefundRequestReason>, a strongly typed enum. This change ensures only supported values (FRAUD, CUSTOMER REQUEST, RETURN, DUPLICATE, OTHER) are used.

### Additional Changes

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

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

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


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->

Postman Test

A. Refund(with valid reason):

Request:

```
curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Emxcc7NEApdtpDZyA54Hc0hN43a4wvBTlJ0wIGGeGquavEbsE4lhVWQcyPV8jMFu' \
--data '{
    "payment_id": "pay_aUwRQFUMFVSuIsfGRRiq",
    "amount": 100,
    "reason": "CUSTOMER REQUEST",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }  
}'
```

Response:

```
{
    "refund_id": "ref_3UOUCY4WziTz0iWhRGjT",
    "payment_id": "pay_aUwRQFUMFVSuIsfGRRiq",
    "amount": 100,
    "currency": "USD",
    "status": "pending",
    "reason": "CUSTOMER REQUEST",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "unified_code": null,
    "unified_message": null,
    "created_at": "2025-08-06T04:02:37.782Z",
    "updated_at": "2025-08-06T04:02:39.280Z",
    "connector": "adyen",
    "profile_id": "pro_H4NAjXx3vY9NI4nQbXom",
    "merchant_connector_id": "mca_EC2PLdAi5hhl8zAKbt0G",
    "split_refunds": null,
    "issuer_error_code": null,
    "issuer_error_message": null
}
```

B. Refund (with invalid reason):

Request:

```
curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_8rgdhjpDZyA54Hc0hN43a4wvBTlJ0wIGGeGquavEbsa.kjsfhgubdhrsj' \
--data '{
    "payment_id": "pay_dwfsrgdfnQSuIsfGRerjgh",
    "amount": 100,
    "reason": "customer returned product",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }  
}'
```

Response:

```
{
    "refund_id": "ref_7JGWziplkiWhRaSg",
    "payment_id": "pay_dwfsrgdfnQSuIsfGRerjgh",
    "amount": 100,
    "currency": "USD",
    "status": "pending",
    "reason": "OTHER",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "unified_code": null,
    "unified_message": null,
    "created_at": "2025-08-06T04:02:37.782Z",
    "updated_at": "2025-08-06T04:02:39.280Z",
    "connector": "adyen",
    "profile_id": "pro_H4NAjXx3vY9NI4nQbXom",
    "merchant_connector_id": "mca_EC2PLdAi5hhl8zAKbt0G",
    "split_refunds": null,
    "issuer_error_code": null,
    "issuer_error_message": null
}
```

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

- [ ] 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
