From 28258a5bfee334be6abbf28ed46846822b4cc240 Mon Sep 17 00:00:00 2001 From: Kashif Date: Fri, 28 Jun 2024 15:50:54 +0530 Subject: [PATCH 1/9] refactor: use hashmap deserializer for generic_link options refactor: use url crate for handling URLs instead of strings in generic_link flows --- Cargo.lock | 1 + config/config.example.toml | 12 +- config/deployments/env_specific.toml | 12 +- config/development.toml | 12 +- config/docker_compose.toml | 12 +- crates/api_models/src/payment_methods.rs | 8 +- crates/api_models/src/payouts.rs | 10 +- crates/common_utils/Cargo.toml | 1 + crates/common_utils/src/link_utils.rs | 14 +-- crates/router/src/configs/settings.rs | 108 ++++++++++++++++-- crates/router/src/configs/validations.rs | 6 - .../generic_link/payout_link/status/script.js | 2 +- crates/router/src/core/payment_methods.rs | 22 +++- crates/router/src/core/payout_link.rs | 20 +++- crates/router/src/core/payouts.rs | 19 ++- crates/router/src/core/payouts/validator.rs | 7 +- 16 files changed, 192 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c7c88e61964..9360bbc19016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1980,6 +1980,7 @@ dependencies = [ "thiserror", "time", "tokio 1.37.0", + "url", "utoipa", "uuid", ] diff --git a/config/config.example.toml b/config/config.example.toml index f29876e648b1..19cd03a69777 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -551,9 +551,9 @@ theme = "#1A1A1A" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payment_method_collect.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [generic_link.payout_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" @@ -563,9 +563,9 @@ theme = "#1A1A1A" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [payment_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index 652b4950316d..92a61478f5a0 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -166,9 +166,9 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payment_method_collect.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [generic_link.payout_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" @@ -178,9 +178,9 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [payment_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" diff --git a/config/development.toml b/config/development.toml index 4e230cd20252..1e4dd062075d 100644 --- a/config/development.toml +++ b/config/development.toml @@ -574,9 +574,9 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payment_method_collect.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [generic_link.payout_link] sdk_url = "http://localhost:9050/HyperLoader.js" @@ -586,9 +586,9 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [payment_link] sdk_url = "http://localhost:9050/HyperLoader.js" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 791f2510824f..c871557abf28 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -522,9 +522,9 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payment_method_collect.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" [generic_link.payout_link] sdk_url = "http://localhost:9090/0.16.7/v0/HyperLoader.js" @@ -534,6 +534,6 @@ theme = "#4285F4" logo = "https://app.hyperswitch.io/HyperswitchFavicon.png" merchant_name = "HyperSwitch" [generic_link.payout_link.enabled_payment_methods] -card = ["credit", "debit"] -bank_transfer = ["ach", "bacs", "sepa"] -wallet = ["paypal", "pix", "venmo"] +card = "credit,debit" +bank_transfer = "ach,bacs,sepa" +wallet = "paypal,pix,venmo" diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 1f82508e15fc..0ec985403605 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -989,7 +989,7 @@ pub struct PaymentMethodCollectLinkResponse { /// URL to the form's link generated for collecting payment method details. #[schema(value_type = String, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1")] - pub link: masking::Secret, + pub link: url::Url, /// Redirect to this URL post completion #[schema(value_type = Option, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")] @@ -1026,7 +1026,7 @@ pub struct PaymentMethodCollectLinkDetails { pub session_expiry: time::PrimitiveDateTime, pub return_url: Option, #[serde(flatten)] - pub ui_config: link_utils::GenericLinkUIConfigFormData, + pub ui_config: link_utils::GenericLinkUiConfigFormData, pub enabled_payment_methods: Option>, } @@ -1036,10 +1036,10 @@ pub struct PaymentMethodCollectLinkStatusDetails { pub customer_id: id_type::CustomerId, #[serde(with = "common_utils::custom_serde::iso8601")] pub session_expiry: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub status: link_utils::PaymentMethodCollectStatus, #[serde(flatten)] - pub ui_config: link_utils::GenericLinkUIConfigFormData, + pub ui_config: link_utils::GenericLinkUiConfigFormData, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index eb696ca59c5e..795b2e9511ba 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -701,7 +701,7 @@ pub struct PayoutListFilters { pub struct PayoutLinkResponse { pub payout_link_id: String, #[schema(value_type = String)] - pub link: Secret, + pub link: url::Url, } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] @@ -719,9 +719,9 @@ pub struct PayoutLinkDetails { pub customer_id: id_type::CustomerId, #[serde(with = "common_utils::custom_serde::iso8601")] pub session_expiry: PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, #[serde(flatten)] - pub ui_config: link_utils::GenericLinkUIConfigFormData, + pub ui_config: link_utils::GenericLinkUiConfigFormData, pub enabled_payment_methods: Vec, pub amount: String, pub currency: common_enums::Currency, @@ -734,10 +734,10 @@ pub struct PayoutLinkStatusDetails { pub customer_id: id_type::CustomerId, #[serde(with = "common_utils::custom_serde::iso8601")] pub session_expiry: PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub status: api_enums::PayoutStatus, pub error_code: Option, pub error_message: Option, #[serde(flatten)] - pub ui_config: link_utils::GenericLinkUIConfigFormData, + pub ui_config: link_utils::GenericLinkUiConfigFormData, } diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index 6bf719da4821..58cdf447fd4d 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -41,6 +41,7 @@ strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"], optional = true } +url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } uuid = { version = "1.8.0", features = ["v7"] } diff --git a/crates/common_utils/src/link_utils.rs b/crates/common_utils/src/link_utils.rs index 393a8cb9a9ef..fd254b5fb411 100644 --- a/crates/common_utils/src/link_utils.rs +++ b/crates/common_utils/src/link_utils.rs @@ -148,7 +148,7 @@ pub struct PayoutLinkData { /// Identifier for the payouts resource pub payout_id: String, /// Link to render the payout link - pub link: Secret, + pub link: url::Url, /// Client secret generated for authenticating frontend APIs pub client_secret: Secret, /// Expiry in seconds from the time it was created @@ -167,11 +167,11 @@ pub struct PayoutLinkData { crate::impl_to_sql_from_sql_json!(PayoutLinkData); /// Object for GenericLinkUiConfig -#[derive(Clone, Debug, Default, serde::Deserialize, Serialize, ToSchema)] +#[derive(Clone, Debug, serde::Deserialize, Serialize, ToSchema)] pub struct GenericLinkUiConfig { /// Merchant's display logo #[schema(value_type = Option, max_length = 255, example = "https://hyperswitch.io/favicon.ico")] - pub logo: Option, + pub logo: Option, /// Custom merchant name for the link #[schema(value_type = Option, max_length = 255, example = "Hyperswitch")] @@ -182,12 +182,12 @@ pub struct GenericLinkUiConfig { pub theme: Option, } -/// Object for GenericLinkUIConfigFormData -#[derive(Clone, Debug, Default, serde::Deserialize, Serialize, ToSchema)] -pub struct GenericLinkUIConfigFormData { +/// Object for GenericLinkUiConfigFormData +#[derive(Clone, Debug, serde::Deserialize, Serialize, ToSchema)] +pub struct GenericLinkUiConfigFormData { /// Merchant's display logo #[schema(value_type = String, max_length = 255, example = "https://hyperswitch.io/favicon.ico")] - pub logo: String, + pub logo: url::Url, /// Custom merchant name for the link #[schema(value_type = String, max_length = 255, example = "Hyperswitch")] diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 807563917419..78e548892b46 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -207,27 +207,62 @@ pub struct KvConfig { pub soft_kill: Option, } -#[derive(Debug, Deserialize, Clone, Default)] +#[derive(Debug, Deserialize, Clone)] pub struct GenericLink { pub payment_method_collect: GenericLinkEnvConfig, pub payout_link: GenericLinkEnvConfig, } -#[derive(Debug, Deserialize, Clone, Default)] +impl Default for GenericLink { + fn default() -> Self { + Self { + payment_method_collect: GenericLinkEnvConfig::default(), + payout_link: GenericLinkEnvConfig::default(), + } + } +} + +#[derive(Debug, Deserialize, Clone)] pub struct GenericLinkEnvConfig { - pub sdk_url: String, + pub sdk_url: url::Url, pub expiry: u32, pub ui_config: GenericLinkEnvUiConfig, + #[serde(deserialize_with = "deserialize_hashmap")] pub enabled_payment_methods: HashMap>, } -#[derive(Debug, Deserialize, Clone, Default)] +#[allow(clippy::panic)] +impl Default for GenericLinkEnvConfig { + fn default() -> Self { + Self { + sdk_url: url::Url::parse("http://localhost:9050/HyperLoader.js") + .unwrap_or_else(|_| panic!("Failed to parse default SDK URL")), + expiry: 900, + ui_config: GenericLinkEnvUiConfig::default(), + enabled_payment_methods: HashMap::default(), + } + } +} + +#[derive(Debug, Deserialize, Clone)] pub struct GenericLinkEnvUiConfig { - pub logo: String, + pub logo: url::Url, pub merchant_name: Secret, pub theme: String, } +#[allow(clippy::panic)] +impl Default for GenericLinkEnvUiConfig { + fn default() -> Self { + Self { + logo: url::Url::parse("https://hyperswitch.io/favicon.ico") + .unwrap_or_else(|_| panic!("Failed to parse default logo URL")), + merchant_name: Secret::new("HyperSwitch".to_string()), + theme: "#4285F4".to_string(), + } + } +} + #[derive(Debug, Deserialize, Clone, Default)] pub struct PaymentLink { pub sdk_url: String, @@ -676,13 +711,7 @@ impl Settings { .with_list_parse_key("redis.cluster_urls") .with_list_parse_key("events.kafka.brokers") .with_list_parse_key("connectors.supported.wallets") - .with_list_parse_key("connector_request_reference_id_config.merchant_ids_send_payment_id_as_connector_request_id") - .with_list_parse_key("generic_link.payment_method_collect.enabled_payment_methods.card") - .with_list_parse_key("generic_link.payment_method_collect.enabled_payment_methods.bank_transfer") - .with_list_parse_key("generic_link.payment_method_collect.enabled_payment_methods.wallet") - .with_list_parse_key("generic_link.payout_link.enabled_payment_methods.card") - .with_list_parse_key("generic_link.payout_link.enabled_payment_methods.bank_transfer") - .with_list_parse_key("generic_link.payout_link.enabled_payment_methods.wallet"), + .with_list_parse_key("connector_request_reference_id_config.merchant_ids_send_payment_id_as_connector_request_id"), ) .build()?; @@ -815,6 +844,61 @@ pub struct PayPalOnboarding { pub enabled: bool, } +fn deserialize_hashmap_inner( + value: HashMap, +) -> Result>, String> +where + K: Eq + std::str::FromStr + std::hash::Hash, + V: Eq + std::str::FromStr + std::hash::Hash, + ::Err: std::fmt::Display, + ::Err: std::fmt::Display, +{ + let (values, errors) = value + .into_iter() + .map( + |(k, v)| match (K::from_str(&k), deserialize_hashset_inner(v)) { + (Err(error), _) => Err(format!( + "Unable to deserialize `{}` as `{}`: {error}", + k, + std::any::type_name::() + )), + (_, Err(error)) => Err(error), + (Ok(key), Ok(value)) => Ok((key, value.into_iter().collect())), + }, + ) + .fold( + (HashMap::new(), Vec::new()), + |(mut values, mut errors), result| match result { + Ok((key, value)) => { + values.insert(key, value); + (values, errors) + } + Err(error) => { + errors.push(error); + (values, errors) + } + }, + ); + if !errors.is_empty() { + Err(format!("Some errors occurred:\n{}", errors.join("\n"))) + } else { + Ok(values) + } +} + +fn deserialize_hashmap<'a, D, K, V>(deserializer: D) -> Result>, D::Error> +where + D: serde::Deserializer<'a>, + K: Eq + std::str::FromStr + std::hash::Hash, + V: Eq + std::str::FromStr + std::hash::Hash, + ::Err: std::fmt::Display, + ::Err: std::fmt::Display, +{ + use serde::de::Error; + deserialize_hashmap_inner(>::deserialize(deserializer)?) + .map_err(D::Error::custom) +} + fn deserialize_hashset_inner(value: impl AsRef) -> Result, String> where T: Eq + std::str::FromStr + std::hash::Hash, diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs index 168ae3181466..441172b05cca 100644 --- a/crates/router/src/configs/validations.rs +++ b/crates/router/src/configs/validations.rs @@ -189,12 +189,6 @@ impl super::settings::GenericLinkEnvConfig { Err(ApplicationError::InvalidConfigurationValueError( "link's expiry should not be 0".into(), )) - })?; - - when(self.sdk_url.is_empty(), || { - Err(ApplicationError::InvalidConfigurationValueError( - "sdk_url to be integrated in the link cannot be empty".into(), - )) }) } } diff --git a/crates/router/src/core/generic_link/payout_link/status/script.js b/crates/router/src/core/generic_link/payout_link/status/script.js index ba19f6bff9e6..829006a43114 100644 --- a/crates/router/src/core/generic_link/payout_link/status/script.js +++ b/crates/router/src/core/generic_link/payout_link/status/script.js @@ -76,6 +76,7 @@ function renderStatusDetails(payoutDetails) { case "success": break; case "initiated": + case "requires_fulfillment": case "pending": statusInfo.statusImageSrc = "https://live.hyperswitch.io/payment-link-assets/pending.png"; @@ -91,7 +92,6 @@ function renderStatusDetails(payoutDetails) { case "requires_creation": case "requires_confirmation": case "requires_payout_method_data": - case "requires_fulfillment": case "requires_vendor_account_creation": default: statusInfo.statusImageSrc = diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index faa28ffdbc85..f95a9872222f 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -12,6 +12,7 @@ use diesel_models::{ }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; +use masking::PeekInterface; use router_env::{instrument, tracing}; use time::Duration; @@ -140,11 +141,16 @@ pub async fn initiate_pm_collect_link( )?; // Return response + let url = pm_collect_link.url.peek(); let response = payment_methods::PaymentMethodCollectLinkResponse { pm_collect_link_id: pm_collect_link.link_id, customer_id, expiry: pm_collect_link.expiry, - link: pm_collect_link.url, + link: url::Url::parse(url) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| { + format!("Failed to parse the payment method collect link - {}", url) + })?, return_url: pm_collect_link.return_url, ui_config: pm_collect_link.link_data.ui_config, enabled_payment_methods: pm_collect_link.link_data.enabled_payment_methods, @@ -209,7 +215,7 @@ pub async fn render_pm_collect_link( let link_data = pm_collect_link.link_data; let default_config = &state.conf.generic_link.payment_method_collect; let default_ui_config = default_config.ui_config.clone(); - let ui_config_data = common_utils::link_utils::GenericLinkUIConfigFormData { + let ui_config_data = common_utils::link_utils::GenericLinkUiConfigFormData { merchant_name: link_data .ui_config .merchant_name @@ -290,7 +296,7 @@ pub async fn render_pm_collect_link( let generic_form_data = services::GenericLinkFormData { js_data: serialized_js_content, css_data: serialized_css_content, - sdk_url: default_config.sdk_url.clone(), + sdk_url: default_config.sdk_url.to_string(), html_meta_tags: String::new(), }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( @@ -305,7 +311,15 @@ pub async fn render_pm_collect_link( pm_collect_link_id: pm_collect_link.link_id, customer_id: link_data.customer_id, session_expiry: pm_collect_link.expiry, - return_url: pm_collect_link.return_url, + return_url: pm_collect_link + .return_url + .as_ref() + .map(|url| url::Url::parse(url)) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Failed to parse return URL for payment method collect's status link", + )?, ui_config: ui_config_data, status, }; diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 200f23f49a40..4bd97267a69e 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -63,7 +63,7 @@ pub async fn initiate_payout_link( let link_data = payout_link.link_data.clone(); let default_config = &state.conf.generic_link.payout_link; let default_ui_config = default_config.ui_config.clone(); - let ui_config_data = link_utils::GenericLinkUIConfigFormData { + let ui_config_data = link_utils::GenericLinkUiConfigFormData { merchant_name: link_data .ui_config .merchant_name @@ -160,7 +160,13 @@ pub async fn initiate_payout_link( payout_id: payout_link.primary_reference, customer_id: customer.customer_id, session_expiry: payout_link.expiry, - return_url: payout_link.return_url, + return_url: payout_link + .return_url + .as_ref() + .map(|url| url::Url::parse(url)) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse payout status link's return URL")?, ui_config: ui_config_data, enabled_payment_methods, amount, @@ -180,7 +186,7 @@ pub async fn initiate_payout_link( let generic_form_data = services::GenericLinkFormData { js_data: serialized_js_content, css_data: serialized_css_content, - sdk_url: default_config.sdk_url.clone(), + sdk_url: default_config.sdk_url.to_string(), html_meta_tags: String::new(), }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( @@ -195,7 +201,13 @@ pub async fn initiate_payout_link( payout_id: payout_link.primary_reference, customer_id: link_data.customer_id, session_expiry: payout_link.expiry, - return_url: payout_link.return_url, + return_url: payout_link + .return_url + .as_ref() + .map(|url| url::Url::parse(url)) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse payout status link's return URL")?, status: payout.status, error_code: payout_attempt.error_code, error_message: payout_attempt.error_message, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 630590750046..d9ee7776188a 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -20,6 +20,7 @@ use error_stack::{report, ResultExt}; use futures::future::join_all; #[cfg(feature = "olap")] use hyperswitch_domain_models::errors::StorageError; +use masking::PeekInterface; #[cfg(feature = "payout_retry")] use retry::GsmValidation; #[cfg(feature = "olap")] @@ -379,7 +380,6 @@ pub async fn payouts_confirm_core( storage_enums::PayoutStatus::Ineligible, storage_enums::PayoutStatus::RequiresFulfillment, storage_enums::PayoutStatus::RequiresVendorAccountCreation, - storage_enums::PayoutStatus::RequiresVendorAccountCreation, ], "confirm", )?; @@ -1946,10 +1946,19 @@ pub async fn response_handler( connector_transaction_id: payout_attempt.connector_payout_id, priority: payouts.priority, attempts: None, - payout_link: payout_link.map(|payout_link| PayoutLinkResponse { - payout_link_id: payout_link.link_id.clone(), - link: payout_link.url, - }), + payout_link: payout_link + .map( + |payout_link| match url::Url::parse(payout_link.url.peek()) { + Ok(link) => Ok(PayoutLinkResponse { + payout_link_id: payout_link.link_id, + link, + }), + Err(err) => Err(err), + }, + ) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse payout link's URL")?, }; Ok(services::ApplicationResponse::Json(response)) } diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index 5bd5d62b6a11..bd7ce7473523 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -251,7 +251,10 @@ pub async fn create_payout_link( .session_expiry .as_ref() .map_or(default_config.expiry, |expiry| *expiry); - let link = Secret::new(format!("{base_url}/payout_link/{merchant_id}/{payout_id}")); + let url = format!("{base_url}/payout_link/{merchant_id}/{payout_id}"); + let link = url::Url::parse(&url) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| format!("Failed to form payout link URL - {}", url))?; let req_enabled_payment_methods = payout_link_config_req .as_ref() .and_then(|req| req.enabled_payment_methods.to_owned()); @@ -301,7 +304,7 @@ pub async fn create_payout_link_db_entry( link_type: common_enums::GenericLinkType::PayoutLink, link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated), link_data, - url: payout_link_data.link.clone(), + url: Secret::new(payout_link_data.link.to_string()), return_url, expiry: common_utils::date_time::now() + Duration::seconds(payout_link_data.session_expiry.into()), From c3452fc2559207205de5916f1b79a3dacc460b67 Mon Sep 17 00:00:00 2001 From: Kashif Date: Fri, 28 Jun 2024 16:09:23 +0530 Subject: [PATCH 2/9] refactor: use default derive macro for for GenericLink in settings --- crates/router/src/configs/settings.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 78e548892b46..23b534644cf3 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -207,21 +207,12 @@ pub struct KvConfig { pub soft_kill: Option, } -#[derive(Debug, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone, Default)] pub struct GenericLink { pub payment_method_collect: GenericLinkEnvConfig, pub payout_link: GenericLinkEnvConfig, } -impl Default for GenericLink { - fn default() -> Self { - Self { - payment_method_collect: GenericLinkEnvConfig::default(), - payout_link: GenericLinkEnvConfig::default(), - } - } -} - #[derive(Debug, Deserialize, Clone)] pub struct GenericLinkEnvConfig { pub sdk_url: url::Url, From 7597e7e8adfeaf7d0cfb0cbfdba8f3913c7b0b2c Mon Sep 17 00:00:00 2001 From: Kashif Date: Sun, 30 Jun 2024 18:16:44 +0530 Subject: [PATCH 3/9] refactor: resolve comments --- Cargo.lock | 2 + crates/api_models/src/payment_methods.rs | 2 +- crates/api_models/src/payouts.rs | 2 +- crates/masking/Cargo.toml | 1 + crates/masking/src/serde.rs | 1 + crates/router/src/configs/settings.rs | 119 +++++++++++++++++++- crates/router/src/core/payment_methods.rs | 3 +- crates/router/src/core/payouts.rs | 15 ++- crates/router/src/core/payouts/validator.rs | 2 +- 9 files changed, 131 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e0d64541582..0a974dfb8c2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2625,6 +2625,7 @@ dependencies = [ "strum 0.26.2", "thiserror", "time", + "url", ] [[package]] @@ -4308,6 +4309,7 @@ dependencies = [ "serde_json", "subtle", "time", + "url", "zeroize", ] diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 9f2b1deef43a..0e2fd7b70393 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -989,7 +989,7 @@ pub struct PaymentMethodCollectLinkResponse { /// URL to the form's link generated for collecting payment method details. #[schema(value_type = String, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1")] - pub link: url::Url, + pub link: masking::Secret, /// Redirect to this URL post completion #[schema(value_type = Option, example = "https://sandbox.hyperswitch.io/payment_method/collect/pm_collect_link_2bdacf398vwzq5n422S1/status")] diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index c15dd1770bf3..49a053dd603b 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -702,7 +702,7 @@ pub struct PayoutListFilters { pub struct PayoutLinkResponse { pub payout_link_id: String, #[schema(value_type = String)] - pub link: url::Url, + pub link: Secret, } #[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)] diff --git a/crates/masking/Cargo.toml b/crates/masking/Cargo.toml index 404d4210f13b..5c73ed7ea328 100644 --- a/crates/masking/Cargo.toml +++ b/crates/masking/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1.0.115", optional = true } subtle = "2.5.0" time = {version = "0.3.35", optional = true, features = ["serde-human-readable"] } +url = { version = "2.5.0", features = ["serde"] } zeroize = { version = "1.7", default-features = false } [dev-dependencies] diff --git a/crates/masking/src/serde.rs b/crates/masking/src/serde.rs index 86f00390d46e..45f7fbcb3d08 100644 --- a/crates/masking/src/serde.rs +++ b/crates/masking/src/serde.rs @@ -29,6 +29,7 @@ impl SerializableSecret for u8 {} impl SerializableSecret for u16 {} impl SerializableSecret for i8 {} impl SerializableSecret for i32 {} +impl SerializableSecret for url::Url {} #[cfg(feature = "time")] impl SerializableSecret for time::Date {} diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index bee4a5442fe8..f99f65b58862 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -222,12 +222,12 @@ pub struct GenericLinkEnvConfig { pub enabled_payment_methods: HashMap>, } -#[allow(clippy::panic)] impl Default for GenericLinkEnvConfig { fn default() -> Self { Self { + #[allow(clippy::expect_used)] sdk_url: url::Url::parse("http://localhost:9050/HyperLoader.js") - .unwrap_or_else(|_| panic!("Failed to parse default SDK URL")), + .expect("Failed to parse default SDK URL"), expiry: 900, ui_config: GenericLinkEnvUiConfig::default(), enabled_payment_methods: HashMap::default(), @@ -246,8 +246,9 @@ pub struct GenericLinkEnvUiConfig { impl Default for GenericLinkEnvUiConfig { fn default() -> Self { Self { + #[allow(clippy::expect_used)] logo: url::Url::parse("https://hyperswitch.io/favicon.ico") - .unwrap_or_else(|_| panic!("Failed to parse default logo URL")), + .expect("Failed to parse default logo URL"), merchant_name: Secret::new("HyperSwitch".to_string()), theme: "#4285F4".to_string(), } @@ -858,7 +859,7 @@ where let (values, errors) = value .into_iter() .map( - |(k, v)| match (K::from_str(&k), deserialize_hashset_inner(v)) { + |(k, v)| match (K::from_str(&k.trim()), deserialize_hashset_inner(v)) { (Err(error), _) => Err(format!( "Unable to deserialize `{}` as `{}`: {error}", k, @@ -969,6 +970,116 @@ where })? } +#[cfg(test)] +mod hashmap_deserialization_test { + #![allow(clippy::expect_used)] + use std::collections::HashMap; + + use serde::de::{ + value::{Error as ValueError, MapDeserializer}, + IntoDeserializer, + }; + + use super::deserialize_hashmap; + + #[test] + fn test_payment_method_and_payment_method_types() { + use diesel_models::enums::{PaymentMethod, PaymentMethodType}; + + let input_map: HashMap = serde_json::json!({ + "bank_transfer": "ach,bacs", + "wallet": "paypal,venmo", + }) + .as_object() + .expect("Failed to parse input JSON as an object") + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + let deserializer: MapDeserializer< + '_, + std::collections::hash_map::IntoIter, + ValueError, + > = input_map.into_deserializer(); + let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer); + let expected_result = HashMap::from([ + ( + PaymentMethod::BankTransfer, + vec![PaymentMethodType::Ach, PaymentMethodType::Bacs], + ), + ( + PaymentMethod::Wallet, + vec![PaymentMethodType::Paypal, PaymentMethodType::Venmo], + ), + ]); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected_result); + } + + #[test] + fn test_payment_method_and_payment_method_types_with_spaces() { + use diesel_models::enums::{PaymentMethod, PaymentMethodType}; + + let input_map: HashMap = serde_json::json!({ + " bank_transfer ": " ach , bacs ", + "wallet ": " paypal , pix , venmo ", + }) + .as_object() + .expect("Failed to parse input JSON as an object") + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + let deserializer: MapDeserializer< + '_, + std::collections::hash_map::IntoIter, + ValueError, + > = input_map.into_deserializer(); + let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer); + let expected_result = HashMap::from([ + ( + PaymentMethod::BankTransfer, + vec![PaymentMethodType::Ach, PaymentMethodType::Bacs], + ), + ( + PaymentMethod::Wallet, + vec![ + PaymentMethodType::Paypal, + PaymentMethodType::Pix, + PaymentMethodType::Venmo, + ], + ), + ]); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected_result); + } + + #[test] + fn test_payment_method_deserializer_error() { + use diesel_models::enums::{PaymentMethod, PaymentMethodType}; + + let input_map: HashMap = serde_json::json!({ + "unknown": "ach,bacs", + "wallet": "paypal,unknown", + }) + .as_object() + .expect("Failed to parse input JSON as an object") + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + let deserializer: MapDeserializer< + '_, + std::collections::hash_map::IntoIter, + ValueError, + > = input_map.into_deserializer(); + let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer); + + println!("TEST ERR {:?}", result); + + assert!(result.is_err()); + } +} + #[cfg(test)] mod hashset_deserialization_test { #![allow(clippy::unwrap_used)] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index f95a9872222f..baf013927bf1 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -150,7 +150,8 @@ pub async fn initiate_pm_collect_link( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { format!("Failed to parse the payment method collect link - {}", url) - })?, + })? + .into(), return_url: pm_collect_link.return_url, ui_config: pm_collect_link.link_data.ui_config, enabled_payment_methods: pm_collect_link.link_data.enabled_payment_methods, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 1b6a616f6f16..2ca99df26922 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -1947,15 +1947,14 @@ pub async fn response_handler( priority: payouts.priority, attempts: None, payout_link: payout_link - .map( - |payout_link| match url::Url::parse(payout_link.url.peek()) { - Ok(link) => Ok(PayoutLinkResponse { + .map(|payout_link| { + url::Url::parse(payout_link.url.peek()).and_then(|link| { + Ok(PayoutLinkResponse { payout_link_id: payout_link.link_id, - link, - }), - Err(err) => Err(err), - }, - ) + link: link.into(), + }) + }) + }) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse payout link's URL")?, diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index bd7ce7473523..3e69216167de 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -304,7 +304,7 @@ pub async fn create_payout_link_db_entry( link_type: common_enums::GenericLinkType::PayoutLink, link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated), link_data, - url: Secret::new(payout_link_data.link.to_string()), + url: payout_link_data.link.to_string().into(), return_url, expiry: common_utils::date_time::now() + Duration::seconds(payout_link_data.session_expiry.into()), From 19ab6437e1f34b8bb38ac1c3d8e7d12c172e93ae Mon Sep 17 00:00:00 2001 From: Kashif Date: Sun, 30 Jun 2024 18:20:28 +0530 Subject: [PATCH 4/9] refactor: clippy fixes --- Cargo.lock | 1 - crates/router/src/configs/settings.rs | 10 +++++----- crates/router/src/core/payouts.rs | 8 +++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a974dfb8c2a..e6bff40bdef2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2625,7 +2625,6 @@ dependencies = [ "strum 0.26.2", "thiserror", "time", - "url", ] [[package]] diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index f99f65b58862..ffc89786cee4 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -859,7 +859,7 @@ where let (values, errors) = value .into_iter() .map( - |(k, v)| match (K::from_str(&k.trim()), deserialize_hashset_inner(v)) { + |(k, v)| match (K::from_str(k.trim()), deserialize_hashset_inner(v)) { (Err(error), _) => Err(format!( "Unable to deserialize `{}` as `{}`: {error}", k, @@ -972,7 +972,7 @@ where #[cfg(test)] mod hashmap_deserialization_test { - #![allow(clippy::expect_used)] + #![allow(clippy::unwrap_used)] use std::collections::HashMap; use serde::de::{ @@ -991,7 +991,7 @@ mod hashmap_deserialization_test { "wallet": "paypal,venmo", }) .as_object() - .expect("Failed to parse input JSON as an object") + .unwrap() .iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); @@ -1025,7 +1025,7 @@ mod hashmap_deserialization_test { "wallet ": " paypal , pix , venmo ", }) .as_object() - .expect("Failed to parse input JSON as an object") + .unwrap() .iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); @@ -1063,7 +1063,7 @@ mod hashmap_deserialization_test { "wallet": "paypal,unknown", }) .as_object() - .expect("Failed to parse input JSON as an object") + .unwrap() .iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 2ca99df26922..f6f78094943c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -1948,11 +1948,9 @@ pub async fn response_handler( attempts: None, payout_link: payout_link .map(|payout_link| { - url::Url::parse(payout_link.url.peek()).and_then(|link| { - Ok(PayoutLinkResponse { - payout_link_id: payout_link.link_id, - link: link.into(), - }) + url::Url::parse(payout_link.url.peek()).map(|link| PayoutLinkResponse { + payout_link_id: payout_link.link_id, + link: link.into(), }) }) .transpose() From 3dd64940dae401e0a2a83167aabb09c6f7f49a5e Mon Sep 17 00:00:00 2001 From: Kashif Date: Sun, 30 Jun 2024 20:16:17 +0530 Subject: [PATCH 5/9] refactor: use HashSet instead of Vec for enabled_payment_methods in Settings config --- crates/router/src/configs/settings.rs | 22 +++++++++---------- .../src/core/payment_methods/validator.rs | 2 +- crates/router/src/core/payout_link.rs | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index ffc89786cee4..c8ffb7b6837c 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -219,7 +219,7 @@ pub struct GenericLinkEnvConfig { pub expiry: u32, pub ui_config: GenericLinkEnvUiConfig, #[serde(deserialize_with = "deserialize_hashmap")] - pub enabled_payment_methods: HashMap>, + pub enabled_payment_methods: HashMap>, } impl Default for GenericLinkEnvConfig { @@ -849,7 +849,7 @@ pub struct PayPalOnboarding { fn deserialize_hashmap_inner( value: HashMap, -) -> Result>, String> +) -> Result>, String> where K: Eq + std::str::FromStr + std::hash::Hash, V: Eq + std::str::FromStr + std::hash::Hash, @@ -866,7 +866,7 @@ where std::any::type_name::() )), (_, Err(error)) => Err(error), - (Ok(key), Ok(value)) => Ok((key, value.into_iter().collect())), + (Ok(key), Ok(value)) => Ok((key, value)), }, ) .fold( @@ -889,7 +889,7 @@ where } } -fn deserialize_hashmap<'a, D, K, V>(deserializer: D) -> Result>, D::Error> +fn deserialize_hashmap<'a, D, K, V>(deserializer: D) -> Result>, D::Error> where D: serde::Deserializer<'a>, K: Eq + std::str::FromStr + std::hash::Hash, @@ -973,7 +973,7 @@ where #[cfg(test)] mod hashmap_deserialization_test { #![allow(clippy::unwrap_used)] - use std::collections::HashMap; + use std::collections::{HashMap, HashSet}; use serde::de::{ value::{Error as ValueError, MapDeserializer}, @@ -1004,11 +1004,11 @@ mod hashmap_deserialization_test { let expected_result = HashMap::from([ ( PaymentMethod::BankTransfer, - vec![PaymentMethodType::Ach, PaymentMethodType::Bacs], + HashSet::from([PaymentMethodType::Ach, PaymentMethodType::Bacs]), ), ( PaymentMethod::Wallet, - vec![PaymentMethodType::Paypal, PaymentMethodType::Venmo], + HashSet::from([PaymentMethodType::Paypal, PaymentMethodType::Venmo]), ), ]); @@ -1038,15 +1038,15 @@ mod hashmap_deserialization_test { let expected_result = HashMap::from([ ( PaymentMethod::BankTransfer, - vec![PaymentMethodType::Ach, PaymentMethodType::Bacs], + HashSet::from([PaymentMethodType::Ach, PaymentMethodType::Bacs]), ), ( PaymentMethod::Wallet, - vec![ + HashSet::from([ PaymentMethodType::Paypal, PaymentMethodType::Pix, PaymentMethodType::Venmo, - ], + ]), ), ]); @@ -1074,8 +1074,6 @@ mod hashmap_deserialization_test { > = input_map.into_deserializer(); let result = deserialize_hashmap::<'_, _, PaymentMethod, PaymentMethodType>(deserializer); - println!("TEST ERR {:?}", result); - assert!(result.is_err()); } } diff --git a/crates/router/src/core/payment_methods/validator.rs b/crates/router/src/core/payment_methods/validator.rs index 19530f8944e9..be34d3db38c8 100644 --- a/crates/router/src/core/payment_methods/validator.rs +++ b/crates/router/src/core/payment_methods/validator.rs @@ -113,7 +113,7 @@ pub async fn validate_request_and_initiate_payment_method_collect_link( { let enabled_payment_method = link_utils::EnabledPaymentMethod { payment_method, - payment_method_types, + payment_method_types: payment_method_types.into_iter().collect(), }; default_enabled_payout_methods.push(enabled_payment_method); } diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index b8ee5fdceeb2..748d39d18fa2 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -134,7 +134,7 @@ pub async fn initiate_payout_link( { let enabled_payment_method = link_utils::EnabledPaymentMethod { payment_method, - payment_method_types, + payment_method_types: payment_method_types.into_iter().collect(), }; default_enabled_payout_methods.push(enabled_payment_method); } From 4d14b0fe9d8cf09c3b8c650891822c894c2f24b4 Mon Sep 17 00:00:00 2001 From: Kashif Date: Mon, 1 Jul 2024 14:40:30 +0530 Subject: [PATCH 6/9] refactor: use HashSet instead of Vec --- api-reference/openapi_spec.json | 3 ++- crates/common_utils/src/link_utils.rs | 6 +++--- crates/router/src/core/payout_link.rs | 12 +++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index eeb68e9d0473..595dc10412ae 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -9127,7 +9127,8 @@ "items": { "$ref": "#/components/schemas/PaymentMethodType" }, - "description": "An array of associated payment method types" + "description": "An array of associated payment method types", + "uniqueItems": true } } }, diff --git a/crates/common_utils/src/link_utils.rs b/crates/common_utils/src/link_utils.rs index fd254b5fb411..2960209dfc90 100644 --- a/crates/common_utils/src/link_utils.rs +++ b/crates/common_utils/src/link_utils.rs @@ -1,6 +1,6 @@ //! Common -use std::primitive::i64; +use std::{collections::HashSet, primitive::i64}; use common_enums::enums; use diesel::{ @@ -206,6 +206,6 @@ pub struct EnabledPaymentMethod { pub payment_method: enums::PaymentMethod, /// An array of associated payment method types - #[schema(value_type = Vec)] - pub payment_method_types: Vec, + #[schema(value_type = HashSet)] + pub payment_method_types: HashSet, } diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 748d39d18fa2..0039b019868e 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -15,7 +15,7 @@ use crate::{ errors, routes::{app::StorageInterface, SessionState}, services::{self, GenericLinks}, - types::{api::enums, domain}, + types::domain, }; pub async fn initiate_payout_link( @@ -134,7 +134,7 @@ pub async fn initiate_payout_link( { let enabled_payment_method = link_utils::EnabledPaymentMethod { payment_method, - payment_method_types: payment_method_types.into_iter().collect(), + payment_method_types, }; default_enabled_payout_methods.push(enabled_payment_method); } @@ -299,12 +299,10 @@ pub async fn filter_payout_methods( } } } - for (pm, method_types) in payment_method_list_hm { - if !method_types.is_empty() { - let payment_method_types: Vec = - method_types.into_iter().collect(); + for (payment_method, payment_method_types) in payment_method_list_hm { + if !payment_method_types.is_empty() { let enabled_payment_method = link_utils::EnabledPaymentMethod { - payment_method: pm, + payment_method, payment_method_types, }; response.push(enabled_payment_method); From 4cf68e84bc8fc070e22b9541de2e9c1c23f6add4 Mon Sep 17 00:00:00 2001 From: Kashif Date: Mon, 1 Jul 2024 17:04:22 +0530 Subject: [PATCH 7/9] refactor(payout_link): updated styles --- .../generic_link/payment_method_collect/initiate/styles.css | 3 +-- .../src/core/generic_link/payout_link/initiate/styles.css | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/generic_link/payment_method_collect/initiate/styles.css b/crates/router/src/core/generic_link/payment_method_collect/initiate/styles.css index 01ca0f20c8bb..89387ab225ae 100644 --- a/crates/router/src/core/generic_link/payment_method_collect/initiate/styles.css +++ b/crates/router/src/core/generic_link/payment_method_collect/initiate/styles.css @@ -28,7 +28,7 @@ body { .main, #payment-method-collect { height: 100vh; - width: 100vw; + width: 100%; } .main { @@ -45,7 +45,6 @@ body { } .main { - width: auto; min-width: 300px; } } diff --git a/crates/router/src/core/generic_link/payout_link/initiate/styles.css b/crates/router/src/core/generic_link/payout_link/initiate/styles.css index 305281cd50a9..63d94724bccb 100644 --- a/crates/router/src/core/generic_link/payout_link/initiate/styles.css +++ b/crates/router/src/core/generic_link/payout_link/initiate/styles.css @@ -28,7 +28,7 @@ body { .main, #payout-link { height: 100vh; - width: 100vw; + width: 100%; } .main { @@ -45,7 +45,6 @@ body { } .main { - width: auto; min-width: 300px; } } From 2e1bda14f9cbdb8d25479a811f467c0a90844883 Mon Sep 17 00:00:00 2001 From: Kashif Date: Mon, 1 Jul 2024 18:01:34 +0530 Subject: [PATCH 8/9] refactor: clippy fixes --- crates/router/src/routes/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0cf7de4d33c2..562eef5da033 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -224,7 +224,7 @@ impl AppState { .await .expect("Failed to create secret management client"); - let conf = secrets_transformers::fetch_raw_secrets(conf, &*secret_management_client).await; + let conf = Box::pin(secrets_transformers::fetch_raw_secrets(conf, &*secret_management_client)).await; #[allow(clippy::expect_used)] let encryption_client = conf From 7a0397bc89b2113bec9fdbf99805b6919720b1aa Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:32:36 +0000 Subject: [PATCH 9/9] chore: run formatter --- crates/router/src/routes/app.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 562eef5da033..15265d49661c 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -224,7 +224,11 @@ impl AppState { .await .expect("Failed to create secret management client"); - let conf = Box::pin(secrets_transformers::fetch_raw_secrets(conf, &*secret_management_client)).await; + let conf = Box::pin(secrets_transformers::fetch_raw_secrets( + conf, + &*secret_management_client, + )) + .await; #[allow(clippy::expect_used)] let encryption_client = conf