From 1622ec1de0513ecf23789668df88857647531a29 Mon Sep 17 00:00:00 2001 From: Gaurav Ghodinde <65962770+gauravghodinde@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:58:04 +0530 Subject: [PATCH] refactor(connector): add amount conversion framework to cybersource (#6335) Co-authored-by: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com> --- crates/common_utils/src/types.rs | 7 +- crates/router/src/connector/cybersource.rs | 125 ++++++++++-------- .../src/connector/cybersource/transformers.rs | 25 ++-- crates/router/src/types/api.rs | 2 +- crates/router/tests/connectors/cybersource.rs | 2 +- 5 files changed, 87 insertions(+), 74 deletions(-) diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index ce2be525989..afa3d38f2d1 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -568,7 +568,7 @@ pub struct StringMajorUnit(String); impl StringMajorUnit { /// forms a new major unit from amount - fn new(value: String) -> Self { + pub fn new(value: String) -> Self { Self(value) } @@ -600,6 +600,11 @@ impl StringMajorUnit { Self("0".to_string()) } + /// forms a new StringMajorUnit default unit i.e zero + pub fn zero() -> Self { + Self("0".to_string()) + } + /// Get string amount from struct to be removed in future pub fn get_amount_as_string(&self) -> String { self.0.clone() diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index 806cf67b2da..06f448e075a 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -1,9 +1,10 @@ pub mod transformers; -use std::fmt::Debug; - use base64::Engine; -use common_utils::request::RequestContent; +use common_utils::{ + request::RequestContent, + types::{AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector}, +}; use diesel_models::enums; use error_stack::{report, Report, ResultExt}; use masking::{ExposeInterface, PeekInterface}; @@ -12,7 +13,7 @@ use time::OffsetDateTime; use transformers as cybersource; use url::Url; -use super::utils::{PaymentsAuthorizeRequestData, RouterData}; +use super::utils::{convert_amount, PaymentsAuthorizeRequestData, RouterData}; use crate::{ configs::settings, connector::{ @@ -36,9 +37,18 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Cybersource; +#[derive(Clone)] +pub struct Cybersource { + amount_converter: &'static (dyn AmountConvertor + Sync), +} +impl Cybersource { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMajorUnitForConnector, + } + } +} impl Cybersource { pub fn generate_digest(&self, payload: &[u8]) -> String { let payload_digest = digest::digest(&digest::SHA256, payload); @@ -617,20 +627,20 @@ impl req: &types::PaymentsPreProcessingRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let minor_amount = req.request - .currency + .minor_amount .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "currency", - })?, + field_name: "minor_amount", + })?; + let currency = req.request - .amount + .currency .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "amount", - })?, - req, - ))?; + field_name: "currency", + })?; + let amount = convert_amount(self.amount_converter, minor_amount, currency)?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); let connector_req = cybersource::CybersourcePreProcessingRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -718,12 +728,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); let connector_req = cybersource::CybersourcePaymentsCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -922,12 +932,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); if req.is_three_ds() && req.request.is_card() && (req.request.connector_mandate_id().is_none() @@ -1068,12 +1078,12 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.destination_currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); let connector_req = cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -1193,12 +1203,12 @@ impl req: &types::PaymentsCompleteAuthorizeRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); let connector_req = cybersource::CybersourcePaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -1317,20 +1327,21 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let minor_amount = req.request - .currency + .minor_amount .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "Currency", - })?, + field_name: "Amount", + })?; + let currency = req.request - .amount + .currency .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "Amount", - })?, - req, - ))?; + field_name: "Currency", + })?; + let amount = convert_amount(self.amount_converter, minor_amount, currency)?; + let connector_router_data = cybersource::CybersourceRouterData::from((amount, req)); + let connector_req = cybersource::CybersourceVoidRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -1443,12 +1454,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let refund_amount = convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + let connector_router_data = cybersource::CybersourceRouterData::from((refund_amount, req)); let connector_req = cybersource::CybersourceRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -1609,12 +1620,14 @@ impl req: &types::PaymentsIncrementalAuthorizationRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = cybersource::CybersourceRouterData::try_from(( - &self.get_currency_unit(), + let minor_additional_amount = MinorUnit::new(req.request.additional_amount); + let additional_amount = convert_amount( + self.amount_converter, + minor_additional_amount, req.request.currency, - req.request.additional_amount, - req, - ))?; + )?; + let connector_router_data = + cybersource::CybersourceRouterData::from((additional_amount, req)); let connector_request = cybersource::CybersourcePaymentsIncrementalAuthorizationRequest::try_from( &connector_router_data, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 4331b4d7983..61537611c40 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -9,7 +9,7 @@ use common_enums::FutureUsage; use common_utils::{ ext_traits::{OptionExt, ValueExt}, pii, - types::SemanticVersion, + types::{SemanticVersion, StringMajorUnit}, }; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret}; @@ -41,21 +41,16 @@ use crate::{ #[derive(Debug, Serialize)] pub struct CybersourceRouterData { - pub amount: String, + pub amount: StringMajorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for CybersourceRouterData { - type Error = error_stack::Report; - fn try_from( - (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), - ) -> Result { - // This conversion function is used at different places in the file, if updating this, keep a check for those - let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; - Ok(Self { +impl From<(StringMajorUnit, T)> for CybersourceRouterData { + fn from((amount, router_data): (StringMajorUnit, T)) -> Self { + Self { amount, - router_data: item, - }) + router_data, + } } } @@ -93,7 +88,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { let order_information = OrderInformationWithBill { amount_details: Amount { - total_amount: "0".to_string(), + total_amount: StringMajorUnit::new("0".to_string()), currency: item.request.currency, }, bill_to: Some(bill_to), @@ -525,14 +520,14 @@ pub struct OrderInformation { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Amount { - total_amount: String, + total_amount: StringMajorUnit, currency: api_models::enums::Currency, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AdditionalAmount { - additional_amount: String, + additional_amount: StringMajorUnit, currency: String, } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index a3046213272..12e7565fc42 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -362,7 +362,7 @@ impl ConnectorData { Ok(ConnectorEnum::Old(Box::new(connector::Cryptopay::new()))) } enums::Connector::Cybersource => { - Ok(ConnectorEnum::Old(Box::new(&connector::Cybersource))) + Ok(ConnectorEnum::Old(Box::new(connector::Cybersource::new()))) } enums::Connector::Datatrans => { Ok(ConnectorEnum::Old(Box::new(connector::Datatrans::new()))) diff --git a/crates/router/tests/connectors/cybersource.rs b/crates/router/tests/connectors/cybersource.rs index 0116b52f4ed..1accece7fa6 100644 --- a/crates/router/tests/connectors/cybersource.rs +++ b/crates/router/tests/connectors/cybersource.rs @@ -14,7 +14,7 @@ impl utils::Connector for Cybersource { fn get_data(&self) -> api::ConnectorData { use router::connector::Cybersource; utils::construct_connector_data_old( - Box::new(&Cybersource), + Box::new(Cybersource::new()), types::Connector::Cybersource, api::GetToken::Connector, None,