refactor(connector): add amount conversion framework to cybersource (#6335)

Co-authored-by: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com>
This commit is contained in:
Gaurav Ghodinde
2024-12-04 15:03:53 +05:30
committed by GitHub
parent fd82cf610a
commit 248be9c73e
5 changed files with 81 additions and 74 deletions

View File

@ -601,7 +601,6 @@ impl StringMajorUnit {
pub fn zero() -> Self { pub fn zero() -> Self {
Self("0".to_string()) Self("0".to_string())
} }
/// Get string amount from struct to be removed in future /// Get string amount from struct to be removed in future
pub fn get_amount_as_string(&self) -> String { pub fn get_amount_as_string(&self) -> String {
self.0.clone() self.0.clone()

View File

@ -1,9 +1,10 @@
pub mod transformers; pub mod transformers;
use std::fmt::Debug;
use base64::Engine; use base64::Engine;
use common_utils::request::RequestContent; use common_utils::{
request::RequestContent,
types::{AmountConvertor, MinorUnit, StringMajorUnit, StringMajorUnitForConnector},
};
use diesel_models::enums; use diesel_models::enums;
use error_stack::{report, Report, ResultExt}; use error_stack::{report, Report, ResultExt};
use masking::{ExposeInterface, PeekInterface}; use masking::{ExposeInterface, PeekInterface};
@ -12,7 +13,7 @@ use time::OffsetDateTime;
use transformers as cybersource; use transformers as cybersource;
use url::Url; use url::Url;
use super::utils::{PaymentsAuthorizeRequestData, RouterData}; use super::utils::{convert_amount, PaymentsAuthorizeRequestData, RouterData};
use crate::{ use crate::{
configs::settings, configs::settings,
connector::{ connector::{
@ -36,9 +37,18 @@ use crate::{
utils::BytesExt, utils::BytesExt,
}; };
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct Cybersource; pub struct Cybersource {
amount_converter: &'static (dyn AmountConvertor<Output = StringMajorUnit> + Sync),
}
impl Cybersource {
pub fn new() -> &'static Self {
&Self {
amount_converter: &StringMajorUnitForConnector,
}
}
}
impl Cybersource { impl Cybersource {
pub fn generate_digest(&self, payload: &[u8]) -> String { pub fn generate_digest(&self, payload: &[u8]) -> String {
let payload_digest = digest::digest(&digest::SHA256, payload); let payload_digest = digest::digest(&digest::SHA256, payload);
@ -617,20 +627,20 @@ impl
req: &types::PaymentsPreProcessingRouterData, req: &types::PaymentsPreProcessingRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let minor_amount =
&self.get_currency_unit(), req.request
.minor_amount
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "minor_amount",
})?;
let currency =
req.request req.request
.currency .currency
.ok_or(errors::ConnectorError::MissingRequiredField { .ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "currency", field_name: "currency",
})?, })?;
req.request let amount = convert_amount(self.amount_converter, minor_amount, currency)?;
.amount let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "amount",
})?,
req,
))?;
let connector_req = let connector_req =
cybersource::CybersourcePreProcessingRequest::try_from(&connector_router_data)?; cybersource::CybersourcePreProcessingRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -718,12 +728,12 @@ impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::Payme
req: &types::PaymentsCaptureRouterData, req: &types::PaymentsCaptureRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let amount = convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount_to_capture,
req.request.currency, req.request.currency,
req.request.amount_to_capture, )?;
req, let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
))?;
let connector_req = let connector_req =
cybersource::CybersourcePaymentsCaptureRequest::try_from(&connector_router_data)?; cybersource::CybersourcePaymentsCaptureRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -922,12 +932,12 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
req: &types::PaymentsAuthorizeRouterData, req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let amount = convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount,
req.request.currency, req.request.currency,
req.request.amount, )?;
req, let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
))?;
if req.is_three_ds() if req.is_three_ds()
&& req.request.is_card() && req.request.is_card()
&& (req.request.connector_mandate_id().is_none() && (req.request.connector_mandate_id().is_none()
@ -1068,12 +1078,12 @@ impl ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::PayoutsResp
req: &types::PayoutsRouterData<api::PoFulfill>, req: &types::PayoutsRouterData<api::PoFulfill>,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let amount = convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount,
req.request.destination_currency, req.request.destination_currency,
req.request.amount, )?;
req, let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
))?;
let connector_req = let connector_req =
cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?; cybersource::CybersourcePayoutFulfillRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -1193,12 +1203,12 @@ impl
req: &types::PaymentsCompleteAuthorizeRouterData, req: &types::PaymentsCompleteAuthorizeRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let amount = convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_amount,
req.request.currency, req.request.currency,
req.request.amount, )?;
req, let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
))?;
let connector_req = let connector_req =
cybersource::CybersourcePaymentsRequest::try_from(&connector_router_data)?; cybersource::CybersourcePaymentsRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -1317,20 +1327,21 @@ impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsR
req: &types::PaymentsCancelRouterData, req: &types::PaymentsCancelRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let minor_amount =
&self.get_currency_unit(), req.request
.minor_amount
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "Amount",
})?;
let currency =
req.request req.request
.currency .currency
.ok_or(errors::ConnectorError::MissingRequiredField { .ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "Currency", field_name: "Currency",
})?, })?;
req.request let amount = convert_amount(self.amount_converter, minor_amount, currency)?;
.amount let connector_router_data = cybersource::CybersourceRouterData::from((amount, req));
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "Amount",
})?,
req,
))?;
let connector_req = cybersource::CybersourceVoidRequest::try_from(&connector_router_data)?; let connector_req = cybersource::CybersourceVoidRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -1443,12 +1454,12 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
req: &types::RefundExecuteRouterData, req: &types::RefundExecuteRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let refund_amount = convert_amount(
&self.get_currency_unit(), self.amount_converter,
req.request.minor_refund_amount,
req.request.currency, req.request.currency,
req.request.refund_amount, )?;
req, let connector_router_data = cybersource::CybersourceRouterData::from((refund_amount, req));
))?;
let connector_req = let connector_req =
cybersource::CybersourceRefundRequest::try_from(&connector_router_data)?; cybersource::CybersourceRefundRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req))) Ok(RequestContent::Json(Box::new(connector_req)))
@ -1609,12 +1620,14 @@ impl
req: &types::PaymentsIncrementalAuthorizationRouterData, req: &types::PaymentsIncrementalAuthorizationRouterData,
_connectors: &settings::Connectors, _connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> { ) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_router_data = cybersource::CybersourceRouterData::try_from(( let minor_additional_amount = MinorUnit::new(req.request.additional_amount);
&self.get_currency_unit(), let additional_amount = convert_amount(
self.amount_converter,
minor_additional_amount,
req.request.currency, req.request.currency,
req.request.additional_amount, )?;
req, let connector_router_data =
))?; cybersource::CybersourceRouterData::from((additional_amount, req));
let connector_request = let connector_request =
cybersource::CybersourcePaymentsIncrementalAuthorizationRequest::try_from( cybersource::CybersourcePaymentsIncrementalAuthorizationRequest::try_from(
&connector_router_data, &connector_router_data,

View File

@ -6,7 +6,7 @@ use common_enums::FutureUsage;
use common_utils::{ use common_utils::{
ext_traits::{OptionExt, ValueExt}, ext_traits::{OptionExt, ValueExt},
pii, pii,
types::SemanticVersion, types::{SemanticVersion, StringMajorUnit},
}; };
use error_stack::ResultExt; use error_stack::ResultExt;
#[cfg(feature = "payouts")] #[cfg(feature = "payouts")]
@ -40,21 +40,16 @@ use crate::{
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct CybersourceRouterData<T> { pub struct CybersourceRouterData<T> {
pub amount: String, pub amount: StringMajorUnit,
pub router_data: T, pub router_data: T,
} }
impl<T> TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for CybersourceRouterData<T> { impl<T> From<(StringMajorUnit, T)> for CybersourceRouterData<T> {
type Error = error_stack::Report<errors::ConnectorError>; fn from((amount, router_data): (StringMajorUnit, T)) -> Self {
fn try_from( Self {
(currency_unit, currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T),
) -> Result<Self, Self::Error> {
// 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 {
amount, amount,
router_data: item, router_data,
}) }
} }
} }
@ -92,7 +87,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest {
let order_information = OrderInformationWithBill { let order_information = OrderInformationWithBill {
amount_details: Amount { amount_details: Amount {
total_amount: "0".to_string(), total_amount: StringMajorUnit::zero(),
currency: item.request.currency, currency: item.request.currency,
}, },
bill_to: Some(bill_to), bill_to: Some(bill_to),
@ -525,14 +520,14 @@ pub struct OrderInformation {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Amount { pub struct Amount {
total_amount: String, total_amount: StringMajorUnit,
currency: api_models::enums::Currency, currency: api_models::enums::Currency,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AdditionalAmount { pub struct AdditionalAmount {
additional_amount: String, additional_amount: StringMajorUnit,
currency: String, currency: String,
} }

View File

@ -367,7 +367,7 @@ impl ConnectorData {
Ok(ConnectorEnum::Old(Box::new(connector::Cryptopay::new()))) Ok(ConnectorEnum::Old(Box::new(connector::Cryptopay::new())))
} }
enums::Connector::Cybersource => { enums::Connector::Cybersource => {
Ok(ConnectorEnum::Old(Box::new(&connector::Cybersource))) Ok(ConnectorEnum::Old(Box::new(connector::Cybersource::new())))
} }
enums::Connector::Datatrans => { enums::Connector::Datatrans => {
Ok(ConnectorEnum::Old(Box::new(connector::Datatrans::new()))) Ok(ConnectorEnum::Old(Box::new(connector::Datatrans::new())))

View File

@ -15,7 +15,7 @@ impl utils::Connector for Cybersource {
fn get_data(&self) -> api::ConnectorData { fn get_data(&self) -> api::ConnectorData {
use router::connector::Cybersource; use router::connector::Cybersource;
utils::construct_connector_data_old( utils::construct_connector_data_old(
Box::new(&Cybersource), Box::new(Cybersource::new()),
types::Connector::Cybersource, types::Connector::Cybersource,
api::GetToken::Connector, api::GetToken::Connector,
None, None,