diff --git a/Cargo.lock b/Cargo.lock index abc265ad69..d5dc4e2ee9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1977,6 +1977,7 @@ dependencies = [ "thiserror", "time", "tokio 1.37.0", + "utoipa", "uuid", ] @@ -4511,6 +4512,7 @@ name = "openapi" version = "0.1.0" dependencies = [ "api_models", + "common_utils", "serde_json", "utoipa", ] diff --git a/connector-template/transformers.rs b/connector-template/transformers.rs index 0d9e99c86a..40e3c2a366 100644 --- a/connector-template/transformers.rs +++ b/connector-template/transformers.rs @@ -4,7 +4,7 @@ use crate::{connector::utils::{PaymentsAuthorizeRequestData},core::errors,types: //TODO: Fill the struct with respective fields pub struct {{project-name | downcase | pascal_case}}RouterData { - pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: MinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. pub router_data: T, } @@ -140,7 +140,7 @@ incremental_authorization_allowed: None, // Type definition for RefundRequest #[derive(Default, Debug, Serialize)] pub struct {{project-name | downcase | pascal_case}}RefundRequest { - pub amount: i64 + pub amount: MinorUnit } impl TryFrom<&{{project-name | downcase | pascal_case}}RouterData<&types::RefundsRouterData>> for {{project-name | downcase | pascal_case}}RefundRequest { diff --git a/crates/api_models/src/currency.rs b/crates/api_models/src/currency.rs index c1d7e422d0..608e4c7a6b 100644 --- a/crates/api_models/src/currency.rs +++ b/crates/api_models/src/currency.rs @@ -1,10 +1,10 @@ -use common_utils::events::ApiEventMetric; +use common_utils::{events::ApiEventMetric, types::MinorUnit}; /// QueryParams to be send to convert the amount -> from_currency -> to_currency #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "snake_case")] pub struct CurrencyConversionParams { - pub amount: i64, + pub amount: MinorUnit, pub to_currency: String, pub from_currency: String, } diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index dd7791ee2d..d26a2deac1 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -5,7 +5,7 @@ use common_utils::{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, crypto::OptionalEncryptableName, pii, - types::{Percentage, Surcharge}, + types::{MinorUnit, Percentage, Surcharge}, }; use serde::de; use utoipa::{schema, ToSchema}; @@ -492,7 +492,7 @@ pub struct SurchargeDetailsResponse { #[serde(rename_all = "snake_case", tag = "type", content = "value")] pub enum SurchargeResponse { /// Fixed Surcharge value - Fixed(i64), + Fixed(MinorUnit), /// Surcharge percentage Rate(SurchargePercentage), } @@ -638,7 +638,7 @@ pub struct PaymentMethodListRequest { /// Filter by amount #[schema(example = 60)] - pub amount: Option, + pub amount: Option, /// Indicates whether the payment method is eligible for recurring payments #[schema(example = true)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 8f4fca59d2..80be635da6 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -10,6 +10,7 @@ use common_utils::{ crypto, ext_traits::{ConfigExt, Encode}, pii::{self, Email}, + types::MinorUnit, }; use masking::{PeekInterface, Secret}; use router_derive::Setter; @@ -223,8 +224,8 @@ pub struct PaymentsRequest { pub currency: Option, /// The Amount to be captured / debited from the users payment method. It shall be in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, the default amount_to_capture will be the payment amount. - #[schema(example = 6540)] - pub amount_to_capture: Option, + #[schema(value_type = Option, example = 6540)] + pub amount_to_capture: Option, /// Unique identifier for the payment. This ensures idempotency for multiple payments /// that have been done by a single merchant. This field is auto generated and is returned in the API response. @@ -476,21 +477,22 @@ pub struct PaymentsRequest { } impl PaymentsRequest { - pub fn get_total_capturable_amount(&self) -> Option { + pub fn get_total_capturable_amount(&self) -> Option { let surcharge_amount = self .surcharge_details .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) - .unwrap_or(0); + .unwrap_or_default(); self.amount - .map(|amount| i64::from(amount) + surcharge_amount) + .map(|amount| MinorUnit::from(amount) + surcharge_amount) } } #[derive( Default, Debug, Clone, serde::Serialize, serde::Deserialize, Copy, ToSchema, PartialEq, )] pub struct RequestSurchargeDetails { - pub surcharge_amount: i64, - pub tax_amount: Option, + #[schema(value_type = i64, example = 6540)] + pub surcharge_amount: MinorUnit, + pub tax_amount: Option, } /// Browser information to be used for 3DS 2.0 @@ -533,10 +535,11 @@ pub struct BrowserInformation { impl RequestSurchargeDetails { pub fn is_surcharge_zero(&self) -> bool { - self.surcharge_amount == 0 && self.tax_amount.unwrap_or(0) == 0 + self.surcharge_amount == MinorUnit::new(0) + && self.tax_amount.unwrap_or_default() == MinorUnit::new(0) } - pub fn get_total_surcharge_amount(&self) -> i64 { - self.surcharge_amount + self.tax_amount.unwrap_or(0) + pub fn get_total_surcharge_amount(&self) -> MinorUnit { + self.surcharge_amount + self.tax_amount.unwrap_or_default() } } @@ -567,7 +570,8 @@ pub struct PaymentAttemptResponse { #[schema(value_type = AttemptStatus, example = "charged")] pub status: enums::AttemptStatus, /// The payment attempt amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, /// The currency of the amount of the payment attempt #[schema(value_type = Option, example = "USD")] pub currency: Option, @@ -621,7 +625,8 @@ pub struct CaptureResponse { #[schema(value_type = CaptureStatus, example = "charged")] pub status: enums::CaptureStatus, /// The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, /// The currency of the amount of the capture #[schema(value_type = Option, example = "USD")] pub currency: Option, @@ -704,21 +709,23 @@ pub enum Amount { Zero, } -impl From for i64 { +impl From for MinorUnit { fn from(amount: Amount) -> Self { match amount { - Amount::Value(val) => val.get(), - Amount::Zero => 0, + Amount::Value(val) => Self::new(val.get()), + Amount::Zero => Self::new(0), } } } -impl From for Amount { - fn from(val: i64) -> Self { - NonZeroI64::new(val).map_or(Self::Zero, Amount::Value) +impl From for Amount { + fn from(minor_unit: MinorUnit) -> Self { + match minor_unit.get_amount_as_i64() { + 0 => Self::Zero, + val => NonZeroI64::new(val).map_or(Self::Zero, Self::Value), + } } } - #[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone)] #[serde(deny_unknown_fields)] pub struct PaymentsRedirectRequest { @@ -806,15 +813,15 @@ pub struct MandateData { #[derive(Clone, Eq, PartialEq, Copy, Debug, Default, serde::Serialize, serde::Deserialize)] pub struct SingleUseMandate { - pub amount: i64, + pub amount: MinorUnit, pub currency: api_enums::Currency, } #[derive(Clone, Eq, PartialEq, Debug, Default, ToSchema, serde::Serialize, serde::Deserialize)] pub struct MandateAmountData { /// The maximum amount to be debited for the mandate transaction - #[schema(example = 6540)] - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, /// The currency for the transaction #[schema(value_type = Currency, example = "USD")] pub currency: api_enums::Currency, @@ -2878,7 +2885,8 @@ pub struct PaymentsCaptureRequest { /// The unique identifier for the merchant pub merchant_id: Option, /// The Amount to be captured/ debited from the user's payment method. - pub amount_to_capture: Option, + #[schema(value_type = i64, example = 6540)] + pub amount_to_capture: Option, /// Decider to refund the uncaptured amount pub refund_uncaptured_amount: Option, /// Provides information about a card payment that customers see on their statements. @@ -3131,21 +3139,21 @@ pub struct PaymentsResponse { pub status: api_enums::IntentStatus, /// The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., - #[schema(example = 100)] - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, /// The payment net amount. net_amount = amount + surcharge_details.surcharge_amount + surcharge_details.tax_amount, /// If no surcharge_details, net_amount = amount - #[schema(example = 110)] - pub net_amount: i64, + #[schema(value_type = i64, example = 6540)] + pub net_amount: MinorUnit, /// The maximum amount that could be captured from the payment - #[schema(minimum = 100, example = 6540)] - pub amount_capturable: Option, + #[schema(value_type = i64, minimum = 100, example = 6540)] + pub amount_capturable: Option, /// The amount which is already captured from the payment - #[schema(minimum = 100, example = 6540)] - pub amount_received: Option, + #[schema(value_type = i64, example = 6540)] + pub amount_received: Option, /// The connector used for the payment #[schema(example = "stripe")] @@ -3511,7 +3519,8 @@ pub struct IncrementalAuthorizationResponse { /// The unique identifier of authorization pub authorization_id: String, /// Amount the authorization has been made for - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, #[schema(value_type= AuthorizationStatus)] /// The status of the authorization pub status: common_enums::AuthorizationStatus, @@ -3520,7 +3529,7 @@ pub struct IncrementalAuthorizationResponse { /// Error message sent by the connector for authorization pub error_message: Option, /// Previously authorized amount for the payment - pub previously_authorized_amount: i64, + pub previously_authorized_amount: MinorUnit, } #[derive(Clone, Debug, serde::Serialize)] @@ -3756,7 +3765,7 @@ pub struct PgRedirectResponse { pub status: api_enums::IntentStatus, pub gateway_id: String, pub customer_id: Option, - pub amount: Option, + pub amount: Option, } #[derive(Debug, serde::Serialize, PartialEq, Eq, serde::Deserialize)] @@ -4374,7 +4383,7 @@ pub struct PaymentsIncrementalAuthorizationRequest { pub payment_id: String, /// The total amount including previously authorized amount and additional amount #[schema(value_type = i64, example = 6540)] - pub amount: i64, + pub amount: MinorUnit, /// Reason for incremental authorization pub reason: Option, } @@ -4577,6 +4586,7 @@ pub mod amount { use super::Amount; struct AmountVisitor; struct OptionalAmountVisitor; + use crate::payments::MinorUnit; // This is defined to provide guarded deserialization of amount // which itself handles zero and non-zero values internally @@ -4609,7 +4619,7 @@ pub mod amount { "invalid value `{v}`, expected a positive integer" ))); } - Ok(Amount::from(v)) + Ok(Amount::from(MinorUnit::new(v))) } } @@ -4681,7 +4691,8 @@ pub struct RetrievePaymentLinkResponse { pub payment_link_id: String, pub merchant_id: String, pub link_to_pay: String, - pub amount: i64, + #[schema(value_type = i64, example = 6540)] + pub amount: MinorUnit, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601::option")] diff --git a/crates/api_models/src/surcharge_decision_configs.rs b/crates/api_models/src/surcharge_decision_configs.rs index 0777bde85d..9c2d1ac26e 100644 --- a/crates/api_models/src/surcharge_decision_configs.rs +++ b/crates/api_models/src/surcharge_decision_configs.rs @@ -1,4 +1,8 @@ -use common_utils::{consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, events, types::Percentage}; +use common_utils::{ + consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, + events, + types::{MinorUnit, Percentage}, +}; use euclid::frontend::{ ast::Program, dir::{DirKeyKind, EuclidDirFilter}, @@ -15,7 +19,7 @@ pub struct SurchargeDetailsOutput { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case", tag = "type", content = "value")] pub enum SurchargeOutput { - Fixed { amount: i64 }, + Fixed { amount: MinorUnit }, Rate(Percentage), } diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index 6f08649fbd..e6fd27da31 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -40,6 +40,7 @@ time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"], optional = true } semver = { version = "1.0.22", features = ["serde"] } uuid = { version = "1.8.0", features = ["v7"] } +utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } # First party crates common_enums = { version = "0.1.0", path = "../common_enums" } diff --git a/crates/common_utils/src/errors.rs b/crates/common_utils/src/errors.rs index 7f34306570..967580f0ae 100644 --- a/crates/common_utils/src/errors.rs +++ b/crates/common_utils/src/errors.rs @@ -1,5 +1,7 @@ //! Errors and error specific types for universal use +use crate::types::MinorUnit; + /// Custom Result /// A custom datatype that wraps the error variant into a report, allowing /// error_stack::Report specific extendability @@ -89,7 +91,7 @@ pub enum PercentageError { /// percentage value percentage: f32, /// amount value - amount: i64, + amount: MinorUnit, }, } diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index b15a519caa..6574d1f1af 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -1,16 +1,24 @@ //! Types that can be used in other crates -use std::{fmt::Display, str::FromStr}; +use std::{ + fmt::Display, + ops::{Add, Sub}, + primitive::i64, + str::FromStr, +}; use diesel::{ backend::Backend, + deserialize, deserialize::FromSql, serialize::{Output, ToSql}, + sql_types, sql_types::Jsonb, - AsExpression, FromSqlRow, + AsExpression, FromSqlRow, Queryable, }; use error_stack::{report, ResultExt}; use semver::Version; use serde::{de::Visitor, Deserialize, Deserializer}; +use utoipa::ToSchema; use crate::{ consts, @@ -52,13 +60,17 @@ impl Percentage { /// apply the percentage to amount and ceil the result #[allow(clippy::as_conversions)] - pub fn apply_and_ceil_result(&self, amount: i64) -> CustomResult { + pub fn apply_and_ceil_result( + &self, + amount: MinorUnit, + ) -> CustomResult { let max_amount = i64::MAX / 10000; + let amount = amount.0; if amount > max_amount { // value gets rounded off after i64::MAX/10000 Err(report!(PercentageError::UnableToApplyPercentage { percentage: self.percentage, - amount, + amount: MinorUnit::new(amount), })) .attach_printable(format!( "Cannot calculate percentage for amount greater than {}", @@ -67,9 +79,10 @@ impl Percentage { } else { let percentage_f64 = f64::from(self.percentage); let result = (amount as f64 * (percentage_f64 / 100.0)).ceil() as i64; - Ok(result) + Ok(MinorUnit::new(result)) } } + fn is_valid_string_value(value: &str) -> CustomResult { let float_value = Self::is_valid_float_string(value)?; Ok(Self::is_valid_range(float_value) && Self::is_valid_precision_length(value)) @@ -151,7 +164,7 @@ impl<'de, const PRECISION: u8> Deserialize<'de> for Percentage { #[serde(rename_all = "snake_case", tag = "type", content = "value")] pub enum Surcharge { /// Fixed Surcharge value - Fixed(i64), + Fixed(MinorUnit), /// Surcharge percentage Rate(Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>), } @@ -193,7 +206,7 @@ impl FromSql for SemanticVersion where serde_json::Value: FromSql, { - fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result { + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { let value = >::from_sql(bytes)?; Ok(serde_json::from_value(value)?) } @@ -212,3 +225,87 @@ where >::to_sql(&value, &mut out.reborrow()) } } + +/// This Unit struct represents MinorUnit in which core amount works +#[derive( + Default, + Debug, + serde::Deserialize, + AsExpression, + serde::Serialize, + Clone, + Copy, + PartialEq, + Eq, + Hash, + ToSchema, + PartialOrd, +)] +#[diesel(sql_type = sql_types::BigInt)] +pub struct MinorUnit(i64); + +impl MinorUnit { + /// gets amount as i64 value + pub fn get_amount_as_i64(&self) -> i64 { + // will be removed in future + self.0 + } + + /// forms a new minor unit from amount + pub fn new(value: i64) -> Self { + Self(value) + } +} + +impl Display for MinorUnit { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl FromSql for MinorUnit +where + DB: Backend, + i64: FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> deserialize::Result { + let val = i64::from_sql(value)?; + Ok(Self(val)) + } +} + +impl ToSql for MinorUnit +where + DB: Backend, + i64: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl Queryable for MinorUnit +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} + +impl Add for MinorUnit { + type Output = Self; + fn add(self, a2: Self) -> Self { + Self(self.0 + a2.0) + } +} + +impl Sub for MinorUnit { + type Output = Self; + fn sub(self, a2: Self) -> Self { + Self(self.0 - a2.0) + } +} diff --git a/crates/diesel_models/src/authorization.rs b/crates/diesel_models/src/authorization.rs index b6f75bbb9b..a7f13821d8 100644 --- a/crates/diesel_models/src/authorization.rs +++ b/crates/diesel_models/src/authorization.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -11,7 +12,7 @@ pub struct Authorization { pub authorization_id: String, pub merchant_id: String, pub payment_id: String, - pub amount: i64, + pub amount: MinorUnit, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601")] @@ -20,7 +21,7 @@ pub struct Authorization { pub error_code: Option, pub error_message: Option, pub connector_authorization_id: Option, - pub previously_authorized_amount: i64, + pub previously_authorized_amount: MinorUnit, } #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)] @@ -29,12 +30,12 @@ pub struct AuthorizationNew { pub authorization_id: String, pub merchant_id: String, pub payment_id: String, - pub amount: i64, + pub amount: MinorUnit, pub status: storage_enums::AuthorizationStatus, pub error_code: Option, pub error_message: Option, pub connector_authorization_id: Option, - pub previously_authorized_amount: i64, + pub previously_authorized_amount: MinorUnit, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/diesel_models/src/capture.rs b/crates/diesel_models/src/capture.rs index adc313ca3d..0a66ae789a 100644 --- a/crates/diesel_models/src/capture.rs +++ b/crates/diesel_models/src/capture.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -12,13 +13,13 @@ pub struct Capture { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::CaptureStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub connector: String, pub error_message: Option, pub error_code: Option, pub error_reason: Option, - pub tax_amount: Option, + pub tax_amount: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601")] @@ -37,13 +38,13 @@ pub struct CaptureNew { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::CaptureStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub connector: String, pub error_message: Option, pub error_code: Option, pub error_reason: Option, - pub tax_amount: Option, + pub tax_amount: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, #[serde(with = "common_utils::custom_serde::iso8601")] diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 660db4d8b8..5b5eb0f79e 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -1,5 +1,5 @@ use common_enums::RequestIncrementalAuthorization; -use common_utils::pii; +use common_utils::{pii, types::MinorUnit}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -13,9 +13,9 @@ pub struct PaymentIntent { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub description: Option, pub return_url: Option, @@ -69,9 +69,9 @@ pub struct PaymentIntentNew { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub description: Option, pub return_url: Option, @@ -119,7 +119,7 @@ pub struct PaymentIntentNew { pub enum PaymentIntentUpdate { ResponseUpdate { status: storage_enums::IntentStatus, - amount_captured: Option, + amount_captured: Option, fingerprint_id: Option, return_url: Option, updated_by: String, @@ -149,7 +149,7 @@ pub enum PaymentIntentUpdate { incremental_authorization_allowed: Option, }, Update { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, setup_future_usage: Option, status: storage_enums::IntentStatus, @@ -197,7 +197,7 @@ pub enum PaymentIntentUpdate { updated_by: String, }, IncrementalAuthorizationAmountUpdate { - amount: i64, + amount: MinorUnit, }, AuthorizationCountUpdate { authorization_count: i32, @@ -210,10 +210,10 @@ pub enum PaymentIntentUpdate { #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = payment_intent)] pub struct PaymentIntentUpdateInternal { - pub amount: Option, + pub amount: Option, pub currency: Option, pub status: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, diff --git a/crates/diesel_models/src/payment_link.rs b/crates/diesel_models/src/payment_link.rs index ed0e979d02..f4630c75d7 100644 --- a/crates/diesel_models/src/payment_link.rs +++ b/crates/diesel_models/src/payment_link.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel::{Identifiable, Insertable, Queryable}; use serde::{self, Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -12,7 +13,7 @@ pub struct PaymentLink { pub payment_id: String, pub link_to_pay: String, pub merchant_id: String, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, #[serde(with = "common_utils::custom_serde::iso8601")] pub created_at: PrimitiveDateTime, @@ -42,7 +43,7 @@ pub struct PaymentLinkNew { pub payment_id: String, pub link_to_pay: String, pub merchant_id: String, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, #[serde(with = "common_utils::custom_serde::iso8601::option")] pub created_at: Option, diff --git a/crates/hyperswitch_domain_models/src/mandates.rs b/crates/hyperswitch_domain_models/src/mandates.rs index 180f733cfe..d9080d6dc9 100644 --- a/crates/hyperswitch_domain_models/src/mandates.rs +++ b/crates/hyperswitch_domain_models/src/mandates.rs @@ -4,7 +4,7 @@ use api_models::payments::{ OnlineMandate as ApiOnlineMandate, }; use common_enums::Currency; -use common_utils::{date_time, errors::ParsingError, pii}; +use common_utils::{date_time, errors::ParsingError, pii, types::MinorUnit}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use time::PrimitiveDateTime; @@ -24,7 +24,7 @@ pub enum MandateDataType { #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct MandateAmountData { - pub amount: i64, + pub amount: MinorUnit, pub currency: Currency, pub start_date: Option, pub end_date: Option, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index cea948d4f9..0491081e87 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -1,4 +1,4 @@ -use common_utils::pii; +use common_utils::{self, pii, types::MinorUnit}; use time::PrimitiveDateTime; pub mod payment_attempt; @@ -15,9 +15,9 @@ pub struct PaymentIntent { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub description: Option, pub return_url: Option, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index fe6f3711e3..ff4c7ac017 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,5 +1,6 @@ use api_models::enums::Connector; use common_enums as storage_enums; +use common_utils::types::MinorUnit; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -111,15 +112,15 @@ pub struct PaymentAttempt { pub merchant_id: String, pub attempt_id: String, pub status: storage_enums::AttemptStatus, - pub amount: i64, - pub net_amount: i64, + pub amount: MinorUnit, + pub net_amount: MinorUnit, pub currency: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub connector_transaction_id: Option, @@ -135,7 +136,7 @@ pub struct PaymentAttempt { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub error_code: Option, @@ -153,7 +154,7 @@ pub struct PaymentAttempt { pub multiple_capture_count: Option, // reference to the payment at connector side pub connector_response_reference_id: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub authentication_data: Option, pub encoded_data: Option, @@ -171,12 +172,15 @@ pub struct PaymentAttempt { } impl PaymentAttempt { - pub fn get_total_amount(&self) -> i64 { - self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0) + pub fn get_total_amount(&self) -> MinorUnit { + self.amount + + self.surcharge_amount.unwrap_or_default() + + self.tax_amount.unwrap_or_default() } - pub fn get_total_surcharge_amount(&self) -> Option { + + pub fn get_total_surcharge_amount(&self) -> Option { self.surcharge_amount - .map(|surcharge_amount| surcharge_amount + self.tax_amount.unwrap_or(0)) + .map(|surcharge_amount| surcharge_amount + self.tax_amount.unwrap_or_default()) } } @@ -196,18 +200,18 @@ pub struct PaymentAttemptNew { pub merchant_id: String, pub attempt_id: String, pub status: storage_enums::AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, /// amount + surcharge_amount + tax_amount /// This field will always be derived before updating in the Database - pub net_amount: i64, + pub net_amount: MinorUnit, pub currency: Option, // pub auto_capture: Option, pub save_to_locker: Option, pub connector: Option, pub error_message: Option, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option, pub payment_method: Option, pub capture_method: Option, @@ -222,7 +226,7 @@ pub struct PaymentAttemptNew { #[serde(default, with = "common_utils::custom_serde::iso8601::option")] pub last_synced: Option, pub cancellation_reason: Option, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option, pub browser_info: Option, pub payment_token: Option, @@ -238,7 +242,7 @@ pub struct PaymentAttemptNew { pub error_reason: Option, pub connector_response_reference_id: Option, pub multiple_capture_count: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub updated_by: String, pub authentication_data: Option, pub encoded_data: Option, @@ -257,8 +261,10 @@ pub struct PaymentAttemptNew { impl PaymentAttemptNew { /// returns amount + surcharge_amount + tax_amount - pub fn calculate_net_amount(&self) -> i64 { - self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0) + pub fn calculate_net_amount(&self) -> MinorUnit { + self.amount + + self.surcharge_amount.unwrap_or_default() + + self.tax_amount.unwrap_or_default() } pub fn populate_derived_fields(self) -> Self { @@ -271,7 +277,7 @@ impl PaymentAttemptNew { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PaymentAttemptUpdate { Update { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, status: storage_enums::AttemptStatus, authentication_type: Option, @@ -281,10 +287,10 @@ pub enum PaymentAttemptUpdate { payment_method_type: Option, payment_experience: Option, business_sub_label: Option, - amount_to_capture: Option, + amount_to_capture: Option, capture_method: Option, - surcharge_amount: Option, - tax_amount: Option, + surcharge_amount: Option, + tax_amount: Option, fingerprint_id: Option, payment_method_billing_address_id: Option, updated_by: String, @@ -293,9 +299,9 @@ pub enum PaymentAttemptUpdate { payment_token: Option, connector: Option, straight_through_algorithm: Option, - amount_capturable: Option, - surcharge_amount: Option, - tax_amount: Option, + amount_capturable: Option, + surcharge_amount: Option, + tax_amount: Option, updated_by: String, merchant_connector_id: Option, }, @@ -304,7 +310,7 @@ pub enum PaymentAttemptUpdate { updated_by: String, }, ConfirmUpdate { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, status: storage_enums::AttemptStatus, authentication_type: Option, @@ -320,10 +326,10 @@ pub enum PaymentAttemptUpdate { straight_through_algorithm: Option, error_code: Option>, error_message: Option>, - amount_capturable: Option, + amount_capturable: Option, updated_by: String, - surcharge_amount: Option, - tax_amount: Option, + surcharge_amount: Option, + tax_amount: Option, merchant_connector_id: Option, external_three_ds_authentication_attempted: Option, authentication_connector: Option, @@ -368,7 +374,7 @@ pub enum PaymentAttemptUpdate { error_message: Option>, error_reason: Option>, connector_response_reference_id: Option, - amount_capturable: Option, + amount_capturable: Option, updated_by: String, authentication_data: Option, encoded_data: Option, @@ -397,7 +403,7 @@ pub enum PaymentAttemptUpdate { error_code: Option>, error_message: Option>, error_reason: Option>, - amount_capturable: Option, + amount_capturable: Option, updated_by: String, unified_code: Option>, unified_message: Option>, @@ -405,13 +411,13 @@ pub enum PaymentAttemptUpdate { payment_method_data: Option, }, CaptureUpdate { - amount_to_capture: Option, + amount_to_capture: Option, multiple_capture_count: Option, updated_by: String, }, AmountToCaptureUpdate { status: storage_enums::AttemptStatus, - amount_capturable: i64, + amount_capturable: MinorUnit, updated_by: String, }, PreprocessingUpdate { @@ -431,8 +437,8 @@ pub enum PaymentAttemptUpdate { updated_by: String, }, IncrementalAuthorizationAmountUpdate { - amount: i64, - amount_capturable: i64, + amount: MinorUnit, + amount_capturable: MinorUnit, }, AuthenticationUpdate { status: storage_enums::AttemptStatus, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 1322e66a14..a3353e6dfa 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -2,6 +2,7 @@ use common_enums as storage_enums; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, pii, + types::MinorUnit, }; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -74,9 +75,9 @@ pub struct PaymentIntentNew { pub payment_id: String, pub merchant_id: String, pub status: storage_enums::IntentStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub description: Option, pub return_url: Option, @@ -120,7 +121,7 @@ pub struct PaymentIntentNew { pub enum PaymentIntentUpdate { ResponseUpdate { status: storage_enums::IntentStatus, - amount_captured: Option, + amount_captured: Option, return_url: Option, updated_by: String, fingerprint_id: Option, @@ -150,7 +151,7 @@ pub enum PaymentIntentUpdate { updated_by: String, }, Update { - amount: i64, + amount: MinorUnit, currency: storage_enums::Currency, setup_future_usage: Option, status: storage_enums::IntentStatus, @@ -198,7 +199,7 @@ pub enum PaymentIntentUpdate { updated_by: String, }, IncrementalAuthorizationAmountUpdate { - amount: i64, + amount: MinorUnit, }, AuthorizationCountUpdate { authorization_count: i32, @@ -210,10 +211,10 @@ pub enum PaymentIntentUpdate { #[derive(Clone, Debug, Default)] pub struct PaymentIntentUpdateInternal { - pub amount: Option, + pub amount: Option, pub currency: Option, pub status: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, diff --git a/crates/openapi/Cargo.toml b/crates/openapi/Cargo.toml index 8cf8c156ba..b8c55084a5 100644 --- a/crates/openapi/Cargo.toml +++ b/crates/openapi/Cargo.toml @@ -12,3 +12,4 @@ serde_json = "1.0.115" utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] } api_models = { version = "0.1.0", path = "../api_models", features = ["frm", "payouts", "openapi"] } +common_utils = {version = "0.1.0", path = "../common_utils"} diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 018ea34209..b040cb0089 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -184,6 +184,7 @@ Never share your secret api keys. Keep them guarded and secure. routes::poll::retrieve_poll_status, ), components(schemas( + common_utils::types::MinorUnit, api_models::refunds::RefundRequest, api_models::refunds::RefundType, api_models::refunds::RefundResponse, diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index 04bb99ef75..d615acff1c 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -6,6 +6,7 @@ use common_utils::{ date_time, ext_traits::StringExt, pii::{IpAddress, SecretSerdeValue, UpiVpaMaskingStrategy}, + types::MinorUnit, }; use error_stack::ResultExt; use serde::{Deserialize, Serialize}; @@ -312,9 +313,11 @@ impl TryFrom for payments::PaymentsRequest { expected_format: "127.0.0.1".to_string(), })?; + let amount = item.amount.map(|amount| MinorUnit::new(amount).into()); + let request = Ok(Self { payment_id: item.id.map(payments::PaymentIdType::PaymentIntentId), - amount: item.amount.map(|amount| amount.into()), + amount, currency: item .currency .as_ref() @@ -324,7 +327,7 @@ impl TryFrom for payments::PaymentsRequest { field_name: "currency", })?, capture_method: item.capture_method, - amount_to_capture: item.amount_capturable, + amount_to_capture: item.amount_capturable.map(MinorUnit::new), confirm: item.confirm, customer_id: item.customer, email: item.receipt_email, @@ -508,9 +511,9 @@ impl From for StripePaymentIntentResponse { object: "payment_intent", id: resp.payment_id, status: StripePaymentStatus::from(resp.status), - amount: resp.amount, - amount_capturable: resp.amount_capturable, - amount_received: resp.amount_received, + amount: resp.amount.get_amount_as_i64(), + amount_capturable: resp.amount_capturable.map(|amt| amt.get_amount_as_i64()), + amount_received: resp.amount_received.map(|amt| amt.get_amount_as_i64()), connector: resp.connector, client_secret: resp.client_secret, created: resp.created.map(|t| t.assume_utc().unix_timestamp()), @@ -729,7 +732,7 @@ impl ForeignTryFrom<(Option, Option)> for Option match item { StripeMandateType::SingleUse => Some(payments::MandateType::SingleUse( payments::MandateAmountData { - amount: mandate.amount.unwrap_or_default(), + amount: MinorUnit::new(mandate.amount.unwrap_or_default()), currency, start_date: mandate.start_date, end_date: mandate.end_date, @@ -738,7 +741,7 @@ impl ForeignTryFrom<(Option, Option)> for Option Some(payments::MandateType::MultiUse(Some( payments::MandateAmountData { - amount: mandate.amount.unwrap_or_default(), + amount: MinorUnit::new(mandate.amount.unwrap_or_default()), currency, start_date: mandate.start_date, end_date: mandate.end_date, @@ -748,7 +751,7 @@ impl ForeignTryFrom<(Option, Option)> for Option Some(payments::MandateType::MultiUse(Some( payments::MandateAmountData { - amount: mandate.amount.unwrap_or_default(), + amount: MinorUnit::new(mandate.amount.unwrap_or_default()), currency, start_date: mandate.start_date, end_date: mandate.end_date, diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index 8fd148864f..de20f37cf3 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -378,7 +378,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for NoonPaymentsRequest { )) | Some(hyperswitch_domain_models::mandates::MandateDataType::MultiUse(Some( mandate, - ))) => conn_utils::to_currency_base_unit(mandate.amount, mandate.currency), + ))) => conn_utils::to_currency_base_unit( + mandate.amount.get_amount_as_i64(), + mandate.currency, + ), Some(hyperswitch_domain_models::mandates::MandateDataType::MultiUse(None)) => { Err(errors::ConnectorError::MissingRequiredField { field_name: diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index a1186dc7bd..8ff7d350f2 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -12,6 +12,7 @@ use common_utils::{ errors::ReportSwitchExt, ext_traits::StringExt, pii::{self, Email, IpAddress}, + types::MinorUnit, }; use diesel_models::enums; use error_stack::{report, ResultExt}; @@ -136,7 +137,7 @@ where { match self.status { enums::AttemptStatus::Voided => { - if payment_data.payment_intent.amount_captured > Some(0) { + if payment_data.payment_intent.amount_captured > Some(MinorUnit::new(0)) { enums::AttemptStatus::PartialCharged } else { self.status @@ -146,7 +147,7 @@ where let captured_amount = types::Capturable::get_captured_amount(&self.request, payment_data); let total_capturable_amount = payment_data.payment_attempt.get_total_amount(); - if Some(total_capturable_amount) == captured_amount { + if Some(total_capturable_amount) == captured_amount.map(MinorUnit::new) { enums::AttemptStatus::Charged } else if captured_amount.is_some() { enums::AttemptStatus::PartialCharged @@ -692,23 +693,27 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { fn get_original_amount(&self) -> i64 { self.surcharge_details .as_ref() - .map(|surcharge_details| surcharge_details.original_amount) + .map(|surcharge_details| surcharge_details.original_amount.get_amount_as_i64()) .unwrap_or(self.amount) } fn get_surcharge_amount(&self) -> Option { self.surcharge_details .as_ref() - .map(|surcharge_details| surcharge_details.surcharge_amount) + .map(|surcharge_details| surcharge_details.surcharge_amount.get_amount_as_i64()) } fn get_tax_on_surcharge_amount(&self) -> Option { - self.surcharge_details - .as_ref() - .map(|surcharge_details| surcharge_details.tax_on_surcharge_amount) + self.surcharge_details.as_ref().map(|surcharge_details| { + surcharge_details + .tax_on_surcharge_amount + .get_amount_as_i64() + }) } fn get_total_surcharge_amount(&self) -> Option { - self.surcharge_details - .as_ref() - .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) + self.surcharge_details.as_ref().map(|surcharge_details| { + surcharge_details + .get_total_surcharge_amount() + .get_amount_as_i64() + }) } fn is_customer_initiated_mandate_payment(&self) -> bool { @@ -1980,7 +1985,9 @@ where status: capture_sync_response.get_capture_attempt_status(), connector_response_reference_id: capture_sync_response .get_connector_reference_id(), - amount: capture_sync_response.get_amount_captured(), + amount: capture_sync_response + .get_amount_captured() + .map(MinorUnit::new), }, ); } diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index e2f605304a..1b050394b3 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -28,7 +28,7 @@ pub async fn perform_authentication( browser_details: Option, business_profile: storage::BusinessProfile, merchant_connector_account: payments_core::helpers::MerchantConnectorAccountType, - amount: Option, + amount: Option, currency: Option, message_category: api::authentication::MessageCategory, device_channel: payments::DeviceChannel, diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 9e00dfe70c..3c48b20ffa 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -32,7 +32,7 @@ pub fn construct_authentication_router_data( billing_address: payments::Address, shipping_address: Option, browser_details: Option, - amount: Option, + amount: Option, currency: Option, message_category: types::api::authentication::MessageCategory, device_channel: payments::DeviceChannel, @@ -61,7 +61,7 @@ pub fn construct_authentication_router_data( billing_address, shipping_address, browser_details, - amount, + amount: amount.map(|amt| amt.get_amount_as_i64()), currency, message_category, device_channel, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index b5427d489c..63bf80bac0 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -632,7 +632,7 @@ pub fn is_operation_allowed(operation: &Op) -> bool { impl From for PaymentDetails { fn from(payment_data: PaymentToFrmData) -> Self { Self { - amount: payment_data.amount.into(), + amount: common_utils::types::MinorUnit::from(payment_data.amount).get_amount_as_i64(), currency: payment_data.payment_attempt.currency, payment_method: payment_data.payment_attempt.payment_method, payment_method_type: payment_data.payment_attempt.payment_method_type, diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index d0e5376b2c..c754682f72 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -69,7 +69,7 @@ impl ConstructFlowSpecificData( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), payment_method_status: None, request: FraudCheckFulfillmentData { - amount: payment_attempt.amount, + amount: payment_attempt.amount.get_amount_as_i64(), order_details: payment_intent.order_details.clone(), fulfillment_req: fulfillment_request, }, diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index ad74f7f702..b854a33377 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -67,7 +67,7 @@ impl ConstructFlowSpecificData bool { - let amount = payment_intent.amount; + let amount = payment_intent.amount.get_amount_as_i64(); (pm.maximum_amount.map_or(true, |amt| amount <= amt.into()) && pm.minimum_amount.map_or(true, |amt| amount >= amt.into())) - || payment_intent.amount == 0 + || payment_intent.amount.get_amount_as_i64() == 0 } async fn filter_payment_mandate_based( diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs index e89fb22e87..88ae82cad3 100644 --- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs +++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs @@ -367,7 +367,7 @@ fn get_surcharge_details_from_surcharge_output( .attach_printable("Failed to Calculate tax amount") }) .transpose()? - .unwrap_or(0); + .unwrap_or_default(); Ok(types::SurchargeDetails { original_amount: payment_attempt.amount, surcharge: match surcharge_details.surcharge { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 0bff5ff72e..ff48d181a7 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -27,7 +27,7 @@ use api_models::{ use common_utils::{ ext_traits::{AsyncExt, StringExt}, pii, - types::Surcharge, + types::{MinorUnit, Surcharge}, }; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; use error_stack::{report, ResultExt}; @@ -694,7 +694,7 @@ where O: Send + Clone + Sync, { if let Some(surcharge_amount) = payment_data.payment_attempt.surcharge_amount { - let tax_on_surcharge_amount = payment_data.payment_attempt.tax_amount.unwrap_or(0); + let tax_on_surcharge_amount = payment_data.payment_attempt.tax_amount.unwrap_or_default(); let final_amount = payment_data.payment_attempt.amount + surcharge_amount + tax_on_surcharge_amount; Ok(Some(api::SessionSurchargeDetails::PreDetermined( @@ -2482,8 +2482,8 @@ impl EventInfo for PaymentEvent { #[derive(Debug, Default, Clone)] pub struct IncrementalAuthorizationDetails { - pub additional_amount: i64, - pub total_amount: i64, + pub additional_amount: MinorUnit, + pub total_amount: MinorUnit, pub reason: Option, pub authorization_id: Option, } @@ -3807,7 +3807,7 @@ pub async fn payment_external_authentication( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("'profile_id' not set in payment intent")?; let currency = payment_attempt.currency.get_required_value("currency")?; - let amount = payment_attempt.get_total_amount().into(); + let amount = payment_attempt.get_total_amount(); let shipping_address = helpers::create_or_find_address_for_payment_by_request( db, None, @@ -3914,7 +3914,7 @@ pub async fn payment_external_authentication( browser_info, business_profile, merchant_connector_account, - amount, + Some(amount), Some(currency), authentication::MessageCategory::Payment, req.device_channel, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 2c442c5ce4..eb3dca1106 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -8,6 +8,7 @@ use base64::Engine; use common_utils::{ ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, fp_utils, generate_id, pii, + types::MinorUnit, }; use diesel_models::enums; // TODO : Evaluate all the helper functions () @@ -599,7 +600,9 @@ pub async fn get_token_for_recurring_mandate( .await .flatten(); - let original_payment_authorized_amount = original_payment_intent.clone().map(|pi| pi.amount); + let original_payment_authorized_amount = original_payment_intent + .clone() + .map(|pi| pi.amount.get_amount_as_i64()); let original_payment_authorized_currency = original_payment_intent.clone().and_then(|pi| pi.currency); @@ -716,7 +719,7 @@ pub fn validate_merchant_id( #[instrument(skip_all)] pub fn validate_request_amount_and_amount_to_capture( op_amount: Option, - op_amount_to_capture: Option, + op_amount_to_capture: Option, surcharge_details: Option, ) -> CustomResult<(), errors::ApiErrorResponse> { match (op_amount, op_amount_to_capture) { @@ -727,10 +730,10 @@ pub fn validate_request_amount_and_amount_to_capture( api::Amount::Value(amount_inner) => { // If both amount and amount to capture is present // then amount to be capture should be less than or equal to request amount - let total_capturable_amount = amount_inner.get() + let total_capturable_amount = MinorUnit::new(amount_inner.get()) + surcharge_details .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) - .unwrap_or(0); + .unwrap_or_default(); utils::when(!amount_to_capture.le(&total_capturable_amount), || { Err(report!(errors::ApiErrorResponse::PreconditionFailed { message: format!( @@ -765,23 +768,25 @@ pub fn validate_amount_to_capture_and_capture_method( if capture_method == api_enums::CaptureMethod::Automatic { let original_amount = request .amount - .map(|amount| amount.into()) + .map(MinorUnit::from) .or(payment_attempt.map(|payment_attempt| payment_attempt.amount)); let surcharge_amount = request .surcharge_details .map(|surcharge_details| surcharge_details.get_total_surcharge_amount()) .or_else(|| { payment_attempt.map(|payment_attempt| { - payment_attempt.surcharge_amount.unwrap_or(0) - + payment_attempt.tax_amount.unwrap_or(0) + payment_attempt.surcharge_amount.unwrap_or_default() + + payment_attempt.tax_amount.unwrap_or_default() }) }) - .unwrap_or(0); + .unwrap_or_default(); let total_capturable_amount = original_amount.map(|original_amount| original_amount + surcharge_amount); + let amount_to_capture = request .amount_to_capture .or(payment_attempt.and_then(|pa| pa.amount_to_capture)); + if let Some((total_capturable_amount, amount_to_capture)) = total_capturable_amount.zip(amount_to_capture) { @@ -1097,7 +1102,7 @@ fn validate_recurring_mandate(req: api::MandateValidationFields) -> RouterResult } pub fn verify_mandate_details( - request_amount: i64, + request_amount: MinorUnit, request_currency: api_enums::Currency, mandate: storage::Mandate, ) -> RouterResult<()> { @@ -1105,7 +1110,7 @@ pub fn verify_mandate_details( storage_enums::MandateType::SingleUse => utils::when( mandate .mandate_amount - .map(|mandate_amount| request_amount > mandate_amount) + .map(|mandate_amount| request_amount.get_amount_as_i64() > mandate_amount) .unwrap_or(true), || { Err(report!(errors::ApiErrorResponse::MandateValidationFailed { @@ -1117,7 +1122,8 @@ pub fn verify_mandate_details( mandate .mandate_amount .map(|mandate_amount| { - (mandate.amount_captured.unwrap_or(0) + request_amount) > mandate_amount + (mandate.amount_captured.unwrap_or(0) + request_amount.get_amount_as_i64()) + > mandate_amount }) .unwrap_or(false), || { @@ -2699,7 +2705,7 @@ pub fn generate_mandate( match data.mandate_type.get_required_value("mandate_type")? { hyperswitch_domain_models::mandates::MandateDataType::SingleUse(data) => { new_mandate - .set_mandate_amount(Some(data.amount)) + .set_mandate_amount(Some(data.amount.get_amount_as_i64())) .set_mandate_currency(Some(data.currency)) .set_mandate_type(storage_enums::MandateType::SingleUse) .to_owned() @@ -2708,7 +2714,7 @@ pub fn generate_mandate( hyperswitch_domain_models::mandates::MandateDataType::MultiUse(op_data) => { match op_data { Some(data) => new_mandate - .set_mandate_amount(Some(data.amount)) + .set_mandate_amount(Some(data.amount.get_amount_as_i64())) .set_mandate_currency(Some(data.currency)) .set_start_date(data.start_date) .set_end_date(data.end_date), @@ -2921,7 +2927,7 @@ mod tests { payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, - amount: 200, + amount: MinorUnit::new(200), currency: None, amount_captured: None, customer_id: None, @@ -2980,7 +2986,7 @@ mod tests { payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, - amount: 200, + amount: MinorUnit::new(200), currency: None, amount_captured: None, customer_id: None, @@ -3038,7 +3044,7 @@ mod tests { payment_id: "23".to_string(), merchant_id: "22".to_string(), status: storage_enums::IntentStatus::RequiresCapture, - amount: 200, + amount: MinorUnit::new(200), currency: None, amount_captured: None, customer_id: None, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index bf416b141b..ec86d1d172 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -77,8 +77,10 @@ impl GetTracker, api::PaymentsCaptu helpers::validate_status_with_capture_method(payment_intent.status, capture_method)?; helpers::validate_amount_to_capture( - payment_attempt.amount_capturable, - request.amount_to_capture, + payment_attempt.amount_capturable.get_amount_as_i64(), + request + .amount_to_capture + .map(|capture_amount| capture_amount.get_amount_as_i64()), )?; helpers::validate_capture_method(capture_method)?; @@ -89,8 +91,8 @@ impl GetTracker, api::PaymentsCaptu .get_required_value("amount_to_capture")?; helpers::validate_amount_to_capture( - payment_attempt.amount_capturable, - Some(amount_to_capture), + payment_attempt.amount_capturable.get_amount_as_i64(), + Some(amount_to_capture.get_amount_as_i64()), )?; let previous_captures = db diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 6cbdb86a75..8dbef81b90 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -81,7 +81,7 @@ impl GetTracker, api::PaymentsRequest> for Pa if let Some(order_details) = &request.order_details { helpers::validate_order_details_amount( order_details.to_owned(), - payment_intent.amount, + payment_intent.amount.get_amount_as_i64(), false, )?; } diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index b12b5ec460..5501dfe7ca 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -4,7 +4,10 @@ use api_models::{ enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData, }; use async_trait::async_trait; -use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, Encode, ValueExt}, + types::MinorUnit, +}; use diesel_models::{ephemeral_key, PaymentMethod}; use error_stack::{self, ResultExt}; use hyperswitch_domain_models::{ @@ -287,7 +290,7 @@ impl GetTracker, api::PaymentsRequest> for Pa if let Some(order_details) = &request.order_details { helpers::validate_order_details_amount( order_details.to_owned(), - payment_intent.amount, + payment_intent.amount.get_amount_as_i64(), false, )?; } @@ -878,7 +881,7 @@ impl PaymentCreate { attempt_id, status, currency, - amount: amount.into(), + amount: MinorUnit::from(amount), payment_method, capture_method: request.capture_method, capture_on: request.capture_on, @@ -904,7 +907,7 @@ impl PaymentCreate { external_three_ds_authentication_attempted: None, mandate_data, payment_method_billing_address_id, - net_amount: i64::default(), + net_amount: MinorUnit::new(i64::default()), save_to_locker: None, connector: None, error_message: None, @@ -920,7 +923,7 @@ impl PaymentCreate { error_reason: None, connector_response_reference_id: None, multiple_capture_count: None, - amount_capturable: i64::default(), + amount_capturable: MinorUnit::new(i64::default()), updated_by: String::default(), authentication_data: None, encoded_data: None, @@ -998,7 +1001,7 @@ impl PaymentCreate { payment_id: payment_id.to_string(), merchant_id: merchant_account.merchant_id.to_string(), status, - amount: amount.into(), + amount: MinorUnit::from(amount), currency, description: request.description.clone(), created_at, @@ -1111,7 +1114,7 @@ async fn create_payment_link( payment_id: payment_id.clone(), merchant_id: merchant_id.clone(), link_to_pay: payment_link.clone(), - amount: amount.into(), + amount: MinorUnit::from(amount), currency: request.currency, created_at, last_modified_at, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 9867d59a69..6723a2ee5a 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use async_trait::async_trait; use common_enums::AuthorizationStatus; -use common_utils::ext_traits::Encode; +use common_utils::{ext_traits::Encode, types::MinorUnit}; use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt; @@ -266,16 +266,19 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu .. }) => { if status == AuthorizationStatus::Success { - (Some( - storage::PaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { - amount: incremental_authorization_details.total_amount, - amount_capturable: incremental_authorization_details.total_amount, - }, - ), Some( - storage::PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { - amount: incremental_authorization_details.total_amount, - }, - )) + ( + Some( + storage::PaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { + amount: incremental_authorization_details.total_amount, + amount_capturable: incremental_authorization_details.total_amount, + }, + ), + Some( + storage::PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { + amount: incremental_authorization_details.total_amount, + }, + ), + ) } else { (None, None) } @@ -793,7 +796,8 @@ async fn payment_response_update_tracker( error_reason: Some(err.reason), amount_capturable: router_data .request - .get_amount_capturable(&payment_data, status), + .get_amount_capturable(&payment_data, status) + .map(MinorUnit::new), updated_by: storage_scheme.to_string(), unified_code: option_gsm.clone().map(|gsm| gsm.unified_code), unified_message: option_gsm.map(|gsm| gsm.unified_message), @@ -915,7 +919,6 @@ async fn payment_response_update_tracker( payment_data.payment_attempt.connector.clone(), payment_data.payment_attempt.merchant_id.clone(), ); - let (capture_updates, payment_attempt_update) = match payment_data .multiple_capture_data { @@ -940,7 +943,8 @@ async fn payment_response_update_tracker( authentication_type: None, amount_capturable: router_data .request - .get_amount_capturable(&payment_data, updated_attempt_status), + .get_amount_capturable(&payment_data, updated_attempt_status) + .map(MinorUnit::new), payment_method_id, mandate_id: payment_data.payment_attempt.mandate_id.clone(), connector_metadata, @@ -1098,7 +1102,7 @@ async fn payment_response_update_tracker( let amount_captured = get_total_amount_captured( &router_data.request, - router_data.amount_captured, + router_data.amount_captured.map(MinorUnit::new), router_data.status, &payment_data, ); @@ -1167,7 +1171,7 @@ async fn payment_response_update_tracker( tokenization::update_connector_mandate_details_in_payment_method( payment_method.clone(), payment_method.payment_method_type, - Some(payment_data.payment_attempt.amount), + Some(payment_data.payment_attempt.amount.get_amount_as_i64()), payment_data.payment_attempt.currency, payment_data.payment_attempt.merchant_connector_id.clone(), connector_mandate_id, @@ -1353,10 +1357,10 @@ fn get_capture_update_for_unmapped_capture_responses( fn get_total_amount_captured( request: &T, - amount_captured: Option, + amount_captured: Option, router_data_status: enums::AttemptStatus, payment_data: &PaymentData, -) -> Option { +) -> Option { match &payment_data.multiple_capture_data { Some(multiple_capture_data) => { //multiple capture @@ -1364,7 +1368,9 @@ fn get_total_amount_captured( } None => { //Non multiple capture - let amount = request.get_captured_amount(payment_data); + let amount = request + .get_captured_amount(payment_data) + .map(MinorUnit::new); amount_captured.or_else(|| { if router_data_status == enums::AttemptStatus::Charged { amount diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index e601b3ece7..d7351b3b5b 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -63,7 +63,7 @@ impl GetTracker, api::PaymentsRequest> for Pa if let Some(order_details) = &request.order_details { helpers::validate_order_details_amount( order_details.to_owned(), - payment_intent.amount, + payment_intent.amount.get_amount_as_i64(), false, )?; } @@ -337,9 +337,9 @@ impl GetTracker, api::PaymentsRequest> for Pa .as_ref() .map(RequestSurchargeDetails::get_total_surcharge_amount) .or(payment_attempt.get_total_surcharge_amount()); - (amount + surcharge_amount.unwrap_or(0)).into() + amount + surcharge_amount.unwrap_or_default() }; - (Box::new(operations::PaymentConfirm), amount) + (Box::new(operations::PaymentConfirm), amount.into()) } else { (Box::new(self), amount) }; @@ -692,7 +692,6 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let metadata = payment_data.payment_intent.metadata.clone(); let frm_metadata = payment_data.payment_intent.frm_metadata.clone(); let session_expiry = payment_data.payment_intent.session_expiry; - payment_data.payment_intent = state .store .update_payment_intent( diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index ead62ae02e..56665d65a3 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -75,7 +75,7 @@ impl })? } - if request.amount < payment_intent.amount { + if payment_intent.amount > request.amount { Err(errors::ApiErrorResponse::PreconditionFailed { message: "Amount should be greater than original authorized amount".to_owned(), })? diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index ebea9207b1..f74ec42075 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -1,6 +1,6 @@ use std::{str::FromStr, vec::IntoIter}; -use common_utils::ext_traits::Encode; +use common_utils::{ext_traits::Encode, types::MinorUnit}; use diesel_models::enums as storage_enums; use error_stack::{report, ResultExt}; use router_env::{ @@ -397,7 +397,7 @@ where error_message: None, error_reason: None, amount_capturable: if router_data.status.is_terminal_status() { - Some(0) + Some(MinorUnit::new(0)) } else { None }, @@ -428,7 +428,7 @@ where error_message: Some(Some(error_response.message.clone())), status: storage_enums::AttemptStatus::Failure, error_reason: Some(error_response.reason.clone()), - amount_capturable: Some(0), + amount_capturable: Some(MinorUnit::new(0)), updated_by: storage_scheme.to_string(), unified_code: option_gsm.clone().map(|gsm| gsm.unified_code), unified_message: option_gsm.map(|gsm| gsm.unified_message), diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 6967c97775..4ea8f00461 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -213,7 +213,7 @@ where }; let payment_input = dsl_inputs::PaymentInput { - amount: payment_data.payment_intent.amount, + amount: payment_data.payment_intent.amount.get_amount_as_i64(), card_bin: payment_data .payment_method_data .as_ref() @@ -897,7 +897,7 @@ pub async fn perform_session_flow_routing( }; let payment_input = dsl_inputs::PaymentInput { - amount: session_input.payment_intent.amount, + amount: session_input.payment_intent.amount.get_amount_as_i64(), currency: session_input .payment_intent .currency @@ -1142,7 +1142,7 @@ pub fn make_dsl_input_for_surcharge( payment_type: None, }; let payment_input = dsl_inputs::PaymentInput { - amount: payment_attempt.amount, + amount: payment_attempt.amount.get_amount_as_i64(), // currency is always populated in payment_attempt during payment create currency: payment_attempt .currency diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 1fd7613059..bcc71f82bc 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -4,7 +4,7 @@ use api_models::payments::{FrmMessage, GetAddressFromPaymentMethodData, RequestS #[cfg(feature = "payouts")] use api_models::payouts::PayoutAttemptResponse; use common_enums::RequestIncrementalAuthorization; -use common_utils::{consts::X_HS_LATENCY, fp_utils}; +use common_utils::{consts::X_HS_LATENCY, fp_utils, types::MinorUnit}; use diesel_models::ephemeral_key; use error_stack::{report, ResultExt}; use masking::{Maskable, Secret}; @@ -152,7 +152,10 @@ where connector_meta_data: merchant_connector_account.get_metadata(), request: T::try_from(additional_data)?, response, - amount_captured: payment_data.payment_intent.amount_captured, + amount_captured: payment_data + .payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), access_token: None, session_token: None, reference_id: None, @@ -373,7 +376,7 @@ where .as_ref() .get_required_value("currency")?; let amount = currency - .to_currency_base_unit(payment_attempt.amount) + .to_currency_base_unit(payment_attempt.amount.get_amount_as_i64()) .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "amount", })?; @@ -1172,7 +1175,7 @@ impl TryFrom> for types::PaymentsAuthoriz statement_descriptor_suffix: payment_data.payment_intent.statement_descriptor_suffix, statement_descriptor: payment_data.payment_intent.statement_descriptor_name, capture_method: payment_data.payment_attempt.capture_method, - amount, + amount: amount.get_amount_as_i64(), currency: payment_data.currency, browser_info, email: payment_data.email, @@ -1248,25 +1251,25 @@ impl TryFrom> api::GetToken::Connector, payment_data.payment_attempt.merchant_connector_id.clone(), )?; + let total_amount = payment_data + .incremental_authorization_details + .clone() + .map(|details| details.total_amount) + .ok_or( + report!(errors::ApiErrorResponse::InternalServerError) + .attach_printable("missing incremental_authorization_details in payment_data"), + )?; + let additional_amount = payment_data + .incremental_authorization_details + .clone() + .map(|details| details.additional_amount) + .ok_or( + report!(errors::ApiErrorResponse::InternalServerError) + .attach_printable("missing incremental_authorization_details in payment_data"), + )?; Ok(Self { - total_amount: payment_data - .incremental_authorization_details - .clone() - .map(|details| details.total_amount) - .ok_or( - report!(errors::ApiErrorResponse::InternalServerError).attach_printable( - "missing incremental_authorization_details in payment_data", - ), - )?, - additional_amount: payment_data - .incremental_authorization_details - .clone() - .map(|details| details.additional_amount) - .ok_or( - report!(errors::ApiErrorResponse::InternalServerError).attach_printable( - "missing incremental_authorization_details in payment_data", - ), - )?, + total_amount: total_amount.get_amount_as_i64(), + additional_amount: additional_amount.get_amount_as_i64(), reason: payment_data .incremental_authorization_details .and_then(|details| details.reason), @@ -1315,7 +1318,7 @@ impl TryFrom> for types::PaymentsCaptureD api::GetToken::Connector, payment_data.payment_attempt.merchant_connector_id.clone(), )?; - let amount_to_capture: i64 = payment_data + let amount_to_capture = payment_data .payment_attempt .amount_to_capture .map_or(payment_data.amount.into(), |capture_amount| capture_amount); @@ -1328,15 +1331,15 @@ impl TryFrom> for types::PaymentsCaptureD .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "browser_info", })?; - + let amount = MinorUnit::from(payment_data.amount); Ok(Self { - amount_to_capture, + amount_to_capture: amount_to_capture.get_amount_as_i64(), // This should be removed once we start moving to connector module currency: payment_data.currency, connector_transaction_id: connector .connector .connector_transaction_id(payment_data.payment_attempt.clone())? .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?, - payment_amount: payment_data.amount.into(), + payment_amount: amount.get_amount_as_i64(), // This should be removed once we start moving to connector module connector_meta: payment_data.payment_attempt.connector_metadata, multiple_capture_data: match payment_data.multiple_capture_data { Some(multiple_capture_data) => Some(MultipleCaptureRequestData { @@ -1374,8 +1377,9 @@ impl TryFrom> for types::PaymentsCancelDa .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "browser_info", })?; + let amount = MinorUnit::from(payment_data.amount); Ok(Self { - amount: Some(payment_data.amount.into()), + amount: Some(amount.get_amount_as_i64()), // This should be removed once we start moving to connector module currency: Some(payment_data.currency), connector_transaction_id: connector .connector @@ -1394,8 +1398,9 @@ impl TryFrom> for types::PaymentsApproveD fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { let payment_data = additional_data.payment_data; + let amount = MinorUnit::from(payment_data.amount); Ok(Self { - amount: Some(payment_data.amount.into()), + amount: Some(amount.get_amount_as_i64()), //need to change after we move to connector module currency: Some(payment_data.currency), }) } @@ -1406,8 +1411,9 @@ impl TryFrom> for types::PaymentsRejectDa fn try_from(additional_data: PaymentAdditionalData<'_, F>) -> Result { let payment_data = additional_data.payment_data; + let amount = MinorUnit::from(payment_data.amount); Ok(Self { - amount: Some(payment_data.amount.into()), + amount: Some(amount.get_amount_as_i64()), //need to change after we move to connector module currency: Some(payment_data.currency), }) } @@ -1443,7 +1449,7 @@ impl TryFrom> for types::PaymentsSessionD .unwrap_or(payment_data.amount.into()); Ok(Self { - amount, + amount: amount.get_amount_as_i64(), //need to change once we move to connector module currency: payment_data.currency, country: payment_data.address.get_payment_method_billing().and_then( |billing_address| { @@ -1491,11 +1497,11 @@ impl TryFrom> for types::SetupMandateRequ .as_ref() .map(|customer| customer.clone().into_inner()) }); - + let amount = MinorUnit::from(payment_data.amount); Ok(Self { currency: payment_data.currency, confirm: true, - amount: Some(payment_data.amount.into()), + amount: Some(amount.get_amount_as_i64()), //need to change once we move to connector module payment_method_data: From::from( payment_data .payment_method_data @@ -1607,7 +1613,7 @@ impl TryFrom> for types::CompleteAuthoriz confirm: payment_data.payment_attempt.confirm, statement_descriptor_suffix: payment_data.payment_intent.statement_descriptor_suffix, capture_method: payment_data.payment_attempt.capture_method, - amount, + amount: amount.get_amount_as_i64(), // need to change once we move to connector module currency: payment_data.currency, browser_info, email: payment_data.email, @@ -1684,7 +1690,7 @@ impl TryFrom> for types::PaymentsPreProce payment_method_data: payment_method_data.map(From::from), email: payment_data.email, currency: Some(payment_data.currency), - amount: Some(amount), + amount: Some(amount.get_amount_as_i64()), // need to change this once we move to connector module payment_method_type: payment_data.payment_attempt.payment_method_type, setup_mandate_details: payment_data.setup_mandate, capture_method: payment_data.payment_attempt.capture_method, diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs index b297470bce..71b7d84b9f 100644 --- a/crates/router/src/core/payments/types.rs +++ b/crates/router/src/core/payments/types.rs @@ -80,27 +80,31 @@ impl MultipleCaptureData { .and_modify(|capture| *capture = updated_capture.clone()); } } - pub fn get_total_blocked_amount(&self) -> i64 { - self.all_captures.iter().fold(0, |accumulator, capture| { - accumulator - + match capture.1.status { - storage_enums::CaptureStatus::Charged - | storage_enums::CaptureStatus::Pending => capture.1.amount, - storage_enums::CaptureStatus::Started - | storage_enums::CaptureStatus::Failed => 0, - } - }) + pub fn get_total_blocked_amount(&self) -> common_types::MinorUnit { + self.all_captures + .iter() + .fold(common_types::MinorUnit::new(0), |accumulator, capture| { + accumulator + + match capture.1.status { + storage_enums::CaptureStatus::Charged + | storage_enums::CaptureStatus::Pending => capture.1.amount, + storage_enums::CaptureStatus::Started + | storage_enums::CaptureStatus::Failed => common_types::MinorUnit::new(0), + } + }) } - pub fn get_total_charged_amount(&self) -> i64 { - self.all_captures.iter().fold(0, |accumulator, capture| { - accumulator - + match capture.1.status { - storage_enums::CaptureStatus::Charged => capture.1.amount, - storage_enums::CaptureStatus::Pending - | storage_enums::CaptureStatus::Started - | storage_enums::CaptureStatus::Failed => 0, - } - }) + pub fn get_total_charged_amount(&self) -> common_types::MinorUnit { + self.all_captures + .iter() + .fold(common_types::MinorUnit::new(0), |accumulator, capture| { + accumulator + + match capture.1.status { + storage_enums::CaptureStatus::Charged => capture.1.amount, + storage_enums::CaptureStatus::Pending + | storage_enums::CaptureStatus::Started + | storage_enums::CaptureStatus::Failed => common_types::MinorUnit::new(0), + } + }) } pub fn get_captures_count(&self) -> RouterResult { i16::try_from(self.all_captures.len()) @@ -123,7 +127,10 @@ impl MultipleCaptureData { accumulator }) } - pub fn get_attempt_status(&self, authorized_amount: i64) -> storage_enums::AttemptStatus { + pub fn get_attempt_status( + &self, + authorized_amount: common_types::MinorUnit, + ) -> storage_enums::AttemptStatus { let total_captured_amount = self.get_total_charged_amount(); if authorized_amount == total_captured_amount { return storage_enums::AttemptStatus::Charged; @@ -182,18 +189,18 @@ impl MultipleCaptureData { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct SurchargeDetails { /// original_amount - pub original_amount: i64, + pub original_amount: common_types::MinorUnit, /// surcharge value pub surcharge: common_types::Surcharge, /// tax on surcharge value pub tax_on_surcharge: Option>, /// surcharge amount for this payment - pub surcharge_amount: i64, + pub surcharge_amount: common_types::MinorUnit, /// tax on surcharge amount for this payment - pub tax_on_surcharge_amount: i64, + pub tax_on_surcharge_amount: common_types::MinorUnit, /// sum of original amount, - pub final_amount: i64, + pub final_amount: common_types::MinorUnit, } impl From<(&RequestSurchargeDetails, &PaymentAttempt)> for SurchargeDetails { @@ -201,10 +208,10 @@ impl From<(&RequestSurchargeDetails, &PaymentAttempt)> for SurchargeDetails { (request_surcharge_details, payment_attempt): (&RequestSurchargeDetails, &PaymentAttempt), ) -> Self { let surcharge_amount = request_surcharge_details.surcharge_amount; - let tax_on_surcharge_amount = request_surcharge_details.tax_amount.unwrap_or(0); + let tax_on_surcharge_amount = request_surcharge_details.tax_amount.unwrap_or_default(); Self { original_amount: payment_attempt.amount, - surcharge: common_types::Surcharge::Fixed(request_surcharge_details.surcharge_amount), + surcharge: common_types::Surcharge::Fixed(request_surcharge_details.surcharge_amount), // need to check this tax_on_surcharge: None, surcharge_amount, tax_on_surcharge_amount, @@ -219,14 +226,18 @@ impl ForeignTryFrom<(&SurchargeDetails, &PaymentAttempt)> for SurchargeDetailsRe (surcharge_details, payment_attempt): (&SurchargeDetails, &PaymentAttempt), ) -> Result { let currency = payment_attempt.currency.unwrap_or_default(); - let display_surcharge_amount = - currency.to_currency_base_unit_asf64(surcharge_details.surcharge_amount)?; - let display_tax_on_surcharge_amount = - currency.to_currency_base_unit_asf64(surcharge_details.tax_on_surcharge_amount)?; - let display_final_amount = - currency.to_currency_base_unit_asf64(surcharge_details.final_amount)?; + let display_surcharge_amount = currency + .to_currency_base_unit_asf64(surcharge_details.surcharge_amount.get_amount_as_i64())?; + let display_tax_on_surcharge_amount = currency.to_currency_base_unit_asf64( + surcharge_details + .tax_on_surcharge_amount + .get_amount_as_i64(), + )?; + let display_final_amount = currency + .to_currency_base_unit_asf64(surcharge_details.final_amount.get_amount_as_i64())?; let display_total_surcharge_amount = currency.to_currency_base_unit_asf64( - surcharge_details.surcharge_amount + surcharge_details.tax_on_surcharge_amount, + (surcharge_details.surcharge_amount + surcharge_details.tax_on_surcharge_amount) + .get_amount_as_i64(), )?; Ok(Self { surcharge: surcharge_details.surcharge.clone().into(), @@ -245,9 +256,10 @@ impl SurchargeDetails { request_surcharge_details: RequestSurchargeDetails, ) -> bool { request_surcharge_details.surcharge_amount == self.surcharge_amount - && request_surcharge_details.tax_amount.unwrap_or(0) == self.tax_on_surcharge_amount + && request_surcharge_details.tax_amount.unwrap_or_default() + == self.tax_on_surcharge_amount } - pub fn get_total_surcharge_amount(&self) -> i64 { + pub fn get_total_surcharge_amount(&self) -> common_types::MinorUnit { self.surcharge_amount + self.tax_on_surcharge_amount } } diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 25f6191920..1170d2a25c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -6,7 +6,7 @@ pub mod validator; use std::vec::IntoIter; use api_models::enums as api_enums; -use common_utils::{consts, crypto::Encryptable, ext_traits::ValueExt, pii}; +use common_utils::{consts, crypto::Encryptable, ext_traits::ValueExt, pii, types::MinorUnit}; use diesel_models::enums as storage_enums; use error_stack::{report, ResultExt}; #[cfg(feature = "olap")] @@ -365,11 +365,12 @@ pub async fn payouts_update_core( ), })); } - // Update DB with new data let payouts = payout_data.payouts.to_owned(); + let amount = MinorUnit::from(req.amount.unwrap_or(MinorUnit::new(payouts.amount).into())) + .get_amount_as_i64(); let updated_payouts = storage::PayoutsUpdate::Update { - amount: req.amount.unwrap_or(payouts.amount.into()).into(), + amount, destination_currency: req.currency.unwrap_or(payouts.destination_currency), source_currency: req.currency.unwrap_or(payouts.source_currency), description: req.description.clone().or(payouts.description.clone()), @@ -1964,14 +1965,14 @@ pub async fn payout_create_db_entries( } else { None }; - + let amount = MinorUnit::from(req.amount.unwrap_or(api::Amount::Zero)).get_amount_as_i64(); let payouts_req = storage::PayoutsNew { payout_id: payout_id.to_string(), merchant_id: merchant_id.to_string(), customer_id: customer_id.to_owned(), address_id: address_id.to_owned(), payout_type, - amount: req.amount.unwrap_or(api::Amount::Zero).into(), + amount, destination_currency: currency, source_currency: currency, description: req.description.to_owned(), diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index fa6b06ba3a..987d627f56 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -73,7 +73,9 @@ pub async fn refund_create_core( // Amount is not passed in request refer from payment intent. amount = req .amount - .or(payment_intent.amount_captured) + .or(payment_intent + .amount_captured + .map(|capture_amount| capture_amount.get_amount_as_i64())) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("amount captured is none in a successful payment")?; @@ -172,7 +174,7 @@ pub async fn trigger_refund_to_gateway( &routed_through, merchant_account, key_store, - (payment_attempt.amount, currency), + (payment_attempt.amount.get_amount_as_i64(), currency), payment_intent, payment_attempt, refund, @@ -451,7 +453,7 @@ pub async fn sync_refund_with_gateway( &connector_id, merchant_account, key_store, - (payment_attempt.amount, currency), + (payment_attempt.amount.get_amount_as_i64(), currency), payment_intent, payment_attempt, refund, @@ -636,8 +638,12 @@ pub async fn validate_and_create_refund( .amount_captured .unwrap_or(payment_attempt.amount); - validator::validate_refund_amount(total_amount_captured, &all_refunds, refund_amount) - .change_context(errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount)?; + validator::validate_refund_amount( + total_amount_captured.get_amount_as_i64(), + &all_refunds, + refund_amount, + ) + .change_context(errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount)?; validator::validate_maximum_refund_against_payment_attempt( &all_refunds, @@ -660,7 +666,7 @@ pub async fn validate_and_create_refund( .set_connector_transaction_id(connecter_transaction_id.to_string()) .set_connector(connector) .set_refund_type(req.refund_type.unwrap_or_default().foreign_into()) - .set_total_amount(payment_attempt.amount) + .set_total_amount(payment_attempt.amount.get_amount_as_i64()) .set_refund_amount(refund_amount) .set_currency(currency) .set_created_at(Some(common_utils::date_time::now())) diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 6571546448..237d8b6223 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -315,7 +315,9 @@ pub async fn construct_refund_router_data<'a, F>( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), payment_method_status: None, request: types::RefundsData { refund_id: refund.refund_id.clone(), @@ -559,7 +561,9 @@ pub async fn construct_accept_dispute_router_data<'a>( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), payment_method_status: None, request: types::AcceptDisputeRequestData { dispute_id: dispute.dispute_id.clone(), @@ -653,7 +657,9 @@ pub async fn construct_submit_evidence_router_data<'a>( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), request: submit_evidence_request_data, response: Err(ErrorResponse::default()), access_token: None, @@ -745,7 +751,9 @@ pub async fn construct_upload_file_router_data<'a>( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), payment_method_status: None, request: types::UploadFileRequestData { file_key, @@ -841,7 +849,9 @@ pub async fn construct_defend_dispute_router_data<'a>( address: PaymentAddress::default(), auth_type: payment_attempt.authentication_type.unwrap_or_default(), connector_meta_data: merchant_connector_account.get_metadata(), - amount_captured: payment_intent.amount_captured, + amount_captured: payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()), payment_method_status: None, request: types::DefendDisputeRequestData { dispute_id: dispute.dispute_id.clone(), diff --git a/crates/router/src/routes/currency.rs b/crates/router/src/routes/currency.rs index e80bd53d8d..15684e6ae8 100644 --- a/crates/router/src/routes/currency.rs +++ b/crates/router/src/routes/currency.rs @@ -31,7 +31,7 @@ pub async fn convert_forex( params: web::Query, ) -> HttpResponse { let flow = Flow::RetrieveForexFlow; - let amount = ¶ms.amount; + let amount = params.amount; let to_currency = ¶ms.to_currency; let from_currency = ¶ms.from_currency; Box::pin(api::server_wrap( @@ -42,7 +42,7 @@ pub async fn convert_forex( |state, _, _, _| { currency::convert_forex( state, - *amount, + amount.get_amount_as_i64(), to_currency.to_string(), from_currency.to_string(), ) diff --git a/crates/router/src/services/kafka/payment_attempt.rs b/crates/router/src/services/kafka/payment_attempt.rs index 9f442876fa..dfa7da0d0a 100644 --- a/crates/router/src/services/kafka/payment_attempt.rs +++ b/crates/router/src/services/kafka/payment_attempt.rs @@ -1,4 +1,5 @@ // use diesel_models::enums::MandateDetails; +use common_utils::types::MinorUnit; use diesel_models::enums as storage_enums; use hyperswitch_domain_models::{ mandates::MandateDetails, payments::payment_attempt::PaymentAttempt, @@ -11,14 +12,14 @@ pub struct KafkaPaymentAttempt<'a> { pub merchant_id: &'a String, pub attempt_id: &'a String, pub status: storage_enums::AttemptStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, pub save_to_locker: Option, pub connector: Option<&'a String>, pub error_message: Option<&'a String>, - pub offer_amount: Option, - pub surcharge_amount: Option, - pub tax_amount: Option, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, pub payment_method_id: Option<&'a String>, pub payment_method: Option, pub connector_transaction_id: Option<&'a String>, @@ -34,7 +35,7 @@ pub struct KafkaPaymentAttempt<'a> { #[serde(default, with = "time::serde::timestamp::option")] pub last_synced: Option, pub cancellation_reason: Option<&'a String>, - pub amount_to_capture: Option, + pub amount_to_capture: Option, pub mandate_id: Option<&'a String>, pub browser_info: Option, pub error_code: Option<&'a String>, @@ -45,9 +46,9 @@ pub struct KafkaPaymentAttempt<'a> { pub payment_method_data: Option, pub error_reason: Option<&'a String>, pub multiple_capture_count: Option, - pub amount_capturable: i64, + pub amount_capturable: MinorUnit, pub merchant_connector_id: Option<&'a String>, - pub net_amount: i64, + pub net_amount: MinorUnit, pub unified_code: Option<&'a String>, pub unified_message: Option<&'a String>, pub mandate_data: Option<&'a MandateDetails>, diff --git a/crates/router/src/services/kafka/payment_intent.rs b/crates/router/src/services/kafka/payment_intent.rs index 2edd0d49cc..ad61e10295 100644 --- a/crates/router/src/services/kafka/payment_intent.rs +++ b/crates/router/src/services/kafka/payment_intent.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel_models::enums as storage_enums; use hyperswitch_domain_models::payments::PaymentIntent; use time::OffsetDateTime; @@ -7,9 +8,9 @@ pub struct KafkaPaymentIntent<'a> { pub payment_id: &'a String, pub merchant_id: &'a String, pub status: storage_enums::IntentStatus, - pub amount: i64, + pub amount: MinorUnit, pub currency: Option, - pub amount_captured: Option, + pub amount_captured: Option, pub customer_id: Option<&'a String>, pub description: Option<&'a String>, pub return_url: Option<&'a String>, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 886e7c65fd..0afef76051 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -22,8 +22,8 @@ pub use api_models::{enums::Connector, mandates}; #[cfg(feature = "payouts")] pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; use common_enums::MandateStatus; -pub use common_utils::request::RequestContent; use common_utils::{pii, pii::Email}; +pub use common_utils::{request::RequestContent, types::MinorUnit}; use error_stack::ResultExt; use hyperswitch_domain_models::mandates::{CustomerAcceptance, MandateData}; pub use hyperswitch_domain_models::{ @@ -568,7 +568,7 @@ impl Capturable for PaymentsAuthorizeData { let final_amount = self .surcharge_details .as_ref() - .map(|surcharge_details| surcharge_details.final_amount); + .map(|surcharge_details| surcharge_details.final_amount.get_amount_as_i64()); final_amount.or(Some(self.amount)) } @@ -601,7 +601,7 @@ impl Capturable for PaymentsAuthorizeData { | common_enums::IntentStatus::PartiallyCapturedAndCapturable => None, } }, - common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount()), + common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount().get_amount_as_i64()), // In case of manual multiple, amount capturable must be inferred from all captures. common_enums::CaptureMethod::ManualMultiple | // Scheduled capture is not supported as of now @@ -678,7 +678,7 @@ impl Capturable for CompleteAuthorizeData { | common_enums::IntentStatus::PartiallyCapturedAndCapturable => None, } }, - common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount()), + common_enums::CaptureMethod::Manual => Some(payment_data.payment_attempt.get_total_amount().get_amount_as_i64()), // In case of manual multiple, amount capturable must be inferred from all captures. common_enums::CaptureMethod::ManualMultiple | // Scheduled capture is not supported as of now @@ -693,7 +693,10 @@ impl Capturable for PaymentsCancelData { F: Clone, { // return previously captured amount - payment_data.payment_intent.amount_captured + payment_data + .payment_intent + .amount_captured + .map(|amt| amt.get_amount_as_i64()) } fn get_amount_capturable( &self, @@ -743,6 +746,7 @@ impl Capturable for PaymentsSyncData { .payment_attempt .amount_to_capture .or_else(|| Some(payment_data.payment_attempt.get_total_amount())) + .map(|amt| amt.get_amount_as_i64()) } fn get_amount_capturable( &self, @@ -777,19 +781,19 @@ pub enum CaptureSyncResponse { resource_id: ResponseId, status: storage_enums::AttemptStatus, connector_response_reference_id: Option, - amount: Option, + amount: Option, }, Error { code: String, message: String, reason: Option, status_code: u16, - amount: Option, + amount: Option, }, } impl CaptureSyncResponse { - pub fn get_amount_captured(&self) -> Option { + pub fn get_amount_captured(&self) -> Option { match self { Self::Success { amount, .. } | Self::Error { amount, .. } => *amount, } diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index a220193629..923f4a5526 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -246,7 +246,7 @@ mod payments_test { #[allow(dead_code)] fn payments_request() -> PaymentsRequest { PaymentsRequest { - amount: Some(Amount::from(200)), + amount: Some(Amount::from(common_utils::types::MinorUnit::new(200))), payment_method_data: Some(PaymentMethodDataRequest { payment_method_data: Some(PaymentMethodData::Card(card())), billing: None, diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index 41e1aae635..4ad45f554c 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel_models::{capture::CaptureNew, enums}; use error_stack::ResultExt; pub use hyperswitch_domain_models::payments::payment_attempt::{ @@ -10,19 +11,19 @@ use crate::{ pub trait PaymentAttemptExt { fn make_new_capture( &self, - capture_amount: i64, + capture_amount: MinorUnit, capture_status: enums::CaptureStatus, ) -> RouterResult; fn get_next_capture_id(&self) -> String; - fn get_total_amount(&self) -> i64; + fn get_total_amount(&self) -> MinorUnit; fn get_surcharge_details(&self) -> Option; } impl PaymentAttemptExt for PaymentAttempt { fn make_new_capture( &self, - capture_amount: i64, + capture_amount: MinorUnit, capture_status: enums::CaptureStatus, ) -> RouterResult { let capture_sequence = self.multiple_capture_count.unwrap_or_default() + 1; @@ -66,8 +67,10 @@ impl PaymentAttemptExt for PaymentAttempt { } }) } - fn get_total_amount(&self) -> i64 { - self.amount + self.surcharge_amount.unwrap_or(0) + self.tax_amount.unwrap_or(0) + fn get_total_amount(&self) -> MinorUnit { + self.amount + + self.surcharge_amount.unwrap_or_default() + + self.tax_amount.unwrap_or_default() } } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 6fb98d944b..da8e8c621b 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -10,6 +10,7 @@ use common_utils::{ ext_traits::{StringExt, ValueExt}, fp_utils::when, pii, + types::MinorUnit, }; use diesel_models::enums as storage_enums; use error_stack::{report, ResultExt}; @@ -311,7 +312,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { impl ForeignFrom for payments::MandateAmountData { fn foreign_from(from: storage_enums::MandateAmountData) -> Self { Self { - amount: from.amount, + amount: MinorUnit::new(from.amount), currency: from.currency, start_date: from.start_date, end_date: from.end_date, @@ -378,7 +379,7 @@ impl ForeignFrom for hyperswitch_domain_models::mandates: impl ForeignFrom for storage_enums::MandateAmountData { fn foreign_from(from: payments::MandateAmountData) -> Self { Self { - amount: from.amount, + amount: from.amount.get_amount_as_i64(), currency: from.currency, start_date: from.start_date, end_date: from.end_date, diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 8cf5caf5f1..ef39a5fca1 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -174,7 +174,7 @@ pub async fn generate_sample_data( true => common_enums::IntentStatus::Failed, _ => common_enums::IntentStatus::Succeeded, }, - amount: amount * 100, + amount: common_utils::types::MinorUnit::new(amount * 100), currency: Some( *currency_vec .get((num - 1) % currency_vec_len) @@ -192,7 +192,7 @@ pub async fn generate_sample_data( ), attempt_count: 1, customer_id: Some("hs-dashboard-user".to_string()), - amount_captured: Some(amount * 100), + amount_captured: Some(common_utils::types::MinorUnit::new(amount * 100)), profile_id: Some(profile_id.clone()), return_url: Default::default(), metadata: Default::default(), diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index 482b076a67..14f372789e 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -127,7 +127,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { error_reason: Some(Some( consts::REQUEST_TIMEOUT_ERROR_MESSAGE_FROM_PSYNC.to_string(), )), - amount_capturable: Some(0), + amount_capturable: Some(common_utils::types::MinorUnit::new(0)), updated_by: merchant_account.storage_scheme.to_string(), unified_code: None, unified_message: None, diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 45d3b29d66..6566004c4c 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -2,6 +2,7 @@ mod utils; +use common_utils::types::MinorUnit; use router::{ configs, core::payments, @@ -303,10 +304,10 @@ async fn payments_create_core() { "pay_mbabizu24mvu3mela5njyhpit10".to_string(), )), merchant_id: Some("jarnura".to_string()), - amount: Some(6540.into()), + amount: Some(MinorUnit::new(6540).into()), currency: Some(api_enums::Currency::USD), capture_method: Some(api_enums::CaptureMethod::Automatic), - amount_to_capture: Some(6540), + amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 11:12)), confirm: Some(true), customer_id: None, @@ -351,7 +352,7 @@ async fn payments_create_core() { let expected_response = api::PaymentsResponse { payment_id: Some("pay_mbabizu24mvu3mela5njyhpit10".to_string()), status: api_enums::IntentStatus::Succeeded, - amount: 6540, + amount: MinorUnit::new(6540), amount_capturable: None, amount_received: None, client_secret: None, @@ -486,10 +487,10 @@ async fn payments_create_core_adyen_no_redirect() { let req = api::PaymentsRequest { payment_id: Some(api::PaymentIdType::PaymentIntentId(payment_id.clone())), merchant_id: Some(merchant_id.clone()), - amount: Some(6540.into()), + amount: Some(MinorUnit::new(6540).into()), currency: Some(api_enums::Currency::USD), capture_method: Some(api_enums::CaptureMethod::Automatic), - amount_to_capture: Some(6540), + amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 10:11:12)), confirm: Some(true), customer_id: Some(customer_id), @@ -533,7 +534,7 @@ async fn payments_create_core_adyen_no_redirect() { api::PaymentsResponse { payment_id: Some(payment_id.clone()), status: api_enums::IntentStatus::Processing, - amount: 6540, + amount: MinorUnit::new(6540), amount_capturable: None, amount_received: None, client_secret: None, diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 278ee80a92..288a1af300 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -2,6 +2,7 @@ mod utils; +use common_utils::types::MinorUnit; use router::{ core::payments, db::StorageImpl, @@ -63,10 +64,10 @@ async fn payments_create_core() { "pay_mbabizu24mvu3mela5njyhpit10".to_string(), )), merchant_id: Some("jarnura".to_string()), - amount: Some(6540.into()), + amount: Some(MinorUnit::new(6540).into()), currency: Some(api_enums::Currency::USD), capture_method: Some(api_enums::CaptureMethod::Automatic), - amount_to_capture: Some(6540), + amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 10:11:12)), confirm: Some(true), customer_id: None, @@ -111,7 +112,7 @@ async fn payments_create_core() { let expected_response = api::PaymentsResponse { payment_id: Some("pay_mbabizu24mvu3mela5njyhpit10".to_string()), status: api_enums::IntentStatus::Succeeded, - amount: 6540, + amount: MinorUnit::new(6540), amount_capturable: None, amount_received: None, client_secret: None, @@ -253,10 +254,10 @@ async fn payments_create_core_adyen_no_redirect() { let req = api::PaymentsRequest { payment_id: Some(api::PaymentIdType::PaymentIntentId(payment_id.clone())), merchant_id: Some(merchant_id.clone()), - amount: Some(6540.into()), + amount: Some(MinorUnit::new(6540).into()), currency: Some(api_enums::Currency::USD), capture_method: Some(api_enums::CaptureMethod::Automatic), - amount_to_capture: Some(6540), + amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 10:11:12)), confirm: Some(true), customer_id: Some(customer_id), @@ -301,7 +302,7 @@ async fn payments_create_core_adyen_no_redirect() { api::PaymentsResponse { payment_id: Some(payment_id.clone()), status: api_enums::IntentStatus::Processing, - amount: 6540, + amount: MinorUnit::new(6540), amount_capturable: None, amount_received: None, client_secret: None, diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index e85e0a131a..fb9a5882ee 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -1,5 +1,5 @@ use api_models::enums::{AuthenticationType, Connector, PaymentMethod, PaymentMethodType}; -use common_utils::{errors::CustomResult, fallback_reverse_lookup_not_found}; +use common_utils::{errors::CustomResult, fallback_reverse_lookup_not_found, types::MinorUnit}; use diesel_models::{ enums::{ MandateAmountData as DieselMandateAmountData, MandateDataType as DieselMandateType, @@ -1073,7 +1073,7 @@ impl DataModelExt for MandateAmountData { fn to_storage_model(self) -> Self::StorageModel { DieselMandateAmountData { - amount: self.amount, + amount: self.amount.get_amount_as_i64(), currency: self.currency, start_date: self.start_date, end_date: self.end_date, @@ -1083,7 +1083,7 @@ impl DataModelExt for MandateAmountData { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - amount: storage_model.amount, + amount: MinorUnit::new(storage_model.amount), currency: storage_model.currency, start_date: storage_model.start_date, end_date: storage_model.end_date, @@ -1141,15 +1141,19 @@ impl DataModelExt for PaymentAttempt { merchant_id: self.merchant_id, attempt_id: self.attempt_id, status: self.status, - amount: self.amount, - net_amount: Some(self.net_amount), + amount: self.amount.get_amount_as_i64(), + net_amount: Some(self.net_amount.get_amount_as_i64()), currency: self.currency, save_to_locker: self.save_to_locker, connector: self.connector, error_message: self.error_message, - offer_amount: self.offer_amount, - surcharge_amount: self.surcharge_amount, - tax_amount: self.tax_amount, + offer_amount: self + .offer_amount + .map(|offer_amt| offer_amt.get_amount_as_i64()), + surcharge_amount: self + .surcharge_amount + .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), + tax_amount: self.tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), payment_method_id: self.payment_method_id, payment_method: self.payment_method, connector_transaction_id: self.connector_transaction_id, @@ -1161,7 +1165,9 @@ impl DataModelExt for PaymentAttempt { modified_at: self.modified_at, last_synced: self.last_synced, cancellation_reason: self.cancellation_reason, - amount_to_capture: self.amount_to_capture, + amount_to_capture: self + .amount_to_capture + .map(|capture_amt| capture_amt.get_amount_as_i64()), mandate_id: self.mandate_id, browser_info: self.browser_info, error_code: self.error_code, @@ -1177,7 +1183,7 @@ impl DataModelExt for PaymentAttempt { error_reason: self.error_reason, multiple_capture_count: self.multiple_capture_count, connector_response_reference_id: self.connector_response_reference_id, - amount_capturable: self.amount_capturable, + amount_capturable: self.amount_capturable.get_amount_as_i64(), updated_by: self.updated_by, authentication_data: self.authentication_data, encoded_data: self.encoded_data, @@ -1198,20 +1204,20 @@ impl DataModelExt for PaymentAttempt { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - net_amount: storage_model.get_or_calculate_net_amount(), + net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), id: storage_model.id, payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, status: storage_model.status, - amount: storage_model.amount, + amount: MinorUnit::new(storage_model.amount), currency: storage_model.currency, save_to_locker: storage_model.save_to_locker, connector: storage_model.connector, error_message: storage_model.error_message, - offer_amount: storage_model.offer_amount, - surcharge_amount: storage_model.surcharge_amount, - tax_amount: storage_model.tax_amount, + offer_amount: storage_model.offer_amount.map(MinorUnit::new), + surcharge_amount: storage_model.surcharge_amount.map(MinorUnit::new), + tax_amount: storage_model.tax_amount.map(MinorUnit::new), payment_method_id: storage_model.payment_method_id, payment_method: storage_model.payment_method, connector_transaction_id: storage_model.connector_transaction_id, @@ -1223,7 +1229,7 @@ impl DataModelExt for PaymentAttempt { modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, cancellation_reason: storage_model.cancellation_reason, - amount_to_capture: storage_model.amount_to_capture, + amount_to_capture: storage_model.amount_to_capture.map(MinorUnit::new), mandate_id: storage_model.mandate_id, browser_info: storage_model.browser_info, error_code: storage_model.error_code, @@ -1241,7 +1247,7 @@ impl DataModelExt for PaymentAttempt { error_reason: storage_model.error_reason, multiple_capture_count: storage_model.multiple_capture_count, connector_response_reference_id: storage_model.connector_response_reference_id, - amount_capturable: storage_model.amount_capturable, + amount_capturable: MinorUnit::new(storage_model.amount_capturable), updated_by: storage_model.updated_by, authentication_data: storage_model.authentication_data, encoded_data: storage_model.encoded_data, @@ -1268,19 +1274,23 @@ impl DataModelExt for PaymentAttemptNew { fn to_storage_model(self) -> Self::StorageModel { DieselPaymentAttemptNew { - net_amount: Some(self.net_amount), + net_amount: Some(self.net_amount.get_amount_as_i64()), payment_id: self.payment_id, merchant_id: self.merchant_id, attempt_id: self.attempt_id, status: self.status, - amount: self.amount, + amount: self.amount.get_amount_as_i64(), currency: self.currency, save_to_locker: self.save_to_locker, connector: self.connector, error_message: self.error_message, - offer_amount: self.offer_amount, - surcharge_amount: self.surcharge_amount, - tax_amount: self.tax_amount, + offer_amount: self + .offer_amount + .map(|offer_amt| offer_amt.get_amount_as_i64()), + surcharge_amount: self + .surcharge_amount + .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), + tax_amount: self.tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), payment_method_id: self.payment_method_id, payment_method: self.payment_method, capture_method: self.capture_method, @@ -1291,7 +1301,9 @@ impl DataModelExt for PaymentAttemptNew { modified_at: self.modified_at, last_synced: self.last_synced, cancellation_reason: self.cancellation_reason, - amount_to_capture: self.amount_to_capture, + amount_to_capture: self + .amount_to_capture + .map(|capture_amt| capture_amt.get_amount_as_i64()), mandate_id: self.mandate_id, browser_info: self.browser_info, payment_token: self.payment_token, @@ -1307,7 +1319,7 @@ impl DataModelExt for PaymentAttemptNew { error_reason: self.error_reason, connector_response_reference_id: self.connector_response_reference_id, multiple_capture_count: self.multiple_capture_count, - amount_capturable: self.amount_capturable, + amount_capturable: self.amount_capturable.get_amount_as_i64(), updated_by: self.updated_by, authentication_data: self.authentication_data, encoded_data: self.encoded_data, @@ -1328,19 +1340,19 @@ impl DataModelExt for PaymentAttemptNew { fn from_storage_model(storage_model: Self::StorageModel) -> Self { Self { - net_amount: storage_model.get_or_calculate_net_amount(), + net_amount: MinorUnit::new(storage_model.get_or_calculate_net_amount()), payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, attempt_id: storage_model.attempt_id, status: storage_model.status, - amount: storage_model.amount, + amount: MinorUnit::new(storage_model.amount), currency: storage_model.currency, save_to_locker: storage_model.save_to_locker, connector: storage_model.connector, error_message: storage_model.error_message, - offer_amount: storage_model.offer_amount, - surcharge_amount: storage_model.surcharge_amount, - tax_amount: storage_model.tax_amount, + offer_amount: storage_model.offer_amount.map(MinorUnit::new), + surcharge_amount: storage_model.surcharge_amount.map(MinorUnit::new), + tax_amount: storage_model.tax_amount.map(MinorUnit::new), payment_method_id: storage_model.payment_method_id, payment_method: storage_model.payment_method, capture_method: storage_model.capture_method, @@ -1351,7 +1363,7 @@ impl DataModelExt for PaymentAttemptNew { modified_at: storage_model.modified_at, last_synced: storage_model.last_synced, cancellation_reason: storage_model.cancellation_reason, - amount_to_capture: storage_model.amount_to_capture, + amount_to_capture: storage_model.amount_to_capture.map(MinorUnit::new), mandate_id: storage_model.mandate_id, browser_info: storage_model.browser_info, payment_token: storage_model.payment_token, @@ -1369,7 +1381,7 @@ impl DataModelExt for PaymentAttemptNew { error_reason: storage_model.error_reason, connector_response_reference_id: storage_model.connector_response_reference_id, multiple_capture_count: storage_model.multiple_capture_count, - amount_capturable: storage_model.amount_capturable, + amount_capturable: MinorUnit::new(storage_model.amount_capturable), updated_by: storage_model.updated_by, authentication_data: storage_model.authentication_data, encoded_data: storage_model.encoded_data, @@ -1415,7 +1427,7 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_billing_address_id, updated_by, } => DieselPaymentAttemptUpdate::Update { - amount, + amount: amount.get_amount_as_i64(), currency, status, authentication_type, @@ -1425,10 +1437,12 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_type, payment_experience, business_sub_label, - amount_to_capture, + amount_to_capture: amount_to_capture + .map(|capture_amt| capture_amt.get_amount_as_i64()), capture_method, - surcharge_amount, - tax_amount, + surcharge_amount: surcharge_amount + .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), + tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), fingerprint_id, payment_method_billing_address_id, updated_by, @@ -1446,9 +1460,11 @@ impl DataModelExt for PaymentAttemptUpdate { payment_token, connector, straight_through_algorithm, - amount_capturable, - surcharge_amount, - tax_amount, + amount_capturable: amount_capturable + .map(|amount_capturable| amount_capturable.get_amount_as_i64()), + surcharge_amount: surcharge_amount + .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), + tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), updated_by, merchant_connector_id, }, @@ -1508,7 +1524,7 @@ impl DataModelExt for PaymentAttemptUpdate { client_source, client_version, } => DieselPaymentAttemptUpdate::ConfirmUpdate { - amount, + amount: amount.get_amount_as_i64(), currency, status, authentication_type, @@ -1524,9 +1540,11 @@ impl DataModelExt for PaymentAttemptUpdate { straight_through_algorithm, error_code, error_message, - amount_capturable, - surcharge_amount, - tax_amount, + amount_capturable: amount_capturable + .map(|capture_amt| capture_amt.get_amount_as_i64()), + surcharge_amount: surcharge_amount + .map(|surcharge_amt| surcharge_amt.get_amount_as_i64()), + tax_amount: tax_amount.map(|tax_amt| tax_amt.get_amount_as_i64()), fingerprint_id, updated_by, merchant_connector_id: connector_id, @@ -1580,7 +1598,8 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, error_reason, connector_response_reference_id, - amount_capturable, + amount_capturable: amount_capturable + .map(|capture_amt| capture_amt.get_amount_as_i64()), updated_by, authentication_data, encoded_data, @@ -1630,7 +1649,8 @@ impl DataModelExt for PaymentAttemptUpdate { error_code, error_message, error_reason, - amount_capturable, + amount_capturable: amount_capturable + .map(|capture_amt| capture_amt.get_amount_as_i64()), updated_by, unified_code, unified_message, @@ -1644,7 +1664,8 @@ impl DataModelExt for PaymentAttemptUpdate { } => DieselPaymentAttemptUpdate::CaptureUpdate { multiple_capture_count, updated_by, - amount_to_capture, + amount_to_capture: amount_to_capture + .map(|capture_amt| capture_amt.get_amount_as_i64()), }, Self::PreprocessingUpdate { status, @@ -1680,7 +1701,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, } => DieselPaymentAttemptUpdate::AmountToCaptureUpdate { status, - amount_capturable, + amount_capturable: amount_capturable.get_amount_as_i64(), updated_by, }, Self::ConnectorResponse { @@ -1700,8 +1721,8 @@ impl DataModelExt for PaymentAttemptUpdate { amount, amount_capturable, } => DieselPaymentAttemptUpdate::IncrementalAuthorizationAmountUpdate { - amount, - amount_capturable, + amount: amount.get_amount_as_i64(), + amount_capturable: amount_capturable.get_amount_as_i64(), }, Self::AuthenticationUpdate { status, @@ -1740,7 +1761,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, payment_method_billing_address_id, } => Self::Update { - amount, + amount: MinorUnit::new(amount), currency, status, authentication_type, @@ -1750,10 +1771,10 @@ impl DataModelExt for PaymentAttemptUpdate { payment_method_type, payment_experience, business_sub_label, - amount_to_capture, + amount_to_capture: amount_to_capture.map(MinorUnit::new), capture_method, - surcharge_amount, - tax_amount, + surcharge_amount: surcharge_amount.map(MinorUnit::new), + tax_amount: tax_amount.map(MinorUnit::new), fingerprint_id, payment_method_billing_address_id, updated_by, @@ -1771,9 +1792,9 @@ impl DataModelExt for PaymentAttemptUpdate { payment_token, connector, straight_through_algorithm, - amount_capturable, - surcharge_amount, - tax_amount, + amount_capturable: amount_capturable.map(MinorUnit::new), + surcharge_amount: surcharge_amount.map(MinorUnit::new), + tax_amount: tax_amount.map(MinorUnit::new), updated_by, merchant_connector_id: connector_id, }, @@ -1815,7 +1836,7 @@ impl DataModelExt for PaymentAttemptUpdate { client_source, client_version, } => Self::ConfirmUpdate { - amount, + amount: MinorUnit::new(amount), currency, status, authentication_type, @@ -1831,9 +1852,9 @@ impl DataModelExt for PaymentAttemptUpdate { straight_through_algorithm, error_code, error_message, - amount_capturable, - surcharge_amount, - tax_amount, + amount_capturable: amount_capturable.map(MinorUnit::new), + surcharge_amount: surcharge_amount.map(MinorUnit::new), + tax_amount: tax_amount.map(MinorUnit::new), fingerprint_id, updated_by, merchant_connector_id: connector_id, @@ -1905,7 +1926,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_message, error_reason, connector_response_reference_id, - amount_capturable, + amount_capturable: amount_capturable.map(MinorUnit::new), updated_by, authentication_data, encoded_data, @@ -1955,7 +1976,7 @@ impl DataModelExt for PaymentAttemptUpdate { error_code, error_message, error_reason, - amount_capturable, + amount_capturable: amount_capturable.map(MinorUnit::new), updated_by, unified_code, unified_message, @@ -1967,7 +1988,7 @@ impl DataModelExt for PaymentAttemptUpdate { multiple_capture_count, updated_by, } => Self::CaptureUpdate { - amount_to_capture, + amount_to_capture: amount_to_capture.map(MinorUnit::new), multiple_capture_count, updated_by, }, @@ -2005,7 +2026,7 @@ impl DataModelExt for PaymentAttemptUpdate { updated_by, } => Self::AmountToCaptureUpdate { status, - amount_capturable, + amount_capturable: MinorUnit::new(amount_capturable), updated_by, }, DieselPaymentAttemptUpdate::ConnectorResponse { @@ -2025,8 +2046,8 @@ impl DataModelExt for PaymentAttemptUpdate { amount, amount_capturable, } => Self::IncrementalAuthorizationAmountUpdate { - amount, - amount_capturable, + amount: MinorUnit::new(amount), + amount_capturable: MinorUnit::new(amount_capturable), }, DieselPaymentAttemptUpdate::AuthenticationUpdate { status, diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 2365f165d6..2fca38a3b8 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -6982,7 +6982,8 @@ "amount": { "type": "integer", "format": "int64", - "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.," + "description": "The capture amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", + "example": 6540 }, "currency": { "allOf": [ @@ -10124,7 +10125,8 @@ "amount": { "type": "integer", "format": "int64", - "description": "Amount the authorization has been made for" + "description": "Amount the authorization has been made for", + "example": 6540 }, "status": { "$ref": "#/components/schemas/AuthorizationStatus" @@ -10140,9 +10142,7 @@ "nullable": true }, "previously_authorized_amount": { - "type": "integer", - "format": "int64", - "description": "Previously authorized amount for the payment" + "$ref": "#/components/schemas/MinorUnit" } } }, @@ -11595,6 +11595,11 @@ } } }, + "MinorUnit": { + "type": "integer", + "format": "int64", + "description": "This Unit struct represents MinorUnit in which core amount works" + }, "MobilePayRedirection": { "type": "object" }, @@ -12415,7 +12420,8 @@ "amount": { "type": "integer", "format": "int64", - "description": "The payment attempt amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.," + "description": "The payment attempt amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", + "example": 6540 }, "currency": { "allOf": [ @@ -13536,6 +13542,9 @@ }, "PaymentsCaptureRequest": { "type": "object", + "required": [ + "amount_to_capture" + ], "properties": { "merchant_id": { "type": "string", @@ -13546,7 +13555,7 @@ "type": "integer", "format": "int64", "description": "The Amount to be captured/ debited from the user's payment method.", - "nullable": true + "example": 6540 }, "refund_uncaptured_amount": { "type": "boolean", @@ -14835,6 +14844,8 @@ "status", "amount", "net_amount", + "amount_capturable", + "amount_received", "currency", "payment_method", "attempt_count" @@ -14867,29 +14878,26 @@ "type": "integer", "format": "int64", "description": "The payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", - "example": 100 + "example": 6540 }, "net_amount": { "type": "integer", "format": "int64", "description": "The payment net amount. net_amount = amount + surcharge_details.surcharge_amount + surcharge_details.tax_amount,\nIf no surcharge_details, net_amount = amount", - "example": 110 + "example": 6540 }, "amount_capturable": { "type": "integer", "format": "int64", "description": "The maximum amount that could be captured from the payment", "example": 6540, - "nullable": true, "minimum": 100 }, "amount_received": { "type": "integer", "format": "int64", "description": "The amount which is already captured from the payment", - "example": 6540, - "nullable": true, - "minimum": 100 + "example": 6540 }, "connector": { "type": "string", @@ -17286,11 +17294,15 @@ "properties": { "surcharge_amount": { "type": "integer", - "format": "int64" + "format": "int64", + "example": 6540 }, "tax_amount": { - "type": "integer", - "format": "int64", + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], "nullable": true } } @@ -17405,7 +17417,8 @@ }, "amount": { "type": "integer", - "format": "int64" + "format": "int64", + "example": 6540 }, "created_at": { "type": "string", @@ -18256,9 +18269,7 @@ ] }, "value": { - "type": "integer", - "format": "int64", - "description": "Fixed Surcharge value" + "$ref": "#/components/schemas/MinorUnit" } } },