From f56d76ffec7ef6c27417a5df1999af7ccc3f1545 Mon Sep 17 00:00:00 2001 From: likhinbopanna <131246334+likhinbopanna@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:06:45 +0530 Subject: [PATCH 001/150] ci(cypress): Fix Iatapay Zero Auth Mandates Flow (#6552) --- .../cypress/e2e/PaymentUtils/Iatapay.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js b/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js index 3eddf0530155..faa810e7bad5 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js @@ -1,3 +1,11 @@ +const successfulNo3DSCardDetails = { + card_number: "4111111111111111", + card_exp_month: "03", + card_exp_year: "30", + card_holder_name: "John Doe", + card_cvc: "737", +}; + export const connectorDetails = { bank_redirect_pm: { Ideal: { @@ -46,6 +54,40 @@ export const connectorDetails = { }, }, }, + ZeroAuthPaymentIntent: { + Request: { + amount: 0, + setup_future_usage: "off_session", + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + setup_future_usage: "off_session", + }, + }, + }, + ZeroAuthConfirmPayment: { + Request: { + payment_type: "setup_mandate", + payment_method: "card", + payment_method_type: "credit", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + }, + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: "Setup Mandate flow for Iatapay is not implemented", + code: "IR_00", + }, + }, + }, + }, }, upi_pm: { PaymentIntent: { From afd7f7d20980f6f39673008c86b89b1e501f05f2 Mon Sep 17 00:00:00 2001 From: Uzair Khan Date: Thu, 14 Nov 2024 14:09:30 +0530 Subject: [PATCH 002/150] feat(analytics): add `sessionized_metrics` and `currency_conversion` for refunds analytics (#6419) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/analytics/src/payment_intents/core.rs | 14 +- crates/analytics/src/payments/accumulator.rs | 4 +- crates/analytics/src/payments/core.rs | 8 +- crates/analytics/src/refunds/accumulator.rs | 15 +- crates/analytics/src/refunds/core.rs | 65 +++++++-- crates/analytics/src/refunds/metrics.rs | 21 +++ .../metrics/refund_processed_amount.rs | 2 + .../metrics/sessionized_metrics/mod.rs | 11 ++ .../sessionized_metrics/refund_count.rs | 120 ++++++++++++++++ .../refund_processed_amount.rs | 129 ++++++++++++++++++ .../refund_success_count.rs | 125 +++++++++++++++++ .../refund_success_rate.rs | 120 ++++++++++++++++ crates/api_models/src/analytics.rs | 13 +- crates/api_models/src/analytics/payments.rs | 2 +- crates/api_models/src/analytics/refunds.rs | 5 + crates/api_models/src/events.rs | 5 + crates/router/src/analytics.rs | 9 +- 17 files changed, 634 insertions(+), 34 deletions(-) create mode 100644 crates/analytics/src/refunds/metrics/sessionized_metrics/mod.rs create mode 100644 crates/analytics/src/refunds/metrics/sessionized_metrics/refund_count.rs create mode 100644 crates/analytics/src/refunds/metrics/sessionized_metrics/refund_processed_amount.rs create mode 100644 crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_count.rs create mode 100644 crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_rate.rs diff --git a/crates/analytics/src/payment_intents/core.rs b/crates/analytics/src/payment_intents/core.rs index 64ca7c3f82b4..3654cad8c09c 100644 --- a/crates/analytics/src/payment_intents/core.rs +++ b/crates/analytics/src/payment_intents/core.rs @@ -69,11 +69,21 @@ pub async fn get_sankey( i.refunds_status.unwrap_or_default().as_ref(), i.attempt_count, ) { + (IntentStatus::Succeeded, SessionizerRefundStatus::FullRefunded, 1) => { + sankey_response.refunded += i.count; + sankey_response.normal_success += i.count + } + (IntentStatus::Succeeded, SessionizerRefundStatus::PartialRefunded, 1) => { + sankey_response.partial_refunded += i.count; + sankey_response.normal_success += i.count + } (IntentStatus::Succeeded, SessionizerRefundStatus::FullRefunded, _) => { - sankey_response.refunded += i.count + sankey_response.refunded += i.count; + sankey_response.smart_retried_success += i.count } (IntentStatus::Succeeded, SessionizerRefundStatus::PartialRefunded, _) => { - sankey_response.partial_refunded += i.count + sankey_response.partial_refunded += i.count; + sankey_response.smart_retried_success += i.count } ( IntentStatus::Succeeded diff --git a/crates/analytics/src/payments/accumulator.rs b/crates/analytics/src/payments/accumulator.rs index 651eeb0bcfe7..5ca9fdf7516a 100644 --- a/crates/analytics/src/payments/accumulator.rs +++ b/crates/analytics/src/payments/accumulator.rs @@ -387,7 +387,7 @@ impl PaymentMetricsAccumulator { payment_processed_count, payment_processed_amount_without_smart_retries, payment_processed_count_without_smart_retries, - payment_processed_amount_usd, + payment_processed_amount_in_usd, payment_processed_amount_without_smart_retries_usd, ) = self.processed_amount.collect(); let ( @@ -417,7 +417,7 @@ impl PaymentMetricsAccumulator { payments_failure_rate_distribution_without_smart_retries, failure_reason_count, failure_reason_count_without_smart_retries, - payment_processed_amount_usd, + payment_processed_amount_in_usd, payment_processed_amount_without_smart_retries_usd, } } diff --git a/crates/analytics/src/payments/core.rs b/crates/analytics/src/payments/core.rs index bcd009270dc1..01a3b1abc15c 100644 --- a/crates/analytics/src/payments/core.rs +++ b/crates/analytics/src/payments/core.rs @@ -228,7 +228,7 @@ pub async fn get_metrics( let mut total_payment_processed_count_without_smart_retries = 0; let mut total_failure_reasons_count = 0; let mut total_failure_reasons_count_without_smart_retries = 0; - let mut total_payment_processed_amount_usd = 0; + let mut total_payment_processed_amount_in_usd = 0; let mut total_payment_processed_amount_without_smart_retries_usd = 0; let query_data: Vec = metrics_accumulator .into_iter() @@ -251,9 +251,9 @@ pub async fn get_metrics( }) .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64()) .unwrap_or_default(); - collected_values.payment_processed_amount_usd = amount_in_usd; + collected_values.payment_processed_amount_in_usd = amount_in_usd; total_payment_processed_amount += amount; - total_payment_processed_amount_usd += amount_in_usd.unwrap_or(0); + total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0); } if let Some(count) = collected_values.payment_processed_count { total_payment_processed_count += count; @@ -299,7 +299,7 @@ pub async fn get_metrics( query_data, meta_data: [PaymentsAnalyticsMetadata { total_payment_processed_amount: Some(total_payment_processed_amount), - total_payment_processed_amount_usd: Some(total_payment_processed_amount_usd), + total_payment_processed_amount_in_usd: Some(total_payment_processed_amount_in_usd), total_payment_processed_amount_without_smart_retries: Some( total_payment_processed_amount_without_smart_retries, ), diff --git a/crates/analytics/src/refunds/accumulator.rs b/crates/analytics/src/refunds/accumulator.rs index 9c51defdcf91..add38c98162c 100644 --- a/crates/analytics/src/refunds/accumulator.rs +++ b/crates/analytics/src/refunds/accumulator.rs @@ -7,7 +7,7 @@ pub struct RefundMetricsAccumulator { pub refund_success_rate: SuccessRateAccumulator, pub refund_count: CountAccumulator, pub refund_success: CountAccumulator, - pub processed_amount: SumAccumulator, + pub processed_amount: PaymentProcessedAmountAccumulator, } #[derive(Debug, Default)] @@ -22,7 +22,7 @@ pub struct CountAccumulator { } #[derive(Debug, Default)] #[repr(transparent)] -pub struct SumAccumulator { +pub struct PaymentProcessedAmountAccumulator { pub total: Option, } @@ -50,8 +50,8 @@ impl RefundMetricAccumulator for CountAccumulator { } } -impl RefundMetricAccumulator for SumAccumulator { - type MetricOutput = Option; +impl RefundMetricAccumulator for PaymentProcessedAmountAccumulator { + type MetricOutput = (Option, Option); #[inline] fn add_metrics_bucket(&mut self, metrics: &RefundMetricRow) { self.total = match ( @@ -68,7 +68,7 @@ impl RefundMetricAccumulator for SumAccumulator { } #[inline] fn collect(self) -> Self::MetricOutput { - self.total.and_then(|i| u64::try_from(i).ok()) + (self.total.and_then(|i| u64::try_from(i).ok()), Some(0)) } } @@ -98,11 +98,14 @@ impl RefundMetricAccumulator for SuccessRateAccumulator { impl RefundMetricsAccumulator { pub fn collect(self) -> RefundMetricsBucketValue { + let (refund_processed_amount, refund_processed_amount_in_usd) = + self.processed_amount.collect(); RefundMetricsBucketValue { refund_success_rate: self.refund_success_rate.collect(), refund_count: self.refund_count.collect(), refund_success_count: self.refund_success.collect(), - refund_processed_amount: self.processed_amount.collect(), + refund_processed_amount, + refund_processed_amount_in_usd, } } } diff --git a/crates/analytics/src/refunds/core.rs b/crates/analytics/src/refunds/core.rs index 9c4770c79eee..e3bfa4da9d10 100644 --- a/crates/analytics/src/refunds/core.rs +++ b/crates/analytics/src/refunds/core.rs @@ -5,9 +5,12 @@ use api_models::analytics::{ refunds::{ RefundDimensions, RefundMetrics, RefundMetricsBucketIdentifier, RefundMetricsBucketResponse, }, - AnalyticsMetadata, GetRefundFilterRequest, GetRefundMetricRequest, MetricsResponse, - RefundFilterValue, RefundFiltersResponse, + GetRefundFilterRequest, GetRefundMetricRequest, RefundFilterValue, RefundFiltersResponse, + RefundsAnalyticsMetadata, RefundsMetricsResponse, }; +use bigdecimal::ToPrimitive; +use common_enums::Currency; +use currency_conversion::{conversion::convert, types::ExchangeRates}; use error_stack::ResultExt; use router_env::{ logger, @@ -29,9 +32,10 @@ use crate::{ pub async fn get_metrics( pool: &AnalyticsProvider, + ex_rates: &ExchangeRates, auth: &AuthInfo, req: GetRefundMetricRequest, -) -> AnalyticsResult> { +) -> AnalyticsResult> { let mut metrics_accumulator: HashMap = HashMap::new(); let mut set = tokio::task::JoinSet::new(); @@ -86,16 +90,20 @@ pub async fn get_metrics( logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}"); let metrics_builder = metrics_accumulator.entry(id).or_default(); match metric { - RefundMetrics::RefundSuccessRate => metrics_builder - .refund_success_rate - .add_metrics_bucket(&value), - RefundMetrics::RefundCount => { + RefundMetrics::RefundSuccessRate | RefundMetrics::SessionizedRefundSuccessRate => { + metrics_builder + .refund_success_rate + .add_metrics_bucket(&value) + } + RefundMetrics::RefundCount | RefundMetrics::SessionizedRefundCount => { metrics_builder.refund_count.add_metrics_bucket(&value) } - RefundMetrics::RefundSuccessCount => { + RefundMetrics::RefundSuccessCount + | RefundMetrics::SessionizedRefundSuccessCount => { metrics_builder.refund_success.add_metrics_bucket(&value) } - RefundMetrics::RefundProcessedAmount => { + RefundMetrics::RefundProcessedAmount + | RefundMetrics::SessionizedRefundProcessedAmount => { metrics_builder.processed_amount.add_metrics_bucket(&value) } } @@ -107,18 +115,45 @@ pub async fn get_metrics( metrics_accumulator ); } + let mut total_refund_processed_amount = 0; + let mut total_refund_processed_amount_in_usd = 0; let query_data: Vec = metrics_accumulator .into_iter() - .map(|(id, val)| RefundMetricsBucketResponse { - values: val.collect(), - dimensions: id, + .map(|(id, val)| { + let mut collected_values = val.collect(); + if let Some(amount) = collected_values.refund_processed_amount { + let amount_in_usd = id + .currency + .and_then(|currency| { + i64::try_from(amount) + .inspect_err(|e| logger::error!("Amount conversion error: {:?}", e)) + .ok() + .and_then(|amount_i64| { + convert(ex_rates, currency, Currency::USD, amount_i64) + .inspect_err(|e| { + logger::error!("Currency conversion error: {:?}", e) + }) + .ok() + }) + }) + .map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64()) + .unwrap_or_default(); + collected_values.refund_processed_amount_in_usd = amount_in_usd; + total_refund_processed_amount += amount; + total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0); + } + RefundMetricsBucketResponse { + values: collected_values, + dimensions: id, + } }) .collect(); - Ok(MetricsResponse { + Ok(RefundsMetricsResponse { query_data, - meta_data: [AnalyticsMetadata { - current_time_range: req.time_range, + meta_data: [RefundsAnalyticsMetadata { + total_refund_processed_amount: Some(total_refund_processed_amount), + total_refund_processed_amount_in_usd: Some(total_refund_processed_amount_in_usd), }], }) } diff --git a/crates/analytics/src/refunds/metrics.rs b/crates/analytics/src/refunds/metrics.rs index 6ecfd8aeb293..c211ea82d7af 100644 --- a/crates/analytics/src/refunds/metrics.rs +++ b/crates/analytics/src/refunds/metrics.rs @@ -10,6 +10,7 @@ mod refund_count; mod refund_processed_amount; mod refund_success_count; mod refund_success_rate; +mod sessionized_metrics; use std::collections::HashSet; use refund_count::RefundCount; @@ -101,6 +102,26 @@ where .load_metrics(dimensions, auth, filters, granularity, time_range, pool) .await } + Self::SessionizedRefundSuccessRate => { + sessionized_metrics::RefundSuccessRate::default() + .load_metrics(dimensions, auth, filters, granularity, time_range, pool) + .await + } + Self::SessionizedRefundCount => { + sessionized_metrics::RefundCount::default() + .load_metrics(dimensions, auth, filters, granularity, time_range, pool) + .await + } + Self::SessionizedRefundSuccessCount => { + sessionized_metrics::RefundSuccessCount::default() + .load_metrics(dimensions, auth, filters, granularity, time_range, pool) + .await + } + Self::SessionizedRefundProcessedAmount => { + sessionized_metrics::RefundProcessedAmount::default() + .load_metrics(dimensions, auth, filters, granularity, time_range, pool) + .await + } } } } diff --git a/crates/analytics/src/refunds/metrics/refund_processed_amount.rs b/crates/analytics/src/refunds/metrics/refund_processed_amount.rs index f0f51a21fe0f..6cba5f58fed5 100644 --- a/crates/analytics/src/refunds/metrics/refund_processed_amount.rs +++ b/crates/analytics/src/refunds/metrics/refund_processed_amount.rs @@ -52,6 +52,7 @@ where alias: Some("total"), }) .switch()?; + query_builder.add_select_column("currency").switch()?; query_builder .add_select_column(Aggregate::Min { field: "created_at", @@ -78,6 +79,7 @@ where query_builder.add_group_by_clause(dim).switch()?; } + query_builder.add_group_by_clause("currency").switch()?; if let Some(granularity) = granularity.as_ref() { granularity .set_group_by_clause(&mut query_builder) diff --git a/crates/analytics/src/refunds/metrics/sessionized_metrics/mod.rs b/crates/analytics/src/refunds/metrics/sessionized_metrics/mod.rs new file mode 100644 index 000000000000..bb404cd34101 --- /dev/null +++ b/crates/analytics/src/refunds/metrics/sessionized_metrics/mod.rs @@ -0,0 +1,11 @@ +mod refund_count; +mod refund_processed_amount; +mod refund_success_count; +mod refund_success_rate; + +pub(super) use refund_count::RefundCount; +pub(super) use refund_processed_amount::RefundProcessedAmount; +pub(super) use refund_success_count::RefundSuccessCount; +pub(super) use refund_success_rate::RefundSuccessRate; + +pub use super::{RefundMetric, RefundMetricAnalytics, RefundMetricRow}; diff --git a/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_count.rs b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_count.rs new file mode 100644 index 000000000000..c77e1f7a52c1 --- /dev/null +++ b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_count.rs @@ -0,0 +1,120 @@ +use std::collections::HashSet; + +use api_models::analytics::{ + refunds::{RefundDimensions, RefundFilters, RefundMetricsBucketIdentifier}, + Granularity, TimeRange, +}; +use common_utils::errors::ReportSwitchExt; +use error_stack::ResultExt; +use time::PrimitiveDateTime; + +use super::RefundMetricRow; +use crate::{ + enums::AuthInfo, + query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window}, + types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, +}; + +#[derive(Default)] +pub(crate) struct RefundCount {} + +#[async_trait::async_trait] +impl super::RefundMetric for RefundCount +where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + PrimitiveDateTime: ToSql, + AnalyticsCollection: ToSql, + Granularity: GroupByClause, + Aggregate<&'static str>: ToSql, + Window<&'static str>: ToSql, +{ + async fn load_metrics( + &self, + dimensions: &[RefundDimensions], + auth: &AuthInfo, + filters: &RefundFilters, + granularity: &Option, + time_range: &TimeRange, + pool: &T, + ) -> MetricsResult> { + let mut query_builder: QueryBuilder = + QueryBuilder::new(AnalyticsCollection::RefundSessionized); + + for dim in dimensions.iter() { + query_builder.add_select_column(dim).switch()?; + } + + query_builder + .add_select_column(Aggregate::Count { + field: None, + alias: Some("count"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Min { + field: "created_at", + alias: Some("start_bucket"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Max { + field: "created_at", + alias: Some("end_bucket"), + }) + .switch()?; + + filters.set_filter_clause(&mut query_builder).switch()?; + + auth.set_filter_clause(&mut query_builder).switch()?; + + time_range + .set_filter_clause(&mut query_builder) + .attach_printable("Error filtering time range") + .switch()?; + + for dim in dimensions.iter() { + query_builder + .add_group_by_clause(dim) + .attach_printable("Error grouping by dimensions") + .switch()?; + } + + if let Some(granularity) = granularity.as_ref() { + granularity + .set_group_by_clause(&mut query_builder) + .attach_printable("Error adding granularity") + .switch()?; + } + + query_builder + .execute_query::(pool) + .await + .change_context(MetricsError::QueryBuildingError)? + .change_context(MetricsError::QueryExecutionFailure)? + .into_iter() + .map(|i| { + Ok(( + RefundMetricsBucketIdentifier::new( + i.currency.as_ref().map(|i| i.0), + i.refund_status.as_ref().map(|i| i.0.to_string()), + i.connector.clone(), + i.refund_type.as_ref().map(|i| i.0.to_string()), + i.profile_id.clone(), + TimeRange { + start_time: match (granularity, i.start_bucket) { + (Some(g), Some(st)) => g.clip_to_start(st)?, + _ => time_range.start_time, + }, + end_time: granularity.as_ref().map_or_else( + || Ok(time_range.end_time), + |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(), + )?, + }, + ), + i, + )) + }) + .collect::, crate::query::PostProcessingError>>() + .change_context(MetricsError::PostProcessingFailure) + } +} diff --git a/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_processed_amount.rs b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_processed_amount.rs new file mode 100644 index 000000000000..c91938228af1 --- /dev/null +++ b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_processed_amount.rs @@ -0,0 +1,129 @@ +use std::collections::HashSet; + +use api_models::analytics::{ + refunds::{RefundDimensions, RefundFilters, RefundMetricsBucketIdentifier}, + Granularity, TimeRange, +}; +use common_utils::errors::ReportSwitchExt; +use diesel_models::enums as storage_enums; +use error_stack::ResultExt; +use time::PrimitiveDateTime; + +use super::RefundMetricRow; +use crate::{ + enums::AuthInfo, + query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window}, + types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, +}; +#[derive(Default)] +pub(crate) struct RefundProcessedAmount {} + +#[async_trait::async_trait] +impl super::RefundMetric for RefundProcessedAmount +where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + PrimitiveDateTime: ToSql, + AnalyticsCollection: ToSql, + Granularity: GroupByClause, + Aggregate<&'static str>: ToSql, + Window<&'static str>: ToSql, +{ + async fn load_metrics( + &self, + dimensions: &[RefundDimensions], + auth: &AuthInfo, + filters: &RefundFilters, + granularity: &Option, + time_range: &TimeRange, + pool: &T, + ) -> MetricsResult> + where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + { + let mut query_builder: QueryBuilder = + QueryBuilder::new(AnalyticsCollection::RefundSessionized); + + for dim in dimensions.iter() { + query_builder.add_select_column(dim).switch()?; + } + + query_builder + .add_select_column(Aggregate::Sum { + field: "refund_amount", + alias: Some("total"), + }) + .switch()?; + query_builder.add_select_column("currency").switch()?; + query_builder + .add_select_column(Aggregate::Min { + field: "created_at", + alias: Some("start_bucket"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Max { + field: "created_at", + alias: Some("end_bucket"), + }) + .switch()?; + + filters.set_filter_clause(&mut query_builder).switch()?; + + auth.set_filter_clause(&mut query_builder).switch()?; + + time_range + .set_filter_clause(&mut query_builder) + .attach_printable("Error filtering time range") + .switch()?; + + for dim in dimensions.iter() { + query_builder.add_group_by_clause(dim).switch()?; + } + + query_builder.add_group_by_clause("currency").switch()?; + + if let Some(granularity) = granularity.as_ref() { + granularity + .set_group_by_clause(&mut query_builder) + .switch()?; + } + + query_builder + .add_filter_clause( + RefundDimensions::RefundStatus, + storage_enums::RefundStatus::Success, + ) + .switch()?; + + query_builder + .execute_query::(pool) + .await + .change_context(MetricsError::QueryBuildingError)? + .change_context(MetricsError::QueryExecutionFailure)? + .into_iter() + .map(|i| { + Ok(( + RefundMetricsBucketIdentifier::new( + i.currency.as_ref().map(|i| i.0), + None, + i.connector.clone(), + i.refund_type.as_ref().map(|i| i.0.to_string()), + i.profile_id.clone(), + TimeRange { + start_time: match (granularity, i.start_bucket) { + (Some(g), Some(st)) => g.clip_to_start(st)?, + _ => time_range.start_time, + }, + end_time: granularity.as_ref().map_or_else( + || Ok(time_range.end_time), + |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(), + )?, + }, + ), + i, + )) + }) + .collect::, crate::query::PostProcessingError>>() + .change_context(MetricsError::PostProcessingFailure) + } +} diff --git a/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_count.rs b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_count.rs new file mode 100644 index 000000000000..332261a32083 --- /dev/null +++ b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_count.rs @@ -0,0 +1,125 @@ +use std::collections::HashSet; + +use api_models::analytics::{ + refunds::{RefundDimensions, RefundFilters, RefundMetricsBucketIdentifier}, + Granularity, TimeRange, +}; +use common_utils::errors::ReportSwitchExt; +use diesel_models::enums as storage_enums; +use error_stack::ResultExt; +use time::PrimitiveDateTime; + +use super::RefundMetricRow; +use crate::{ + enums::AuthInfo, + query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window}, + types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, +}; + +#[derive(Default)] +pub(crate) struct RefundSuccessCount {} + +#[async_trait::async_trait] +impl super::RefundMetric for RefundSuccessCount +where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + PrimitiveDateTime: ToSql, + AnalyticsCollection: ToSql, + Granularity: GroupByClause, + Aggregate<&'static str>: ToSql, + Window<&'static str>: ToSql, +{ + async fn load_metrics( + &self, + dimensions: &[RefundDimensions], + auth: &AuthInfo, + filters: &RefundFilters, + granularity: &Option, + time_range: &TimeRange, + pool: &T, + ) -> MetricsResult> + where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + { + let mut query_builder = QueryBuilder::new(AnalyticsCollection::RefundSessionized); + + for dim in dimensions.iter() { + query_builder.add_select_column(dim).switch()?; + } + + query_builder + .add_select_column(Aggregate::Count { + field: None, + alias: Some("count"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Min { + field: "created_at", + alias: Some("start_bucket"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Max { + field: "created_at", + alias: Some("end_bucket"), + }) + .switch()?; + + filters.set_filter_clause(&mut query_builder).switch()?; + + auth.set_filter_clause(&mut query_builder).switch()?; + + time_range.set_filter_clause(&mut query_builder).switch()?; + + for dim in dimensions.iter() { + query_builder.add_group_by_clause(dim).switch()?; + } + + if let Some(granularity) = granularity.as_ref() { + granularity + .set_group_by_clause(&mut query_builder) + .switch()?; + } + + query_builder + .add_filter_clause( + RefundDimensions::RefundStatus, + storage_enums::RefundStatus::Success, + ) + .switch()?; + query_builder + .execute_query::(pool) + .await + .change_context(MetricsError::QueryBuildingError)? + .change_context(MetricsError::QueryExecutionFailure)? + .into_iter() + .map(|i| { + Ok(( + RefundMetricsBucketIdentifier::new( + i.currency.as_ref().map(|i| i.0), + None, + i.connector.clone(), + i.refund_type.as_ref().map(|i| i.0.to_string()), + i.profile_id.clone(), + TimeRange { + start_time: match (granularity, i.start_bucket) { + (Some(g), Some(st)) => g.clip_to_start(st)?, + _ => time_range.start_time, + }, + end_time: granularity.as_ref().map_or_else( + || Ok(time_range.end_time), + |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(), + )?, + }, + ), + i, + )) + }) + .collect::, + crate::query::PostProcessingError, + >>() + .change_context(MetricsError::PostProcessingFailure) + } +} diff --git a/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_rate.rs b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_rate.rs new file mode 100644 index 000000000000..35ee0d61b509 --- /dev/null +++ b/crates/analytics/src/refunds/metrics/sessionized_metrics/refund_success_rate.rs @@ -0,0 +1,120 @@ +use std::collections::HashSet; + +use api_models::analytics::{ + refunds::{RefundDimensions, RefundFilters, RefundMetricsBucketIdentifier}, + Granularity, TimeRange, +}; +use common_utils::errors::ReportSwitchExt; +use error_stack::ResultExt; +use time::PrimitiveDateTime; + +use super::RefundMetricRow; +use crate::{ + enums::AuthInfo, + query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window}, + types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, +}; +#[derive(Default)] +pub(crate) struct RefundSuccessRate {} + +#[async_trait::async_trait] +impl super::RefundMetric for RefundSuccessRate +where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + PrimitiveDateTime: ToSql, + AnalyticsCollection: ToSql, + Granularity: GroupByClause, + Aggregate<&'static str>: ToSql, + Window<&'static str>: ToSql, +{ + async fn load_metrics( + &self, + dimensions: &[RefundDimensions], + auth: &AuthInfo, + filters: &RefundFilters, + granularity: &Option, + time_range: &TimeRange, + pool: &T, + ) -> MetricsResult> + where + T: AnalyticsDataSource + super::RefundMetricAnalytics, + { + let mut query_builder = QueryBuilder::new(AnalyticsCollection::RefundSessionized); + let mut dimensions = dimensions.to_vec(); + + dimensions.push(RefundDimensions::RefundStatus); + + for dim in dimensions.iter() { + query_builder.add_select_column(dim).switch()?; + } + + query_builder + .add_select_column(Aggregate::Count { + field: None, + alias: Some("count"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Min { + field: "created_at", + alias: Some("start_bucket"), + }) + .switch()?; + query_builder + .add_select_column(Aggregate::Max { + field: "created_at", + alias: Some("end_bucket"), + }) + .switch()?; + + filters.set_filter_clause(&mut query_builder).switch()?; + + auth.set_filter_clause(&mut query_builder).switch()?; + + time_range.set_filter_clause(&mut query_builder).switch()?; + + for dim in dimensions.iter() { + query_builder.add_group_by_clause(dim).switch()?; + } + + if let Some(granularity) = granularity.as_ref() { + granularity + .set_group_by_clause(&mut query_builder) + .switch()?; + } + + query_builder + .execute_query::(pool) + .await + .change_context(MetricsError::QueryBuildingError)? + .change_context(MetricsError::QueryExecutionFailure)? + .into_iter() + .map(|i| { + Ok(( + RefundMetricsBucketIdentifier::new( + i.currency.as_ref().map(|i| i.0), + None, + i.connector.clone(), + i.refund_type.as_ref().map(|i| i.0.to_string()), + i.profile_id.clone(), + TimeRange { + start_time: match (granularity, i.start_bucket) { + (Some(g), Some(st)) => g.clip_to_start(st)?, + _ => time_range.start_time, + }, + end_time: granularity.as_ref().map_or_else( + || Ok(time_range.end_time), + |g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(), + )?, + }, + ), + i, + )) + }) + .collect::, + crate::query::PostProcessingError, + >>() + .change_context(MetricsError::PostProcessingFailure) + } +} diff --git a/crates/api_models/src/analytics.rs b/crates/api_models/src/analytics.rs index 8d63bc3096ca..70c0e0e78dc8 100644 --- a/crates/api_models/src/analytics.rs +++ b/crates/api_models/src/analytics.rs @@ -203,7 +203,7 @@ pub struct AnalyticsMetadata { #[derive(Debug, serde::Serialize)] pub struct PaymentsAnalyticsMetadata { pub total_payment_processed_amount: Option, - pub total_payment_processed_amount_usd: Option, + pub total_payment_processed_amount_in_usd: Option, pub total_payment_processed_amount_without_smart_retries: Option, pub total_payment_processed_amount_without_smart_retries_usd: Option, pub total_payment_processed_count: Option, @@ -228,6 +228,11 @@ pub struct PaymentIntentsAnalyticsMetadata { pub total_payment_processed_count_without_smart_retries: Option, } +#[derive(Debug, serde::Serialize)] +pub struct RefundsAnalyticsMetadata { + pub total_refund_processed_amount: Option, + pub total_refund_processed_amount_in_usd: Option, +} #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct GetPaymentFiltersRequest { @@ -362,6 +367,12 @@ pub struct PaymentIntentsMetricsResponse { pub meta_data: [PaymentIntentsAnalyticsMetadata; 1], } +#[derive(Debug, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RefundsMetricsResponse { + pub query_data: Vec, + pub meta_data: [RefundsAnalyticsMetadata; 1], +} #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct GetApiEventFiltersRequest { diff --git a/crates/api_models/src/analytics/payments.rs b/crates/api_models/src/analytics/payments.rs index 1faba79eb378..b34f8c9293cf 100644 --- a/crates/api_models/src/analytics/payments.rs +++ b/crates/api_models/src/analytics/payments.rs @@ -271,7 +271,7 @@ pub struct PaymentMetricsBucketValue { pub payment_count: Option, pub payment_success_count: Option, pub payment_processed_amount: Option, - pub payment_processed_amount_usd: Option, + pub payment_processed_amount_in_usd: Option, pub payment_processed_count: Option, pub payment_processed_amount_without_smart_retries: Option, pub payment_processed_amount_without_smart_retries_usd: Option, diff --git a/crates/api_models/src/analytics/refunds.rs b/crates/api_models/src/analytics/refunds.rs index ef17387d1ea4..d981bd4382f1 100644 --- a/crates/api_models/src/analytics/refunds.rs +++ b/crates/api_models/src/analytics/refunds.rs @@ -88,6 +88,10 @@ pub enum RefundMetrics { RefundCount, RefundSuccessCount, RefundProcessedAmount, + SessionizedRefundSuccessRate, + SessionizedRefundCount, + SessionizedRefundSuccessCount, + SessionizedRefundProcessedAmount, } pub mod metric_behaviour { @@ -176,6 +180,7 @@ pub struct RefundMetricsBucketValue { pub refund_count: Option, pub refund_success_count: Option, pub refund_processed_amount: Option, + pub refund_processed_amount_in_usd: Option, } #[derive(Debug, serde::Serialize)] pub struct RefundMetricsBucketResponse { diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 7272abffbff6..77d8cb117ef4 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -168,6 +168,11 @@ impl ApiEventMetric for PaymentIntentsMetricsResponse { } } +impl ApiEventMetric for RefundsMetricsResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } +} #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] impl ApiEventMetric for PaymentMethodIntentConfirmInternal { fn get_api_event_type(&self) -> Option { diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index e0808b92260c..a13f476950b0 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -659,7 +659,8 @@ pub mod routes { org_id: org_id.clone(), merchant_ids: vec![merchant_id.clone()], }; - analytics::refunds::get_metrics(&state.pool, &auth, req) + let ex_rates = get_forex_exchange_rates(state.clone()).await?; + analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req) .await .map(ApplicationResponse::Json) }, @@ -697,7 +698,8 @@ pub mod routes { let auth: AuthInfo = AuthInfo::OrgLevel { org_id: org_id.clone(), }; - analytics::refunds::get_metrics(&state.pool, &auth, req) + let ex_rates = get_forex_exchange_rates(state.clone()).await?; + analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req) .await .map(ApplicationResponse::Json) }, @@ -743,7 +745,8 @@ pub mod routes { merchant_id: merchant_id.clone(), profile_ids: vec![profile_id.clone()], }; - analytics::refunds::get_metrics(&state.pool, &auth, req) + let ex_rates = get_forex_exchange_rates(state.clone()).await?; + analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req) .await .map(ApplicationResponse::Json) }, From 29be1d4fadc55948c99cc8bd33b3b8e8d341ae11 Mon Sep 17 00:00:00 2001 From: Mani Chandra <84711804+ThisIsMani@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:10:20 +0530 Subject: [PATCH 003/150] feat(themes): Setup themes table (#6533) --- crates/common_utils/src/types.rs | 2 + crates/common_utils/src/types/theme.rs | 39 ++++ crates/diesel_models/src/query/user.rs | 2 + crates/diesel_models/src/query/user/theme.rs | 95 ++++++++ crates/diesel_models/src/schema.rs | 21 ++ crates/diesel_models/src/schema_v2.rs | 21 ++ crates/diesel_models/src/user.rs | 3 +- crates/diesel_models/src/user/theme.rs | 29 +++ crates/router/src/db.rs | 1 + crates/router/src/db/kafka_store.rs | 35 ++- crates/router/src/db/user.rs | 1 + crates/router/src/db/user/theme.rs | 203 ++++++++++++++++++ crates/storage_impl/src/mock_db.rs | 2 + .../down.sql | 3 + .../up.sql | 17 ++ 15 files changed, 471 insertions(+), 3 deletions(-) create mode 100644 crates/common_utils/src/types/theme.rs create mode 100644 crates/diesel_models/src/query/user/theme.rs create mode 100644 crates/diesel_models/src/user/theme.rs create mode 100644 crates/router/src/db/user/theme.rs create mode 100644 migrations/2024-11-06-121933_setup-themes-table/down.sql create mode 100644 migrations/2024-11-06-121933_setup-themes-table/up.sql diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index ef7a4b847c45..f7cdfd3617bc 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -3,6 +3,8 @@ pub mod keymanager; /// Enum for Authentication Level pub mod authentication; +/// Enum for Theme Lineage +pub mod theme; use std::{ borrow::Cow, diff --git a/crates/common_utils/src/types/theme.rs b/crates/common_utils/src/types/theme.rs new file mode 100644 index 000000000000..a2e6fe4b19c4 --- /dev/null +++ b/crates/common_utils/src/types/theme.rs @@ -0,0 +1,39 @@ +use crate::id_type; + +/// Enum for having all the required lineage for every level. +/// Currently being used for theme related APIs and queries. +#[derive(Debug)] +pub enum ThemeLineage { + /// Tenant lineage variant + Tenant { + /// tenant_id: String + tenant_id: String, + }, + /// Org lineage variant + Organization { + /// tenant_id: String + tenant_id: String, + /// org_id: OrganizationId + org_id: id_type::OrganizationId, + }, + /// Merchant lineage variant + Merchant { + /// tenant_id: String + tenant_id: String, + /// org_id: OrganizationId + org_id: id_type::OrganizationId, + /// merchant_id: MerchantId + merchant_id: id_type::MerchantId, + }, + /// Profile lineage variant + Profile { + /// tenant_id: String + tenant_id: String, + /// org_id: OrganizationId + org_id: id_type::OrganizationId, + /// merchant_id: MerchantId + merchant_id: id_type::MerchantId, + /// profile_id: ProfileId + profile_id: id_type::ProfileId, + }, +} diff --git a/crates/diesel_models/src/query/user.rs b/crates/diesel_models/src/query/user.rs index 2bd403a847b3..1f6e16702fb7 100644 --- a/crates/diesel_models/src/query/user.rs +++ b/crates/diesel_models/src/query/user.rs @@ -1,6 +1,8 @@ use common_utils::pii; use diesel::{associations::HasTable, ExpressionMethods}; + pub mod sample_data; +pub mod theme; use crate::{ query::generics, schema::users::dsl as users_dsl, user::*, PgPooledConn, StorageResult, diff --git a/crates/diesel_models/src/query/user/theme.rs b/crates/diesel_models/src/query/user/theme.rs new file mode 100644 index 000000000000..c021edca3259 --- /dev/null +++ b/crates/diesel_models/src/query/user/theme.rs @@ -0,0 +1,95 @@ +use common_utils::types::theme::ThemeLineage; +use diesel::{ + associations::HasTable, + pg::Pg, + sql_types::{Bool, Nullable}, + BoolExpressionMethods, ExpressionMethods, NullableExpressionMethods, +}; + +use crate::{ + query::generics, + schema::themes::dsl, + user::theme::{Theme, ThemeNew}, + PgPooledConn, StorageResult, +}; + +impl ThemeNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { + generics::generic_insert(conn, self).await + } +} + +impl Theme { + fn lineage_filter( + lineage: ThemeLineage, + ) -> Box< + dyn diesel::BoxableExpression<::Table, Pg, SqlType = Nullable> + + 'static, + > { + match lineage { + ThemeLineage::Tenant { tenant_id } => Box::new( + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.is_null()) + .and(dsl::merchant_id.is_null()) + .and(dsl::profile_id.is_null()) + .nullable(), + ), + ThemeLineage::Organization { tenant_id, org_id } => Box::new( + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.eq(org_id)) + .and(dsl::merchant_id.is_null()) + .and(dsl::profile_id.is_null()), + ), + ThemeLineage::Merchant { + tenant_id, + org_id, + merchant_id, + } => Box::new( + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.eq(org_id)) + .and(dsl::merchant_id.eq(merchant_id)) + .and(dsl::profile_id.is_null()), + ), + ThemeLineage::Profile { + tenant_id, + org_id, + merchant_id, + profile_id, + } => Box::new( + dsl::tenant_id + .eq(tenant_id) + .and(dsl::org_id.eq(org_id)) + .and(dsl::merchant_id.eq(merchant_id)) + .and(dsl::profile_id.eq(profile_id)), + ), + } + } + + pub async fn find_by_lineage( + conn: &PgPooledConn, + lineage: ThemeLineage, + ) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + Self::lineage_filter(lineage), + ) + .await + } + + pub async fn delete_by_theme_id_and_lineage( + conn: &PgPooledConn, + theme_id: String, + lineage: ThemeLineage, + ) -> StorageResult { + generics::generic_delete_one_with_result::<::Table, _, _>( + conn, + dsl::theme_id + .eq(theme_id) + .and(Self::lineage_filter(lineage)), + ) + .await + } +} diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 782d7f50eac2..19a0763d770c 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1262,6 +1262,26 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + themes (theme_id) { + #[max_length = 64] + theme_id -> Varchar, + #[max_length = 64] + tenant_id -> Varchar, + #[max_length = 64] + org_id -> Nullable, + #[max_length = 64] + merchant_id -> Nullable, + #[max_length = 64] + profile_id -> Nullable, + created_at -> Timestamp, + last_modified_at -> Timestamp, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -1408,6 +1428,7 @@ diesel::allow_tables_to_appear_in_same_query!( reverse_lookup, roles, routing_algorithm, + themes, unified_translations, user_authentication_methods, user_key_store, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 1c287567fba4..e3097f80db91 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1208,6 +1208,26 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + themes (theme_id) { + #[max_length = 64] + theme_id -> Varchar, + #[max_length = 64] + tenant_id -> Varchar, + #[max_length = 64] + org_id -> Nullable, + #[max_length = 64] + merchant_id -> Nullable, + #[max_length = 64] + profile_id -> Nullable, + created_at -> Timestamp, + last_modified_at -> Timestamp, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -1355,6 +1375,7 @@ diesel::allow_tables_to_appear_in_same_query!( reverse_lookup, roles, routing_algorithm, + themes, unified_translations, user_authentication_methods, user_key_store, diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 9f7b77dc5d68..cf584c09b129 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -6,8 +6,9 @@ use time::PrimitiveDateTime; use crate::{diesel_impl::OptionalDieselArray, enums::TotpStatus, schema::users}; pub mod dashboard_metadata; - pub mod sample_data; +pub mod theme; + #[derive(Clone, Debug, Identifiable, Queryable, Selectable)] #[diesel(table_name = users, primary_key(user_id), check_for_backend(diesel::pg::Pg))] pub struct User { diff --git a/crates/diesel_models/src/user/theme.rs b/crates/diesel_models/src/user/theme.rs new file mode 100644 index 000000000000..0824ae71919b --- /dev/null +++ b/crates/diesel_models/src/user/theme.rs @@ -0,0 +1,29 @@ +use common_utils::id_type; +use diesel::{Identifiable, Insertable, Queryable, Selectable}; +use time::PrimitiveDateTime; + +use crate::schema::themes; + +#[derive(Clone, Debug, Identifiable, Queryable, Selectable)] +#[diesel(table_name = themes, primary_key(theme_id), check_for_backend(diesel::pg::Pg))] +pub struct Theme { + pub theme_id: String, + pub tenant_id: String, + pub org_id: Option, + pub merchant_id: Option, + pub profile_id: Option, + pub created_at: PrimitiveDateTime, + pub last_modified_at: PrimitiveDateTime, +} + +#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] +#[diesel(table_name = themes)] +pub struct ThemeNew { + pub theme_id: String, + pub tenant_id: String, + pub org_id: Option, + pub merchant_id: Option, + pub profile_id: Option, + pub created_at: PrimitiveDateTime, + pub last_modified_at: PrimitiveDateTime, +} diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index de0c6282fc21..1ed83be4283e 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -145,6 +145,7 @@ pub trait GlobalStorageInterface: + user::UserInterface + user_role::UserRoleInterface + user_key_store::UserKeyStoreInterface + + user::theme::ThemeInterface + 'static { } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 4b99a9c8f3ab..d7d283819e5f 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1,7 +1,11 @@ use std::sync::Arc; use common_enums::enums::MerchantStorageScheme; -use common_utils::{errors::CustomResult, id_type, pii, types::keymanager::KeyManagerState}; +use common_utils::{ + errors::CustomResult, + id_type, pii, + types::{keymanager::KeyManagerState, theme::ThemeLineage}, +}; use diesel_models::{ enums, enums::ProcessTrackerStatus, @@ -34,7 +38,7 @@ use time::PrimitiveDateTime; use super::{ dashboard_metadata::DashboardMetadataInterface, role::RoleInterface, - user::{sample_data::BatchSampleDataInterface, UserInterface}, + user::{sample_data::BatchSampleDataInterface, theme::ThemeInterface, UserInterface}, user_authentication_method::UserAuthenticationMethodInterface, user_key_store::UserKeyStoreInterface, user_role::{ListUserRolesByOrgIdPayload, ListUserRolesByUserIdPayload, UserRoleInterface}, @@ -3683,3 +3687,30 @@ impl UserAuthenticationMethodInterface for KafkaStore { .await } } + +#[async_trait::async_trait] +impl ThemeInterface for KafkaStore { + async fn insert_theme( + &self, + theme: storage::theme::ThemeNew, + ) -> CustomResult { + self.diesel_store.insert_theme(theme).await + } + + async fn find_theme_by_lineage( + &self, + lineage: ThemeLineage, + ) -> CustomResult { + self.diesel_store.find_theme_by_lineage(lineage).await + } + + async fn delete_theme_by_lineage_and_theme_id( + &self, + theme_id: String, + lineage: ThemeLineage, + ) -> CustomResult { + self.diesel_store + .delete_theme_by_lineage_and_theme_id(theme_id, lineage) + .await + } +} diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 3cf68551b52f..14bed15fa453 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -11,6 +11,7 @@ use crate::{ services::Store, }; pub mod sample_data; +pub mod theme; #[async_trait::async_trait] pub trait UserInterface { diff --git a/crates/router/src/db/user/theme.rs b/crates/router/src/db/user/theme.rs new file mode 100644 index 000000000000..d71b82cdea49 --- /dev/null +++ b/crates/router/src/db/user/theme.rs @@ -0,0 +1,203 @@ +use common_utils::types::theme::ThemeLineage; +use diesel_models::user::theme as storage; +use error_stack::report; + +use super::MockDb; +use crate::{ + connection, + core::errors::{self, CustomResult}, + services::Store, +}; + +#[async_trait::async_trait] +pub trait ThemeInterface { + async fn insert_theme( + &self, + theme: storage::ThemeNew, + ) -> CustomResult; + + async fn find_theme_by_lineage( + &self, + lineage: ThemeLineage, + ) -> CustomResult; + + async fn delete_theme_by_lineage_and_theme_id( + &self, + theme_id: String, + lineage: ThemeLineage, + ) -> CustomResult; +} + +#[async_trait::async_trait] +impl ThemeInterface for Store { + async fn insert_theme( + &self, + theme: storage::ThemeNew, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + theme + .insert(&conn) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + + async fn find_theme_by_lineage( + &self, + lineage: ThemeLineage, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::Theme::find_by_lineage(&conn, lineage) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + + async fn delete_theme_by_lineage_and_theme_id( + &self, + theme_id: String, + lineage: ThemeLineage, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::Theme::delete_by_theme_id_and_lineage(&conn, theme_id, lineage) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } +} + +fn check_theme_with_lineage(theme: &storage::Theme, lineage: &ThemeLineage) -> bool { + match lineage { + ThemeLineage::Tenant { tenant_id } => { + &theme.tenant_id == tenant_id + && theme.org_id.is_none() + && theme.merchant_id.is_none() + && theme.profile_id.is_none() + } + ThemeLineage::Organization { tenant_id, org_id } => { + &theme.tenant_id == tenant_id + && theme + .org_id + .as_ref() + .is_some_and(|org_id_inner| org_id_inner == org_id) + && theme.merchant_id.is_none() + && theme.profile_id.is_none() + } + ThemeLineage::Merchant { + tenant_id, + org_id, + merchant_id, + } => { + &theme.tenant_id == tenant_id + && theme + .org_id + .as_ref() + .is_some_and(|org_id_inner| org_id_inner == org_id) + && theme + .merchant_id + .as_ref() + .is_some_and(|merchant_id_inner| merchant_id_inner == merchant_id) + && theme.profile_id.is_none() + } + ThemeLineage::Profile { + tenant_id, + org_id, + merchant_id, + profile_id, + } => { + &theme.tenant_id == tenant_id + && theme + .org_id + .as_ref() + .is_some_and(|org_id_inner| org_id_inner == org_id) + && theme + .merchant_id + .as_ref() + .is_some_and(|merchant_id_inner| merchant_id_inner == merchant_id) + && theme + .profile_id + .as_ref() + .is_some_and(|profile_id_inner| profile_id_inner == profile_id) + } + } +} + +#[async_trait::async_trait] +impl ThemeInterface for MockDb { + async fn insert_theme( + &self, + new_theme: storage::ThemeNew, + ) -> CustomResult { + let mut themes = self.themes.lock().await; + for theme in themes.iter() { + if new_theme.theme_id == theme.theme_id { + return Err(errors::StorageError::DuplicateValue { + entity: "theme_id", + key: None, + } + .into()); + } + + if new_theme.tenant_id == theme.tenant_id + && new_theme.org_id == theme.org_id + && new_theme.merchant_id == theme.merchant_id + && new_theme.profile_id == theme.profile_id + { + return Err(errors::StorageError::DuplicateValue { + entity: "lineage", + key: None, + } + .into()); + } + } + + let theme = storage::Theme { + theme_id: new_theme.theme_id, + tenant_id: new_theme.tenant_id, + org_id: new_theme.org_id, + merchant_id: new_theme.merchant_id, + profile_id: new_theme.profile_id, + created_at: new_theme.created_at, + last_modified_at: new_theme.last_modified_at, + }; + themes.push(theme.clone()); + + Ok(theme) + } + + async fn find_theme_by_lineage( + &self, + lineage: ThemeLineage, + ) -> CustomResult { + let themes = self.themes.lock().await; + themes + .iter() + .find(|theme| check_theme_with_lineage(theme, &lineage)) + .cloned() + .ok_or( + errors::StorageError::ValueNotFound(format!( + "Theme with lineage {:?} not found", + lineage + )) + .into(), + ) + } + + async fn delete_theme_by_lineage_and_theme_id( + &self, + theme_id: String, + lineage: ThemeLineage, + ) -> CustomResult { + let mut themes = self.themes.lock().await; + let index = themes + .iter() + .position(|theme| { + theme.theme_id == theme_id && check_theme_with_lineage(theme, &lineage) + }) + .ok_or(errors::StorageError::ValueNotFound(format!( + "Theme with id {} and lineage {:?} not found", + theme_id, lineage + )))?; + + let theme = themes.remove(index); + + Ok(theme) + } +} diff --git a/crates/storage_impl/src/mock_db.rs b/crates/storage_impl/src/mock_db.rs index b3358d898b24..efcce5677270 100644 --- a/crates/storage_impl/src/mock_db.rs +++ b/crates/storage_impl/src/mock_db.rs @@ -60,6 +60,7 @@ pub struct MockDb { pub user_key_store: Arc>>, pub user_authentication_methods: Arc>>, + pub themes: Arc>>, } impl MockDb { @@ -105,6 +106,7 @@ impl MockDb { roles: Default::default(), user_key_store: Default::default(), user_authentication_methods: Default::default(), + themes: Default::default(), }) } } diff --git a/migrations/2024-11-06-121933_setup-themes-table/down.sql b/migrations/2024-11-06-121933_setup-themes-table/down.sql new file mode 100644 index 000000000000..4b590c34705e --- /dev/null +++ b/migrations/2024-11-06-121933_setup-themes-table/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +DROP INDEX IF EXISTS themes_index; +DROP TABLE IF EXISTS themes; diff --git a/migrations/2024-11-06-121933_setup-themes-table/up.sql b/migrations/2024-11-06-121933_setup-themes-table/up.sql new file mode 100644 index 000000000000..3d84fcd81405 --- /dev/null +++ b/migrations/2024-11-06-121933_setup-themes-table/up.sql @@ -0,0 +1,17 @@ +-- Your SQL goes here +CREATE TABLE IF NOT EXISTS themes ( + theme_id VARCHAR(64) PRIMARY KEY, + tenant_id VARCHAR(64) NOT NULL, + org_id VARCHAR(64), + merchant_id VARCHAR(64), + profile_id VARCHAR(64), + created_at TIMESTAMP NOT NULL, + last_modified_at TIMESTAMP NOT NULL +); + +CREATE UNIQUE INDEX IF NOT EXISTS themes_index ON themes ( + tenant_id, + COALESCE(org_id, '0'), + COALESCE(merchant_id, '0'), + COALESCE(profile_id, '0') +); From 7d73e9095a532aa5c2bb4bf8806fc678460cf8d4 Mon Sep 17 00:00:00 2001 From: Kartikeya Hegde Date: Thu, 14 Nov 2024 14:10:41 +0530 Subject: [PATCH 004/150] feat: implement scylla traits for StrongSecret (#6500) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 116 ++++++++++++++++++++++++++++++++ crates/masking/Cargo.toml | 2 + crates/masking/src/cassandra.rs | 50 ++++++++++++++ crates/masking/src/lib.rs | 3 + 4 files changed, 171 insertions(+) create mode 100644 crates/masking/src/cassandra.rs diff --git a/Cargo.lock b/Cargo.lock index 7c9cb19d5c98..27b4158ba619 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3699,6 +3699,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + [[package]] name = "hkdf" version = "0.12.4" @@ -4310,6 +4316,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -4567,6 +4582,15 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + [[package]] name = "masking" version = "0.1.0" @@ -4574,6 +4598,7 @@ dependencies = [ "bytes 1.7.1", "diesel", "erased-serde 0.4.5", + "scylla", "serde", "serde_json", "subtle", @@ -5971,6 +5996,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core", +] + [[package]] name = "rand_xorshift" version = "0.3.0" @@ -6892,6 +6926,66 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "scylla" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8139623d3fb0c8205b15e84fa587f3aa0ba61f876c19a9157b688f7c1763a7c5" +dependencies = [ + "arc-swap", + "async-trait", + "byteorder", + "bytes 1.7.1", + "chrono", + "dashmap", + "futures 0.3.30", + "hashbrown 0.14.5", + "histogram", + "itertools 0.13.0", + "lazy_static", + "lz4_flex", + "rand", + "rand_pcg", + "scylla-cql", + "scylla-macros", + "smallvec 1.13.2", + "snap", + "socket2", + "thiserror", + "tokio 1.40.0", + "tracing", + "uuid", +] + +[[package]] +name = "scylla-cql" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7020bcd1f6fdbeaed356cd426bf294b2071bd7120d48d2e8e319295e2acdcd" +dependencies = [ + "async-trait", + "byteorder", + "bytes 1.7.1", + "lz4_flex", + "scylla-macros", + "snap", + "thiserror", + "tokio 1.40.0", + "uuid", +] + +[[package]] +name = "scylla-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3859b6938663fc5062e3b26f3611649c9bd26fb252e85f6fdfa581e0d2ce74b6" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "sdd" version = "3.0.2" @@ -7315,6 +7409,12 @@ dependencies = [ "serde", ] +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "socket2" version = "0.5.7" @@ -7569,6 +7669,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "storage_impl" version = "0.1.0" @@ -8670,6 +8776,16 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "static_assertions", +] + [[package]] name = "typeid" version = "1.0.2" diff --git a/crates/masking/Cargo.toml b/crates/masking/Cargo.toml index ed20a08de343..425c1ca36ec4 100644 --- a/crates/masking/Cargo.toml +++ b/crates/masking/Cargo.toml @@ -12,6 +12,7 @@ default = ["alloc", "serde", "diesel", "time"] alloc = ["zeroize/alloc"] serde = ["dep:serde", "dep:serde_json"] time = ["dep:time"] +cassandra = ["dep:scylla"] [package.metadata.docs.rs] all-features = true @@ -27,6 +28,7 @@ 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 } +scylla = { version = "0.14.0", optional = true} [dev-dependencies] serde_json = "1.0.115" diff --git a/crates/masking/src/cassandra.rs b/crates/masking/src/cassandra.rs new file mode 100644 index 000000000000..dc81512e79d5 --- /dev/null +++ b/crates/masking/src/cassandra.rs @@ -0,0 +1,50 @@ +use scylla::{ + cql_to_rust::FromCqlVal, + deserialize::DeserializeValue, + frame::response::result::{ColumnType, CqlValue}, + serialize::{ + value::SerializeValue, + writers::{CellWriter, WrittenCellProof}, + SerializationError, + }, +}; + +use crate::{abs::PeekInterface, StrongSecret}; + +impl SerializeValue for StrongSecret +where + T: SerializeValue + zeroize::Zeroize + Clone, +{ + fn serialize<'b>( + &self, + typ: &ColumnType, + writer: CellWriter<'b>, + ) -> Result, SerializationError> { + self.peek().serialize(typ, writer) + } +} + +impl<'frame, T> DeserializeValue<'frame> for StrongSecret +where + T: DeserializeValue<'frame> + zeroize::Zeroize + Clone, +{ + fn type_check(typ: &ColumnType) -> Result<(), scylla::deserialize::TypeCheckError> { + T::type_check(typ) + } + + fn deserialize( + typ: &'frame ColumnType, + v: Option>, + ) -> Result { + Ok(Self::new(T::deserialize(typ, v)?)) + } +} + +impl FromCqlVal for StrongSecret +where + T: FromCqlVal + zeroize::Zeroize + Clone, +{ + fn from_cql(cql_val: CqlValue) -> Result { + Ok(Self::new(T::from_cql(cql_val)?)) + } +} diff --git a/crates/masking/src/lib.rs b/crates/masking/src/lib.rs index ca0da6b67672..d376e935bd6e 100644 --- a/crates/masking/src/lib.rs +++ b/crates/masking/src/lib.rs @@ -57,6 +57,9 @@ pub mod prelude { #[cfg(feature = "diesel")] mod diesel; +#[cfg(feature = "cassandra")] +mod cassandra; + pub mod maskable; pub use maskable::*; From a35a4f314242af3c11a27c031388049c8fe4e72d Mon Sep 17 00:00:00 2001 From: Debarati Ghatak <88573135+cookieg13@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:43:46 +0530 Subject: [PATCH 005/150] feat(connector): [Novalnet] Add supported currencies (#6547) --- api-reference-v2/openapi_spec.json | 12 --- api-reference/openapi_spec.json | 12 --- config/config.example.toml | 7 ++ config/deployments/integration_test.toml | 7 ++ config/deployments/production.toml | 7 ++ config/deployments/sandbox.toml | 7 ++ config/development.toml | 7 ++ config/docker_compose.toml | 7 ++ crates/api_models/src/enums.rs | 2 - .../payment_connector_required_fields.rs | 90 ------------------- 10 files changed, 42 insertions(+), 116 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 74a2bb8d2a7d..43bb5bfa4921 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -8066,18 +8066,6 @@ "user_iban" ] }, - { - "type": "string", - "enum": [ - "browser_language" - ] - }, - { - "type": "string", - "enum": [ - "browser_ip" - ] - }, { "type": "string", "enum": [ diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 2e4133c473e4..34d5be1b0ec0 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -10819,18 +10819,6 @@ "user_iban" ] }, - { - "type": "string", - "enum": [ - "browser_language" - ] - }, - { - "type": "string", - "enum": [ - "browser_ip" - ] - }, { "type": "string", "enum": [ diff --git a/config/config.example.toml b/config/config.example.toml index 815687b81113..289087f4a333 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -566,6 +566,13 @@ klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,C credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.mifinity] mifinity = { country = "BR,CN,SG,MY,DE,CH,DK,GB,ES,AD,GI,FI,FR,GR,HR,IT,JP,MX,AR,CO,CL,PE,VE,UY,PY,BO,EC,GT,HN,SV,NI,CR,PA,DO,CU,PR,NL,NO,PL,PT,SE,RU,TR,TW,HK,MO,AX,AL,DZ,AS,AO,AI,AG,AM,AW,AU,AT,AZ,BS,BH,BD,BB,BE,BZ,BJ,BM,BT,BQ,BA,BW,IO,BN,BG,BF,BI,KH,CM,CA,CV,KY,CF,TD,CX,CC,KM,CG,CK,CI,CW,CY,CZ,DJ,DM,EG,GQ,ER,EE,ET,FK,FO,FJ,GF,PF,TF,GA,GM,GE,GH,GL,GD,GP,GU,GG,GN,GW,GY,HT,HM,VA,IS,IN,ID,IE,IM,IL,JE,JO,KZ,KE,KI,KW,KG,LA,LV,LB,LS,LI,LT,LU,MK,MG,MW,MV,ML,MT,MH,MQ,MR,MU,YT,FM,MD,MC,MN,ME,MS,MA,MZ,NA,NR,NP,NC,NZ,NE,NG,NU,NF,MP,OM,PK,PW,PS,PG,PH,PN,QA,RE,RO,RW,BL,SH,KN,LC,MF,PM,VC,WS,SM,ST,SA,SN,RS,SC,SL,SX,SK,SI,SB,SO,ZA,GS,KR,LK,SR,SJ,SZ,TH,TL,TG,TK,TO,TT,TN,TM,TC,TV,UG,UA,AE,UZ,VU,VN,VG,VI,WF,EH,ZM", currency = "AUD,CAD,CHF,CNY,CZK,DKK,EUR,GBP,INR,JPY,NOK,NZD,PLN,RUB,SEK,ZAR,USD,EGP,UYU,UZS" } diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 024f7373f5e2..1c7a40ea5398 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -318,6 +318,13 @@ paze = { currency = "USD" } credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.volt] open_banking_uk = {country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,MT,NL,PT,SI,SK,BG,CZ,DK,HU,NO,PL,RO,SE,AU,BR", currency = "EUR,GBP,DKK,NOK,PLN,SEK,AUD,BRL"} diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 3dd46e68509f..73e5794f0420 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -292,6 +292,13 @@ paze = { currency = "USD" } credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 88f215678d7e..98e1e7e00d9c 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -295,6 +295,13 @@ paze = { currency = "USD" } credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" diff --git a/config/development.toml b/config/development.toml index 8f3ffdbcbb59..b6638fe1d476 100644 --- a/config/development.toml +++ b/config/development.toml @@ -468,6 +468,13 @@ paze = { currency = "USD" } credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.braintree] paypal = { currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 3b4c3fa49da0..7adeee8a3763 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -469,6 +469,13 @@ paze = { currency = "USD" } credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +[pm_filters.novalnet] +credit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +debit = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +apple_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +google_pay = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} +paypal = { country = "AD,AE,AL,AM,AR,AT,AU,AZ,BA,BB,BD,BE,BG,BH,BI,BM,BN,BO,BR,BS,BW,BY,BZ,CA,CD,CH,CL,CN,CO,CR,CU,CY,CZ,DE,DJ,DK,DO,DZ,EE,EG,ET,ES,FI,FJ,FR,GB,GE,GH,GI,GM,GR,GT,GY,HK,HN,HR,HU,ID,IE,IL,IN,IS,IT,JM,JO,JP,KE,KH,KR,KW,KY,KZ,LB,LK,LT,LV,LY,MA,MC,MD,ME,MG,MK,MN,MO,MT,MV,MW,MX,MY,NG,NI,NO,NP,NL,NZ,OM,PA,PE,PG,PH,PK,PL,PT,PY,QA,RO,RS,RU,RW,SA,SB,SC,SE,SG,SH,SI,SK,SL,SO,SM,SR,ST,SV,SY,TH,TJ,TN,TO,TR,TW,TZ,UA,UG,US,UY,UZ,VE,VA,VN,VU,WS,CF,AG,DM,GD,KN,LC,VC,YE,ZA,ZM", currency = "AED,ALL,AMD,ARS,AUD,AZN,BAM,BBD,BDT,BGN,BHD,BIF,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CDF,CHF,CLP,CNY,COP,CRC,CUP,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,GBP,GEL,GHS,GIP,GMD,GTQ,GYD,HKD,HNL,HRK,HUF,IDR,ILS,INR,ISK,JMD,JOD,JPY,KES,KHR,KRW,KWD,KYD,KZT,LBP,LKR,LYD,MAD,MDL,MGA,MKD,MNT,MOP,MVR,MWK,MXN,MYR,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLL,SOS,SRD,STN,SVC,SYP,THB,TJS,TND,TOP,TRY,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,YER,ZAR,ZMW"} + [pm_filters.helcim] credit = { currency = "USD" } debit = { currency = "USD" } diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 817b047f38c4..646ad122d7c9 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -224,8 +224,6 @@ pub enum FieldType { UserCpf, UserCnpj, UserIban, - BrowserLanguage, - BrowserIp, UserMsisdn, UserClientIdentifier, OrderDetailsProductName, diff --git a/crates/router/src/configs/defaults/payment_connector_required_fields.rs b/crates/router/src/configs/defaults/payment_connector_required_fields.rs index 452e388c4074..fc37d937d00a 100644 --- a/crates/router/src/configs/defaults/payment_connector_required_fields.rs +++ b/crates/router/src/configs/defaults/payment_connector_required_fields.rs @@ -2041,24 +2041,6 @@ impl Default for settings::RequiredFields { non_mandate: HashMap::new(), common: HashMap::from( [ - ( - "browser_info.language".to_string(), - RequiredFieldInfo { - required_field: "browser_info.language".to_string(), - display_name: "browser_info_language".to_string(), - field_type: enums::FieldType::BrowserLanguage, - value: None, - } - ), - ( - "browser_info.ip_address".to_string(), - RequiredFieldInfo { - required_field: "browser_info.ip_address".to_string(), - display_name: "browser_info_ip_address".to_string(), - field_type: enums::FieldType::BrowserIp, - value: None, - } - ), ( "billing.address.line1".to_string(), RequiredFieldInfo { @@ -5244,24 +5226,6 @@ impl Default for settings::RequiredFields { non_mandate: HashMap::new(), common: HashMap::from( [ - ( - "browser_info.language".to_string(), - RequiredFieldInfo { - required_field: "browser_info.language".to_string(), - display_name: "browser_info_language".to_string(), - field_type: enums::FieldType::BrowserLanguage, - value: None, - } - ), - ( - "browser_info.ip_address".to_string(), - RequiredFieldInfo { - required_field: "browser_info.ip_address".to_string(), - display_name: "browser_info_ip_address".to_string(), - field_type: enums::FieldType::BrowserIp, - value: None, - } - ), ( "billing.address.line1".to_string(), RequiredFieldInfo { @@ -8239,24 +8203,6 @@ impl Default for settings::RequiredFields { non_mandate: HashMap::new(), common: HashMap::from( [ - ( - "browser_info.language".to_string(), - RequiredFieldInfo { - required_field: "browser_info.language".to_string(), - display_name: "browser_info_language".to_string(), - field_type: enums::FieldType::BrowserLanguage, - value: None, - } - ), - ( - "browser_info.ip_address".to_string(), - RequiredFieldInfo { - required_field: "browser_info.ip_address".to_string(), - display_name: "browser_info_ip_address".to_string(), - field_type: enums::FieldType::BrowserIp, - value: None, - } - ), ( "billing.address.line1".to_string(), RequiredFieldInfo { @@ -8617,24 +8563,6 @@ impl Default for settings::RequiredFields { non_mandate: HashMap::new(), common: HashMap::from( [ - ( - "browser_info.language".to_string(), - RequiredFieldInfo { - required_field: "browser_info.language".to_string(), - display_name: "browser_info_language".to_string(), - field_type: enums::FieldType::BrowserLanguage, - value: None, - } - ), - ( - "browser_info.ip_address".to_string(), - RequiredFieldInfo { - required_field: "browser_info.ip_address".to_string(), - display_name: "browser_info_ip_address".to_string(), - field_type: enums::FieldType::BrowserIp, - value: None, - } - ), ( "billing.address.line1".to_string(), RequiredFieldInfo { @@ -9385,24 +9313,6 @@ impl Default for settings::RequiredFields { non_mandate: HashMap::new(), common: HashMap::from( [ - ( - "browser_info.language".to_string(), - RequiredFieldInfo { - required_field: "browser_info.language".to_string(), - display_name: "browser_info_language".to_string(), - field_type: enums::FieldType::BrowserLanguage, - value: None, - } - ), - ( - "browser_info.ip_address".to_string(), - RequiredFieldInfo { - required_field: "browser_info.ip_address".to_string(), - display_name: "browser_info_ip_address".to_string(), - field_type: enums::FieldType::BrowserIp, - value: None, - } - ), ( "billing.address.line1".to_string(), RequiredFieldInfo { From 8cc5d3db9afb120b00115c6714be2e362951cc94 Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:49:10 +0530 Subject: [PATCH 006/150] ci: introduce `cybersource` cypress test to run in parallel (#6541) --- .github/workflows/cypress-tests-runner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cypress-tests-runner.yml b/.github/workflows/cypress-tests-runner.yml index 055aacdcf4f2..84680c945e10 100644 --- a/.github/workflows/cypress-tests-runner.yml +++ b/.github/workflows/cypress-tests-runner.yml @@ -13,7 +13,7 @@ concurrency: env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 - PAYMENTS_CONNECTORS: "stripe" + PAYMENTS_CONNECTORS: "cybersource stripe" PAYOUTS_CONNECTORS: "wise" RUST_BACKTRACE: short RUSTUP_MAX_RETRIES: 10 From c562cebb6d97dbb350b15c9dec97c95946701aa3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:22:07 +0000 Subject: [PATCH 007/150] chore(version): 2024.11.15.0 --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13007b6f6355..fb5fa4101d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.11.15.0 + +### Features + +- **analytics:** Add `sessionized_metrics` and `currency_conversion` for refunds analytics ([#6419](https://github.com/juspay/hyperswitch/pull/6419)) ([`afd7f7d`](https://github.com/juspay/hyperswitch/commit/afd7f7d20980f6f39673008c86b89b1e501f05f2)) +- **connector:** [Novalnet] Add supported currencies ([#6547](https://github.com/juspay/hyperswitch/pull/6547)) ([`a35a4f3`](https://github.com/juspay/hyperswitch/commit/a35a4f314242af3c11a27c031388049c8fe4e72d)) +- **themes:** Setup themes table ([#6533](https://github.com/juspay/hyperswitch/pull/6533)) ([`29be1d4`](https://github.com/juspay/hyperswitch/commit/29be1d4fadc55948c99cc8bd33b3b8e8d341ae11)) +- Implement scylla traits for StrongSecret ([#6500](https://github.com/juspay/hyperswitch/pull/6500)) ([`7d73e90`](https://github.com/juspay/hyperswitch/commit/7d73e9095a532aa5c2bb4bf8806fc678460cf8d4)) + +**Full Changelog:** [`2024.11.14.0...2024.11.15.0`](https://github.com/juspay/hyperswitch/compare/2024.11.14.0...2024.11.15.0) + +- - - + ## 2024.11.14.0 ### Features From 0b63efbd4295f79d4d0cab649f26ec1876c04372 Mon Sep 17 00:00:00 2001 From: likhinbopanna <131246334+likhinbopanna@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:18:38 +0530 Subject: [PATCH 008/150] ci(cypress): Add Checkout Connector (#6559) --- .../e2e/PaymentTest/00014-SaveCardFlow.cy.js | 6 +- .../00016-ThreeDSManualCapture.cy.js | 8 + .../cypress/e2e/PaymentUtils/Checkout.js | 298 ++++++++++++++++++ .../cypress/e2e/PaymentUtils/Utils.js | 2 + .../cypress/support/redirectionHandler.js | 12 + 5 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 cypress-tests/cypress/e2e/PaymentUtils/Checkout.js diff --git a/cypress-tests/cypress/e2e/PaymentTest/00014-SaveCardFlow.cy.js b/cypress-tests/cypress/e2e/PaymentTest/00014-SaveCardFlow.cy.js index cbff06ed6405..ae8e276c8d4c 100644 --- a/cypress-tests/cypress/e2e/PaymentTest/00014-SaveCardFlow.cy.js +++ b/cypress-tests/cypress/e2e/PaymentTest/00014-SaveCardFlow.cy.js @@ -47,11 +47,7 @@ describe("Card - SaveCard payment flow test", () => { globalState ); if (should_continue) { - // Don't continue if payment status is processing during auto capture - // Payment data is tokenized only after payment is successful - let notProcessing = res_data?.body?.status != "processing"; - should_continue = - notProcessing && utils.should_continue_further(res_data); + should_continue = utils.should_continue_further(res_data); } }); diff --git a/cypress-tests/cypress/e2e/PaymentTest/00016-ThreeDSManualCapture.cy.js b/cypress-tests/cypress/e2e/PaymentTest/00016-ThreeDSManualCapture.cy.js index 3c9fca1428f4..3d236af7a910 100644 --- a/cypress-tests/cypress/e2e/PaymentTest/00016-ThreeDSManualCapture.cy.js +++ b/cypress-tests/cypress/e2e/PaymentTest/00016-ThreeDSManualCapture.cy.js @@ -6,6 +6,14 @@ import getConnectorDetails, * as utils from "../PaymentUtils/Utils"; let globalState; describe("Card - ThreeDS Manual payment flow test", () => { + let should_continue = true; + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + before("seed global state", () => { cy.task("getGlobalState").then((state) => { globalState = new State(state); diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Checkout.js b/cypress-tests/cypress/e2e/PaymentUtils/Checkout.js new file mode 100644 index 000000000000..4653e3e77c2f --- /dev/null +++ b/cypress-tests/cypress/e2e/PaymentUtils/Checkout.js @@ -0,0 +1,298 @@ +const successfulNo3DSCardDetails = { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", +}; + +const successfulThreeDSTestCardDetails = { + card_number: "4242424242424242", + card_exp_month: "01", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", +}; + +const customerAcceptance = { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, +}; + +export const connectorDetails = { + card_pm: { + PaymentIntent: { + Request: { + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }, + "3DSManualCapture": { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, + }, + "3DSAutoCapture": { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, + }, + No3DSManualCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + No3DSAutoCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + Capture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + amount: 6500, + amount_capturable: 0, + amount_received: 6500, + }, + }, + }, + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "partially_captured", + amount: 6500, + amount_capturable: 0, + amount_received: 100, + }, + }, + }, + Void: { + Request: {}, + Response: { + status: 200, + body: { + status: "cancelled", + }, + }, + }, + Refund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + PartialRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + manualPaymentRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + manualPaymentPartialRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + SyncRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + ZeroAuthMandate: { + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: "Setup Mandate flow for Checkout is not implemented", + code: "IR_00", + }, + }, + }, + }, + ZeroAuthPaymentIntent: { + Request: { + amount: 0, + setup_future_usage: "off_session", + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + setup_future_usage: "off_session", + }, + }, + }, + ZeroAuthConfirmPayment: { + Request: { + payment_type: "setup_mandate", + payment_method: "card", + payment_method_type: "credit", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + }, + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: "Setup Mandate flow for Checkout is not implemented", + code: "IR_00", + }, + }, + }, + }, + SaveCardUseNo3DSAutoCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + setup_future_usage: "on_session", + customer_acceptance: customerAcceptance, + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + SaveCardUseNo3DSManualCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + setup_future_usage: "on_session", + customer_acceptance: customerAcceptance, + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + }, +}; diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js index 8848450bc61e..618f5864f6d5 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js @@ -20,11 +20,13 @@ import { connectorDetails as trustpayConnectorDetails } from "./Trustpay.js"; import { connectorDetails as wellsfargoConnectorDetails } from "./WellsFargo.js"; import { connectorDetails as fiuuConnectorDetails } from "./Fiuu.js"; import { connectorDetails as worldpayConnectorDetails } from "./WorldPay.js"; +import { connectorDetails as checkoutConnectorDetails } from "./Checkout.js"; const connectorDetails = { adyen: adyenConnectorDetails, bankofamerica: bankOfAmericaConnectorDetails, bluesnap: bluesnapConnectorDetails, + checkout: checkoutConnectorDetails, commons: CommonConnectorDetails, cybersource: cybersourceConnectorDetails, fiservemea: fiservemeaConnectorDetails, diff --git a/cypress-tests/cypress/support/redirectionHandler.js b/cypress-tests/cypress/support/redirectionHandler.js index 5e05ad279118..ee8e21979034 100644 --- a/cypress-tests/cypress/support/redirectionHandler.js +++ b/cypress-tests/cypress/support/redirectionHandler.js @@ -296,6 +296,18 @@ function threeDsRedirection(redirection_url, expected_url, connectorId) { cy.get('input[type="text"]').click().type("1234"); cy.get('input[value="SUBMIT"]').click(); }); + } else if (connectorId === "checkout") { + cy.get("iframe", { timeout: TIMEOUT }) + .its("0.contentDocument.body") + .within((body) => { + cy.get('form[id="form"]', { timeout: WAIT_TIME }) + .should("exist") + .then((form) => { + cy.get('input[id="password"]').click(); + cy.get('input[id="password"]').type("Checkout1!"); + cy.get("#txtButton").click(); + }); + }); } else if (connectorId === "nmi" || connectorId === "noon") { cy.get("iframe", { timeout: TIMEOUT }) .its("0.contentDocument.body") From 0805a937b1bc12ac1dfb23922036733ed971a87a Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:06:48 +0530 Subject: [PATCH 009/150] feat(payments_v2): add finish redirection endpoint (#6549) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: hrithikesh026 --- api-reference-v2/openapi_spec.json | 8 +- crates/api_models/src/admin.rs | 6 +- crates/api_models/src/payments.rs | 12 + crates/common_utils/src/consts.rs | 3 + crates/common_utils/src/events.rs | 5 + crates/common_utils/src/types.rs | 82 +++++- crates/diesel_models/src/business_profile.rs | 6 +- .../src/business_profile.rs | 6 +- .../hyperswitch_domain_models/src/payments.rs | 19 ++ crates/router/src/analytics.rs | 5 + crates/router/src/core/admin.rs | 4 +- crates/router/src/core/payments.rs | 278 ++++++++++++++++-- crates/router/src/core/payments/helpers.rs | 3 + crates/router/src/core/payments/operations.rs | 13 +- .../operations/payment_confirm_intent.rs | 13 +- .../operations/payment_create_intent.rs | 32 +- .../core/payments/operations/payment_get.rs | 13 +- .../payments/operations/payment_get_intent.rs | 32 +- .../router/src/core/payments/transformers.rs | 14 +- crates/router/src/core/user.rs | 1 + crates/router/src/events/api_logs.rs | 10 + crates/router/src/lib.rs | 2 +- crates/router/src/routes/app.rs | 6 +- crates/router/src/routes/dummy_connector.rs | 11 + .../router/src/routes/dummy_connector/core.rs | 5 + crates/router/src/routes/payments.rs | 68 +++++ crates/router/src/routes/user.rs | 1 + crates/router/src/services/api.rs | 4 +- crates/router/src/types.rs | 8 + 29 files changed, 534 insertions(+), 136 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 43bb5bfa4921..d2fa50d86293 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -13510,7 +13510,8 @@ ], "nullable": true } - } + }, + "additionalProperties": false }, "PaymentsConfirmIntentResponse": { "type": "object", @@ -15244,6 +15245,11 @@ "force_sync": { "type": "boolean", "description": "A boolean used to indicate if the payment status should be fetched from the connector\nIf this is set to true, the status will be fetched from the connector" + }, + "param": { + "type": "string", + "description": "These are the query params that are sent in case of redirect response.\nThese can be ingested by the connector to take necessary actions.", + "nullable": true } } }, diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 96335f34710c..7f5ae1556d27 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2007,7 +2007,7 @@ pub struct ProfileCreate { /// The URL to redirect after the completion of the operation #[schema(value_type = Option, max_length = 255, example = "https://www.example.com/success")] - pub return_url: Option, + pub return_url: Option, /// A boolean value to indicate if payment response hash needs to be enabled #[schema(default = true, example = true)] @@ -2244,7 +2244,7 @@ pub struct ProfileResponse { /// The URL to redirect after the completion of the operation #[schema(value_type = Option, max_length = 255, example = "https://www.example.com/success")] - pub return_url: Option, + pub return_url: Option, /// A boolean value to indicate if payment response hash needs to be enabled #[schema(default = true, example = true)] @@ -2474,7 +2474,7 @@ pub struct ProfileUpdate { /// The URL to redirect after the completion of the operation #[schema(value_type = Option, max_length = 255, example = "https://www.example.com/success")] - pub return_url: Option, + pub return_url: Option, /// A boolean value to indicate if payment response hash needs to be enabled #[schema(default = true, example = true)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 29c1b81df2fb..75dda21097ad 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -4517,6 +4517,7 @@ pub struct PaymentsResponse { /// Request for Payment Intent Confirm #[cfg(feature = "v2")] #[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(deny_unknown_fields)] pub struct PaymentsConfirmIntentRequest { /// The URL to which you want the user to be redirected after the completion of the payment operation /// If this url is not passed, the url configured in the business profile will be used @@ -4557,6 +4558,10 @@ pub struct PaymentsRetrieveRequest { /// If this is set to true, the status will be fetched from the connector #[serde(default)] pub force_sync: bool, + + /// These are the query params that are sent in case of redirect response. + /// These can be ingested by the connector to take necessary actions. + pub param: Option, } /// Error details for the payment @@ -5208,6 +5213,7 @@ pub struct PgRedirectResponse { pub amount: Option, } +#[cfg(feature = "v1")] #[derive(Debug, serde::Serialize, PartialEq, Eq, serde::Deserialize)] pub struct RedirectionResponse { pub return_url: String, @@ -5217,6 +5223,12 @@ pub struct RedirectionResponse { pub headers: Vec<(String, String)>, } +#[cfg(feature = "v2")] +#[derive(Debug, serde::Serialize, PartialEq, Eq, serde::Deserialize)] +pub struct RedirectionResponse { + pub return_url_with_query_params: String, +} + #[derive(Debug, serde::Deserialize)] pub struct PaymentsResponseForm { pub transaction_id: String, diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 5fc2fea9c8b2..a50555ced920 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -159,6 +159,9 @@ pub const MAX_STATEMENT_DESCRIPTOR_LENGTH: u16 = 22; /// Payout flow identifier used for performing GSM operations pub const PAYOUT_FLOW_STR: &str = "payout_flow"; +/// length of the publishable key +pub const PUBLISHABLE_KEY_LENGTH: u16 = 39; + /// The number of bytes allocated for the hashed connector transaction ID. /// Total number of characters equals CONNECTOR_TRANSACTION_ID_HASH_BYTES times 2. pub const CONNECTOR_TRANSACTION_ID_HASH_BYTES: usize = 25; diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index a83c1d481cc7..afe19d412390 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -66,10 +66,15 @@ pub enum ApiEventsType { }, Routing, ResourceListAPI, + #[cfg(feature = "v1")] PaymentRedirectionResponse { connector: Option, payment_id: Option, }, + #[cfg(feature = "v2")] + PaymentRedirectionResponse { + payment_id: id_type::GlobalPaymentId, + }, Gsm, // TODO: This has to be removed once the corresponding apiEventTypes are created Miscellaneous, diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index f7cdfd3617bc..84a70e44a32a 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -37,7 +37,9 @@ use time::PrimitiveDateTime; use utoipa::ToSchema; use crate::{ - consts::{self, MAX_DESCRIPTION_LENGTH, MAX_STATEMENT_DESCRIPTOR_LENGTH}, + consts::{ + self, MAX_DESCRIPTION_LENGTH, MAX_STATEMENT_DESCRIPTOR_LENGTH, PUBLISHABLE_KEY_LENGTH, + }, errors::{CustomResult, ParsingError, PercentageError, ValidationError}, fp_utils::when, }; @@ -639,6 +641,17 @@ impl Url { pub fn into_inner(self) -> url::Url { self.0 } + + /// Add query params to the url + pub fn add_query_params(mut self, (key, value): (&str, &str)) -> Self { + let url = self + .0 + .query_pairs_mut() + .append_pair(key, value) + .finish() + .clone(); + Self(url) + } } impl ToSql for Url @@ -1064,7 +1077,7 @@ crate::impl_to_sql_from_sql_json!(ChargeRefunds); /// A common type of domain type that can be used for fields that contain a string with restriction of length #[derive(Debug, Clone, Serialize, Hash, PartialEq, Eq, AsExpression)] #[diesel(sql_type = sql_types::Text)] -pub(crate) struct LengthString(String); +pub(crate) struct LengthString(String); /// Error generated from violation of constraints for MerchantReferenceId #[derive(Debug, Error, PartialEq, Eq)] @@ -1075,10 +1088,10 @@ pub(crate) enum LengthStringError { #[error("the minimum required length for this field is {0}")] /// Minimum length of string violated - MinLengthViolated(u8), + MinLengthViolated(u16), } -impl LengthString { +impl LengthString { /// Generates new [MerchantReferenceId] from the given input string pub fn from(input_string: Cow<'static, str>) -> Result { let trimmed_input_string = input_string.trim().to_string(); @@ -1089,7 +1102,7 @@ impl LengthString LengthString Deserialize<'de> +impl<'de, const MAX_LENGTH: u16, const MIN_LENGTH: u16> Deserialize<'de> for LengthString { fn deserialize(deserializer: D) -> Result @@ -1113,7 +1126,7 @@ impl<'de, const MAX_LENGTH: u16, const MIN_LENGTH: u8> Deserialize<'de> } } -impl FromSql +impl FromSql for LengthString where DB: Backend, @@ -1125,7 +1138,7 @@ where } } -impl ToSql +impl ToSql for LengthString where DB: Backend, @@ -1136,7 +1149,7 @@ where } } -impl Queryable +impl Queryable for LengthString where DB: Backend, @@ -1518,3 +1531,54 @@ pub trait ConnectorTransactionIdTrait { self.get_optional_connector_transaction_id() } } + +/// Domain type for PublishableKey +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, AsExpression)] +#[diesel(sql_type = sql_types::Text)] +pub struct PublishableKey(LengthString); + +impl PublishableKey { + /// Create a new PublishableKey Domain type without any length check from a static str + pub fn generate(env_prefix: &'static str) -> Self { + let publishable_key_string = format!("pk_{env_prefix}_{}", uuid::Uuid::now_v7().simple()); + Self(LengthString::new_unchecked(publishable_key_string)) + } + + /// Get the string representation of the PublishableKey + pub fn get_string_repr(&self) -> &str { + &self.0 .0 + } +} + +impl Queryable for PublishableKey +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> deserialize::Result { + Ok(row) + } +} + +impl FromSql for PublishableKey +where + DB: Backend, + LengthString: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let val = LengthString::::from_sql(bytes)?; + Ok(Self(val)) + } +} + +impl ToSql for PublishableKey +where + DB: Backend, + LengthString: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index ecb536206400..05c124c4c9cf 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -255,7 +255,7 @@ pub struct Profile { pub profile_name: String, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: bool, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: bool, @@ -314,7 +314,7 @@ pub struct ProfileNew { pub profile_name: String, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: bool, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: bool, @@ -358,7 +358,7 @@ pub struct ProfileNew { pub struct ProfileUpdateInternal { pub profile_name: Option, pub modified_at: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: Option, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: Option, diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index fb846e288626..3e8213a3588f 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -668,7 +668,7 @@ pub struct Profile { pub profile_name: String, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: bool, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: bool, @@ -709,7 +709,7 @@ pub struct ProfileSetter { pub profile_name: String, pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: bool, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: bool, @@ -815,7 +815,7 @@ impl Profile { #[derive(Debug)] pub struct ProfileGeneralUpdate { pub profile_name: Option, - pub return_url: Option, + pub return_url: Option, pub enable_payment_response_hash: Option, pub payment_response_hash_key: Option, pub redirect_to_merchant_with_http_post: Option, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 8b4c351daa35..5c629f6198ed 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -113,6 +113,7 @@ impl PaymentIntent { } #[cfg(feature = "v2")] + /// This is the url to which the customer will be redirected to, to complete the redirection flow pub fn create_start_redirection_url( &self, base_url: &str, @@ -129,6 +130,24 @@ impl PaymentIntent { .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) .attach_printable("Error creating start redirection url") } + + #[cfg(feature = "v2")] + /// This is the url to which the customer will be redirected to, after completing the redirection flow + pub fn create_finish_redirection_url( + &self, + base_url: &str, + publishable_key: &str, + ) -> CustomResult { + let finish_redirection_url = format!( + "{base_url}/v2/payments/{}/finish_redirection/{publishable_key}/{}", + self.id.get_string_repr(), + self.profile_id.get_string_repr() + ); + + url::Url::parse(&finish_redirection_url) + .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) + .attach_printable("Error creating finish redirection url") + } } #[cfg(feature = "v2")] diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs index a13f476950b0..96f41f75ee07 100644 --- a/crates/router/src/analytics.rs +++ b/crates/router/src/analytics.rs @@ -792,6 +792,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetSdkEventMetricRequest` element. @@ -830,6 +831,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetActivePaymentsMetricRequest` element. @@ -869,6 +871,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] /// # Panics /// /// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element. @@ -1148,6 +1151,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_sdk_event_filters( state: web::Data, req: actix_web::HttpRequest, @@ -1241,6 +1245,7 @@ pub mod routes { .await } + #[cfg(feature = "v1")] pub async fn get_profile_sdk_events( state: web::Data, req: actix_web::HttpRequest, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 49b16aaa3f46..97a40f38ac4b 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3604,7 +3604,7 @@ impl ProfileCreateBridge for api::ProfileCreate { profile_name, created_at: current_time, modified_at: current_time, - return_url: self.return_url.map(|return_url| return_url.to_string()), + return_url: self.return_url, enable_payment_response_hash: self.enable_payment_response_hash.unwrap_or(true), payment_response_hash_key: Some(payment_response_hash_key), redirect_to_merchant_with_http_post: self @@ -3968,7 +3968,7 @@ impl ProfileUpdateBridge for api::ProfileUpdate { Ok(domain::ProfileUpdate::Update(Box::new( domain::ProfileGeneralUpdate { profile_name: self.profile_name, - return_url: self.return_url.map(|return_url| return_url.to_string()), + return_url: self.return_url, enable_payment_response_hash: self.enable_payment_response_hash, payment_response_hash_key: self.payment_response_hash_key, redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 2d480d71fe7f..ed41650840a4 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -49,6 +49,8 @@ pub use hyperswitch_domain_models::{ router_request_types::CustomerDetails, }; use masking::{ExposeInterface, PeekInterface, Secret}; +#[cfg(feature = "v2")] +use operations::ValidateStatusForOperation; use redis_interface::errors::RedisError; use router_env::{instrument, metrics::add_attributes, tracing}; #[cfg(feature = "olap")] @@ -123,7 +125,7 @@ pub async fn payments_operation_core( profile: domain::Profile, operation: Op, req: Req, - payment_id: id_type::GlobalPaymentId, + get_tracker_response: operations::GetTrackerResponse, call_connector_action: CallConnectorAction, header_payload: HeaderPayload, ) -> RouterResult<(D, Req, Option, Option, Option)> @@ -150,27 +152,8 @@ where { let operation: BoxedOperation<'_, F, Req, D> = Box::new(operation); - // Validate the request fields - let (operation, validate_result) = operation - .to_validate_request()? - .validate_request(&req, &merchant_account)?; - // Get the trackers related to track the state of the payment - let operations::GetTrackerResponse { - operation, - mut payment_data, - } = operation - .to_get_tracker()? - .get_trackers( - state, - &payment_id, - &req, - &merchant_account, - &profile, - &key_store, - &header_payload, - ) - .await?; + let operations::GetTrackerResponse { mut payment_data } = get_tracker_response; let (_operation, customer) = operation .to_domain()? @@ -207,7 +190,6 @@ where &mut payment_data, &customer, call_connector_action.clone(), - &validate_result, None, header_payload.clone(), #[cfg(feature = "frm")] @@ -1029,7 +1011,7 @@ where tracing::Span::current().record("merchant_id", merchant_account.get_id().get_string_repr()); - let (operation, _validate_result) = operation + let _validate_result = operation .to_validate_request()? .validate_request(&req, &merchant_account)?; @@ -1037,10 +1019,7 @@ where tracing::Span::current().record("global_payment_id", payment_id.get_string_repr()); - let operations::GetTrackerResponse { - operation, - mut payment_data, - } = operation + let operations::GetTrackerResponse { mut payment_data } = operation .to_get_tracker()? .get_trackers( state, @@ -1520,6 +1499,24 @@ where RouterData: hyperswitch_domain_models::router_data::TrackerPostUpdateObjects, { + // Validate the request fields + let validate_result = operation + .to_validate_request()? + .validate_request(&req, &merchant_account)?; + + let get_tracker_response = operation + .to_get_tracker()? + .get_trackers( + &state, + &payment_id, + &req, + &merchant_account, + &profile, + &key_store, + &header_payload, + ) + .await?; + let (payment_data, _req, customer, connector_http_status_code, external_latency) = payments_operation_core::<_, _, _, _, _>( &state, @@ -1529,7 +1526,7 @@ where profile, operation.clone(), req, - payment_id, + get_tracker_response, call_connector_action, header_payload.clone(), ) @@ -1552,6 +1549,7 @@ fn is_start_pay(operation: &Op) -> bool { format!("{operation:?}").eq("PaymentStart") } +#[cfg(feature = "v1")] #[derive(Clone, Debug, serde::Serialize)] pub struct PaymentsRedirectResponseData { pub connector: Option, @@ -1563,11 +1561,20 @@ pub struct PaymentsRedirectResponseData { pub creds_identifier: Option, } +#[cfg(feature = "v2")] +#[derive(Clone, Debug, serde::Serialize)] +pub struct PaymentsRedirectResponseData { + pub payment_id: id_type::GlobalPaymentId, + pub query_params: String, + pub json_payload: Option, +} + #[async_trait::async_trait] pub trait PaymentRedirectFlow: Sync { // Associated type for call_payment_flow response type PaymentFlowResponse; + #[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, @@ -1581,8 +1588,20 @@ pub trait PaymentRedirectFlow: Sync { payment_id: id_type::PaymentId, ) -> RouterResult; + #[cfg(feature = "v2")] + async fn call_payment_flow( + &self, + state: &SessionState, + req_state: ReqState, + merchant_account: domain::MerchantAccount, + merchant_key_store: domain::MerchantKeyStore, + profile: domain::Profile, + req: PaymentsRedirectResponseData, + ) -> RouterResult; + fn get_payment_action(&self) -> services::PaymentAction; + #[cfg(feature = "v1")] fn generate_response( &self, payment_flow_response: &Self::PaymentFlowResponse, @@ -1590,7 +1609,13 @@ pub trait PaymentRedirectFlow: Sync { connector: String, ) -> RouterResult>; - #[allow(clippy::too_many_arguments)] + #[cfg(feature = "v2")] + fn generate_response( + &self, + payment_flow_response: &Self::PaymentFlowResponse, + ) -> RouterResult>; + + #[cfg(feature = "v1")] async fn handle_payments_redirect_response( &self, state: SessionState, @@ -1656,6 +1681,39 @@ pub trait PaymentRedirectFlow: Sync { self.generate_response(&payment_flow_response, resource_id, connector) } + + #[cfg(feature = "v2")] + async fn handle_payments_redirect_response( + &self, + state: SessionState, + req_state: ReqState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + profile: domain::Profile, + request: PaymentsRedirectResponseData, + ) -> RouterResponse { + metrics::REDIRECTION_TRIGGERED.add( + &metrics::CONTEXT, + 1, + &add_attributes([( + "merchant_id", + merchant_account.get_id().get_string_repr().to_owned(), + )]), + ); + + let payment_flow_response = self + .call_payment_flow( + &state, + req_state, + merchant_account, + key_store, + profile, + request, + ) + .await?; + + self.generate_response(&payment_flow_response) + } } #[derive(Clone, Debug)] @@ -1894,6 +1952,167 @@ impl PaymentRedirectFlow for PaymentRedirectSync { } } +#[cfg(feature = "v2")] +impl ValidateStatusForOperation for &PaymentRedirectSync { + fn validate_status_for_operation( + &self, + intent_status: common_enums::IntentStatus, + ) -> Result<(), errors::ApiErrorResponse> { + match intent_status { + common_enums::IntentStatus::RequiresCustomerAction => Ok(()), + common_enums::IntentStatus::Succeeded + | common_enums::IntentStatus::Failed + | common_enums::IntentStatus::Cancelled + | common_enums::IntentStatus::Processing + | common_enums::IntentStatus::RequiresPaymentMethod + | common_enums::IntentStatus::RequiresMerchantAction + | common_enums::IntentStatus::RequiresCapture + | common_enums::IntentStatus::PartiallyCaptured + | common_enums::IntentStatus::RequiresConfirmation + | common_enums::IntentStatus::PartiallyCapturedAndCapturable => { + Err(errors::ApiErrorResponse::PaymentUnexpectedState { + current_flow: format!("{self:?}"), + field_name: "status".to_string(), + current_value: intent_status.to_string(), + states: ["requires_customer_action".to_string()].join(", "), + }) + } + } + } +} + +#[cfg(feature = "v2")] +#[async_trait::async_trait] +impl PaymentRedirectFlow for PaymentRedirectSync { + type PaymentFlowResponse = + router_types::RedirectPaymentFlowResponse>; + + async fn call_payment_flow( + &self, + state: &SessionState, + req_state: ReqState, + merchant_account: domain::MerchantAccount, + merchant_key_store: domain::MerchantKeyStore, + profile: domain::Profile, + req: PaymentsRedirectResponseData, + ) -> RouterResult { + let payment_id = req.payment_id.clone(); + + let payment_sync_request = api::PaymentsRetrieveRequest { + param: Some(req.query_params.clone()), + force_sync: true, + }; + + let operation = operations::PaymentGet; + let boxed_operation: BoxedOperation< + '_, + api::PSync, + api::PaymentsRetrieveRequest, + PaymentStatusData, + > = Box::new(operation); + + let get_tracker_response = boxed_operation + .to_get_tracker()? + .get_trackers( + state, + &payment_id, + &payment_sync_request, + &merchant_account, + &profile, + &merchant_key_store, + &HeaderPayload::default(), + ) + .await?; + + let payment_data = &get_tracker_response.payment_data; + self.validate_status_for_operation(payment_data.payment_intent.status)?; + + let payment_attempt = payment_data + .payment_attempt + .as_ref() + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable("payment_attempt not found in get_tracker_response")?; + + let connector = payment_attempt + .connector + .as_ref() + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "connector is not set in payment attempt in finish redirection flow", + )?; + + // This connector data is ephemeral, the call payment flow will get new connector data + // with merchant account details, so the connector_id can be safely set to None here + let connector_data = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + connector, + api::GetToken::Connector, + None, + )?; + + let call_connector_action = connector_data + .connector + .get_flow_type( + &req.query_params, + req.json_payload.clone(), + self.get_payment_action(), + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decide the response flow")?; + + let (payment_data, _, _, _, _) = + Box::pin(payments_operation_core::( + state, + req_state, + merchant_account, + merchant_key_store.clone(), + profile.clone(), + operation, + payment_sync_request, + get_tracker_response, + call_connector_action, + HeaderPayload::default(), + )) + .await?; + + Ok(router_types::RedirectPaymentFlowResponse { + payment_data, + profile, + }) + } + fn generate_response( + &self, + payment_flow_response: &Self::PaymentFlowResponse, + ) -> RouterResult> { + let payment_intent = &payment_flow_response.payment_data.payment_intent; + let profile = &payment_flow_response.profile; + + let return_url = payment_intent + .return_url + .as_ref() + .or(profile.return_url.as_ref()) + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable("return url not found in payment intent and profile")? + .to_owned(); + + let return_url = return_url + .add_query_params(("id", payment_intent.id.get_string_repr())) + .add_query_params(("status", &payment_intent.status.to_string())); + + let return_url_str = return_url.into_inner().to_string(); + + Ok(services::ApplicationResponse::JsonForRedirection( + api::RedirectionResponse { + return_url_with_query_params: return_url_str, + }, + )) + } + + fn get_payment_action(&self) -> services::PaymentAction { + services::PaymentAction::PSync + } +} + #[derive(Clone, Debug)] pub struct PaymentAuthenticateCompleteAuthorize; @@ -2410,7 +2629,6 @@ pub async fn call_connector_service( payment_data: &mut D, customer: &Option, call_connector_action: CallConnectorAction, - validate_result: &operations::ValidateResult, schedule_time: Option, header_payload: HeaderPayload, frm_suggestion: Option, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 478211ec14cc..f00f03d929fa 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2904,6 +2904,7 @@ pub(super) fn validate_payment_list_request_for_joins( Ok(()) } +#[cfg(feature = "v1")] pub fn get_handle_response_url( payment_id: id_type::PaymentId, business_profile: &domain::Profile, @@ -2926,6 +2927,7 @@ pub fn get_handle_response_url( make_url_with_signature(&return_url, business_profile) } +#[cfg(feature = "v1")] pub fn make_merchant_url_with_response( business_profile: &domain::Profile, redirection_response: api::PgRedirectResponse, @@ -3034,6 +3036,7 @@ pub fn make_pg_redirect_response( } } +#[cfg(feature = "v1")] pub fn make_url_with_signature( redirect_url: &str, business_profile: &domain::Profile, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index edf0485a77df..e936f3725455 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -144,16 +144,15 @@ pub trait ValidateRequest { #[cfg(feature = "v2")] pub trait ValidateRequest { - fn validate_request<'b>( - &'b self, + fn validate_request( + &self, request: &R, merchant_account: &domain::MerchantAccount, - ) -> RouterResult<(BoxedOperation<'b, F, R, D>, ValidateResult)>; + ) -> RouterResult; } #[cfg(feature = "v2")] -pub struct GetTrackerResponse<'a, F: Clone, R, D> { - pub operation: BoxedOperation<'a, F, R, D>, +pub struct GetTrackerResponse { pub payment_data: D, } @@ -183,8 +182,6 @@ pub trait GetTracker: Send { header_payload: &hyperswitch_domain_models::payments::HeaderPayload, ) -> RouterResult>; - // TODO: this need not return the operation, since operation does not change in v2 - // Operation remains the same from start to finish #[cfg(feature = "v2")] #[allow(clippy::too_many_arguments)] async fn get_trackers<'a>( @@ -196,7 +193,7 @@ pub trait GetTracker: Send { profile: &domain::Profile, mechant_key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult>; + ) -> RouterResult>; } #[async_trait] diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index c89f0bdc5c1c..0a03fc741f02 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -131,14 +131,14 @@ impl ValidateRequest RouterResult<(BoxedConfirmOperation<'b, F>, operations::ValidateResult)> { + ) -> RouterResult { let validate_result = operations::ValidateResult { merchant_id: merchant_account.get_id().to_owned(), storage_scheme: merchant_account.storage_scheme, requeue: false, }; - Ok((Box::new(self), validate_result)) + Ok(validate_result) } } @@ -156,9 +156,7 @@ impl GetTracker, PaymentsConfirmIntent profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse<'a, F, PaymentsConfirmIntentRequest, PaymentConfirmData>, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -211,10 +209,7 @@ impl GetTracker, PaymentsConfirmIntent payment_method_data, }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } diff --git a/crates/router/src/core/payments/operations/payment_create_intent.rs b/crates/router/src/core/payments/operations/payment_create_intent.rs index d57d24e3edd1..b46992a6aed6 100644 --- a/crates/router/src/core/payments/operations/payment_create_intent.rs +++ b/crates/router/src/core/payments/operations/payment_create_intent.rs @@ -95,14 +95,7 @@ impl GetTracker, PaymentsCrea profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse< - 'a, - F, - PaymentsCreateIntentRequest, - payments::PaymentIntentData, - >, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -176,10 +169,7 @@ impl GetTracker, PaymentsCrea payment_intent, }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } @@ -221,18 +211,12 @@ impl &'b self, _request: &PaymentsCreateIntentRequest, merchant_account: &'a domain::MerchantAccount, - ) -> RouterResult<( - PaymentsCreateIntentOperation<'b, F>, - operations::ValidateResult, - )> { - Ok(( - Box::new(self), - operations::ValidateResult { - merchant_id: merchant_account.get_id().to_owned(), - storage_scheme: merchant_account.storage_scheme, - requeue: false, - }, - )) + ) -> RouterResult { + Ok(operations::ValidateResult { + merchant_id: merchant_account.get_id().to_owned(), + storage_scheme: merchant_account.storage_scheme, + requeue: false, + }) } } diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs index 2a417568047c..a69c3187c781 100644 --- a/crates/router/src/core/payments/operations/payment_get.rs +++ b/crates/router/src/core/payments/operations/payment_get.rs @@ -109,14 +109,14 @@ impl ValidateRequest RouterResult<(BoxedConfirmOperation<'b, F>, operations::ValidateResult)> { + ) -> RouterResult { let validate_result = operations::ValidateResult { merchant_id: merchant_account.get_id().to_owned(), storage_scheme: merchant_account.storage_scheme, requeue: false, }; - Ok((Box::new(self), validate_result)) + Ok(validate_result) } } @@ -132,9 +132,7 @@ impl GetTracker, PaymentsRetrieveReques _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse<'a, F, PaymentsRetrieveRequest, PaymentStatusData>, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); @@ -172,10 +170,7 @@ impl GetTracker, PaymentsRetrieveReques should_sync_with_connector, }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } diff --git a/crates/router/src/core/payments/operations/payment_get_intent.rs b/crates/router/src/core/payments/operations/payment_get_intent.rs index ca09d021b779..6424ff5a2b35 100644 --- a/crates/router/src/core/payments/operations/payment_get_intent.rs +++ b/crates/router/src/core/payments/operations/payment_get_intent.rs @@ -89,14 +89,7 @@ impl GetTracker, PaymentsGetI _profile: &domain::Profile, key_store: &domain::MerchantKeyStore, _header_payload: &hyperswitch_domain_models::payments::HeaderPayload, - ) -> RouterResult< - operations::GetTrackerResponse< - 'a, - F, - PaymentsGetIntentRequest, - payments::PaymentIntentData, - >, - > { + ) -> RouterResult>> { let db = &*state.store; let key_manager_state = &state.into(); let storage_scheme = merchant_account.storage_scheme; @@ -110,10 +103,7 @@ impl GetTracker, PaymentsGetI payment_intent, }; - let get_trackers_response = operations::GetTrackerResponse { - operation: Box::new(self), - payment_data, - }; + let get_trackers_response = operations::GetTrackerResponse { payment_data }; Ok(get_trackers_response) } @@ -154,18 +144,12 @@ impl ValidateRequest RouterResult<( - PaymentsGetIntentOperation<'b, F>, - operations::ValidateResult, - )> { - Ok(( - Box::new(self), - operations::ValidateResult { - merchant_id: merchant_account.get_id().to_owned(), - storage_scheme: merchant_account.storage_scheme, - requeue: false, - }, - )) + ) -> RouterResult { + Ok(operations::ValidateResult { + merchant_id: merchant_account.get_id().to_owned(), + storage_scheme: merchant_account.storage_scheme, + requeue: false, + }) } } diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 8f2280355f9f..b09034e28cb3 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -226,12 +226,12 @@ pub async fn construct_payment_router_data_for_authorize<'a>( connector_id, )); - let router_return_url = Some(helpers::create_redirect_url( - router_base_url, - attempt, - connector_id, - None, - )); + let router_return_url = payment_data + .payment_intent + .create_finish_redirection_url(router_base_url, &merchant_account.publishable_key) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to construct finish redirection url")? + .to_string(); let connector_request_reference_id = payment_data .payment_intent @@ -269,7 +269,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>( enrolled_for_3ds: true, related_transaction_id: None, payment_method_type: Some(payment_data.payment_attempt.payment_method_subtype), - router_return_url, + router_return_url: Some(router_return_url), webhook_url, complete_authorize_url, customer_id: None, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index aad2537c3d9c..e0ae1c531a9f 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -2223,6 +2223,7 @@ pub async fn list_user_authentication_methods( )) } +#[cfg(feature = "v1")] pub async fn get_sso_auth_url( state: SessionState, request: user_api::GetSsoAuthUrlRequest, diff --git a/crates/router/src/events/api_logs.rs b/crates/router/src/events/api_logs.rs index e818b7440b0d..76cbcb958c87 100644 --- a/crates/router/src/events/api_logs.rs +++ b/crates/router/src/events/api_logs.rs @@ -124,6 +124,7 @@ impl_api_event_type!( ) ); +#[cfg(feature = "v1")] impl ApiEventMetric for PaymentsRedirectResponseData { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::PaymentRedirectionResponse { @@ -136,6 +137,15 @@ impl ApiEventMetric for PaymentsRedirectResponseData { } } +#[cfg(feature = "v2")] +impl ApiEventMetric for PaymentsRedirectResponseData { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentRedirectionResponse { + payment_id: self.payment_id.clone(), + }) + } +} + impl ApiEventMetric for DisputeId { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Dispute { diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index cc9d5b04ebd9..7efaab21d5bf 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -113,7 +113,7 @@ pub fn mk_app( > { let mut server_app = get_application_builder(request_body_limit, state.conf.cors.clone()); - #[cfg(feature = "dummy_connector")] + #[cfg(all(feature = "dummy_connector", feature = "v1"))] { use routes::DummyConnector; server_app = server_app.service(DummyConnector::server(state.clone())); diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index d747f96b9fb9..ebf6c7d384d2 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -472,7 +472,7 @@ impl Health { #[cfg(feature = "dummy_connector")] pub struct DummyConnector; -#[cfg(feature = "dummy_connector")] +#[cfg(all(feature = "dummy_connector", feature = "v1"))] impl DummyConnector { pub fn server(state: AppState) -> Scope { let mut routes_with_restricted_access = web::scope(""); @@ -540,6 +540,10 @@ impl Payments { .service( web::resource("/start_redirection") .route(web::get().to(payments::payments_start_redirection)), + ) + .service( + web::resource("/finish_redirection/{publishable_key}/{profile_id}") + .route(web::get().to(payments::payments_finish_redirection)), ), ); diff --git a/crates/router/src/routes/dummy_connector.rs b/crates/router/src/routes/dummy_connector.rs index e376bcd7f9e0..709c76486c4a 100644 --- a/crates/router/src/routes/dummy_connector.rs +++ b/crates/router/src/routes/dummy_connector.rs @@ -13,6 +13,7 @@ mod errors; pub mod types; mod utils; +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] pub async fn dummy_connector_authorize_payment( state: web::Data, @@ -33,6 +34,8 @@ pub async fn dummy_connector_authorize_payment( ) .await } + +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] pub async fn dummy_connector_complete_payment( state: web::Data, @@ -57,6 +60,8 @@ pub async fn dummy_connector_complete_payment( )) .await } + +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] pub async fn dummy_connector_payment( state: web::Data, @@ -76,6 +81,8 @@ pub async fn dummy_connector_payment( )) .await } + +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentRetrieve))] pub async fn dummy_connector_payment_data( state: web::Data, @@ -96,6 +103,8 @@ pub async fn dummy_connector_payment_data( ) .await } + +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyRefundCreate))] pub async fn dummy_connector_refund( state: web::Data, @@ -117,6 +126,8 @@ pub async fn dummy_connector_refund( ) .await } + +#[cfg(all(feature = "dummy_connector", feature = "v1"))] #[instrument(skip_all, fields(flow = ?types::Flow::DummyRefundRetrieve))] pub async fn dummy_connector_refund_data( state: web::Data, diff --git a/crates/router/src/routes/dummy_connector/core.rs b/crates/router/src/routes/dummy_connector/core.rs index f8e500e5b272..aadd7dcaa729 100644 --- a/crates/router/src/routes/dummy_connector/core.rs +++ b/crates/router/src/routes/dummy_connector/core.rs @@ -9,6 +9,7 @@ use crate::{ utils::OptionExt, }; +#[cfg(all(feature = "dummy_connector", feature = "v1"))] pub async fn payment( state: SessionState, req: types::DummyConnectorPaymentRequest, @@ -54,6 +55,7 @@ pub async fn payment_data( Ok(api::ApplicationResponse::Json(payment_data.into())) } +#[cfg(all(feature = "dummy_connector", feature = "v1"))] pub async fn payment_authorize( state: SessionState, req: types::DummyConnectorPaymentConfirmRequest, @@ -82,6 +84,7 @@ pub async fn payment_authorize( } } +#[cfg(all(feature = "dummy_connector", feature = "v1"))] pub async fn payment_complete( state: SessionState, req: types::DummyConnectorPaymentCompleteRequest, @@ -144,6 +147,7 @@ pub async fn payment_complete( )) } +#[cfg(all(feature = "dummy_connector", feature = "v1"))] pub async fn refund_payment( state: SessionState, req: types::DummyConnectorRefundRequest, @@ -197,6 +201,7 @@ pub async fn refund_payment( Ok(api::ApplicationResponse::Json(refund_data)) } +#[cfg(all(feature = "dummy_connector", feature = "v1"))] pub async fn refund_data( state: SessionState, req: types::DummyConnectorRefundRetrieveRequest, diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index d674770f4e8b..48b15c3b2047 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -1842,6 +1842,23 @@ impl GetLockingInput for payments::PaymentsRedirectResponseData { } } +#[cfg(feature = "v2")] +impl GetLockingInput for payments::PaymentsRedirectResponseData { + fn get_locking_input(&self, flow: F) -> api_locking::LockAction + where + F: types::FlowMetric, + lock_utils::ApiIdentifier: From, + { + api_locking::LockAction::Hold { + input: api_locking::LockingInput { + unique_locking_key: self.payment_id.get_string_repr().to_owned(), + api_identifier: lock_utils::ApiIdentifier::from(flow), + override_lock_retries: None, + }, + } + } +} + #[cfg(feature = "v1")] impl GetLockingInput for payment_types::PaymentsCompleteAuthorizeRequest { fn get_locking_input(&self, flow: F) -> api_locking::LockAction @@ -2254,3 +2271,54 @@ pub async fn payment_status( )) .await } + +#[cfg(feature = "v2")] +#[instrument(skip_all, fields(flow = ?Flow::PaymentsRedirect, payment_id))] +pub async fn payments_finish_redirection( + state: web::Data, + req: actix_web::HttpRequest, + json_payload: Option>, + path: web::Path<( + common_utils::id_type::GlobalPaymentId, + String, + common_utils::id_type::ProfileId, + )>, +) -> impl Responder { + let flow = Flow::PaymentsRedirect; + let (payment_id, publishable_key, profile_id) = path.into_inner(); + let param_string = req.query_string(); + + tracing::Span::current().record("payment_id", payment_id.get_string_repr()); + + let payload = payments::PaymentsRedirectResponseData { + payment_id, + json_payload: json_payload.map(|payload| payload.0), + query_params: param_string.to_string(), + }; + + let locking_action = payload.get_locking_input(flow.clone()); + + api::server_wrap( + flow, + state, + &req, + payload, + |state, auth, req, req_state| { + ::handle_payments_redirect_response( + &payments::PaymentRedirectSync {}, + state, + req_state, + auth.merchant_account, + auth.key_store, + auth.profile, + req, + ) + }, + &auth::PublishableKeyAndProfileIdAuth { + publishable_key: publishable_key.clone(), + profile_id: profile_id.clone(), + }, + locking_action, + ) + .await +} diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index f52d0dca7a83..068c2f30c795 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -691,6 +691,7 @@ pub async fn check_two_factor_auth_status_with_attempts( .await } +#[cfg(feature = "v1")] pub async fn get_sso_auth_url( state: web::Data, req: HttpRequest, diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 2cf8b721a057..bdd650389a70 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -1880,7 +1880,7 @@ pub fn build_redirection_form( var collectionReference = data[collectionField]; return submitCollectionReference(collectionReference); }} else {{ - console.error("Collection field not found in event data (" + collectionField + ")"); + console.error("Collection field not found in event data (" + collectionField + ")"); }} }} catch (error) {{ console.error("Error parsing event data: ", error); @@ -1914,7 +1914,7 @@ pub fn build_redirection_form( } (PreEscaped(format!(r#"