refactor(router): include currency conversion utility functions as Currency methods (#1790)

This commit is contained in:
udev
2023-08-01 12:33:29 +05:30
committed by GitHub
parent d06adc705c
commit 2c9c8f081d
5 changed files with 73 additions and 74 deletions

View File

@ -1,3 +1,5 @@
use std::num::TryFromIntError;
use router_derive; use router_derive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
@ -289,6 +291,40 @@ pub enum Currency {
} }
impl 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<String, TryFromIntError> {
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<f64, TryFromIntError> {
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<String, TryFromIntError> {
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 { pub fn iso_4217(&self) -> &'static str {
match *self { match *self {
Self::AED => "784", Self::AED => "784",

View File

@ -21,7 +21,7 @@ use crate::{
core::errors::{self, CustomResult}, core::errors::{self, CustomResult},
pii::PeekInterface, pii::PeekInterface,
types::{self, api, transformers::ForeignTryFrom, PaymentsCancelData, ResponseId}, types::{self, api, transformers::ForeignTryFrom, PaymentsCancelData, ResponseId},
utils::{self, OptionExt, ValueExt}, utils::{OptionExt, ValueExt},
}; };
pub fn missing_field_err( pub fn missing_field_err(
@ -924,7 +924,9 @@ pub fn to_currency_base_unit(
amount: i64, amount: i64,
currency: diesel_models::enums::Currency, currency: diesel_models::enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> { ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
utils::to_currency_base_unit(amount, currency) currency
.to_currency_base_unit(amount)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed) .change_context(errors::ConnectorError::RequestEncodingFailed)
} }
@ -932,7 +934,9 @@ pub fn to_currency_base_unit_with_zero_decimal_check(
amount: i64, amount: i64,
currency: diesel_models::enums::Currency, currency: diesel_models::enums::Currency,
) -> Result<String, error_stack::Report<errors::ConnectorError>> { ) -> Result<String, error_stack::Report<errors::ConnectorError>> {
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) .change_context(errors::ConnectorError::RequestEncodingFailed)
} }
@ -940,7 +944,9 @@ pub fn to_currency_base_unit_asf64(
amount: i64, amount: i64,
currency: diesel_models::enums::Currency, currency: diesel_models::enums::Currency,
) -> Result<f64, error_stack::Report<errors::ConnectorError>> { ) -> Result<f64, error_stack::Report<errors::ConnectorError>> {
utils::to_currency_base_unit_asf64(amount, currency) currency
.to_currency_base_unit_asf64(amount)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed) .change_context(errors::ConnectorError::RequestEncodingFailed)
} }

View File

@ -1,11 +1,10 @@
use api_models::payments as payment_types; use api_models::payments as payment_types;
use async_trait::async_trait; use async_trait::async_trait;
use common_utils::ext_traits::ByteSliceExt; use common_utils::ext_traits::ByteSliceExt;
use error_stack::{Report, ResultExt}; use error_stack::{IntoReport, Report, ResultExt};
use super::{ConstructFlowSpecificData, Feature}; use super::{ConstructFlowSpecificData, Feature};
use crate::{ use crate::{
connector,
core::{ core::{
errors::{self, ConnectorErrorExt, RouterResult}, errors::{self, ConnectorErrorExt, RouterResult},
payments::{self, access_token, transformers, PaymentData}, payments::{self, access_token, transformers, PaymentData},
@ -172,10 +171,11 @@ async fn create_applepay_session_token(
let amount_info = payment_types::AmountInfo { let amount_info = payment_types::AmountInfo {
label: applepay_metadata.data.payment_request_data.label, label: applepay_metadata.data.payment_request_data.label,
total_type: Some("final".to_string()), total_type: Some("final".to_string()),
amount: connector::utils::to_currency_base_unit( amount: router_data
router_data.request.amount, .request
router_data.request.currency, .currency
) .to_currency_base_unit(router_data.request.amount)
.into_report()
.change_context(errors::ApiErrorResponse::PreconditionFailed { .change_context(errors::ApiErrorResponse::PreconditionFailed {
message: "Failed to convert currency to base unit".to_string(), message: "Failed to convert currency to base unit".to_string(),
})?, })?,
@ -324,10 +324,11 @@ fn create_gpay_session_token(
country_code: session_data.country.unwrap_or_default(), country_code: session_data.country.unwrap_or_default(),
currency_code: router_data.request.currency, currency_code: router_data.request.currency,
total_price_status: "Final".to_string(), total_price_status: "Final".to_string(),
total_price: utils::to_currency_base_unit( total_price: router_data
router_data.request.amount, .request
router_data.request.currency, .currency
) .to_currency_base_unit(router_data.request.amount)
.into_report()
.attach_printable( .attach_printable(
"Cannot convert given amount to base currency denomination".to_string(), "Cannot convert given amount to base currency denomination".to_string(),
) )

View File

@ -2,7 +2,7 @@ use std::{fmt::Debug, marker::PhantomData};
use common_utils::fp_utils; use common_utils::fp_utils;
use diesel_models::{ephemeral_key, payment_attempt::PaymentListFilters}; use diesel_models::{ephemeral_key, payment_attempt::PaymentListFilters};
use error_stack::ResultExt; use error_stack::{IntoReport, ResultExt};
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
use super::{flows::Feature, PaymentAddress, PaymentData}; use super::{flows::Feature, PaymentAddress, PaymentData};
@ -21,7 +21,7 @@ use crate::{
storage::{self, enums}, storage::{self, enums},
transformers::{ForeignFrom, ForeignInto}, transformers::{ForeignFrom, ForeignInto},
}, },
utils::{self, OptionExt, ValueExt}, utils::{OptionExt, ValueExt},
}; };
#[instrument(skip_all)] #[instrument(skip_all)]
@ -303,11 +303,12 @@ where
.currency .currency
.as_ref() .as_ref()
.get_required_value("currency")?; .get_required_value("currency")?;
let amount = utils::to_currency_base_unit(payment_attempt.amount, *currency).change_context( let amount = currency
errors::ApiErrorResponse::InvalidDataValue { .to_currency_base_unit(payment_attempt.amount)
.into_report()
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "amount", field_name: "amount",
}, })?;
)?;
let mandate_id = payment_attempt.mandate_id.clone(); let mandate_id = payment_attempt.mandate_id.clone();
let refunds_response = if refunds.is_empty() { let refunds_response = if refunds.is_empty() {
None None

View File

@ -129,51 +129,6 @@ impl<E> 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<String, error_stack::Report<errors::ValidationError>> {
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<String, error_stack::Report<errors::ValidationError>> {
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<f64, error_stack::Report<errors::ValidationError>> {
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] #[inline]
pub fn get_payment_attempt_id(payment_id: impl std::fmt::Display, attempt_count: i16) -> String { pub fn get_payment_attempt_id(payment_id: impl std::fmt::Display, attempt_count: i16) -> String {
format!("{payment_id}_{attempt_count}") format!("{payment_id}_{attempt_count}")