From 6d33c8503d00ea159dbc21c8e32240fe665e84a6 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Thu, 11 Jan 2024 15:27:38 +0530 Subject: [PATCH 1/4] feat(connector): send metadata in payment authorize request for noon nmi cryptopay --- crates/router/src/connector/cryptopay/transformers.rs | 3 +++ crates/router/src/connector/nmi/transformers.rs | 6 +++++- crates/router/src/connector/noon/transformers.rs | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index 4102945b201e..fb806fda68fe 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 fcf35bfbe370..4b74be539fc1 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, Secret}; use serde::{Deserialize, Serialize}; @@ -403,6 +403,8 @@ pub struct NmiPaymentsRequest { currency: enums::Currency, #[serde(flatten)] payment_method: PaymentMethod, + #[serde(rename = "merchant_defined_field_#")] + merchant_defined_field: Option, orderid: String, } @@ -451,6 +453,7 @@ impl TryFrom<&NmiRouterData<&types::PaymentsAuthorizeRouterData>> for NmiPayment amount, currency: item.router_data.request.currency, payment_method, + merchant_defined_field: item.router_data.request.metadata.clone(), orderid: item.router_data.connector_request_reference_id.clone(), }) } @@ -566,6 +569,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/transformers.rs b/crates/router/src/connector/noon/transformers.rs index bbf284848b59..b9d2283b9667 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -64,6 +64,7 @@ pub struct NoonOrder { reference: String, //Short description of the order. name: String, + nvp: Option, ip_address: Option>, } @@ -348,6 +349,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest { category, reference: item.connector_request_reference_id.clone(), name, + nvp: item.request.metadata.clone(), ip_address, }; let payment_action = if item.request.is_auto_capture()? { From b48b70d9a58c3cf59dfbe8dae8fc44c54e259446 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Wed, 31 Jan 2024 20:35:37 +0530 Subject: [PATCH 2/4] address few issues with nmi and noon --- .../router/src/connector/nmi/transformers.rs | 37 ++++++++++++++-- .../router/src/connector/noon/transformers.rs | 42 +++++++++++++++++-- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index 4b74be539fc1..aa136d26a371 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -2,7 +2,7 @@ use api_models::webhooks; use cards::CardNumber; 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,11 +403,35 @@ pub struct NmiPaymentsRequest { currency: enums::Currency, #[serde(flatten)] payment_method: PaymentMethod, - #[serde(rename = "merchant_defined_field_#")] - merchant_defined_field: Option, + #[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 { @@ -453,7 +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.clone(), + merchant_defined_field: item + .router_data + .request + .metadata + .as_ref() + .map(NmiMerchantDefinedField::new), orderid: item.router_data.connector_request_reference_id.clone(), }) } diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index b03ac19b121d..e67264d3ddf4 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,10 +67,46 @@ pub struct NoonOrder { reference: String, //Short description of the order. name: String, - nvp: Option, + 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::Null => "null".to_string(), + serde_json::Value::Bool(bool) => bool.to_string(), + serde_json::Value::Number(num) => num.to_string(), + serde_json::Value::String(string) => string.to_owned(), + serde_json::Value::Array(_) => "null".to_string(), + serde_json::Value::Object(_) => "null".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 { @@ -368,7 +404,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest { category, reference: item.connector_request_reference_id.clone(), name, - nvp: item.request.metadata.clone(), + nvp: item.request.metadata.as_ref().map(NoonOrderNvp::new), ip_address, }; let payment_action = if item.request.is_auto_capture()? { From df2ec546b75a0fc1e9525e9010ce75a492062c9b Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 5 Feb 2024 17:05:20 +0530 Subject: [PATCH 3/4] gracefully handle noon returning Html as error response --- crates/router/src/connector/noon.rs | 35 +++++++++++-------- .../router/src/connector/noon/transformers.rs | 10 +++--- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/crates/router/src/connector/noon.rs b/crates/router/src/connector/noon.rs index 6c98a3076375..6e1959b46d00 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)?; - - 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> = + 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 e67264d3ddf4..4f4275e173bc 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -79,12 +79,12 @@ pub struct NoonOrderNvp { fn get_value_as_string(value: &serde_json::Value) -> String { match value { - serde_json::Value::Null => "null".to_string(), - serde_json::Value::Bool(bool) => bool.to_string(), - serde_json::Value::Number(num) => num.to_string(), serde_json::Value::String(string) => string.to_owned(), - serde_json::Value::Array(_) => "null".to_string(), - serde_json::Value::Object(_) => "null".to_string(), + serde_json::Value::Null + | serde_json::Value::Bool(_) + | serde_json::Value::Number(_) + | serde_json::Value::Array(_) + | serde_json::Value::Object(_) => value.to_string(), } } From 9b9054ae322e285a3fe4370b25afdfc908926261 Mon Sep 17 00:00:00 2001 From: hrithikesh026 Date: Mon, 5 Feb 2024 17:41:16 +0530 Subject: [PATCH 4/4] fix openapapi_spec file --- crates/api_models/src/payment_methods.rs | 2 +- openapi/openapi_spec.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 0540d470a946..ef1812acf113 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/openapi/openapi_spec.json b/openapi/openapi_spec.json index 98cfaecf6b8a..9992360723e4 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": {