diff --git a/Cargo.lock b/Cargo.lock index 9b08fcbd57..33a587ca0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2870,6 +2870,7 @@ name = "euclid" version = "0.1.0" dependencies = [ "common_enums", + "common_utils", "criterion", "erased-serde 0.4.4", "euclid_macros", @@ -4149,6 +4150,7 @@ version = "0.1.0" dependencies = [ "api_models", "common_enums", + "common_utils", "criterion", "euclid", "hyperswitch_constraint_graph", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index c586fce896..e8d82cffdd 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -12460,8 +12460,7 @@ "$ref": "#/components/schemas/ComparisonType" }, "number": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/MinorUnit" } } }, @@ -17003,7 +17002,8 @@ "amount": { "type": "integer", "format": "int64", - "description": "The payout attempt amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.," + "description": "The payout attempt amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc.,", + "example": 6583 }, "currency": { "allOf": [ @@ -18586,17 +18586,19 @@ "nullable": true }, "minimum_amount": { - "type": "integer", - "format": "int32", - "description": "Minimum amount supported by the processor. To be represented in the lowest denomination of the target currency (For example, for USD it should be in cents)", - "example": 1, + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], "nullable": true }, "maximum_amount": { - "type": "integer", - "format": "int32", - "description": "Maximum amount supported by the processor. To be represented in the lowest denomination of\nthe target currency (For example, for USD it should be in cents)", - "example": 1313, + "allOf": [ + { + "$ref": "#/components/schemas/MinorUnit" + } + ], "nullable": true }, "recurring_enabled": { @@ -19916,9 +19918,7 @@ ] }, "value": { - "type": "integer", - "format": "int64", - "description": "Represents a number literal" + "$ref": "#/components/schemas/MinorUnit" } } }, @@ -19994,8 +19994,7 @@ "value": { "type": "array", "items": { - "type": "integer", - "format": "int64" + "$ref": "#/components/schemas/MinorUnit" }, "description": "Represents an array of numbers. This is basically used for\n\"one of the given numbers\" operations\neg: payment.method.amount = (1, 2, 3)" } diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 1f82508e15..19f183d28f 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -605,12 +605,12 @@ pub struct RequestPaymentMethodTypes { /// Minimum amount supported by the processor. To be represented in the lowest denomination of the target currency (For example, for USD it should be in cents) #[schema(example = 1)] - pub minimum_amount: Option, + pub minimum_amount: Option, /// Maximum amount supported by the processor. To be represented in the lowest denomination of /// the target currency (For example, for USD it should be in cents) #[schema(example = 1313)] - pub maximum_amount: Option, + pub maximum_amount: Option, /// Boolean to enable recurring payments / mandates. Default is true. #[schema(default = true, example = false)] diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index 33be58af10..a7eb2cd568 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -366,7 +366,7 @@ pub struct PayoutCreateResponse { /// The payout amount. Amount for the payout in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., #[schema(value_type = i64, example = 1000)] - pub amount: i64, + pub amount: common_utils::types::MinorUnit, /// Recipient's currency for the payout request #[schema(value_type = Currency, example = "USD")] @@ -501,7 +501,8 @@ pub struct PayoutAttemptResponse { #[schema(value_type = PayoutStatus, example = "failed")] pub status: api_enums::PayoutStatus, /// The payout attempt amount. Amount for the payout 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 = 6583)] + pub amount: common_utils::types::MinorUnit, /// The currency of the amount of the payout attempt #[schema(value_type = Option, example = "USD")] pub currency: Option, @@ -723,7 +724,7 @@ pub struct PayoutLinkDetails { #[serde(flatten)] pub ui_config: link_utils::GenericLinkUIConfigFormData, pub enabled_payment_methods: Vec, - pub amount: String, + pub amount: common_utils::types::StringMajorUnit, pub currency: common_enums::Currency, } diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index 0f30270d32..36f40da2dc 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -343,6 +343,11 @@ impl MinorUnit { self.0 } + /// forms a new minor default unit i.e zero + pub fn zero() -> Self { + Self(0) + } + /// forms a new minor unit from amount pub fn new(value: i64) -> Self { Self(value) @@ -543,6 +548,11 @@ impl StringMajorUnit { .ok_or(ParsingError::DecimalToI64ConversionFailure)?; Ok(MinorUnit::new(amount_i64)) } + + /// Get string amount from struct to be removed in future + pub fn get_amount_as_string(&self) -> String { + self.0.clone() + } } #[cfg(test)] diff --git a/crates/connector_configs/src/transformer.rs b/crates/connector_configs/src/transformer.rs index 5003013ac9..2e42d75657 100644 --- a/crates/connector_configs/src/transformer.rs +++ b/crates/connector_configs/src/transformer.rs @@ -2,10 +2,11 @@ use std::str::FromStr; use api_models::{ enums::{ - Connector, PaymentMethod, PaymentMethodType, - PaymentMethodType::{AliPay, ApplePay, GooglePay, Klarna, Paypal, WeChatPay}, + Connector, PaymentMethod, + PaymentMethodType::{self, AliPay, ApplePay, GooglePay, Klarna, Paypal, WeChatPay}, }, payment_methods, payments, + refunds::MinorUnit, }; use crate::common_config::{ @@ -21,8 +22,8 @@ impl DashboardRequestPayload { payment_methods::RequestPaymentMethodTypes { payment_method_type, card_networks: Some(card_provider), - minimum_amount: Some(0), - maximum_amount: Some(68607706), + minimum_amount: Some(MinorUnit::zero()), + maximum_amount: Some(MinorUnit::new(68607706)), recurring_enabled: true, installment_payment_enabled: false, accepted_currencies: None, @@ -74,8 +75,8 @@ impl DashboardRequestPayload { let data = payment_methods::RequestPaymentMethodTypes { payment_method_type: method_type.payment_method_type, card_networks: None, - minimum_amount: Some(0), - maximum_amount: Some(68607706), + minimum_amount: Some(MinorUnit::zero()), + maximum_amount: Some(MinorUnit::new(68607706)), recurring_enabled: true, installment_payment_enabled: false, accepted_currencies: method_type.accepted_currencies, @@ -113,8 +114,8 @@ impl DashboardRequestPayload { let data = payment_methods::RequestPaymentMethodTypes { payment_method_type: payment_type, card_networks: Some(vec![method.payment_method_type]), - minimum_amount: Some(0), - maximum_amount: Some(68607706), + minimum_amount: Some(MinorUnit::zero()), + maximum_amount: Some(MinorUnit::new(68607706)), recurring_enabled: true, installment_payment_enabled: false, accepted_currencies: method.accepted_currencies, diff --git a/crates/diesel_models/src/payouts.rs b/crates/diesel_models/src/payouts.rs index bf6ce772cb..5cb06d7755 100644 --- a/crates/diesel_models/src/payouts.rs +++ b/crates/diesel_models/src/payouts.rs @@ -1,4 +1,4 @@ -use common_utils::{id_type, pii}; +use common_utils::{id_type, pii, types::MinorUnit}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{self, Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -16,7 +16,7 @@ pub struct Payouts { pub address_id: String, pub payout_type: Option, pub payout_method_id: Option, - pub amount: i64, + pub amount: MinorUnit, pub destination_currency: storage_enums::Currency, pub source_currency: storage_enums::Currency, pub description: Option, @@ -57,7 +57,7 @@ pub struct PayoutsNew { pub address_id: String, pub payout_type: Option, pub payout_method_id: Option, - pub amount: i64, + pub amount: MinorUnit, pub destination_currency: storage_enums::Currency, pub source_currency: storage_enums::Currency, pub description: Option, @@ -82,7 +82,7 @@ pub struct PayoutsNew { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PayoutsUpdate { Update { - amount: i64, + amount: MinorUnit, destination_currency: storage_enums::Currency, source_currency: storage_enums::Currency, description: Option, @@ -113,7 +113,7 @@ pub enum PayoutsUpdate { #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = payouts)] pub struct PayoutsUpdateInternal { - pub amount: Option, + pub amount: Option, pub destination_currency: Option, pub source_currency: Option, pub description: Option, diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index b408c01357..295f032778 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -23,6 +23,7 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order common_enums = { version = "0.1.0", path = "../common_enums" } hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } euclid_macros = { version = "0.1.0", path = "../euclid_macros" } +common_utils = { version = "0.1.0", path = "../common_utils"} [features] default = [] diff --git a/crates/euclid/benches/backends.rs b/crates/euclid/benches/backends.rs index 9d29c41d34..b699991ffb 100644 --- a/crates/euclid/benches/backends.rs +++ b/crates/euclid/benches/backends.rs @@ -1,5 +1,6 @@ #![allow(unused, clippy::expect_used)] +use common_utils::types::MinorUnit; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use euclid::{ backend::{inputs, EuclidBackend, InterpreterBackend, VirInterpreterBackend}, @@ -37,7 +38,7 @@ fn get_program_data() -> (ast::Program, inputs::BackendInput) { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), card_bin: None, currency: enums::Currency::USD, authentication_type: Some(enums::AuthenticationType::NoThreeDs), diff --git a/crates/euclid/src/backend/inputs.rs b/crates/euclid/src/backend/inputs.rs index 18298d4c35..3872ed2d34 100644 --- a/crates/euclid/src/backend/inputs.rs +++ b/crates/euclid/src/backend/inputs.rs @@ -19,7 +19,7 @@ pub struct PaymentMethodInput { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PaymentInput { - pub amount: i64, + pub amount: common_utils::types::MinorUnit, pub currency: enums::Currency, pub authentication_type: Option, pub card_bin: Option, diff --git a/crates/euclid/src/backend/interpreter.rs b/crates/euclid/src/backend/interpreter.rs index bf0a561bf3..a6b0dd55fd 100644 --- a/crates/euclid/src/backend/interpreter.rs +++ b/crates/euclid/src/backend/interpreter.rs @@ -1,5 +1,7 @@ pub mod types; +use common_utils::types::MinorUnit; + use crate::{ backend::{self, inputs, EuclidBackend}, frontend::ast, @@ -14,7 +16,7 @@ where O: Clone, { fn eval_number_comparison_array( - num: i64, + num: MinorUnit, array: &[ast::NumberComparison], ) -> Result { for comparison in array { diff --git a/crates/euclid/src/backend/vir_interpreter.rs b/crates/euclid/src/backend/vir_interpreter.rs index b8d14e228a..0e7988bb3b 100644 --- a/crates/euclid/src/backend/vir_interpreter.rs +++ b/crates/euclid/src/backend/vir_interpreter.rs @@ -105,6 +105,7 @@ where #[cfg(all(test, feature = "ast_parser"))] mod test { #![allow(clippy::expect_used)] + use common_utils::types::MinorUnit; use rustc_hash::FxHashMap; use super::*; @@ -130,7 +131,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), card_bin: None, currency: enums::Currency::USD, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -170,7 +171,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: Some("123456".to_string()), authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -211,7 +212,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: Some("123456".to_string()), authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -252,7 +253,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: Some("123456".to_string()), authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -293,7 +294,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: Some("123456".to_string()), authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -334,7 +335,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: None, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -375,7 +376,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: None, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -416,7 +417,7 @@ mod test { let inp = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), currency: enums::Currency::USD, card_bin: None, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -459,7 +460,7 @@ mod test { let inp = inputs::BackendInput { metadata: Some(meta_map), payment: inputs::PaymentInput { - amount: 32, + amount: MinorUnit::new(32), card_bin: None, currency: enums::Currency::USD, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -500,7 +501,7 @@ mod test { let inp_greater = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 150, + amount: MinorUnit::new(150), card_bin: None, currency: enums::Currency::USD, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -522,7 +523,7 @@ mod test { }, }; let mut inp_equal = inp_greater.clone(); - inp_equal.payment.amount = 123; + inp_equal.payment.amount = MinorUnit::new(123); let backend = VirInterpreterBackend::::with_program(program).expect("Program"); let result_greater = backend.execute(inp_greater).expect("Execution"); let result_equal = backend.execute(inp_equal).expect("Execution"); @@ -550,7 +551,7 @@ mod test { let inp_lower = inputs::BackendInput { metadata: None, payment: inputs::PaymentInput { - amount: 120, + amount: MinorUnit::new(120), card_bin: None, currency: enums::Currency::USD, authentication_type: Some(enums::AuthenticationType::NoThreeDs), @@ -572,7 +573,7 @@ mod test { }, }; let mut inp_equal = inp_lower.clone(); - inp_equal.payment.amount = 123; + inp_equal.payment.amount = MinorUnit::new(123); let backend = VirInterpreterBackend::::with_program(program).expect("Program"); let result_equal = backend.execute(inp_equal).expect("Execution"); let result_lower = backend.execute(inp_lower).expect("Execution"); diff --git a/crates/euclid/src/frontend/ast.rs b/crates/euclid/src/frontend/ast.rs index 905bf04de0..ecb6aa87f2 100644 --- a/crates/euclid/src/frontend/ast.rs +++ b/crates/euclid/src/frontend/ast.rs @@ -3,6 +3,7 @@ pub mod lowering; pub mod parser; use common_enums::RoutableConnectors; +use common_utils::types::MinorUnit; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -26,7 +27,7 @@ pub struct MetadataValue { #[serde(tag = "type", content = "value", rename_all = "snake_case")] pub enum ValueType { /// Represents a number literal - Number(i64), + Number(MinorUnit), /// Represents an enum variant EnumVariant(String), /// Represents a Metadata variant @@ -36,7 +37,7 @@ pub enum ValueType { /// Represents an array of numbers. This is basically used for /// "one of the given numbers" operations /// eg: payment.method.amount = (1, 2, 3) - NumberArray(Vec), + NumberArray(Vec), /// Similar to NumberArray but for enum variants /// eg: payment.method.cardtype = (debit, credit) EnumVariantArray(Vec), @@ -65,7 +66,7 @@ impl ValueType { #[serde(rename_all = "camelCase")] pub struct NumberComparison { pub comparison_type: ComparisonType, - pub number: i64, + pub number: MinorUnit, } /// Conditional comparison type diff --git a/crates/euclid/src/frontend/ast/parser.rs b/crates/euclid/src/frontend/ast/parser.rs index cbfc21c96a..0c586e178e 100644 --- a/crates/euclid/src/frontend/ast/parser.rs +++ b/crates/euclid/src/frontend/ast/parser.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use nom::{ branch, bytes::complete, character::complete as pchar, combinator, error, multi, sequence, }; @@ -115,7 +116,7 @@ pub fn percentage(input: &str) -> ParseResult<&str, u8> { pub fn number_value(input: &str) -> ParseResult<&str, ast::ValueType> { error::context( "number_value", - combinator::map(num_i64, ast::ValueType::Number), + combinator::map(num_i64, |n| ast::ValueType::Number(MinorUnit::new(n))), )(input) } @@ -143,12 +144,15 @@ pub fn enum_variant_value(input: &str) -> ParseResult<&str, ast::ValueType> { } pub fn number_array_value(input: &str) -> ParseResult<&str, ast::ValueType> { + fn num_minor_unit(input: &str) -> ParseResult<&str, MinorUnit> { + combinator::map(num_i64, MinorUnit::new)(input) + } let many_with_comma = multi::many0(sequence::preceded( skip_ws(complete::tag(",")), - skip_ws(num_i64), + skip_ws(num_minor_unit), )); - let full_sequence = sequence::pair(skip_ws(num_i64), many_with_comma); + let full_sequence = sequence::pair(skip_ws(num_minor_unit), many_with_comma); error::context( "number_array_value", @@ -158,7 +162,7 @@ pub fn number_array_value(input: &str) -> ParseResult<&str, ast::ValueType> { full_sequence, skip_ws(complete::tag(")")), ), - |tup: (i64, Vec)| { + |tup: (MinorUnit, Vec)| { let mut rest = tup.1; rest.insert(0, tup.0); ast::ValueType::NumberArray(rest) @@ -215,7 +219,7 @@ pub fn number_comparison(input: &str) -> ParseResult<&str, ast::NumberComparison sequence::pair(operator, num_i64), |tup: (ast::ComparisonType, i64)| ast::NumberComparison { comparison_type: tup.0, - number: tup.1, + number: MinorUnit::new(tup.1), }, ), )(input) diff --git a/crates/euclid/src/frontend/dir.rs b/crates/euclid/src/frontend/dir.rs index 68a32a528b..a21ae10488 100644 --- a/crates/euclid/src/frontend/dir.rs +++ b/crates/euclid/src/frontend/dir.rs @@ -5,6 +5,7 @@ pub mod transformers; use strum::IntoEnumIterator; +// use common_utils::types::MinorUnit; use crate::{enums as euclid_enums, frontend::ast, types}; #[macro_export] @@ -26,7 +27,7 @@ macro_rules! dirval { ($key:ident = $num:literal) => {{ $crate::frontend::dir::DirValue::$key($crate::types::NumValue { - number: $num, + number: common_utils::types::MinorUnit::new($num), refinement: None, }) }}; @@ -74,7 +75,7 @@ macro_rules! dirval { ($key:ident = $num:literal) => {{ $crate::frontend::dir::DirValue::$key($crate::types::NumValue { - number: $num, + number: common_utils::types::MinorUnit::new($num), refinement: None, }) }}; diff --git a/crates/euclid/src/types.rs b/crates/euclid/src/types.rs index 05d3fe6bb8..d7122d8ea8 100644 --- a/crates/euclid/src/types.rs +++ b/crates/euclid/src/types.rs @@ -1,5 +1,6 @@ pub mod transformers; +use common_utils::types::MinorUnit; use euclid_macros::EnumNums; use serde::{Deserialize, Serialize}; use strum::VariantNames; @@ -191,7 +192,7 @@ pub struct MetadataValue { #[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub struct NumValue { - pub number: i64, + pub number: MinorUnit, pub refinement: Option, } @@ -291,11 +292,11 @@ mod global_type_tests { #[test] fn test_num_value_fits_greater_than() { let val1 = NumValue { - number: 10, + number: MinorUnit::new(10), refinement: Some(NumValueRefinement::GreaterThan), }; let val2 = NumValue { - number: 30, + number: MinorUnit::new(30), refinement: Some(NumValueRefinement::GreaterThan), }; @@ -305,11 +306,11 @@ mod global_type_tests { #[test] fn test_num_value_fits_less_than() { let val1 = NumValue { - number: 30, + number: MinorUnit::new(30), refinement: Some(NumValueRefinement::LessThan), }; let val2 = NumValue { - number: 10, + number: MinorUnit::new(10), refinement: Some(NumValueRefinement::LessThan), }; diff --git a/crates/hyperswitch_domain_models/src/payouts/payouts.rs b/crates/hyperswitch_domain_models/src/payouts/payouts.rs index 206e9bcdbf..78cb572a8b 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payouts.rs @@ -1,5 +1,5 @@ use common_enums as storage_enums; -use common_utils::{id_type, pii}; +use common_utils::{id_type, pii, types::MinorUnit}; use serde::{Deserialize, Serialize}; use storage_enums::MerchantStorageScheme; use time::PrimitiveDateTime; @@ -75,7 +75,7 @@ pub struct Payouts { pub address_id: String, pub payout_type: Option, pub payout_method_id: Option, - pub amount: i64, + pub amount: MinorUnit, pub destination_currency: storage_enums::Currency, pub source_currency: storage_enums::Currency, pub description: Option, @@ -103,7 +103,7 @@ pub struct PayoutsNew { pub address_id: String, pub payout_type: Option, pub payout_method_id: Option, - pub amount: i64, + pub amount: MinorUnit, pub destination_currency: storage_enums::Currency, pub source_currency: storage_enums::Currency, pub description: Option, @@ -134,7 +134,7 @@ impl Default for PayoutsNew { address_id: String::default(), payout_type: Some(storage_enums::PayoutType::default()), payout_method_id: Option::default(), - amount: i64::default(), + amount: MinorUnit::new(i64::default()), destination_currency: storage_enums::Currency::default(), source_currency: storage_enums::Currency::default(), description: Option::default(), @@ -159,7 +159,7 @@ impl Default for PayoutsNew { #[derive(Debug, Serialize, Deserialize)] pub enum PayoutsUpdate { Update { - amount: i64, + amount: MinorUnit, destination_currency: storage_enums::Currency, source_currency: storage_enums::Currency, description: Option, @@ -189,7 +189,7 @@ pub enum PayoutsUpdate { #[derive(Clone, Debug, Default)] pub struct PayoutsUpdateInternal { - pub amount: Option, + pub amount: Option, pub destination_currency: Option, pub source_currency: Option, pub description: Option, diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index bcf84e560e..8fc32991a3 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -653,6 +653,9 @@ pub struct PayoutsData { pub entity_type: storage_enums::PayoutEntityType, pub customer_details: Option, pub vendor_details: Option, + + // New minor amount for amount framework + pub minor_amount: MinorUnit, pub priority: Option, } diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index aafec6c8e1..7528461dbc 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -16,6 +16,7 @@ common_enums = { version = "0.1.0", path = "../common_enums" } hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } euclid = { version = "0.1.0", path = "../euclid" } masking = { version = "0.1.0", path = "../masking/" } +common_utils = {version = "0.1.0", path = "../common_utils"} # Third party crates serde = "1.0.197" diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index 4cc526f973..6710dfc764 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -5,6 +5,7 @@ use std::{collections::HashMap, str::FromStr}; use api_models::{ admin as admin_api, enums as api_enums, payment_methods::RequestPaymentMethodTypes, }; +use common_utils::types::MinorUnit; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use euclid::{ dirval, @@ -38,8 +39,8 @@ fn build_test_data<'a>( api_enums::Currency::INR, ])), accepted_countries: None, - minimum_amount: Some(10), - maximum_amount: Some(1000), + minimum_amount: Some(MinorUnit::new(10)), + maximum_amount: Some(MinorUnit::new(1000)), recurring_enabled: true, installment_payment_enabled: true, }); diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index cb24d9de93..453ccb74e2 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use api_models::{ admin as admin_api, enums as api_enums, payment_methods::RequestPaymentMethodTypes, + refunds::MinorUnit, }; use euclid::{ dirval, @@ -227,7 +228,7 @@ fn compile_request_pm_types( if let Some(min_amt) = pm_types.minimum_amount { let num_val = NumValue { - number: min_amt.into(), + number: min_amt, refinement: Some(NumValueRefinement::GreaterThanEqual), }; @@ -243,7 +244,7 @@ fn compile_request_pm_types( if let Some(max_amt) = pm_types.maximum_amount { let num_val = NumValue { - number: max_amt.into(), + number: max_amt, refinement: Some(NumValueRefinement::LessThanEqual), }; @@ -259,7 +260,7 @@ fn compile_request_pm_types( if !amount_nodes.is_empty() { let zero_num_val = NumValue { - number: 0, + number: MinorUnit::zero(), refinement: None, }; @@ -729,8 +730,8 @@ mod tests { api_enums::Currency::INR, ])), accepted_countries: None, - minimum_amount: Some(10), - maximum_amount: Some(1000), + minimum_amount: Some(MinorUnit::new(10)), + maximum_amount: Some(MinorUnit::new(1000)), recurring_enabled: true, installment_payment_enabled: true, }, @@ -745,8 +746,8 @@ mod tests { api_enums::Currency::GBP, ])), accepted_countries: None, - minimum_amount: Some(10), - maximum_amount: Some(1000), + minimum_amount: Some(MinorUnit::new(10)), + maximum_amount: Some(MinorUnit::new(1000)), recurring_enabled: true, installment_payment_enabled: true, }, diff --git a/crates/router/src/compatibility/stripe/webhooks.rs b/crates/router/src/compatibility/stripe/webhooks.rs index e02c55a0e7..4b08b0d36e 100644 --- a/crates/router/src/compatibility/stripe/webhooks.rs +++ b/crates/router/src/compatibility/stripe/webhooks.rs @@ -169,7 +169,7 @@ impl From for StripePayoutResponse { fn from(res: payout_models::PayoutCreateResponse) -> Self { Self { id: res.payout_id, - amount: res.amount, + amount: res.amount.get_amount_as_i64(), currency: res.currency.to_string(), payout_type: res.payout_type, status: StripePayoutStatus::from(res.status), diff --git a/crates/router/src/connector/paypal.rs b/crates/router/src/connector/paypal.rs index 9e7b208ef7..456332ea0f 100644 --- a/crates/router/src/connector/paypal.rs +++ b/crates/router/src/connector/paypal.rs @@ -1,8 +1,12 @@ pub mod transformers; -use std::fmt::{Debug, Write}; +use std::fmt::Write; use base64::Engine; -use common_utils::{ext_traits::ByteSliceExt, request::RequestContent}; +use common_utils::{ + ext_traits::ByteSliceExt, + request::RequestContent, + types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector}, +}; use diesel_models::enums; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret}; @@ -40,8 +44,18 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Paypal; +#[derive(Clone)] +pub struct Paypal { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Paypal { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMajorUnitForConnector, + } + } +} impl api::Payment for Paypal {} impl api::PaymentSession for Paypal {} @@ -103,7 +117,7 @@ impl Paypal { let errors_list = response.details.unwrap_or_default(); let option_error_code_message = connector_utils::get_error_code_error_message_based_on_priority( - Self.clone(), + self.clone(), errors_list .into_iter() .map(|errors| errors.into()) @@ -274,7 +288,7 @@ impl ConnectorCommon for Paypal { let errors_list = response.details.unwrap_or_default(); let option_error_code_message = connector_utils::get_error_code_error_message_based_on_priority( - Self.clone(), + self.clone(), errors_list .into_iter() .map(|errors| errors.into()) @@ -458,12 +472,12 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = paypal::PaypalRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.destination_currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = paypal::PaypalRouterData::try_from((amount, req))?; let connector_req = paypal::PaypalFulfillRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -588,12 +602,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = paypal::PaypalRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = paypal::PaypalRouterData::try_from((amount, req))?; let connector_req = paypal::PaypalPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1093,12 +1107,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = paypal::PaypalRouterData::try_from(( - &self.get_currency_unit(), + let amount_to_capture = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + let connector_router_data = paypal::PaypalRouterData::try_from((amount_to_capture, req))?; let connector_req = paypal::PaypalPaymentsCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1260,12 +1274,12 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = paypal::PaypalRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + let connector_router_data = paypal::PaypalRouterData::try_from((amount, req))?; let connector_req = paypal::PaypalRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1614,7 +1628,7 @@ impl api::IncomingWebhook for Paypal { .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?; Ok(api::disputes::DisputePayload { amount: connector_utils::to_currency_lower_unit( - payload.dispute_amount.value, + payload.dispute_amount.value.get_amount_as_string(), payload.dispute_amount.currency_code, )?, currency: payload.dispute_amount.currency_code.to_string(), diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index 755623b878..c12c7d5da1 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -1,8 +1,8 @@ use api_models::enums; use base64::Engine; -use common_utils::errors::CustomResult; #[cfg(feature = "payouts")] use common_utils::pii::Email; +use common_utils::{errors::CustomResult, types::StringMajorUnit}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -27,23 +27,13 @@ use crate::{ #[derive(Debug, Serialize)] pub struct PaypalRouterData { - pub amount: String, + pub amount: StringMajorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, types::storage::enums::Currency, i64, T)> - for PaypalRouterData -{ +impl TryFrom<(StringMajorUnit, T)> for PaypalRouterData { type Error = error_stack::Report; - fn try_from( - (currency_unit, currency, amount, item): ( - &api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), - ) -> Result { - let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; + fn try_from((amount, item): (StringMajorUnit, T)) -> Result { Ok(Self { amount, router_data: item, @@ -76,13 +66,13 @@ pub enum PaypalPaymentIntent { #[derive(Default, Debug, Clone, Serialize, Eq, PartialEq, Deserialize)] pub struct OrderAmount { pub currency_code: storage_enums::Currency, - pub value: String, + pub value: StringMajorUnit, } #[derive(Default, Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct OrderRequestAmount { pub currency_code: storage_enums::Currency, - pub value: String, + pub value: StringMajorUnit, pub breakdown: AmountBreakdown, } @@ -90,11 +80,11 @@ impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for OrderReque fn from(item: &PaypalRouterData<&types::PaymentsAuthorizeRouterData>) -> Self { Self { currency_code: item.router_data.request.currency, - value: item.amount.to_owned(), + value: item.amount.clone(), breakdown: AmountBreakdown { item_total: OrderAmount { currency_code: item.router_data.request.currency, - value: item.amount.to_owned(), + value: item.amount.clone(), }, }, } @@ -140,7 +130,7 @@ impl From<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for ItemDetail quantity: 1, unit_amount: OrderAmount { currency_code: item.router_data.request.currency, - value: item.amount.to_string(), + value: item.amount.clone(), }, } } @@ -1564,7 +1554,7 @@ pub enum PaypalPayoutDataType { #[cfg(feature = "payouts")] #[derive(Debug, Serialize)] pub struct PayoutAmount { - value: String, + value: StringMajorUnit, currency: storage_enums::Currency, } @@ -1593,7 +1583,7 @@ impl TryFrom<&PaypalRouterData<&types::PayoutsRouterData>> for P item: &PaypalRouterData<&types::PayoutsRouterData>, ) -> Result { let amount = PayoutAmount { - value: item.amount.to_owned(), + value: item.amount.clone(), currency: item.router_data.request.destination_currency, }; @@ -1726,7 +1716,7 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsCaptureRouterData>> ) -> Result { let amount = OrderAmount { currency_code: item.router_data.request.currency, - value: item.amount.to_owned(), + value: item.amount.clone(), }; Ok(Self { amount, @@ -1907,7 +1897,7 @@ impl TryFrom<&PaypalRouterData<&types::RefundsRouterData>> for PaypalRefun Ok(Self { amount: OrderAmount { currency_code: item.router_data.request.currency, - value: item.amount.to_owned(), + value: item.amount.clone(), }, }) } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 53b0063c6e..7d0ab75054 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -24,6 +24,7 @@ use common_utils::{ consts, ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, generate_id, id_type, + types::MinorUnit, }; use diesel_models::{ business_profile::BusinessProfile, encryption::Encryption, enums as storage_enums, @@ -2862,11 +2863,7 @@ pub async fn filter_payment_methods( &payment_method_type_info, req.installment_payment_enabled, ) - && filter_amount_based( - &payment_method_type_info, - req.amount - .map(|minor_amount| minor_amount.get_amount_as_i64()), - ) + && filter_amount_based(&payment_method_type_info, req.amount) { let mut payment_method_object = payment_method_type_info; @@ -3108,6 +3105,19 @@ fn filter_pm_based_on_capture_method_used( .unwrap_or(true) } +fn filter_amount_based( + payment_method: &RequestPaymentMethodTypes, + amount: Option, +) -> bool { + let min_check = amount + .and_then(|amt| payment_method.minimum_amount.map(|min_amt| amt >= min_amt)) + .unwrap_or(true); + let max_check = amount + .and_then(|amt| payment_method.maximum_amount.map(|max_amt| amt <= max_amt)) + .unwrap_or(true); + (min_check && max_check) || amount == Some(MinorUnit::zero()) +} + fn card_network_filter( country: &Option, currency: Option, @@ -3279,32 +3289,6 @@ fn filter_disabled_enum_based( } } -fn filter_amount_based(payment_method: &RequestPaymentMethodTypes, amount: Option) -> bool { - let min_check = amount - .and_then(|amt| { - payment_method - .minimum_amount - .map(|min_amt| amt >= min_amt.into()) - }) - .unwrap_or(true); - let max_check = amount - .and_then(|amt| { - payment_method - .maximum_amount - .map(|max_amt| amt <= max_amt.into()) - }) - .unwrap_or(true); - // let min_check = match (amount, payment_method.minimum_amount) { - // (Some(amt), Some(min_amt)) => amt >= min_amt, - // (_, _) => true, - // }; - // let max_check = match (amount, payment_method.maximum_amount) { - // (Some(amt), Some(max_amt)) => amt <= max_amt, - // (_, _) => true, - // }; - (min_check && max_check) || amount == Some(0) -} - fn filter_pm_based_on_allowed_types( allowed_types: Option<&Vec>, payment_method_type: &api_enums::PaymentMethodType, @@ -3360,10 +3344,10 @@ fn filter_payment_amount_based( payment_intent: &storage::PaymentIntent, pm: &RequestPaymentMethodTypes, ) -> bool { - 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.get_amount_as_i64() == 0 + let amount = payment_intent.amount; + (pm.maximum_amount.map_or(true, |amt| amount <= amt) + && pm.minimum_amount.map_or(true, |amt| amount >= amt)) + || payment_intent.amount == MinorUnit::zero() } async fn filter_payment_mandate_based( diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 3afb933d46..042bf8612e 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -211,7 +211,7 @@ where }; let payment_input = dsl_inputs::PaymentInput { - amount: payment_data.payment_intent.amount.get_amount_as_i64(), + amount: payment_data.payment_intent.amount, card_bin: payment_data .payment_method_data .as_ref() @@ -904,7 +904,7 @@ pub async fn perform_session_flow_routing( }; let payment_input = dsl_inputs::PaymentInput { - amount: session_input.payment_intent.amount.get_amount_as_i64(), + amount: session_input.payment_intent.amount, currency: session_input .payment_intent .currency @@ -1138,7 +1138,7 @@ pub fn make_dsl_input_for_surcharge( payment_type: None, }; let payment_input = dsl_inputs::PaymentInput { - amount: payment_attempt.amount.get_amount_as_i64(), + amount: payment_attempt.amount, // currency is always populated in payment_attempt during payment create currency: payment_attempt .currency diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 200f23f49a..ec5935d0ab 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -4,6 +4,7 @@ use api_models::payouts; use common_utils::{ ext_traits::{Encode, OptionExt}, link_utils, + types::{AmountConvertor, StringMajorUnitForConnector}, }; use diesel_models::PayoutLinkUpdate; use error_stack::ResultExt; @@ -102,9 +103,9 @@ pub async fn initiate_payout_link( // Initiate Payout link flow (_, link_utils::PayoutLinkStatus::Initiated) => { let customer_id = link_data.customer_id; - let amount = payout - .destination_currency - .to_currency_base_unit(payout.amount) + let required_amount_type = StringMajorUnitForConnector; + let amount = required_amount_type + .convert(payout.amount, payout.destination_currency) .change_context(errors::ApiErrorResponse::CurrencyConversionFailed)?; // Fetch customer let customer = db diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 6305907500..e968f8bb6f 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -1919,7 +1919,7 @@ pub async fn response_handler( let response = api::PayoutCreateResponse { payout_id: payouts.payout_id.to_owned(), merchant_id: merchant_account.merchant_id.to_owned(), - amount: payouts.amount.to_owned(), + amount: payouts.amount, currency: payouts.destination_currency.to_owned(), connector: payout_attempt.connector.to_owned(), payout_type: payouts.payout_type.to_owned(), @@ -2045,7 +2045,7 @@ pub async fn payout_create_db_entries( consts::ID_LENGTH, format!("payout_{payout_id}_secret").as_str(), ); - let amount = MinorUnit::from(req.amount.unwrap_or(api::Amount::Zero)).get_amount_as_i64(); + let amount = MinorUnit::from(req.amount.unwrap_or(api::Amount::Zero)); let payouts_req = storage::PayoutsNew { payout_id: payout_id.to_string(), merchant_id: merchant_id.to_string(), diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 0b54205bf6..7a353ec157 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -959,8 +959,7 @@ pub async fn update_payouts_and_payout_attempt( // 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 amount = MinorUnit::from(req.amount.unwrap_or(payouts.amount.into())); let updated_payouts = storage::PayoutsUpdate::Update { amount, destination_currency: req diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index bcd7a451c9..7c7215b57c 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -170,7 +170,8 @@ pub async fn construct_payout_router_data<'a, F>( payment_method_status: None, request: types::PayoutsData { payout_id: payouts.payout_id.to_owned(), - amount: payouts.amount, + amount: payouts.amount.get_amount_as_i64(), + minor_amount: payouts.amount, connector_payout_id: payout_attempt.connector_payout_id.clone(), destination_currency: payouts.destination_currency, source_currency: payouts.source_currency, diff --git a/crates/router/src/services/kafka/payout.rs b/crates/router/src/services/kafka/payout.rs index ed00888a46..8796d2ca90 100644 --- a/crates/router/src/services/kafka/payout.rs +++ b/crates/router/src/services/kafka/payout.rs @@ -1,4 +1,4 @@ -use common_utils::{id_type, pii}; +use common_utils::{id_type, pii, types::MinorUnit}; use diesel_models::enums as storage_enums; use hyperswitch_domain_models::payouts::{payout_attempt::PayoutAttempt, payouts::Payouts}; use time::OffsetDateTime; @@ -13,7 +13,7 @@ pub struct KafkaPayout<'a> { pub profile_id: &'a String, pub payout_method_id: Option<&'a String>, pub payout_type: Option, - pub amount: i64, + pub amount: MinorUnit, pub destination_currency: storage_enums::Currency, pub source_currency: storage_enums::Currency, pub description: Option<&'a String>, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 03ea2b2d61..58bb0d8c1d 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -477,7 +477,9 @@ impl ConnectorData { enums::Connector::Nexinets => { Ok(ConnectorEnum::Old(Box::new(&connector::Nexinets))) } - enums::Connector::Paypal => Ok(ConnectorEnum::Old(Box::new(&connector::Paypal))), + enums::Connector::Paypal => { + Ok(ConnectorEnum::Old(Box::new(connector::Paypal::new()))) + } enums::Connector::Trustpay => { Ok(ConnectorEnum::Old(Box::new(connector::Trustpay::new()))) } diff --git a/crates/router/tests/connectors/paypal.rs b/crates/router/tests/connectors/paypal.rs index 51effb276c..0b3d040838 100644 --- a/crates/router/tests/connectors/paypal.rs +++ b/crates/router/tests/connectors/paypal.rs @@ -14,7 +14,7 @@ impl Connector for PaypalTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Paypal; utils::construct_connector_data_old( - Box::new(&Paypal), + Box::new(Paypal::new()), types::Connector::Paypal, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index ea7787a2c2..a9ffdbe697 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -451,6 +451,7 @@ pub trait ConnectorActions: Connector { payout_id: core_utils::get_or_generate_uuid("payout_id", None) .map_or("payout_3154763247".to_string(), |p| p), amount: 1, + minor_amount: MinorUnit::new(1), connector_payout_id, destination_currency: payment_info.to_owned().map_or(enums::Currency::EUR, |pi| { pi.currency.map_or(enums::Currency::EUR, |c| c)