Bug: [FEATURE] send metadata field in authentication request for nmi, noon and cryptopay



### Feature Description

Send metadata in the authorize request for noon, nmi and cryptopay.

### Possible Implementation

metadata field is being sent in routerData.request for Authorize request. This needs to be mapped to connector request body.

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

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

### Have you read the Contributing Guidelines?

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

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index 0540d470a94..ef1812acf11 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -642,7 +642,7 @@ pub struct PaymentMethodListResponse {
     pub redirect_url: Option<String>,
 
     /// currency of the Payment to be done
-    #[schema(example = "USD")]
+    #[schema(example = "USD", value_type = Currency)]
     pub currency: Option<api_enums::Currency>,
 
     /// Information about the payment method
diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs
index 4102945b201..fb806fda68f 100644
--- a/crates/router/src/connector/cryptopay/transformers.rs
+++ b/crates/router/src/connector/cryptopay/transformers.rs
@@ -1,3 +1,4 @@
+use common_utils::pii;
 use masking::Secret;
 use reqwest::Url;
 use serde::{Deserialize, Serialize};
@@ -47,6 +48,7 @@ pub struct CryptopayPaymentsRequest {
     pay_currency: String,
     success_redirect_url: Option<String>,
     unsuccess_redirect_url: Option<String>,
+    metadata: Option<pii::SecretSerdeValue>,
     custom_id: String,
 }
 
@@ -66,6 +68,7 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>>
                     pay_currency,
                     success_redirect_url: item.router_data.request.router_return_url.clone(),
                     unsuccess_redirect_url: item.router_data.request.router_return_url.clone(),
+                    metadata: item.router_data.request.metadata.clone(),
                     custom_id: item.router_data.connector_request_reference_id.clone(),
                 })
             }
diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs
index 5b486aae600..395781cc516 100644
--- a/crates/router/src/connector/nmi/transformers.rs
+++ b/crates/router/src/connector/nmi/transformers.rs
@@ -1,8 +1,8 @@
 use api_models::webhooks;
 use cards::CardNumber;
-use common_utils::{errors::CustomResult, ext_traits::XmlExt};
+use common_utils::{errors::CustomResult, ext_traits::XmlExt, pii};
 use error_stack::{IntoReport, Report, ResultExt};
-use masking::{ExposeInterface, Secret};
+use masking::{ExposeInterface, PeekInterface, Secret};
 use serde::{Deserialize, Serialize};
 
 use crate::{
@@ -403,9 +403,35 @@ pub struct NmiPaymentsRequest {
     currency: enums::Currency,
     #[serde(flatten)]
     payment_method: PaymentMethod,
+    #[serde(flatten)]
+    merchant_defined_field: Option<NmiMerchantDefinedField>,
     orderid: String,
 }
 
+#[derive(Debug, Serialize)]
+pub struct NmiMerchantDefinedField {
+    #[serde(flatten)]
+    inner: std::collections::BTreeMap<String, Secret<String>>,
+}
+
+impl NmiMerchantDefinedField {
+    pub fn new(metadata: &pii::SecretSerdeValue) -> Self {
+        let metadata_as_string = metadata.peek().to_string();
+        let hash_map: std::collections::BTreeMap<String, serde_json::Value> =
+            serde_json::from_str(&metadata_as_string).unwrap_or(std::collections::BTreeMap::new());
+        let inner = hash_map
+            .into_iter()
+            .enumerate()
+            .map(|(index, (hs_key, hs_value))| {
+                let nmi_key = format!("merchant_defined_field_{}", index + 1);
+                let nmi_value = format!("{hs_key}={hs_value}");
+                (nmi_key, Secret::new(nmi_value))
+            })
+            .collect();
+        Self { inner }
+    }
+}
+
 #[derive(Debug, Serialize)]
 #[serde(untagged)]
 pub enum PaymentMethod {
@@ -451,6 +477,12 @@ impl TryFrom<&NmiRouterData<&types::PaymentsAuthorizeRouterData>> for NmiPayment
             amount,
             currency: item.router_data.request.currency,
             payment_method,
+            merchant_defined_field: item
+                .router_data
+                .request
+                .metadata
+                .as_ref()
+                .map(NmiMerchantDefinedField::new),
             orderid: item.router_data.connector_request_reference_id.clone(),
         })
     }
@@ -564,6 +596,7 @@ impl TryFrom<&types::SetupMandateRouterData> for NmiPaymentsRequest {
             amount: 0.0,
             currency: item.request.currency,
             payment_method,
+            merchant_defined_field: None,
             orderid: item.connector_request_reference_id.clone(),
         })
     }
diff --git a/crates/router/src/connector/noon.rs b/crates/router/src/connector/noon.rs
index 6c98a307637..6e1959b46d0 100644
--- a/crates/router/src/connector/noon.rs
+++ b/crates/router/src/connector/noon.rs
@@ -5,8 +5,9 @@ use std::fmt::Debug;
 use base64::Engine;
 use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent};
 use diesel_models::enums;
-use error_stack::{IntoReport, ResultExt};
+use error_stack::{IntoReport, Report, ResultExt};
 use masking::PeekInterface;
+use router_env::logger;
 use transformers as noon;
 
 use crate::{
@@ -28,7 +29,7 @@ use crate::{
         api::{self, ConnectorCommon, ConnectorCommonExt},
         ErrorResponse, Response,
     },
-    utils::BytesExt,
+    utils::{self, BytesExt},
 };
 
 #[derive(Debug, Clone)]
@@ -127,19 +128,23 @@ impl ConnectorCommon for Noon {
         &self,
         res: Response,
     ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
-        let response: noon::NoonErrorResponse = res
-            .response
-            .parse_struct("NoonErrorResponse")
-            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
-
-        Ok(ErrorResponse {
-            status_code: res.status_code,
-            code: response.result_code.to_string(),
-            message: response.class_description,
-            reason: Some(response.message),
-            attempt_status: None,
-            connector_transaction_id: None,
-        })
+        let response: Result<noon::NoonErrorResponse, Report<common_utils::errors::ParsingError>> =
+            res.response.parse_struct("NoonErrorResponse");
+
+        match response {
+            Ok(noon_error_response) => Ok(ErrorResponse {
+                status_code: res.status_code,
+                code: consts::NO_ERROR_CODE.to_string(),
+                message: noon_error_response.class_description,
+                reason: Some(noon_error_response.message),
+                attempt_status: None,
+                connector_transaction_id: None,
+            }),
+            Err(error_message) => {
+                logger::error!(deserialization_error =? error_message);
+                utils::handle_json_response_deserialization_failure(res, "noon".to_owned())
+            }
+        }
     }
 }
 
diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs
index 8bb3a96a3ca..9a6490c5756 100644
--- a/crates/router/src/connector/noon/transformers.rs
+++ b/crates/router/src/connector/noon/transformers.rs
@@ -1,6 +1,6 @@
 use common_utils::pii;
 use error_stack::ResultExt;
-use masking::Secret;
+use masking::{PeekInterface, Secret};
 use serde::{Deserialize, Serialize};
 
 use crate::{
@@ -67,9 +67,46 @@ pub struct NoonOrder {
     reference: String,
     //Short description of the order.
     name: String,
+    nvp: Option<NoonOrderNvp>,
     ip_address: Option<Secret<String, pii::IpAddress>>,
 }
 
+#[derive(Debug, Serialize)]
+pub struct NoonOrderNvp {
+    #[serde(flatten)]
+    inner: std::collections::BTreeMap<String, Secret<String>>,
+}
+
+fn get_value_as_string(value: &serde_json::Value) -> String {
+    match value {
+        serde_json::Value::String(string) => string.to_owned(),
+        serde_json::Value::Null
+        | serde_json::Value::Bool(_)
+        | serde_json::Value::Number(_)
+        | serde_json::Value::Array(_)
+        | serde_json::Value::Object(_) => value.to_string(),
+    }
+}
+
+impl NoonOrderNvp {
+    pub fn new(metadata: &pii::SecretSerdeValue) -> Self {
+        let metadata_as_string = metadata.peek().to_string();
+        let hash_map: std::collections::BTreeMap<String, serde_json::Value> =
+            serde_json::from_str(&metadata_as_string).unwrap_or(std::collections::BTreeMap::new());
+        let inner = hash_map
+            .into_iter()
+            .enumerate()
+            .map(|(index, (hs_key, hs_value))| {
+                let noon_key = format!("{}", index + 1);
+                // to_string() function on serde_json::Value returns a string with "" quotes. Noon doesn't allow this. Hence get_value_as_string function
+                let noon_value = format!("{hs_key}={}", get_value_as_string(&hs_value));
+                (noon_key, Secret::new(noon_value))
+            })
+            .collect();
+        Self { inner }
+    }
+}
+
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "UPPERCASE")]
 pub enum NoonPaymentActions {
@@ -365,6 +402,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest {
             category,
             reference: item.connector_request_reference_id.clone(),
             name,
+            nvp: item.request.metadata.as_ref().map(NoonOrderNvp::new),
             ip_address,
         };
         let payment_action = if item.request.is_auto_capture()? {
diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json
index 98cfaecf6b8..9992360723e 100644
--- a/openapi/openapi_spec.json
+++ b/openapi/openapi_spec.json
@@ -11637,6 +11637,7 @@
       "PaymentMethodListResponse": {
         "type": "object",
         "required": [
+          "currency",
           "payment_methods",
           "mandate_payment",
           "show_surcharge_breakup_screen"
@@ -11648,6 +11649,9 @@
             "example": "https://www.google.com",
             "nullable": true
           },
+          "currency": {
+            "$ref": "#/components/schemas/Currency"
+          },
           "payment_methods": {
             "type": "array",
             "items": {

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

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

## Description
<!-- Describe your changes in detail -->
Send metadata in the authorize request for noon, nmi and cryptopay.

### 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)?
-->
Manual.
1. CryptoPay (connector force sync response)
<img width="1728" alt="Cryptopay log" src="https://github.com/juspay/hyperswitch/assets/61539176/1975c066-bcb1-4ae8-ad91-c200d92d28fa">


2. NMI (connector force sync response)
Authomatic capture (auth+capture)
<img width="1728" alt="Screenshot 2024-02-06 at 12 36 19 PM" src="https://github.com/juspay/hyperswitch/assets/61539176/1d512585-1211-4fae-a7ed-d79f4f30f718">

Manual Capture(auth and then capture)
<img width="1728" alt="Screenshot 2024-02-06 at 1 16 28 PM" src="https://github.com/juspay/hyperswitch/assets/61539176/6c9c5401-0bad-4b6f-b419-2d2bddf729ab">



4. Noon (connector dashboard)
<img width="1728" alt="Screenshot 2024-01-31 at 8 25 10 PM" src="https://github.com/juspay/hyperswitch/assets/61539176/6b2b759a-b97d-4acf-894b-2b179f7bbbdc">



## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
