mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(connector): send metadata in payment authorize request for noon nmi cryptopay (#3325)
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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()? {
|
||||||
|
|||||||
@ -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": {
|
||||||
|
|||||||
Reference in New Issue
Block a user