diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 0540d470a9..ef1812acf1 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, /// currency of the Payment to be done - #[schema(example = "USD")] + #[schema(example = "USD", value_type = Currency)] pub currency: Option, /// Information about the payment method diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index 4102945b20..fb806fda68 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, unsuccess_redirect_url: Option, + metadata: Option, 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 f7fc0c0a4f..65acb5832b 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -1,6 +1,6 @@ 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, PeekInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -395,9 +395,35 @@ pub struct NmiPaymentsRequest { currency: enums::Currency, #[serde(flatten)] payment_method: PaymentMethod, + #[serde(flatten)] + merchant_defined_field: Option, orderid: String, } +#[derive(Debug, Serialize)] +pub struct NmiMerchantDefinedField { + #[serde(flatten)] + inner: std::collections::BTreeMap>, +} + +impl NmiMerchantDefinedField { + pub fn new(metadata: &pii::SecretSerdeValue) -> Self { + let metadata_as_string = metadata.peek().to_string(); + let hash_map: std::collections::BTreeMap = + 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 { @@ -443,6 +469,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(), }) } @@ -556,6 +588,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 6c98a30763..6e1959b46d 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 { - let response: noon::NoonErrorResponse = res - .response - .parse_struct("NoonErrorResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: Result> = + res.response.parse_struct("NoonErrorResponse"); - 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, - }) + 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 8bb3a96a3c..9a6490c575 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, ip_address: Option>, } +#[derive(Debug, Serialize)] +pub struct NoonOrderNvp { + #[serde(flatten)] + inner: std::collections::BTreeMap>, +} + +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 = + 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 9b255cd373..1ec6dcacf3 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": {