Bug: [BUG(GlobalPay)] 5xx when making a failure payment



```
{
    "id": "TRN_OCPelRWxFohVEl0AuYglPyqG3fQ9r6_ca0584bb55bf",
    "time_created": "2025-04-14T14:54:57.880Z",
    "type": "SALE",
    "status": "DECLINED",
    "channel": "CNP",
    "capture_mode": "AUTO",
    "amount": "",
    "currency": "",
    "country": "DE",
    "merchant_id": "MER_c4c0df11039c48a9b63701adeaa296c3",
    "merchant_name": "Sandbox_merchant_2",
    "account_id": "TRA_6716058969854a48b33347043ff8225f",
    "account_name": "Transaction_Processing",
    "reference": "93459c78-f3f9-427c-84df-ca0584bb55bf",
    "payment_method": {
        "result": "108",
        "message": "Using test system. Please use pre-approved test cards ONLY",
        "entry_mode": "ECOM",
        "authentication": {
            "cavv_result": ""
        },
        "card": {
            "brand": "VISA",
            "masked_number_last4": "XXXXXXXXXXXXXX0091",
            "authcode": "",
            "brand_reference": "",
            "brand_time_created": "",
            "cvv_result": "",
            "avs_address_result": "",
            "avs_postal_code_result": "",
            "avs_action": "",
            "provider": {
                "result": "108"
            }
        }
    },
    "batch_id": "",
    "action": {
        "id": "ACT_OCPelRWxFohVEl0AuYglPyqG3fQ9r6",
        "type": "AUTHORIZE",
        "time_created": "2025-04-14T14:54:57.880Z",
        "result_code": "DECLINED",
        "app_id": "U1lRHKomEn7DN907RCDPxVhyMfiMLcfy",
        "app_name": "dev_portal_demo"
    }
}
```


connector sends above response in case of failure (card decline). we get an empty currency. this results in deserialization failure and it is not intended.
diff --git a/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs b/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs
index f0f7851a419..11bd9e1a047 100644
--- a/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs
+++ b/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs
@@ -4,6 +4,7 @@ use masking::Secret;
 use serde::{Deserialize, Serialize};
 
 use super::requests;
+use crate::utils::deserialize_optional_currency;
 
 #[derive(Debug, Serialize, Deserialize)]
 pub struct GlobalpayPaymentsResponse {
@@ -31,6 +32,7 @@ pub struct GlobalpayPaymentsResponse {
     /// The country in ISO-3166-1(alpha-2 code) format.
     pub country: Option<String>,
     /// The currency of the amount in ISO-4217(alpha-3)
+    #[serde(deserialize_with = "deserialize_optional_currency")]
     pub currency: Option<Currency>,
     /// Information relating to a currency conversion.
     pub currency_conversion: Option<requests::CurrencyConversion>,
diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs
index ef5559d5453..60cb878d9a3 100644
--- a/crates/hyperswitch_connectors/src/utils.rs
+++ b/crates/hyperswitch_connectors/src/utils.rs
@@ -6073,3 +6073,21 @@ pub fn normalize_string(value: String) -> Result<String, regex::Error> {
     let normalized = regex.replace_all(&lowercase_value, "").to_string();
     Ok(normalized)
 }
+
+/// Custom deserializer for Option<Currency> that treats empty strings as None
+pub fn deserialize_optional_currency<'de, D>(
+    deserializer: D,
+) -> Result<Option<enums::Currency>, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let string_data: Option<String> = Option::deserialize(deserializer)?;
+    match string_data {
+        Some(ref value) if !value.is_empty() => value
+            .clone()
+            .parse_enum("Currency")
+            .map(Some)
+            .map_err(|_| serde::de::Error::custom(format!("Invalid currency code: {}", value))),
+        _ => Ok(None),
+    }
+}

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

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

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

This PR fixes an edge case with GlobalPay where, upon payment decline, we get a deserialization error and that is because `currency` field is not expected to be empty but rather a valid `Currency` enum. 

### 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).
-->

closes https://github.com/juspay/hyperswitch/issues/7813


## 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)?
-->

cards:

```txt
// successful cards
"card_number": "4012001038488884",
"card_number": "4222000006285344",
"card_number": "4012001037167778",
"card_number": "4222000005218627",
"card_number": "4222000009719489",
// failure cards
"card_number": "4242420000000091",
"card_number": "5100000000000131",
```

create a payment:

```curl
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_5WtyfFxKHentO9ERR1HP8qrlIPzPP5s1Khs2G2q7mZBl1zJDOUFC3ZNzPcQtfynW' \
--data-raw '{
    "amount": 1000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 1000,
    "customer_id": "StripeCustomer",
    "email": "abcdef123@gmail.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
      "payment_method": "card",
        "payment_method_type": "credit",
        "payment_method_data": {
            "card": {
                "card_number": "4242420000000091",
                "card_exp_month": "10",
                "card_exp_year": "25",
                "card_holder_name": "joseph Doe",
                "card_cvc": "101"
            }
        },
    "billing": {
        "address": {
            "country": "US"
        }
    } 
}'
```
```json
{
    "payment_id": "pay_9iWUNJmnm8Le4l8iZCV9",
    "merchant_id": "postman_merchant_GHAction_95a905d4-93fe-42b4-b125-c1ddafa26313",
    "status": "failed",
    "amount": 1000,
    "net_amount": 1000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "globalpay",
    "client_secret": "pay_9iWUNJmnm8Le4l8iZCV9_secret_RyifkSKcDrOFEEqC0OZ7",
    "created": "2025-04-15T09:21:39.674Z",
    "currency": "USD",
    "customer_id": "StripeCustomer",
    "customer": {
        "id": "StripeCustomer",
        "name": "John Doe",
        "email": "abcdef123@gmail.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": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0091",
            "card_type": "CREDIT",
            "card_network": "Visa",
            "card_issuer": "STRIPE PAYMENTS UK LIMITED",
            "card_issuing_country": "UNITEDKINGDOM",
            "card_isin": "424242",
            "card_extended_bin": null,
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": {
            "city": null,
            "country": "US",
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": null,
            "state": null,
            "first_name": null,
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "abcdef123@gmail.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "111",
    "error_message": "(111)[ test system ] Strong Customer Authentication Required",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "StripeCustomer",
        "created_at": 1744708899,
        "expires": 1744712499,
        "secret": "epk_24e7b6b84ca647aaa39a01fbdf5c0f42"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": "TRN_uRk00jWtySUhGnJD4e7mZhEQQwZ6vF_Le4l8iZCV9_1",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_I2x5oXH0EQeuMaAZSe6G",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_NN71g0D5WXMeggXKXhBM",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-04-15T09:36:39.674Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-04-15T09:21:41.821Z",
    "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": "111",
    "issuer_error_message": "(111)[ test system ] Strong Customer Authentication Required"
}
```

payment now fails with valid error message when `failure` cards are used.

previously, we would get a 5xx:

```
{"error":{"type":"api","message":"Something went wrong","code":"HE_00"}}
├╴at crates/router/src/services/api.rs:806:14
│
├─▶ {"error":{"type":"server_not_available","code":"HE_00","message":"Something went wrong"}}
│   ╰╴at crates/router/src/core/errors/utils.rs:281:17
│
├─▶ Failed to deserialize connector response
│   ╰╴at crates/hyperswitch_connectors/src/connectors/globalpay.rs:757:14
│
├─▶ Failed to parse struct: Globalpay PaymentsResponse
│   ├╴at /router/crates/common_utils/src/ext_traits.rs:175:14
│   ╰╴Unable to parse hyperswitch_connectors::connectors::globalpay::response::GlobalpayPaymentsResponse from bytes b"{\"id\":\"TRN_dsWF5Tk8k8m1ylUC4Fz3319DPwyUt0_mCSfvURrUj_1\",\"time_created\":\"2025-04-14T14:43:41.794Z\",\"type\":\"SALE\",\"status\":\"DECLINED\",\"channel\":\"CNP\",\"capture_mode\":\"AUTO\",\"amount\":\"\",\"currency\":\"\",\"country\":\"US\",\"merchant_id\":\"MER_7e3e2c7df34f42819b3edee31022ee3f\",\"merchant_name\":\"Sandbox_merchant_3\",\"account_id\":\"TRA_c9967ad7d8ec4b46b6dd44a61cde9a91\",\"account_name\":\"transaction_processing\",\"reference\":\"pay_gO7eiUCttdmCSfvURrUj_1\",\"payment_method\":{\"result\":\"111\",\"message\":\"(111)[ test system ] Strong Customer Authentication Required\",\"entry_mode\":\"ECOM\",\"authentication\":{\"cavv_result\":\"\"},\"card\":{\"funding\":\"CREDIT\",\"brand\":\"MASTERCARD\",\"masked_number_last4\":\"XXXXXXXXXXXX0131\",\"authcode\":\"\",\"brand_reference\":\"YWLv8HtAwSh6aG8F\",\"brand_time_created\":\"\",\"tag_response\":\"\",\"cvv_result\":\"MATCHED\",\"avs_address_result\":\"MATCHED\",\"avs_postal_code_result\":\"MATCHED\",\"avs_action\":\"\",\"provider\":{\"result\":\"111\",\"cvv_result\":\"M\",\"avs_address_result\":\"M\",\"avs_postal_code_result\":\"M\"}}},\"risk_assessment\":[{\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\",\"rules\":[{\"reference\":\"0c93a6c9-7649-4822-b5ea-1efa356337fd\",\"description\":\"Cardholder Name Rule\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"},{\"reference\":\"a539d51a-abc1-4fff-a38e-b34e00ad0cc3\",\"description\":\"CardNumber block\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"},{\"reference\":\"d023a19e-6985-4fda-bb9b-5d4e0dedbb1e\",\"description\":\"Amount test\",\"mode\":\"ACTIVE\",\"result\":\"ACCEPTED\"}]}],\"currency_conversion\":{\"payer_amount\":\"\",\"payer_currency\":\"\",\"exchange_rate\":\"\",\"margin_rate_percentage\":\"\",\"commission_percentage\":\"\",\"exchange_rate_source\":\"\",\"exchange_rate_time_created\":\"\"},\"batch_id\":\"\",\"action\":{\"id\":\"ACT_dsWF5Tk8k8m1ylUC4Fz3319DPwyUt0\",\"type\":\"AUTHORIZE\",\"time_created\":\"2025-04-14T14:43:41.794Z\",\"result_code\":\"DECLINED\",\"app_id\":\"OXKlGGm6ecZLIqMyRgPHRfMxdUAiEcp8\",\"app_name\":\"rotate_again\"}}"
│
╰─▶ unknown variant ``, expected one of `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN`, `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL`, `BSD`, `BTN`, `BWP`, `BYN`, `BZD`, `CAD`, `CDF`, `CHF`, `CLF`, `CLP`, `CNY`, `COP`, `CRC`, `CUC`, `CUP`, `CVE`, `CZK`, `DJF`, `DKK`, `DOP`, `DZD`, `EGP`, `ERN`, `ETB`, `EUR`, `FJD`, `FKP`, `GBP`, `GEL`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD`, `HKD`, `HNL`, `HRK`, `HTG`, `HUF`, `IDR`, `ILS`, `INR`, `IQD`, `IRR`, `ISK`, `JMD`, `JOD`, `JPY`, `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT`, `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LYD`, `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRU`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN`, `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD`, `OMR`, `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG`, `QAR`, `RON`, `RSD`, `RUB`, `RWF`, `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLE`, `SLL`, `SOS`, `SRD`, `SSP`, `STD`, `STN`, `SVC`, `SYP`, `SZL`, `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS`, `UAH`, `UGX`, `USD`, `UYU`, `UZS`, `VES`, `VND`, `VUV`, `WST`, `XAF`, `XCD`, `XOF`, `XPF`, `YER`, `ZAR`, `ZMW`, `ZWL` at line 1 column 195
    ╰╴at /router/crates/common_utils/src/ext_traits.rs:175:14
```



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