From a57016a18f41177f3fa9d8a6f91be0cd775f9561 Mon Sep 17 00:00:00 2001 From: Harshvardhan Bahukhandi Date: Fri, 21 Feb 2025 14:19:19 +0530 Subject: [PATCH 1/3] refactor(connector): add amount conversion framework to FISERV --- .../src/connectors/fiserv.rs | 52 +++++++++++-------- .../src/connectors/fiserv/transformers.rs | 27 ++++------ crates/hyperswitch_connectors/src/utils.rs | 11 ---- crates/router/src/types/api.rs | 4 +- crates/router/tests/connectors/fiserv.rs | 2 +- 5 files changed, 44 insertions(+), 52 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv.rs b/crates/hyperswitch_connectors/src/connectors/fiserv.rs index e80c57454a0..467f58bfa6c 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv.rs @@ -1,13 +1,12 @@ pub mod transformers; -use std::fmt::Debug; - use base64::Engine; use common_enums::enums; use common_utils::{ errors::CustomResult, ext_traits::BytesExt, request::{Method, Request, RequestBuilder, RequestContent}, + types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ @@ -44,12 +43,23 @@ use time::OffsetDateTime; use transformers as fiserv; use uuid::Uuid; -use crate::{constants::headers, types::ResponseRouterData, utils}; +use crate::{ + constants::headers, + types::ResponseRouterData, + utils::{construct_not_implemented_error_report, convert_amount}, +}; -#[derive(Debug, Clone)] -pub struct Fiserv; +#[derive(Clone)] +pub struct Fiserv { + amount_converter: &'static (dyn AmountConvertor + Sync), +} impl Fiserv { + pub fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } pub fn generate_authorization_signature( &self, auth: fiserv::FiservAuthType, @@ -194,7 +204,7 @@ impl ConnectorValidation for Fiserv { | enums::CaptureMethod::Manual | enums::CaptureMethod::SequentialAutomatic => Ok(()), enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err( - utils::construct_not_implemented_error_report(capture_method, self.id()), + construct_not_implemented_error_report(capture_method, self.id()), ), } } @@ -421,12 +431,12 @@ impl ConnectorIntegration fo req: &PaymentsCaptureRouterData, _connectors: &Connectors, ) -> CustomResult { - let router_obj = fiserv::FiservRouterData::try_from(( - &self.get_currency_unit(), + let amount_to_capture = convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + let router_obj = fiserv::FiservRouterData::try_from((amount_to_capture, req))?; let connector_req = fiserv::FiservCaptureRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -530,12 +540,12 @@ impl ConnectorIntegration CustomResult { - let router_obj = fiserv::FiservRouterData::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 router_obj = fiserv::FiservRouterData::try_from((amount, req))?; let connector_req = fiserv::FiservPaymentsRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -624,12 +634,12 @@ impl ConnectorIntegration for Fiserv req: &RefundsRouterData, _connectors: &Connectors, ) -> CustomResult { - let router_obj = fiserv::FiservRouterData::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 router_obj = fiserv::FiservRouterData::try_from((refund_amount, req))?; let connector_req = fiserv::FiservRefundRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs index d6e2cd54db7..ecf152cf16b 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs @@ -1,5 +1,5 @@ use common_enums::enums; -use common_utils::{ext_traits::ValueExt, pii}; +use common_utils::{ext_traits::ValueExt, pii, types::MinorUnit}; use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, @@ -9,7 +9,7 @@ use hyperswitch_domain_models::{ router_response_types::{PaymentsResponseData, RefundsResponseData}, types, }; -use hyperswitch_interfaces::{api, errors}; +use hyperswitch_interfaces::errors; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -23,22 +23,14 @@ use crate::{ #[derive(Debug, Serialize)] pub struct FiservRouterData { - pub amount: String, + pub amount: MinorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for FiservRouterData { +impl TryFrom<(MinorUnit, T)> for FiservRouterData { type Error = error_stack::Report; - fn try_from( - (currency_unit, currency, amount, router_data): ( - &api::CurrencyUnit, - enums::Currency, - i64, - T, - ), - ) -> Result { - let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; + fn try_from((amount, router_data): (MinorUnit, T)) -> Result { Ok(Self { amount, router_data, @@ -89,8 +81,7 @@ pub struct GooglePayToken { #[derive(Default, Debug, Serialize)] pub struct Amount { - #[serde(serialize_with = "utils::str_to_f32")] - total: String, + total: MinorUnit, currency: String, } @@ -143,7 +134,7 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP ) -> Result { let auth: FiservAuthType = FiservAuthType::try_from(&item.router_data.connector_auth_type)?; let amount = Amount { - total: item.amount.clone(), + total: item.amount, currency: item.router_data.request.currency.to_string(), }; let transaction_details = TransactionDetails { @@ -484,7 +475,7 @@ impl TryFrom<&FiservRouterData<&types::PaymentsCaptureRouterData>> for FiservCap })?; Ok(Self { amount: Amount { - total: item.amount.clone(), + total: item.amount, currency: item.router_data.request.currency.to_string(), }, transaction_details: TransactionDetails { @@ -579,7 +570,7 @@ impl TryFrom<&FiservRouterData<&types::RefundsRouterData>> for FiservRefun })?; Ok(Self { amount: Amount { - total: item.amount.clone(), + total: item.amount, currency: item.router_data.request.currency.to_string(), }, merchant_details: MerchantDetails { diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 05657216d9d..a5f93638d31 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -56,7 +56,6 @@ use masking::{ExposeInterface, PeekInterface, Secret}; use once_cell::sync::Lazy; use regex::Regex; use router_env::logger; -use serde::Serializer; use serde_json::Value; use time::PrimitiveDateTime; @@ -343,16 +342,6 @@ pub(crate) fn construct_not_implemented_error_report( .into() } -pub(crate) fn str_to_f32(value: &str, serializer: S) -> Result -where - S: Serializer, -{ - let float_value = value.parse::().map_err(|_| { - serde::ser::Error::custom("Invalid string, cannot be converted to float value") - })?; - serializer.serialize_f64(float_value) -} - pub(crate) const SELECTED_PAYMENT_METHOD: &str = "Selected payment method"; pub(crate) fn get_unimplemented_payment_method_error_message(connector: &str) -> String { diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 43556eb581a..b890760e741 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -424,7 +424,9 @@ impl ConnectorData { enums::Connector::Elavon => { Ok(ConnectorEnum::Old(Box::new(connector::Elavon::new()))) } - enums::Connector::Fiserv => Ok(ConnectorEnum::Old(Box::new(&connector::Fiserv))), + enums::Connector::Fiserv => { + Ok(ConnectorEnum::Old(Box::new(connector::Fiserv::new()))) + } enums::Connector::Fiservemea => { Ok(ConnectorEnum::Old(Box::new(connector::Fiservemea::new()))) } diff --git a/crates/router/tests/connectors/fiserv.rs b/crates/router/tests/connectors/fiserv.rs index 7f13de31004..78235278ed9 100644 --- a/crates/router/tests/connectors/fiserv.rs +++ b/crates/router/tests/connectors/fiserv.rs @@ -16,7 +16,7 @@ impl utils::Connector for FiservTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Fiserv; utils::construct_connector_data_old( - Box::new(&Fiserv), + Box::new(Fiserv::new()), types::Connector::Fiserv, types::api::GetToken::Connector, None, From 00667d9bf0b9fb5f6571c7aeba9c204af240c662 Mon Sep 17 00:00:00 2001 From: Harshvardhan Bahukhandi Date: Mon, 24 Feb 2025 14:43:35 +0530 Subject: [PATCH 2/3] refactor(connector): add amount conversion framework to HELCIM --- .../src/connectors/helcim.rs | 55 +++++++++++-------- .../src/connectors/helcim/transformers.rs | 29 +++++----- crates/router/src/types/api.rs | 4 +- crates/router/tests/connectors/helcim.rs | 2 +- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/helcim.rs b/crates/hyperswitch_connectors/src/connectors/helcim.rs index 5f0e49d84e1..60c9698a283 100644 --- a/crates/hyperswitch_connectors/src/connectors/helcim.rs +++ b/crates/hyperswitch_connectors/src/connectors/helcim.rs @@ -1,13 +1,12 @@ pub mod transformers; -use std::fmt::Debug; - use api_models::webhooks::IncomingWebhookEvent; use common_enums::enums; use common_utils::{ errors::CustomResult, ext_traits::BytesExt, request::{Method, Request, RequestBuilder, RequestContent}, + types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}, }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ @@ -46,11 +45,21 @@ use transformers as helcim; use crate::{ constants::headers, types::ResponseRouterData, - utils::{to_connector_meta, PaymentsAuthorizeRequestData}, + utils::{convert_amount, to_connector_meta, PaymentsAuthorizeRequestData}, }; -#[derive(Debug, Clone)] -pub struct Helcim; +#[derive(Clone)] +pub struct Helcim { + amount_convertor: &'static (dyn AmountConvertor + Sync), +} + +impl Helcim { + pub fn new() -> &'static Self { + &Self { + amount_convertor: &FloatMajorUnitForConnector, + } + } +} impl api::Payment for Helcim {} impl api::PaymentSession for Helcim {} @@ -302,13 +311,13 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = helcim::HelcimRouterData::try_from(( - &self.get_currency_unit(), + let connector_router_data = convert_amount( + self.amount_convertor, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; - let connector_req = helcim::HelcimPaymentsRequest::try_from(&connector_router_data)?; + )?; + let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?; + let connector_req = helcim::HelcimPaymentsRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -481,13 +490,13 @@ impl ConnectorIntegration fo req: &PaymentsCaptureRouterData, _connectors: &Connectors, ) -> CustomResult { - let connector_router_data = helcim::HelcimRouterData::try_from(( - &self.get_currency_unit(), + let connector_router_data = convert_amount( + self.amount_convertor, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; - let connector_req = helcim::HelcimCaptureRequest::try_from(&connector_router_data)?; + )?; + let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?; + let connector_req = helcim::HelcimCaptureRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -648,13 +657,13 @@ impl ConnectorIntegration for Helcim req: &RefundsRouterData, _connectors: &Connectors, ) -> CustomResult { - let connector_router_data = helcim::HelcimRouterData::try_from(( - &self.get_currency_unit(), + let connector_router_data = convert_amount( + self.amount_convertor, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; - let connector_req = helcim::HelcimRefundRequest::try_from(&connector_router_data)?; + )?; + let router_obj = helcim::HelcimRouterData::try_from((connector_router_data, req))?; + let connector_req = helcim::HelcimRefundRequest::try_from(&router_obj)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs index 0a5453a49d0..d890bd88ac3 100644 --- a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs @@ -1,5 +1,8 @@ use common_enums::enums; -use common_utils::pii::{Email, IpAddress}; +use common_utils::{ + pii::{Email, IpAddress}, + types::FloatMajorUnit, +}; use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::{Card, PaymentMethodData}, @@ -15,10 +18,7 @@ use hyperswitch_domain_models::{ RefundsRouterData, SetupMandateRouterData, }, }; -use hyperswitch_interfaces::{ - api::{self}, - errors, -}; +use hyperswitch_interfaces::errors; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -33,16 +33,13 @@ use crate::{ #[derive(Debug, Serialize)] pub struct HelcimRouterData { - pub amount: f64, + pub amount: FloatMajorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for HelcimRouterData { +impl TryFrom<(FloatMajorUnit, T)> for HelcimRouterData { type Error = error_stack::Report; - fn try_from( - (currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), - ) -> Result { - let amount = crate::utils::get_amount_as_f64(currency_unit, amount, currency)?; + fn try_from((amount, item): (FloatMajorUnit, T)) -> Result { Ok(Self { amount, router_data: item, @@ -77,7 +74,7 @@ pub struct HelcimVerifyRequest { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimPaymentsRequest { - amount: f64, + amount: FloatMajorUnit, currency: enums::Currency, ip_address: Secret, card_data: HelcimCard, @@ -115,8 +112,8 @@ pub struct HelcimInvoice { pub struct HelcimLineItems { description: String, quantity: u8, - price: f64, - total: f64, + price: FloatMajorUnit, + total: FloatMajorUnit, } #[derive(Debug, Serialize)] @@ -514,7 +511,7 @@ impl #[serde(rename_all = "camelCase")] pub struct HelcimCaptureRequest { pre_auth_transaction_id: u64, - amount: f64, + amount: FloatMajorUnit, ip_address: Secret, #[serde(skip_serializing_if = "Option::is_none")] ecommerce: Option, @@ -637,7 +634,7 @@ impl #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct HelcimRefundRequest { - amount: f64, + amount: FloatMajorUnit, original_transaction_id: u64, ip_address: Secret, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index b890760e741..ea7e09a19f6 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -446,7 +446,9 @@ impl ConnectorData { enums::Connector::Gocardless => { Ok(ConnectorEnum::Old(Box::new(&connector::Gocardless))) } - enums::Connector::Helcim => Ok(ConnectorEnum::Old(Box::new(&connector::Helcim))), + enums::Connector::Helcim => { + Ok(ConnectorEnum::Old(Box::new(connector::Helcim::new()))) + } enums::Connector::Iatapay => { Ok(ConnectorEnum::Old(Box::new(connector::Iatapay::new()))) } diff --git a/crates/router/tests/connectors/helcim.rs b/crates/router/tests/connectors/helcim.rs index 761b07736ce..5b02e2f8431 100644 --- a/crates/router/tests/connectors/helcim.rs +++ b/crates/router/tests/connectors/helcim.rs @@ -11,7 +11,7 @@ impl utils::Connector for HelcimTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Helcim; utils::construct_connector_data_old( - Box::new(&Helcim), + Box::new(Helcim::new()), types::Connector::Helcim, types::api::GetToken::Connector, None, From 840e9c1dfdf84a726ff714524fb687d9b9e93e2f Mon Sep 17 00:00:00 2001 From: Harshvardhan Bahukhandi Date: Tue, 25 Feb 2025 11:34:11 +0530 Subject: [PATCH 3/3] refactor(Fiserv):changed amount conversion framework to FloatMajorUnit for Fiserv --- crates/hyperswitch_connectors/src/connectors/fiserv.rs | 6 +++--- .../src/connectors/fiserv/transformers.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv.rs b/crates/hyperswitch_connectors/src/connectors/fiserv.rs index 467f58bfa6c..50eebb99518 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv.rs @@ -6,7 +6,7 @@ use common_utils::{ errors::CustomResult, ext_traits::BytesExt, request::{Method, Request, RequestBuilder, RequestContent}, - types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, + types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}, }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ @@ -51,13 +51,13 @@ use crate::{ #[derive(Clone)] pub struct Fiserv { - amount_converter: &'static (dyn AmountConvertor + Sync), + amount_converter: &'static (dyn AmountConvertor + Sync), } impl Fiserv { pub fn new() -> &'static Self { &Self { - amount_converter: &MinorUnitForConnector, + amount_converter: &FloatMajorUnitForConnector, } } pub fn generate_authorization_signature( diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs index ecf152cf16b..8f9a15e49c2 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs @@ -1,5 +1,5 @@ use common_enums::enums; -use common_utils::{ext_traits::ValueExt, pii, types::MinorUnit}; +use common_utils::{ext_traits::ValueExt, pii, types::FloatMajorUnit}; use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, @@ -23,14 +23,14 @@ use crate::{ #[derive(Debug, Serialize)] pub struct FiservRouterData { - pub amount: MinorUnit, + pub amount: FloatMajorUnit, pub router_data: T, } -impl TryFrom<(MinorUnit, T)> for FiservRouterData { +impl TryFrom<(FloatMajorUnit, T)> for FiservRouterData { type Error = error_stack::Report; - fn try_from((amount, router_data): (MinorUnit, T)) -> Result { + fn try_from((amount, router_data): (FloatMajorUnit, T)) -> Result { Ok(Self { amount, router_data, @@ -81,7 +81,7 @@ pub struct GooglePayToken { #[derive(Default, Debug, Serialize)] pub struct Amount { - total: MinorUnit, + total: FloatMajorUnit, currency: String, }