diff --git a/crates/analytics/src/payment_intents/accumulator.rs b/crates/analytics/src/payment_intents/accumulator.rs index ef3cd3129c48..d8f27501b567 100644 --- a/crates/analytics/src/payment_intents/accumulator.rs +++ b/crates/analytics/src/payment_intents/accumulator.rs @@ -273,8 +273,14 @@ impl PaymentIntentMetricAccumulator for PaymentsDistributionAccumulator { } } - if let Some(total) = metrics.count.and_then(|total| u32::try_from(total).ok()) { - self.total += total; + if status.as_ref() != &storage_enums::IntentStatus::RequiresCustomerAction + && status.as_ref() != &storage_enums::IntentStatus::RequiresPaymentMethod + && status.as_ref() != &storage_enums::IntentStatus::RequiresMerchantAction + && status.as_ref() != &storage_enums::IntentStatus::RequiresConfirmation + { + if let Some(total) = metrics.count.and_then(|total| u32::try_from(total).ok()) { + self.total += total; + } } } } diff --git a/crates/analytics/src/payment_intents/core.rs b/crates/analytics/src/payment_intents/core.rs index 3654cad8c09c..0b66dfda58ca 100644 --- a/crates/analytics/src/payment_intents/core.rs +++ b/crates/analytics/src/payment_intents/core.rs @@ -8,10 +8,9 @@ use api_models::analytics::{ }, GetPaymentIntentFiltersRequest, GetPaymentIntentMetricRequest, PaymentIntentFilterValue, PaymentIntentFiltersResponse, PaymentIntentsAnalyticsMetadata, PaymentIntentsMetricsResponse, - SankeyResponse, }; use bigdecimal::ToPrimitive; -use common_enums::{Currency, IntentStatus}; +use common_enums::Currency; use common_utils::{errors::CustomResult, types::TimeRange}; use currency_conversion::{conversion::convert, types::ExchangeRates}; use error_stack::ResultExt; @@ -24,7 +23,7 @@ use router_env::{ use super::{ filters::{get_payment_intent_filter_for_dimension, PaymentIntentFilterRow}, metrics::PaymentIntentMetricRow, - sankey::{get_sankey_data, SessionizerRefundStatus}, + sankey::{get_sankey_data, SankeyRow}, PaymentIntentMetricsAccumulator, }; use crate::{ @@ -51,7 +50,7 @@ pub async fn get_sankey( pool: &AnalyticsProvider, auth: &AuthInfo, req: TimeRange, -) -> AnalyticsResult { +) -> AnalyticsResult> { match pool { AnalyticsProvider::Sqlx(_) => Err(AnalyticsError::NotImplemented( "Sankey not implemented for sqlx", @@ -62,69 +61,7 @@ pub async fn get_sankey( let sankey_rows = get_sankey_data(ckh_pool, auth, &req) .await .change_context(AnalyticsError::UnknownError)?; - let mut sankey_response = SankeyResponse::default(); - for i in sankey_rows { - match ( - i.status.as_ref(), - 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.smart_retried_success += i.count - } - (IntentStatus::Succeeded, SessionizerRefundStatus::PartialRefunded, _) => { - sankey_response.partial_refunded += i.count; - sankey_response.smart_retried_success += i.count - } - ( - IntentStatus::Succeeded - | IntentStatus::PartiallyCaptured - | IntentStatus::PartiallyCapturedAndCapturable - | IntentStatus::RequiresCapture, - SessionizerRefundStatus::NotRefunded, - 1, - ) => sankey_response.normal_success += i.count, - ( - IntentStatus::Succeeded - | IntentStatus::PartiallyCaptured - | IntentStatus::PartiallyCapturedAndCapturable - | IntentStatus::RequiresCapture, - SessionizerRefundStatus::NotRefunded, - _, - ) => sankey_response.smart_retried_success += i.count, - (IntentStatus::Failed, _, 1) => sankey_response.normal_failure += i.count, - (IntentStatus::Failed, _, _) => { - sankey_response.smart_retried_failure += i.count - } - (IntentStatus::Cancelled, _, _) => sankey_response.cancelled += i.count, - (IntentStatus::Processing, _, _) => sankey_response.pending += i.count, - (IntentStatus::RequiresCustomerAction, _, _) => { - sankey_response.customer_awaited += i.count - } - (IntentStatus::RequiresMerchantAction, _, _) => { - sankey_response.merchant_awaited += i.count - } - (IntentStatus::RequiresPaymentMethod, _, _) => { - sankey_response.pm_awaited += i.count - } - (IntentStatus::RequiresConfirmation, _, _) => { - sankey_response.confirmation_awaited += i.count - } - i @ (_, _, _) => { - router_env::logger::error!(status=?i, "Unknown status in sankey data"); - } - } - } - Ok(sankey_response) + Ok(sankey_rows) } } } diff --git a/crates/analytics/src/payment_intents/sankey.rs b/crates/analytics/src/payment_intents/sankey.rs index 53fd03562f13..626dcef27443 100644 --- a/crates/analytics/src/payment_intents/sankey.rs +++ b/crates/analytics/src/payment_intents/sankey.rs @@ -5,7 +5,6 @@ use common_utils::{ }; use error_stack::ResultExt; use router_env::logger; -use time::PrimitiveDateTime; use crate::{ clickhouse::ClickhouseClient, @@ -13,29 +12,19 @@ use crate::{ types::{AnalyticsCollection, DBEnumWrapper, MetricsError, MetricsResult}, }; -#[derive(Debug, PartialEq, Eq, serde::Deserialize, Hash)] -pub struct PaymentIntentMetricRow { - pub profile_id: Option, - pub connector: Option, - pub authentication_type: Option>, - pub payment_method: Option, - pub payment_method_type: Option, - pub card_network: Option, - pub merchant_id: Option, - pub card_last_4: Option, - pub card_issuer: Option, - pub error_reason: Option, - pub first_attempt: Option, - pub total: Option, - pub count: Option, - #[serde(with = "common_utils::custom_serde::iso8601::option")] - pub start_bucket: Option, - #[serde(with = "common_utils::custom_serde::iso8601::option")] - pub end_bucket: Option, -} - #[derive( - Debug, Default, serde::Deserialize, strum::AsRefStr, strum::EnumString, strum::Display, + Clone, + Copy, + Debug, + Default, + Eq, + Hash, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumString, )] #[serde(rename_all = "snake_case")] pub enum SessionizerRefundStatus { @@ -45,13 +34,36 @@ pub enum SessionizerRefundStatus { PartialRefunded, } -#[derive(Debug, serde::Deserialize)] +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + Hash, + PartialEq, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumString, +)] +#[serde(rename_all = "snake_case")] +pub enum SessionizerDisputeStatus { + DisputePresent, + #[default] + NotDisputed, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct SankeyRow { + pub count: i64, pub status: DBEnumWrapper, #[serde(default)] pub refunds_status: Option>, - pub attempt_count: i64, - pub count: i64, + #[serde(default)] + pub dispute_status: Option>, + pub first_attempt: i64, } impl TryInto for serde_json::Value { @@ -90,7 +102,12 @@ pub async fn get_sankey_data( .change_context(MetricsError::QueryBuildingError)?; query_builder - .add_select_column("attempt_count") + .add_select_column("dispute_status") + .attach_printable("Error adding select clause") + .change_context(MetricsError::QueryBuildingError)?; + + query_builder + .add_select_column("(attempt_count = 1) as first_attempt") .attach_printable("Error adding select clause") .change_context(MetricsError::QueryBuildingError)?; @@ -112,7 +129,12 @@ pub async fn get_sankey_data( .change_context(MetricsError::QueryBuildingError)?; query_builder - .add_group_by_clause("attempt_count") + .add_group_by_clause("dispute_status") + .attach_printable("Error adding group by clause") + .change_context(MetricsError::QueryBuildingError)?; + + query_builder + .add_group_by_clause("first_attempt") .attach_printable("Error adding group by clause") .change_context(MetricsError::QueryBuildingError)?; diff --git a/crates/analytics/src/payments/accumulator.rs b/crates/analytics/src/payments/accumulator.rs index 291d7364071a..20ccc634068d 100644 --- a/crates/analytics/src/payments/accumulator.rs +++ b/crates/analytics/src/payments/accumulator.rs @@ -218,12 +218,19 @@ impl PaymentMetricAccumulator for PaymentsDistributionAccumulator { } } } - if let Some(total) = metrics.count.and_then(|total| u32::try_from(total).ok()) { - self.total += total; - if metrics.first_attempt.unwrap_or(false) { - self.total_without_retries += total; - } else { - self.total_with_only_retries += total; + if status.as_ref() != &storage_enums::AttemptStatus::AuthenticationFailed + && status.as_ref() != &storage_enums::AttemptStatus::PaymentMethodAwaited + && status.as_ref() != &storage_enums::AttemptStatus::DeviceDataCollectionPending + && status.as_ref() != &storage_enums::AttemptStatus::ConfirmationAwaited + && status.as_ref() != &storage_enums::AttemptStatus::Unresolved + { + if let Some(total) = metrics.count.and_then(|total| u32::try_from(total).ok()) { + self.total += total; + if metrics.first_attempt.unwrap_or(false) { + self.total_without_retries += total; + } else { + self.total_with_only_retries += total; + } } } } diff --git a/crates/analytics/src/payments/metrics/sessionized_metrics/failure_reasons.rs b/crates/analytics/src/payments/metrics/sessionized_metrics/failure_reasons.rs index bcbce0502d2d..c472c12795f7 100644 --- a/crates/analytics/src/payments/metrics/sessionized_metrics/failure_reasons.rs +++ b/crates/analytics/src/payments/metrics/sessionized_metrics/failure_reasons.rs @@ -160,11 +160,6 @@ where .switch()?; } - outer_query_builder - .set_limit_by(5, &filtered_dimensions) - .attach_printable("Error adding limit clause") - .switch()?; - outer_query_builder .execute_query::(pool) .await diff --git a/crates/api_models/src/analytics.rs b/crates/api_models/src/analytics.rs index ee9046521549..d25f35589b67 100644 --- a/crates/api_models/src/analytics.rs +++ b/crates/api_models/src/analytics.rs @@ -458,17 +458,9 @@ pub struct GetDisputeMetricRequest { #[derive(Clone, Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] pub struct SankeyResponse { - pub normal_success: i64, - pub normal_failure: i64, - pub cancelled: i64, - pub smart_retried_success: i64, - pub smart_retried_failure: i64, - pub pending: i64, - pub partial_refunded: i64, - pub refunded: i64, - pub disputed: i64, - pub pm_awaited: i64, - pub customer_awaited: i64, - pub merchant_awaited: i64, - pub confirmation_awaited: i64, + pub count: i64, + pub status: String, + pub refunds_status: Option, + pub dispute_status: Option, + pub first_attempt: i64, }