feat(connector): send metadata in payment authorize request for noon nmi cryptopay (#3325)

This commit is contained in:
Hrithikesh
2024-02-06 19:04:19 +05:30
committed by GitHub
parent 2c52b377e0
commit ebe4ac30a8
6 changed files with 100 additions and 17 deletions

View File

@ -642,7 +642,7 @@ pub struct PaymentMethodListResponse {
pub redirect_url: Option<String>, pub redirect_url: Option<String>,
/// currency of the Payment to be done /// currency of the Payment to be done
#[schema(example = "USD")] #[schema(example = "USD", value_type = Currency)]
pub currency: Option<api_enums::Currency>, pub currency: Option<api_enums::Currency>,
/// Information about the payment method /// Information about the payment method

View File

@ -1,3 +1,4 @@
use common_utils::pii;
use masking::Secret; use masking::Secret;
use reqwest::Url; use reqwest::Url;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -47,6 +48,7 @@ pub struct CryptopayPaymentsRequest {
pay_currency: String, pay_currency: String,
success_redirect_url: Option<String>, success_redirect_url: Option<String>,
unsuccess_redirect_url: Option<String>, unsuccess_redirect_url: Option<String>,
metadata: Option<pii::SecretSerdeValue>,
custom_id: String, custom_id: String,
} }
@ -66,6 +68,7 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>>
pay_currency, pay_currency,
success_redirect_url: item.router_data.request.router_return_url.clone(), success_redirect_url: item.router_data.request.router_return_url.clone(),
unsuccess_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(), custom_id: item.router_data.connector_request_reference_id.clone(),
}) })
} }

View File

@ -1,6 +1,6 @@
use api_models::webhooks; use api_models::webhooks;
use cards::CardNumber; 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 error_stack::{IntoReport, Report, ResultExt};
use masking::{ExposeInterface, PeekInterface, Secret}; use masking::{ExposeInterface, PeekInterface, Secret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -395,9 +395,35 @@ pub struct NmiPaymentsRequest {
currency: enums::Currency, currency: enums::Currency,
#[serde(flatten)] #[serde(flatten)]
payment_method: PaymentMethod, payment_method: PaymentMethod,
#[serde(flatten)]
merchant_defined_field: Option<NmiMerchantDefinedField>,
orderid: String, 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)] #[derive(Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum PaymentMethod { pub enum PaymentMethod {
@ -443,6 +469,12 @@ impl TryFrom<&NmiRouterData<&types::PaymentsAuthorizeRouterData>> for NmiPayment
amount, amount,
currency: item.router_data.request.currency, currency: item.router_data.request.currency,
payment_method, payment_method,
merchant_defined_field: item
.router_data
.request
.metadata
.as_ref()
.map(NmiMerchantDefinedField::new),
orderid: item.router_data.connector_request_reference_id.clone(), orderid: item.router_data.connector_request_reference_id.clone(),
}) })
} }
@ -556,6 +588,7 @@ impl TryFrom<&types::SetupMandateRouterData> for NmiPaymentsRequest {
amount: 0.0, amount: 0.0,
currency: item.request.currency, currency: item.request.currency,
payment_method, payment_method,
merchant_defined_field: None,
orderid: item.connector_request_reference_id.clone(), orderid: item.connector_request_reference_id.clone(),
}) })
} }

View File

@ -5,8 +5,9 @@ use std::fmt::Debug;
use base64::Engine; use base64::Engine;
use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent};
use diesel_models::enums; use diesel_models::enums;
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, Report, ResultExt};
use masking::PeekInterface; use masking::PeekInterface;
use router_env::logger;
use transformers as noon; use transformers as noon;
use crate::{ use crate::{
@ -28,7 +29,7 @@ use crate::{
api::{self, ConnectorCommon, ConnectorCommonExt}, api::{self, ConnectorCommon, ConnectorCommonExt},
ErrorResponse, Response, ErrorResponse, Response,
}, },
utils::BytesExt, utils::{self, BytesExt},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -127,19 +128,23 @@ impl ConnectorCommon for Noon {
&self, &self,
res: Response, res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> { ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
let response: noon::NoonErrorResponse = res let response: Result<noon::NoonErrorResponse, Report<common_utils::errors::ParsingError>> =
.response res.response.parse_struct("NoonErrorResponse");
.parse_struct("NoonErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Ok(ErrorResponse { match response {
status_code: res.status_code, Ok(noon_error_response) => Ok(ErrorResponse {
code: response.result_code.to_string(), status_code: res.status_code,
message: response.class_description, code: consts::NO_ERROR_CODE.to_string(),
reason: Some(response.message), message: noon_error_response.class_description,
attempt_status: None, reason: Some(noon_error_response.message),
connector_transaction_id: None, 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())
}
}
} }
} }

View File

@ -1,6 +1,6 @@
use common_utils::pii; use common_utils::pii;
use error_stack::ResultExt; use error_stack::ResultExt;
use masking::Secret; use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -67,9 +67,46 @@ pub struct NoonOrder {
reference: String, reference: String,
//Short description of the order. //Short description of the order.
name: String, name: String,
nvp: Option<NoonOrderNvp>,
ip_address: Option<Secret<String, pii::IpAddress>>, 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)] #[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")] #[serde(rename_all = "UPPERCASE")]
pub enum NoonPaymentActions { pub enum NoonPaymentActions {
@ -365,6 +402,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest {
category, category,
reference: item.connector_request_reference_id.clone(), reference: item.connector_request_reference_id.clone(),
name, name,
nvp: item.request.metadata.as_ref().map(NoonOrderNvp::new),
ip_address, ip_address,
}; };
let payment_action = if item.request.is_auto_capture()? { let payment_action = if item.request.is_auto_capture()? {

View File

@ -11637,6 +11637,7 @@
"PaymentMethodListResponse": { "PaymentMethodListResponse": {
"type": "object", "type": "object",
"required": [ "required": [
"currency",
"payment_methods", "payment_methods",
"mandate_payment", "mandate_payment",
"show_surcharge_breakup_screen" "show_surcharge_breakup_screen"
@ -11648,6 +11649,9 @@
"example": "https://www.google.com", "example": "https://www.google.com",
"nullable": true "nullable": true
}, },
"currency": {
"$ref": "#/components/schemas/Currency"
},
"payment_methods": { "payment_methods": {
"type": "array", "type": "array",
"items": { "items": {