diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 37ba25ce2c..ba69cedad9 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1,3 +1,5 @@ +use std::num::TryFromIntError; + use router_derive; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -289,6 +291,40 @@ pub enum Currency { } impl Currency { + /// Convert the amount to its base denomination based on Currency and return String + pub fn to_currency_base_unit(&self, amount: i64) -> Result { + let amount_f64 = self.to_currency_base_unit_asf64(amount)?; + Ok(format!("{amount_f64:.2}")) + } + + /// Convert the amount to its base denomination based on Currency and return f64 + pub fn to_currency_base_unit_asf64(&self, amount: i64) -> Result { + let amount_f64: f64 = u32::try_from(amount)?.into(); + let amount = if self.is_zero_decimal_currency() { + amount_f64 + } else if self.is_three_decimal_currency() { + amount_f64 / 1000.00 + } else { + amount_f64 / 100.00 + }; + Ok(amount) + } + + /// Convert the amount to its base denomination based on Currency and check for zero decimal currency and return String + /// Paypal Connector accepts Zero and Two decimal currency but not three decimal and it should be updated as required for 3 decimal currencies. + /// Paypal Ref - https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/ + pub fn to_currency_base_unit_with_zero_decimal_check( + &self, + amount: i64, + ) -> Result { + let amount_f64 = self.to_currency_base_unit_asf64(amount)?; + if self.is_zero_decimal_currency() { + Ok(amount_f64.to_string()) + } else { + Ok(format!("{amount_f64:.2}")) + } + } + pub fn iso_4217(&self) -> &'static str { match *self { Self::AED => "784", diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 9a00a42222..221ad0487b 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -21,7 +21,7 @@ use crate::{ core::errors::{self, CustomResult}, pii::PeekInterface, types::{self, api, transformers::ForeignTryFrom, PaymentsCancelData, ResponseId}, - utils::{self, OptionExt, ValueExt}, + utils::{OptionExt, ValueExt}, }; pub fn missing_field_err( @@ -924,7 +924,9 @@ pub fn to_currency_base_unit( amount: i64, currency: diesel_models::enums::Currency, ) -> Result> { - utils::to_currency_base_unit(amount, currency) + currency + .to_currency_base_unit(amount) + .into_report() .change_context(errors::ConnectorError::RequestEncodingFailed) } @@ -932,7 +934,9 @@ pub fn to_currency_base_unit_with_zero_decimal_check( amount: i64, currency: diesel_models::enums::Currency, ) -> Result> { - utils::to_currency_base_unit_with_zero_decimal_check(amount, currency) + currency + .to_currency_base_unit_with_zero_decimal_check(amount) + .into_report() .change_context(errors::ConnectorError::RequestEncodingFailed) } @@ -940,7 +944,9 @@ pub fn to_currency_base_unit_asf64( amount: i64, currency: diesel_models::enums::Currency, ) -> Result> { - utils::to_currency_base_unit_asf64(amount, currency) + currency + .to_currency_base_unit_asf64(amount) + .into_report() .change_context(errors::ConnectorError::RequestEncodingFailed) } diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 3b4528406b..7e348b2c43 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -1,11 +1,10 @@ use api_models::payments as payment_types; use async_trait::async_trait; use common_utils::ext_traits::ByteSliceExt; -use error_stack::{Report, ResultExt}; +use error_stack::{IntoReport, Report, ResultExt}; use super::{ConstructFlowSpecificData, Feature}; use crate::{ - connector, core::{ errors::{self, ConnectorErrorExt, RouterResult}, payments::{self, access_token, transformers, PaymentData}, @@ -172,13 +171,14 @@ async fn create_applepay_session_token( let amount_info = payment_types::AmountInfo { label: applepay_metadata.data.payment_request_data.label, total_type: Some("final".to_string()), - amount: connector::utils::to_currency_base_unit( - router_data.request.amount, - router_data.request.currency, - ) - .change_context(errors::ApiErrorResponse::PreconditionFailed { - message: "Failed to convert currency to base unit".to_string(), - })?, + amount: router_data + .request + .currency + .to_currency_base_unit(router_data.request.amount) + .into_report() + .change_context(errors::ApiErrorResponse::PreconditionFailed { + message: "Failed to convert currency to base unit".to_string(), + })?, }; let applepay_payment_request = payment_types::ApplePayPaymentRequest { @@ -324,16 +324,17 @@ fn create_gpay_session_token( country_code: session_data.country.unwrap_or_default(), currency_code: router_data.request.currency, total_price_status: "Final".to_string(), - total_price: utils::to_currency_base_unit( - router_data.request.amount, - router_data.request.currency, - ) - .attach_printable( - "Cannot convert given amount to base currency denomination".to_string(), - ) - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "amount", - })?, + total_price: router_data + .request + .currency + .to_currency_base_unit(router_data.request.amount) + .into_report() + .attach_printable( + "Cannot convert given amount to base currency denomination".to_string(), + ) + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "amount", + })?, }; Ok(types::PaymentsSessionRouterData { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index bd6ced78a3..511d0048d2 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, marker::PhantomData}; use common_utils::fp_utils; use diesel_models::{ephemeral_key, payment_attempt::PaymentListFilters}; -use error_stack::ResultExt; +use error_stack::{IntoReport, ResultExt}; use router_env::{instrument, tracing}; use super::{flows::Feature, PaymentAddress, PaymentData}; @@ -21,7 +21,7 @@ use crate::{ storage::{self, enums}, transformers::{ForeignFrom, ForeignInto}, }, - utils::{self, OptionExt, ValueExt}, + utils::{OptionExt, ValueExt}, }; #[instrument(skip_all)] @@ -303,11 +303,12 @@ where .currency .as_ref() .get_required_value("currency")?; - let amount = utils::to_currency_base_unit(payment_attempt.amount, *currency).change_context( - errors::ApiErrorResponse::InvalidDataValue { + let amount = currency + .to_currency_base_unit(payment_attempt.amount) + .into_report() + .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "amount", - }, - )?; + })?; let mandate_id = payment_attempt.mandate_id.clone(); let refunds_response = if refunds.is_empty() { None diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 763062fddf..bf943c09ef 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -129,51 +129,6 @@ impl ConnectorResponseExt } } -/// Convert the amount to its base denomination based on Currency and return String -pub fn to_currency_base_unit( - amount: i64, - currency: diesel_models::enums::Currency, -) -> Result> { - let amount_f64 = to_currency_base_unit_asf64(amount, currency)?; - Ok(format!("{amount_f64:.2}")) -} - -/// Convert the amount to its base denomination based on Currency and check for zero decimal currency and return String -/// Paypal Connector accepts Zero and Two decimal currency but not three decimal and it should be updated as required for 3 decimal currencies. -/// Paypal Ref - https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/ -pub fn to_currency_base_unit_with_zero_decimal_check( - amount: i64, - currency: diesel_models::enums::Currency, -) -> Result> { - let amount_f64 = to_currency_base_unit_asf64(amount, currency)?; - if currency.is_zero_decimal_currency() { - Ok(amount_f64.to_string()) - } else { - Ok(format!("{amount_f64:.2}")) - } -} - -/// Convert the amount to its base denomination based on Currency and return f64 -pub fn to_currency_base_unit_asf64( - amount: i64, - currency: diesel_models::enums::Currency, -) -> Result> { - let amount_u32 = u32::try_from(amount).into_report().change_context( - errors::ValidationError::InvalidValue { - message: amount.to_string(), - }, - )?; - let amount_f64 = f64::from(amount_u32); - let amount = if currency.is_zero_decimal_currency() { - amount_f64 - } else if currency.is_three_decimal_currency() { - amount_f64 / 1000.00 - } else { - amount_f64 / 100.00 - }; - Ok(amount) -} - #[inline] pub fn get_payment_attempt_id(payment_id: impl std::fmt::Display, attempt_count: i16) -> String { format!("{payment_id}_{attempt_count}")