mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(core): Add masking support for form-data types request (#9496)
Co-authored-by: Sayak Bhattacharya <sayak.b@Sayak-Bhattacharya-G092THXJ34.local> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
d98adb2e03
commit
1af7f42762
@ -1,4 +1,5 @@
|
||||
use masking::{Maskable, Secret};
|
||||
use reqwest::multipart::Form;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
@ -66,7 +67,7 @@ impl std::fmt::Debug for RequestContent {
|
||||
pub enum RequestContent {
|
||||
Json(Box<dyn masking::ErasedMaskSerialize + Send>),
|
||||
FormUrlEncoded(Box<dyn masking::ErasedMaskSerialize + Send>),
|
||||
FormData(reqwest::multipart::Form),
|
||||
FormData((Form, Box<dyn masking::ErasedMaskSerialize + Send>)),
|
||||
Xml(Box<dyn masking::ErasedMaskSerialize + Send>),
|
||||
RawBytes(Vec<u8>),
|
||||
}
|
||||
@ -77,7 +78,7 @@ impl RequestContent {
|
||||
Self::Json(i) => serde_json::to_string(&i).unwrap_or_default().into(),
|
||||
Self::FormUrlEncoded(i) => serde_urlencoded::to_string(i).unwrap_or_default().into(),
|
||||
Self::Xml(i) => quick_xml::se::to_string(&i).unwrap_or_default().into(),
|
||||
Self::FormData(_) => String::new().into(),
|
||||
Self::FormData((_, i)) => serde_json::to_string(i).unwrap_or_default().into(),
|
||||
Self::RawBytes(_) => String::new().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ pub async fn send_request(
|
||||
let client = client.post(url);
|
||||
match request.body {
|
||||
Some(RequestContent::Json(payload)) => client.json(&payload),
|
||||
Some(RequestContent::FormData(form)) => client.multipart(form),
|
||||
Some(RequestContent::FormData((form, _))) => client.multipart(form),
|
||||
Some(RequestContent::FormUrlEncoded(payload)) => client.form(&payload),
|
||||
Some(RequestContent::Xml(payload)) => {
|
||||
let body = quick_xml::se::to_string(&payload)
|
||||
@ -59,7 +59,7 @@ pub async fn send_request(
|
||||
let client = client.put(url);
|
||||
match request.body {
|
||||
Some(RequestContent::Json(payload)) => client.json(&payload),
|
||||
Some(RequestContent::FormData(form)) => client.multipart(form),
|
||||
Some(RequestContent::FormData((form, _))) => client.multipart(form),
|
||||
Some(RequestContent::FormUrlEncoded(payload)) => client.form(&payload),
|
||||
Some(RequestContent::Xml(payload)) => {
|
||||
let body = quick_xml::se::to_string(&payload)
|
||||
@ -74,7 +74,7 @@ pub async fn send_request(
|
||||
let client = client.patch(url);
|
||||
match request.body {
|
||||
Some(RequestContent::Json(payload)) => client.json(&payload),
|
||||
Some(RequestContent::FormData(form)) => client.multipart(form),
|
||||
Some(RequestContent::FormData((form, _))) => client.multipart(form),
|
||||
Some(RequestContent::FormUrlEncoded(payload)) => client.form(&payload),
|
||||
Some(RequestContent::Xml(payload)) => {
|
||||
let body = quick_xml::se::to_string(&payload)
|
||||
|
||||
@ -1069,8 +1069,7 @@ impl ConnectorIntegration<Upload, UploadFileRequestData, UploadFileResponse> for
|
||||
req: &UploadFileRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = transformers::construct_file_upload_request(req.clone())?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
transformers::construct_file_upload_request(req.clone())
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -6,7 +6,7 @@ use common_utils::{
|
||||
errors::{CustomResult, ParsingError},
|
||||
ext_traits::ByteSliceExt,
|
||||
id_type::CustomerId,
|
||||
request::Method,
|
||||
request::{Method, RequestContent},
|
||||
types::MinorUnit,
|
||||
};
|
||||
use error_stack::ResultExt;
|
||||
@ -1812,10 +1812,27 @@ pub struct CheckoutWebhookObjectResource {
|
||||
pub data: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct CheckoutFileRequest {
|
||||
pub purpose: &'static str,
|
||||
#[serde(skip)]
|
||||
pub file: Vec<u8>,
|
||||
#[serde(skip)]
|
||||
pub file_key: String,
|
||||
#[serde(skip)]
|
||||
pub file_type: String,
|
||||
}
|
||||
|
||||
pub fn construct_file_upload_request(
|
||||
file_upload_router_data: UploadFileRouterData,
|
||||
) -> CustomResult<reqwest::multipart::Form, errors::ConnectorError> {
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let request = file_upload_router_data.request;
|
||||
let checkout_file_request = CheckoutFileRequest {
|
||||
purpose: "dispute_evidence",
|
||||
file: request.file.clone(),
|
||||
file_key: request.file_key.clone(),
|
||||
file_type: request.file_type.to_string(),
|
||||
};
|
||||
let mut multipart = reqwest::multipart::Form::new();
|
||||
multipart = multipart.text("purpose", "dispute_evidence");
|
||||
let file_data = reqwest::multipart::Part::bytes(request.file)
|
||||
@ -1833,7 +1850,10 @@ pub fn construct_file_upload_request(
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)
|
||||
.attach_printable("Failure in constructing file data")?;
|
||||
multipart = multipart.part("file", file_data);
|
||||
Ok(multipart)
|
||||
Ok(RequestContent::FormData((
|
||||
multipart,
|
||||
Box::new(checkout_file_request),
|
||||
)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
||||
@ -246,7 +246,9 @@ impl ConnectorCommon for Fiuu {
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn build_form_from_struct<T: Serialize>(data: T) -> Result<Form, common_errors::ParsingError> {
|
||||
pub fn build_form_from_struct<T: Serialize + Send + 'static>(
|
||||
data: T,
|
||||
) -> Result<RequestContent, common_errors::ParsingError> {
|
||||
let mut form = Form::new();
|
||||
let serialized = serde_json::to_value(&data).map_err(|e| {
|
||||
router_env::logger::error!("Error serializing data to JSON value: {:?}", e);
|
||||
@ -268,7 +270,7 @@ pub fn build_form_from_struct<T: Serialize>(data: T) -> Result<Form, common_erro
|
||||
};
|
||||
form = form.text(key.clone(), value.clone());
|
||||
}
|
||||
Ok(form)
|
||||
Ok(RequestContent::FormData((form, Box::new(data))))
|
||||
}
|
||||
|
||||
impl ConnectorValidation for Fiuu {
|
||||
@ -345,19 +347,18 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
.as_ref()
|
||||
.map(|mandate_id| mandate_id.is_network_transaction_id_flow());
|
||||
|
||||
let connector_req = match (optional_is_mit_flow, optional_is_nti_flow) {
|
||||
match (optional_is_mit_flow, optional_is_nti_flow) {
|
||||
(Some(true), Some(false)) => {
|
||||
let recurring_request = fiuu::FiuuMandateRequest::try_from(&connector_router_data)?;
|
||||
build_form_from_struct(recurring_request)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
_ => {
|
||||
let payment_request = fiuu::FiuuPaymentRequest::try_from(&connector_router_data)?;
|
||||
build_form_from_struct(payment_request)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -429,9 +430,7 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Fiu
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let sync_request = fiuu::FiuuPaymentSyncRequest::try_from(req)?;
|
||||
let connector_req = build_form_from_struct(sync_request)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(sync_request).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -528,11 +527,10 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
|
||||
)?;
|
||||
|
||||
let connector_router_data = fiuu::FiuuRouterData::from((amount, req));
|
||||
let connector_req = build_form_from_struct(fiuu::PaymentCaptureRequest::try_from(
|
||||
build_form_from_struct(fiuu::PaymentCaptureRequest::try_from(
|
||||
&connector_router_data,
|
||||
)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -595,9 +593,8 @@ impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Fi
|
||||
req: &PaymentsCancelRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = build_form_from_struct(fiuu::FiuuPaymentCancelRequest::try_from(req)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(fiuu::FiuuPaymentCancelRequest::try_from(req)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -667,10 +664,8 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Fiuu {
|
||||
)?;
|
||||
|
||||
let connector_router_data = fiuu::FiuuRouterData::from((refund_amount, req));
|
||||
let connector_req =
|
||||
build_form_from_struct(fiuu::FiuuRefundRequest::try_from(&connector_router_data)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -732,9 +727,8 @@ impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Fiuu {
|
||||
req: &RefundSyncRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = build_form_from_struct(fiuu::FiuuRefundSyncRequest::try_from(req)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(fiuu::FiuuRefundSyncRequest::try_from(req)?)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -51,7 +51,9 @@ use transformers as hipay;
|
||||
|
||||
use crate::{constants::headers, types::ResponseRouterData, utils};
|
||||
|
||||
pub fn build_form_from_struct<T: Serialize>(data: T) -> Result<Form, common_errors::ParsingError> {
|
||||
pub fn build_form_from_struct<T: Serialize + Send + 'static>(
|
||||
data: T,
|
||||
) -> Result<RequestContent, common_errors::ParsingError> {
|
||||
let mut form = Form::new();
|
||||
let serialized = serde_json::to_value(&data).map_err(|e| {
|
||||
router_env::logger::error!("Error serializing data to JSON value: {:?}", e);
|
||||
@ -74,7 +76,7 @@ pub fn build_form_from_struct<T: Serialize>(data: T) -> Result<Form, common_erro
|
||||
};
|
||||
form = form.text(key.clone(), value.clone());
|
||||
}
|
||||
Ok(form)
|
||||
Ok(RequestContent::FormData((form, Box::new(data))))
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Hipay {
|
||||
@ -131,9 +133,7 @@ impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, Pay
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = transformers::HiPayTokenRequest::try_from(req)?;
|
||||
router_env::logger::info!(raw_connector_request=?connector_req);
|
||||
let connector_req = build_form_from_struct(connector_req)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(connector_req).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -296,9 +296,7 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
let connector_router_data = hipay::HipayRouterData::from((amount, req));
|
||||
let connector_req = hipay::HipayPaymentsRequest::try_from(&connector_router_data)?;
|
||||
router_env::logger::info!(raw_connector_request=?connector_req);
|
||||
let connector_req = build_form_from_struct(connector_req)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(connector_req).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -454,9 +452,7 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
|
||||
let connector_router_data = hipay::HipayRouterData::from((capture_amount, req));
|
||||
let connector_req = hipay::HipayMaintenanceRequest::try_from(&connector_router_data)?;
|
||||
router_env::logger::info!(raw_connector_request=?connector_req);
|
||||
let connector_req = build_form_from_struct(connector_req)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(connector_req).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -549,9 +545,7 @@ impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Hi
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = hipay::HipayMaintenanceRequest::try_from(req)?;
|
||||
router_env::logger::info!(raw_connector_request=?connector_req);
|
||||
let connector_req = build_form_from_struct(connector_req)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(connector_req).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
@ -609,9 +603,7 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Hipay {
|
||||
let connector_router_data = hipay::HipayRouterData::from((refund_amount, req));
|
||||
let connector_req = hipay::HipayMaintenanceRequest::try_from(&connector_router_data)?;
|
||||
router_env::logger::info!(raw_connector_request=?connector_req);
|
||||
let connector_req = build_form_from_struct(connector_req)
|
||||
.change_context(errors::ConnectorError::ParsingFailed)?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
build_form_from_struct(connector_req).change_context(errors::ConnectorError::ParsingFailed)
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -1880,8 +1880,7 @@ impl ConnectorIntegration<Upload, UploadFileRequestData, UploadFileResponse> for
|
||||
req: &UploadFileRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, ConnectorError> {
|
||||
let connector_req = transformers::construct_file_upload_request(req.clone())?;
|
||||
Ok(RequestContent::FormData(connector_req))
|
||||
transformers::construct_file_upload_request(req.clone())
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
|
||||
@ -4500,10 +4500,27 @@ pub fn get_bank_transfer_request_data(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct StripeFileRequest {
|
||||
pub purpose: &'static str,
|
||||
#[serde(skip)]
|
||||
pub file: Vec<u8>,
|
||||
#[serde(skip)]
|
||||
pub file_key: String,
|
||||
#[serde(skip)]
|
||||
pub file_type: String,
|
||||
}
|
||||
|
||||
pub fn construct_file_upload_request(
|
||||
file_upload_router_data: UploadFileRouterData,
|
||||
) -> CustomResult<reqwest::multipart::Form, ConnectorError> {
|
||||
) -> CustomResult<RequestContent, ConnectorError> {
|
||||
let request = file_upload_router_data.request;
|
||||
let stripe_file_request = StripeFileRequest {
|
||||
purpose: "dispute_evidence",
|
||||
file: request.file.clone(),
|
||||
file_key: request.file_key.clone(),
|
||||
file_type: request.file_type.to_string(),
|
||||
};
|
||||
let mut multipart = reqwest::multipart::Form::new();
|
||||
multipart = multipart.text("purpose", "dispute_evidence");
|
||||
let file_data = reqwest::multipart::Part::bytes(request.file)
|
||||
@ -4511,7 +4528,10 @@ pub fn construct_file_upload_request(
|
||||
.mime_str(request.file_type.as_ref())
|
||||
.map_err(|_| ConnectorError::RequestEncodingFailed)?;
|
||||
multipart = multipart.part("file", file_data);
|
||||
Ok(multipart)
|
||||
Ok(RequestContent::FormData((
|
||||
multipart,
|
||||
Box::new(stripe_file_request),
|
||||
)))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
||||
@ -948,7 +948,7 @@ pub async fn create_payment_method_core(
|
||||
|
||||
match &req.payment_method_data {
|
||||
api::PaymentMethodCreateData::Card(_) => {
|
||||
create_payment_method_card_core(
|
||||
Box::pin(create_payment_method_card_core(
|
||||
state,
|
||||
req,
|
||||
merchant_context,
|
||||
@ -957,7 +957,7 @@ pub async fn create_payment_method_core(
|
||||
&customer_id,
|
||||
payment_method_id,
|
||||
payment_method_billing_address,
|
||||
)
|
||||
))
|
||||
.await
|
||||
}
|
||||
api::PaymentMethodCreateData::ProxyCard(_) => {
|
||||
|
||||
@ -334,7 +334,9 @@ where
|
||||
| RequestContent::Xml(i) => i
|
||||
.masked_serialize()
|
||||
.unwrap_or(json!({ "error": "failed to mask serialize"})),
|
||||
RequestContent::FormData(_) => json!({"request_type": "FORM_DATA"}),
|
||||
RequestContent::FormData((_, i)) => i
|
||||
.masked_serialize()
|
||||
.unwrap_or(json!({ "error": "failed to mask serialize"})),
|
||||
RequestContent::RawBytes(_) => json!({"request_type": "RAW_BYTES"}),
|
||||
},
|
||||
None => serde_json::Value::Null,
|
||||
|
||||
Reference in New Issue
Block a user