Bug: fix(analytics): fix bugs in payments page metrics in Analytics V2 dashboard



Fix bugs in metrics calculations for Payments page - Analytics V2 dashboard:
 - `Successful Payments Distribution` and `Failure Payments Distribution`: Exclude the dropoffs from the denominator while calculating the rates
 - `Failure reasons`: Remove limit by clause and extract data for all error_reasons
 - `Payments lifecycle`: Update the sankey response structure to look like this:
 ```bash
   { 
     "count":"25",
     "first_attempt":0,
     "status":"requires_payment_method",
     "refunds_status":null,
     "dispute_status":null
   }
```
diff --git a/crates/analytics/src/payment_intents/accumulator.rs b/crates/analytics/src/payment_intents/accumulator.rs
index ef3cd3129c4..d8f27501b56 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 3654cad8c09..0b66dfda58c 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<SankeyResponse> {
+) -> AnalyticsResult<Vec<SankeyRow>> {
     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 53fd03562f1..626dcef2744 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<String>,
-    pub connector: Option<String>,
-    pub authentication_type: Option<DBEnumWrapper<enums::AuthenticationType>>,
-    pub payment_method: Option<String>,
-    pub payment_method_type: Option<String>,
-    pub card_network: Option<String>,
-    pub merchant_id: Option<String>,
-    pub card_last_4: Option<String>,
-    pub card_issuer: Option<String>,
-    pub error_reason: Option<String>,
-    pub first_attempt: Option<i64>,
-    pub total: Option<bigdecimal::BigDecimal>,
-    pub count: Option<i64>,
-    #[serde(with = "common_utils::custom_serde::iso8601::option")]
-    pub start_bucket: Option<PrimitiveDateTime>,
-    #[serde(with = "common_utils::custom_serde::iso8601::option")]
-    pub end_bucket: Option<PrimitiveDateTime>,
-}
-
 #[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<enums::IntentStatus>,
     #[serde(default)]
     pub refunds_status: Option<DBEnumWrapper<SessionizerRefundStatus>>,
-    pub attempt_count: i64,
-    pub count: i64,
+    #[serde(default)]
+    pub dispute_status: Option<DBEnumWrapper<SessionizerDisputeStatus>>,
+    pub first_attempt: i64,
 }
 
 impl TryInto<SankeyRow> 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 291d7364071..20ccc634068 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 bcbce0502d2..c472c12795f 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::<PaymentMetricRow, _>(pool)
             .await
diff --git a/crates/api_models/src/analytics.rs b/crates/api_models/src/analytics.rs
index ee904652154..d25f35589b6 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<String>,
+    pub dispute_status: Option<String>,
+    pub first_attempt: i64,
 }

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [x] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
Fixing some bugs in metrics calculations for Payments page - Analytics V2 dashboard:
 - `Successful Payments Distribution` and `Failure Payments Distribution`: Excluded the following dropoffs from the denominator while calculating the rates
   - Without smart retries - From Payment Intents
     - RequiresCustomerAction
     - RequiresPaymentMethod
     - RequiresMerchantAction
     - RequiresConfirmation
   - With smart retries
     - AuthenticationFailed
     - PaymentMethodAwaited
     - DeviceDataCollectionPending
     - ConfirmationAwaited
     - Unresolved
 - `Failure reasons`: Removed limit by clause and extracting data for all error_reasons
 - `Payments lifecycle`: Updated the sankey response structure, now it will look like this:
 ```bash
   { 
     "count":"25",
     "first_attempt":0,
     "status":"requires_payment_method",
     "refunds_status":null,
     "dispute_status":null
   }
```

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
Fix bugs and send correct data to be displayed in the analytics v2 dashboard.

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Sankey:
```bash
curl --location 'http://localhost:8080/analytics/v1/org/metrics/sankey' \
--header 'Accept: */*' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'Referer: http://localhost:9000/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: same-site' \
--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--header 'api-key: test_admin' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTQ5ZTNkMmItMTY5Yi00NzUzLWJmNTQtZDcxMTM2YjRiN2JkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzI2MDQ2MzI4Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTczMjYwODUzNCwib3JnX2lkIjoib3JnX1ZwU0hPanNZZkR2YWJWWUpnQ0FKIiwicHJvZmlsZV9pZCI6InByb192NXNGb0hlODBPZWlVbElvbm9jTSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.tQYvWyhWQInhAymh0c4itE1CWVwhOG3L4_yHMY4RLkQ' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--data '{
    "startTime": "2024-05-11T00:30:00Z",
    "endTime": "2024-11-25T23:30:00Z"
}'
```
Response:
```json
[
    {
        "count": 2,
        "status": "succeeded",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 0
    },
    {
        "count": 40,
        "status": "succeeded",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 1
    },
    {
        "count": 1,
        "status": "failed",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 0
    },
    {
        "count": 2,
        "status": "requires_confirmation",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 1
    },
    {
        "count": 3,
        "status": "failed",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 1
    },
    {
        "count": 2,
        "status": "requires_payment_method",
        "refunds_status": null,
        "dispute_status": null,
        "first_attempt": 1
    },
    {
        "count": 7,
        "status": "succeeded",
        "refunds_status": "partial_refunded",
        "dispute_status": null,
        "first_attempt": 1
    }
]
```

Payments Distribution
```bash
curl --location 'http://localhost:8080/analytics/v1/org/metrics/payments' \
--header 'Accept: */*' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'Referer: http://localhost:9000/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: same-site' \
--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--header 'api-key: test_admin' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTQ5ZTNkMmItMTY5Yi00NzUzLWJmNTQtZDcxMTM2YjRiN2JkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzI2MDQ2MzI4Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTczMjYwODUzNCwib3JnX2lkIjoib3JnX1ZwU0hPanNZZkR2YWJWWUpnQ0FKIiwicHJvZmlsZV9pZCI6InByb192NXNGb0hlODBPZWlVbElvbm9jTSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.tQYvWyhWQInhAymh0c4itE1CWVwhOG3L4_yHMY4RLkQ' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--data '[
    {
        "timeRange": {
            "startTime": "2024-11-19T00:30:00Z",
            "endTime": "2024-11-19T23:30:00Z"
        },
        "groupByNames": [
            "connector"
        ],
        "source": "BATCH",
        "metrics": [
            "payments_distribution"
        ],
        "delta": true
    }
]'
```
Response:
```json
{
    "queryData": [
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 0,
            "payment_processed_amount_in_usd": null,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": null,
            "payment_processed_count_without_smart_retries": null,
            "avg_ticket_size": null,
            "payment_error_message": null,
            "retries_count": null,
            "retries_amount_processed": 0,
            "connector_success_rate": null,
            "payments_success_rate_distribution": null,
            "payments_success_rate_distribution_without_smart_retries": null,
            "payments_success_rate_distribution_with_only_retries": null,
            "payments_failure_rate_distribution": null,
            "payments_failure_rate_distribution_without_smart_retries": null,
            "payments_failure_rate_distribution_with_only_retries": null,
            "failure_reason_count": 0,
            "failure_reason_count_without_smart_retries": 0,
            "currency": null,
            "status": null,
            "connector": null,
            "authentication_type": null,
            "payment_method": null,
            "payment_method_type": null,
            "client_source": null,
            "client_version": null,
            "profile_id": null,
            "card_network": null,
            "merchant_id": null,
            "card_last_4": null,
            "card_issuer": null,
            "error_reason": null,
            "time_range": {
                "start_time": "2024-11-19T00:30:00.000Z",
                "end_time": "2024-11-19T23:30:00.000Z"
            },
            "time_bucket": "2024-11-19 00:30:00"
        },
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 0,
            "payment_processed_amount_in_usd": null,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": null,
            "payment_processed_count_without_smart_retries": null,
            "avg_ticket_size": null,
            "payment_error_message": null,
            "retries_count": null,
            "retries_amount_processed": 0,
            "connector_success_rate": null,
            "payments_success_rate_distribution": 88.88888888888889,
            "payments_success_rate_distribution_without_smart_retries": 87.5,
            "payments_success_rate_distribution_with_only_retries": 100.0,
            "payments_failure_rate_distribution": 11.11111111111111,
            "payments_failure_rate_distribution_without_smart_retries": 12.5,
            "payments_failure_rate_distribution_with_only_retries": 0.0,
            "failure_reason_count": 0,
            "failure_reason_count_without_smart_retries": 0,
            "currency": null,
            "status": null,
            "connector": "stripe_test",
            "authentication_type": null,
            "payment_method": null,
            "payment_method_type": null,
            "client_source": null,
            "client_version": null,
            "profile_id": null,
            "card_network": null,
            "merchant_id": null,
            "card_last_4": null,
            "card_issuer": null,
            "error_reason": null,
            "time_range": {
                "start_time": "2024-11-19T00:30:00.000Z",
                "end_time": "2024-11-19T23:30:00.000Z"
            },
            "time_bucket": "2024-11-19 00:30:00"
        }
    ],
    "metaData": [
        {
            "total_payment_processed_amount": 0,
            "total_payment_processed_amount_in_usd": 0,
            "total_payment_processed_amount_without_smart_retries": 0,
            "total_payment_processed_amount_without_smart_retries_usd": 0,
            "total_payment_processed_count": 0,
            "total_payment_processed_count_without_smart_retries": 0,
            "total_failure_reasons_count": 0,
            "total_failure_reasons_count_without_smart_retries": 0
        }
    ]
}
```

Failure Reasons:
```bash
curl --location 'http://localhost:8080/analytics/v1/org/metrics/payments' \
--header 'Accept: */*' \
--header 'Accept-Language: en-US,en;q=0.9' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'Referer: http://localhost:9000/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: same-site' \
--header 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--header 'api-key: test_admin' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNTQ5ZTNkMmItMTY5Yi00NzUzLWJmNTQtZDcxMTM2YjRiN2JkIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzI2MDQ2MzI4Iiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTczMjYwODUzNCwib3JnX2lkIjoib3JnX1ZwU0hPanNZZkR2YWJWWUpnQ0FKIiwicHJvZmlsZV9pZCI6InByb192NXNGb0hlODBPZWlVbElvbm9jTSIsInRlbmFudF9pZCI6InB1YmxpYyJ9.tQYvWyhWQInhAymh0c4itE1CWVwhOG3L4_yHMY4RLkQ' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--data '[
    {
        "timeRange": {
            "startTime": "2024-11-17T00:30:00Z",
            "endTime": "2024-11-25T23:30:00Z"
        },
        "groupByNames": [
            "connector", "error_reason"
        ],
        "source": "BATCH",
        "metrics": [
            "failure_reasons"
        ],
        "delta": true
    }
]'
```
Response:
```json
{
    "queryData": [
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 0,
            "payment_processed_amount_in_usd": null,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": null,
            "payment_processed_count_without_smart_retries": null,
            "avg_ticket_size": null,
            "payment_error_message": null,
            "retries_count": null,
            "retries_amount_processed": 0,
            "connector_success_rate": null,
            "payments_success_rate_distribution": null,
            "payments_success_rate_distribution_without_smart_retries": null,
            "payments_success_rate_distribution_with_only_retries": null,
            "payments_failure_rate_distribution": null,
            "payments_failure_rate_distribution_without_smart_retries": null,
            "payments_failure_rate_distribution_with_only_retries": null,
            "failure_reason_count": 1,
            "failure_reason_count_without_smart_retries": 1,
            "currency": null,
            "status": null,
            "connector": "stripe_test",
            "authentication_type": null,
            "payment_method": null,
            "payment_method_type": null,
            "client_source": null,
            "client_version": null,
            "profile_id": null,
            "card_network": null,
            "merchant_id": null,
            "card_last_4": null,
            "card_issuer": null,
            "error_reason": "TEST ERROR - 1",
            "time_range": {
                "start_time": "2024-11-17T00:30:00.000Z",
                "end_time": "2024-11-25T23:30:00.000Z"
            },
            "time_bucket": "2024-11-17 00:30:00"
        }
    ],
    "metaData": [
        {
            "total_payment_processed_amount": 0,
            "total_payment_processed_amount_in_usd": 0,
            "total_payment_processed_amount_without_smart_retries": 0,
            "total_payment_processed_amount_without_smart_retries_usd": 0,
            "total_payment_processed_count": 0,
            "total_payment_processed_count_without_smart_retries": 0,
            "total_failure_reasons_count": 1,
            "total_failure_reasons_count_without_smart_retries": 1
        }
    ]
}
```

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
