Bug: feat(analytics): implement currency conversion to power multi-currency aggregation



## Current Behaviour

The total amount calculated for all payment analytics buckets related to amounts is incorrect because currency conversion is not being applied before summing the values. As a result, amounts in different currencies are being added together, leading to an inaccurate total.

## Proposed Changes

- introduce `<AMOUNT_RELATED_FIELD>_in_usd` fields to convert and store value in `USD`
- introduce total_in_usd to store the total value in usd
- Generally provide currency_conversion facility to metrics function for other future use cases
diff --git a/Cargo.lock b/Cargo.lock
index 26ad619ee0b..8c69154fcea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -353,6 +353,7 @@ dependencies = [
  "bigdecimal",
  "common_enums",
  "common_utils",
+ "currency_conversion",
  "diesel_models",
  "error-stack",
  "futures 0.3.30",
@@ -363,6 +364,7 @@ dependencies = [
  "opensearch",
  "reqwest 0.11.27",
  "router_env",
+ "rust_decimal",
  "serde",
  "serde_json",
  "sqlx",
diff --git a/crates/analytics/Cargo.toml b/crates/analytics/Cargo.toml
index 687d5a2e208..db516c49abd 100644
--- a/crates/analytics/Cargo.toml
+++ b/crates/analytics/Cargo.toml
@@ -21,6 +21,7 @@ hyperswitch_interfaces = { version = "0.1.0", path = "../hyperswitch_interfaces"
 masking = { version = "0.1.0", path = "../masking" }
 router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
 storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false }
+currency_conversion = { version = "0.1.0", path = "../currency_conversion" }
 
 #Third Party dependencies
 actix-web = "4.5.1"
@@ -34,6 +35,7 @@ futures = "0.3.30"
 once_cell = "1.19.0"
 opensearch = { version = "2.2.0", features = ["aws-auth"] }
 reqwest = { version = "0.11.27", features = ["serde_json"] }
+rust_decimal = "1.35"
 serde = { version = "1.0.197", features = ["derive", "rc"] }
 serde_json = "1.0.115"
 sqlx = { version = "0.8.2", features = ["postgres", "runtime-tokio", "runtime-tokio-native-tls", "time", "bigdecimal"] }
diff --git a/crates/analytics/src/errors.rs b/crates/analytics/src/errors.rs
index 0e39a4ddd92..d7b15a6db11 100644
--- a/crates/analytics/src/errors.rs
+++ b/crates/analytics/src/errors.rs
@@ -12,6 +12,8 @@ pub enum AnalyticsError {
     UnknownError,
     #[error("Access Forbidden Analytics Error")]
     AccessForbiddenError,
+    #[error("Failed to fetch currency exchange rate")]
+    ForexFetchFailed,
 }
 
 impl ErrorSwitch<ApiErrorResponse> for AnalyticsError {
@@ -32,6 +34,12 @@ impl ErrorSwitch<ApiErrorResponse> for AnalyticsError {
             Self::AccessForbiddenError => {
                 ApiErrorResponse::Unauthorized(ApiError::new("IR", 0, "Access Forbidden", None))
             }
+            Self::ForexFetchFailed => ApiErrorResponse::InternalServerError(ApiError::new(
+                "HE",
+                0,
+                "Failed to fetch currency exchange rate",
+                None,
+            )),
         }
     }
 }
diff --git a/crates/analytics/src/payment_intents/accumulator.rs b/crates/analytics/src/payment_intents/accumulator.rs
index cbb8335cea0..ef3cd3129c4 100644
--- a/crates/analytics/src/payment_intents/accumulator.rs
+++ b/crates/analytics/src/payment_intents/accumulator.rs
@@ -86,7 +86,7 @@ impl PaymentIntentMetricAccumulator for CountAccumulator {
 }
 
 impl PaymentIntentMetricAccumulator for SmartRetriedAmountAccumulator {
-    type MetricOutput = (Option<u64>, Option<u64>);
+    type MetricOutput = (Option<u64>, Option<u64>, Option<u64>, Option<u64>);
     #[inline]
     fn add_metrics_bucket(&mut self, metrics: &PaymentIntentMetricRow) {
         self.amount = match (
@@ -117,7 +117,7 @@ impl PaymentIntentMetricAccumulator for SmartRetriedAmountAccumulator {
             .amount_without_retries
             .and_then(|i| u64::try_from(i).ok())
             .or(Some(0));
-        (with_retries, without_retries)
+        (with_retries, without_retries, Some(0), Some(0))
     }
 }
 
@@ -185,7 +185,14 @@ impl PaymentIntentMetricAccumulator for PaymentsSuccessRateAccumulator {
 }
 
 impl PaymentIntentMetricAccumulator for ProcessedAmountAccumulator {
-    type MetricOutput = (Option<u64>, Option<u64>, Option<u64>, Option<u64>);
+    type MetricOutput = (
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+    );
     #[inline]
     fn add_metrics_bucket(&mut self, metrics: &PaymentIntentMetricRow) {
         self.total_with_retries = match (
@@ -235,6 +242,8 @@ impl PaymentIntentMetricAccumulator for ProcessedAmountAccumulator {
             count_with_retries,
             total_without_retries,
             count_without_retries,
+            Some(0),
+            Some(0),
         )
     }
 }
@@ -301,13 +310,19 @@ impl PaymentIntentMetricsAccumulator {
             payments_success_rate,
             payments_success_rate_without_smart_retries,
         ) = self.payments_success_rate.collect();
-        let (smart_retried_amount, smart_retried_amount_without_smart_retries) =
-            self.smart_retried_amount.collect();
+        let (
+            smart_retried_amount,
+            smart_retried_amount_without_smart_retries,
+            smart_retried_amount_in_usd,
+            smart_retried_amount_without_smart_retries_in_usd,
+        ) = self.smart_retried_amount.collect();
         let (
             payment_processed_amount,
             payment_processed_count,
             payment_processed_amount_without_smart_retries,
             payment_processed_count_without_smart_retries,
+            payment_processed_amount_in_usd,
+            payment_processed_amount_without_smart_retries_in_usd,
         ) = self.payment_processed_amount.collect();
         let (
             payments_success_rate_distribution_without_smart_retries,
@@ -317,7 +332,9 @@ impl PaymentIntentMetricsAccumulator {
             successful_smart_retries: self.successful_smart_retries.collect(),
             total_smart_retries: self.total_smart_retries.collect(),
             smart_retried_amount,
+            smart_retried_amount_in_usd,
             smart_retried_amount_without_smart_retries,
+            smart_retried_amount_without_smart_retries_in_usd,
             payment_intent_count: self.payment_intent_count.collect(),
             successful_payments,
             successful_payments_without_smart_retries,
@@ -330,6 +347,8 @@ impl PaymentIntentMetricsAccumulator {
             payment_processed_count_without_smart_retries,
             payments_success_rate_distribution_without_smart_retries,
             payments_failure_rate_distribution_without_smart_retries,
+            payment_processed_amount_in_usd,
+            payment_processed_amount_without_smart_retries_in_usd,
         }
     }
 }
diff --git a/crates/analytics/src/payment_intents/core.rs b/crates/analytics/src/payment_intents/core.rs
index e04c3b7bd9e..076c28f4f37 100644
--- a/crates/analytics/src/payment_intents/core.rs
+++ b/crates/analytics/src/payment_intents/core.rs
@@ -10,8 +10,10 @@ use api_models::analytics::{
     PaymentIntentFiltersResponse, PaymentIntentsAnalyticsMetadata, PaymentIntentsMetricsResponse,
     SankeyResponse,
 };
-use common_enums::IntentStatus;
+use bigdecimal::ToPrimitive;
+use common_enums::{Currency, IntentStatus};
 use common_utils::{errors::CustomResult, types::TimeRange};
+use currency_conversion::{conversion::convert, types::ExchangeRates};
 use error_stack::ResultExt;
 use router_env::{
     instrument, logger,
@@ -120,6 +122,7 @@ pub async fn get_sankey(
 #[instrument(skip_all)]
 pub async fn get_metrics(
     pool: &AnalyticsProvider,
+    ex_rates: &ExchangeRates,
     auth: &AuthInfo,
     req: GetPaymentIntentMetricRequest,
 ) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
@@ -226,16 +229,20 @@ pub async fn get_metrics(
     let mut success = 0;
     let mut success_without_smart_retries = 0;
     let mut total_smart_retried_amount = 0;
+    let mut total_smart_retried_amount_in_usd = 0;
     let mut total_smart_retried_amount_without_smart_retries = 0;
+    let mut total_smart_retried_amount_without_smart_retries_in_usd = 0;
     let mut total = 0;
     let mut total_payment_processed_amount = 0;
+    let mut total_payment_processed_amount_in_usd = 0;
     let mut total_payment_processed_count = 0;
     let mut total_payment_processed_amount_without_smart_retries = 0;
+    let mut total_payment_processed_amount_without_smart_retries_in_usd = 0;
     let mut total_payment_processed_count_without_smart_retries = 0;
     let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
         .into_iter()
         .map(|(id, val)| {
-            let collected_values = val.collect();
+            let mut collected_values = val.collect();
             if let Some(success_count) = collected_values.successful_payments {
                 success += success_count;
             }
@@ -247,20 +254,95 @@ pub async fn get_metrics(
                 total += total_count;
             }
             if let Some(retried_amount) = collected_values.smart_retried_amount {
+                let amount_in_usd = id
+                    .currency
+                    .and_then(|currency| {
+                        i64::try_from(retried_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.smart_retried_amount_in_usd = amount_in_usd;
                 total_smart_retried_amount += retried_amount;
+                total_smart_retried_amount_in_usd += amount_in_usd.unwrap_or(0);
             }
             if let Some(retried_amount) =
                 collected_values.smart_retried_amount_without_smart_retries
             {
+                let amount_in_usd = id
+                    .currency
+                    .and_then(|currency| {
+                        i64::try_from(retried_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.smart_retried_amount_without_smart_retries_in_usd = amount_in_usd;
                 total_smart_retried_amount_without_smart_retries += retried_amount;
+                total_smart_retried_amount_without_smart_retries_in_usd +=
+                    amount_in_usd.unwrap_or(0);
             }
             if let Some(amount) = collected_values.payment_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.payment_processed_amount_in_usd = amount_in_usd;
+                total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
                 total_payment_processed_amount += amount;
             }
             if let Some(count) = collected_values.payment_processed_count {
                 total_payment_processed_count += count;
             }
             if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
+                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.payment_processed_amount_without_smart_retries_in_usd =
+                    amount_in_usd;
+                total_payment_processed_amount_without_smart_retries_in_usd +=
+                    amount_in_usd.unwrap_or(0);
                 total_payment_processed_amount_without_smart_retries += amount;
             }
             if let Some(count) = collected_values.payment_processed_count_without_smart_retries {
@@ -293,6 +375,14 @@ pub async fn get_metrics(
             total_payment_processed_amount_without_smart_retries: Some(
                 total_payment_processed_amount_without_smart_retries,
             ),
+            total_smart_retried_amount_in_usd: Some(total_smart_retried_amount_in_usd),
+            total_smart_retried_amount_without_smart_retries_in_usd: Some(
+                total_smart_retried_amount_without_smart_retries_in_usd,
+            ),
+            total_payment_processed_amount_in_usd: Some(total_payment_processed_amount_in_usd),
+            total_payment_processed_amount_without_smart_retries_in_usd: Some(
+                total_payment_processed_amount_without_smart_retries_in_usd,
+            ),
             total_payment_processed_count: Some(total_payment_processed_count),
             total_payment_processed_count_without_smart_retries: Some(
                 total_payment_processed_count_without_smart_retries,
diff --git a/crates/analytics/src/payment_intents/metrics/sessionized_metrics/payment_processed_amount.rs b/crates/analytics/src/payment_intents/metrics/sessionized_metrics/payment_processed_amount.rs
index 506965375f5..2ba75ca8519 100644
--- a/crates/analytics/src/payment_intents/metrics/sessionized_metrics/payment_processed_amount.rs
+++ b/crates/analytics/src/payment_intents/metrics/sessionized_metrics/payment_processed_amount.rs
@@ -61,7 +61,7 @@ where
         query_builder
             .add_select_column("attempt_count == 1 as first_attempt")
             .switch()?;
-
+        query_builder.add_select_column("currency").switch()?;
         query_builder
             .add_select_column(Aggregate::Sum {
                 field: "amount",
@@ -101,7 +101,10 @@ where
             .add_group_by_clause("attempt_count")
             .attach_printable("Error grouping by attempt_count")
             .switch()?;
-
+        query_builder
+            .add_group_by_clause("currency")
+            .attach_printable("Error grouping by currency")
+            .switch()?;
         if let Some(granularity) = granularity.as_ref() {
             granularity
                 .set_group_by_clause(&mut query_builder)
diff --git a/crates/analytics/src/payment_intents/metrics/sessionized_metrics/smart_retried_amount.rs b/crates/analytics/src/payment_intents/metrics/sessionized_metrics/smart_retried_amount.rs
index 8105a4c82a4..b92b7356924 100644
--- a/crates/analytics/src/payment_intents/metrics/sessionized_metrics/smart_retried_amount.rs
+++ b/crates/analytics/src/payment_intents/metrics/sessionized_metrics/smart_retried_amount.rs
@@ -63,6 +63,7 @@ where
             .add_select_column("attempt_count == 1 as first_attempt")
             .switch()?;
 
+        query_builder.add_select_column("currency").switch()?;
         query_builder
             .add_select_column(Aggregate::Min {
                 field: "created_at",
@@ -102,7 +103,10 @@ where
             .add_group_by_clause("first_attempt")
             .attach_printable("Error grouping by first_attempt")
             .switch()?;
-
+        query_builder
+            .add_group_by_clause("currency")
+            .attach_printable("Error grouping by first_attempt")
+            .switch()?;
         if let Some(granularity) = granularity.as_ref() {
             granularity
                 .set_group_by_clause(&mut query_builder)
diff --git a/crates/analytics/src/payment_intents/metrics/smart_retried_amount.rs b/crates/analytics/src/payment_intents/metrics/smart_retried_amount.rs
index 8468911f7bb..ac08f59f358 100644
--- a/crates/analytics/src/payment_intents/metrics/smart_retried_amount.rs
+++ b/crates/analytics/src/payment_intents/metrics/smart_retried_amount.rs
@@ -62,7 +62,7 @@ where
         query_builder
             .add_select_column("attempt_count == 1 as first_attempt")
             .switch()?;
-
+        query_builder.add_select_column("currency").switch()?;
         query_builder
             .add_select_column(Aggregate::Min {
                 field: "created_at",
@@ -102,7 +102,10 @@ where
             .add_group_by_clause("first_attempt")
             .attach_printable("Error grouping by first_attempt")
             .switch()?;
-
+        query_builder
+            .add_group_by_clause("currency")
+            .attach_printable("Error grouping by currency")
+            .switch()?;
         if let Some(granularity) = granularity.as_ref() {
             granularity
                 .set_group_by_clause(&mut query_builder)
diff --git a/crates/analytics/src/payments/accumulator.rs b/crates/analytics/src/payments/accumulator.rs
index 4388b2071fe..651eeb0bcfe 100644
--- a/crates/analytics/src/payments/accumulator.rs
+++ b/crates/analytics/src/payments/accumulator.rs
@@ -272,7 +272,14 @@ impl PaymentMetricAccumulator for CountAccumulator {
 }
 
 impl PaymentMetricAccumulator for ProcessedAmountAccumulator {
-    type MetricOutput = (Option<u64>, Option<u64>, Option<u64>, Option<u64>);
+    type MetricOutput = (
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+        Option<u64>,
+    );
     #[inline]
     fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) {
         self.total_with_retries = match (
@@ -322,6 +329,8 @@ impl PaymentMetricAccumulator for ProcessedAmountAccumulator {
             count_with_retries,
             total_without_retries,
             count_without_retries,
+            Some(0),
+            Some(0),
         )
     }
 }
@@ -378,6 +387,8 @@ impl PaymentMetricsAccumulator {
             payment_processed_count,
             payment_processed_amount_without_smart_retries,
             payment_processed_count_without_smart_retries,
+            payment_processed_amount_usd,
+            payment_processed_amount_without_smart_retries_usd,
         ) = self.processed_amount.collect();
         let (
             payments_success_rate_distribution,
@@ -406,6 +417,8 @@ 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_without_smart_retries_usd,
         }
     }
 }
diff --git a/crates/analytics/src/payments/core.rs b/crates/analytics/src/payments/core.rs
index 59ae549b283..bcd009270dc 100644
--- a/crates/analytics/src/payments/core.rs
+++ b/crates/analytics/src/payments/core.rs
@@ -9,7 +9,10 @@ use api_models::analytics::{
     FilterValue, GetPaymentFiltersRequest, GetPaymentMetricRequest, PaymentFiltersResponse,
     PaymentsAnalyticsMetadata, PaymentsMetricsResponse,
 };
+use bigdecimal::ToPrimitive;
+use common_enums::Currency;
 use common_utils::errors::CustomResult;
+use currency_conversion::{conversion::convert, types::ExchangeRates};
 use error_stack::ResultExt;
 use router_env::{
     instrument, logger,
@@ -46,6 +49,7 @@ pub enum TaskType {
 #[instrument(skip_all)]
 pub async fn get_metrics(
     pool: &AnalyticsProvider,
+    ex_rates: &ExchangeRates,
     auth: &AuthInfo,
     req: GetPaymentMetricRequest,
 ) -> AnalyticsResult<PaymentsMetricsResponse<MetricsBucketResponse>> {
@@ -224,18 +228,57 @@ 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_without_smart_retries_usd = 0;
     let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
         .into_iter()
         .map(|(id, val)| {
-            let collected_values = val.collect();
+            let mut collected_values = val.collect();
             if let Some(amount) = collected_values.payment_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.payment_processed_amount_usd = amount_in_usd;
                 total_payment_processed_amount += amount;
+                total_payment_processed_amount_usd += amount_in_usd.unwrap_or(0);
             }
             if let Some(count) = collected_values.payment_processed_count {
                 total_payment_processed_count += count;
             }
             if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
+                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.payment_processed_amount_without_smart_retries_usd = amount_in_usd;
                 total_payment_processed_amount_without_smart_retries += amount;
+                total_payment_processed_amount_without_smart_retries_usd +=
+                    amount_in_usd.unwrap_or(0);
             }
             if let Some(count) = collected_values.payment_processed_count_without_smart_retries {
                 total_payment_processed_count_without_smart_retries += count;
@@ -252,14 +295,17 @@ pub async fn get_metrics(
             }
         })
         .collect();
-
     Ok(PaymentsMetricsResponse {
         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_without_smart_retries: Some(
                 total_payment_processed_amount_without_smart_retries,
             ),
+            total_payment_processed_amount_without_smart_retries_usd: Some(
+                total_payment_processed_amount_without_smart_retries_usd,
+            ),
             total_payment_processed_count: Some(total_payment_processed_count),
             total_payment_processed_count_without_smart_retries: Some(
                 total_payment_processed_count_without_smart_retries,
diff --git a/crates/analytics/src/payments/metrics/payment_processed_amount.rs b/crates/analytics/src/payments/metrics/payment_processed_amount.rs
index b8b3868803c..fa54c173041 100644
--- a/crates/analytics/src/payments/metrics/payment_processed_amount.rs
+++ b/crates/analytics/src/payments/metrics/payment_processed_amount.rs
@@ -50,6 +50,7 @@ where
                 alias: Some("total"),
             })
             .switch()?;
+        query_builder.add_select_column("currency").switch()?;
         query_builder
             .add_select_column(Aggregate::Min {
                 field: "created_at",
@@ -79,6 +80,11 @@ where
                 .switch()?;
         }
 
+        query_builder
+            .add_group_by_clause("currency")
+            .attach_printable("Error grouping by currency")
+            .switch()?;
+
         if let Some(granularity) = granularity.as_ref() {
             granularity
                 .set_group_by_clause(&mut query_builder)
diff --git a/crates/analytics/src/payments/metrics/sessionized_metrics/payment_processed_amount.rs b/crates/analytics/src/payments/metrics/sessionized_metrics/payment_processed_amount.rs
index 9bc554eaae7..a315b2fc4c8 100644
--- a/crates/analytics/src/payments/metrics/sessionized_metrics/payment_processed_amount.rs
+++ b/crates/analytics/src/payments/metrics/sessionized_metrics/payment_processed_amount.rs
@@ -57,6 +57,8 @@ where
 
         query_builder.add_select_column("first_attempt").switch()?;
 
+        query_builder.add_select_column("currency").switch()?;
+
         query_builder
             .add_select_column(Aggregate::Sum {
                 field: "amount",
@@ -95,6 +97,12 @@ where
             .add_group_by_clause("first_attempt")
             .attach_printable("Error grouping by first_attempt")
             .switch()?;
+
+        query_builder
+            .add_group_by_clause("currency")
+            .attach_printable("Error grouping by currency")
+            .switch()?;
+
         if let Some(granularity) = granularity.as_ref() {
             granularity
                 .set_group_by_clause(&mut query_builder)
diff --git a/crates/api_models/src/analytics.rs b/crates/api_models/src/analytics.rs
index b95404080b0..8d63bc3096c 100644
--- a/crates/api_models/src/analytics.rs
+++ b/crates/api_models/src/analytics.rs
@@ -203,7 +203,9 @@ pub struct AnalyticsMetadata {
 #[derive(Debug, serde::Serialize)]
 pub struct PaymentsAnalyticsMetadata {
     pub total_payment_processed_amount: Option<u64>,
+    pub total_payment_processed_amount_usd: Option<u64>,
     pub total_payment_processed_amount_without_smart_retries: Option<u64>,
+    pub total_payment_processed_amount_without_smart_retries_usd: Option<u64>,
     pub total_payment_processed_count: Option<u64>,
     pub total_payment_processed_count_without_smart_retries: Option<u64>,
     pub total_failure_reasons_count: Option<u64>,
@@ -218,6 +220,10 @@ pub struct PaymentIntentsAnalyticsMetadata {
     pub total_smart_retried_amount_without_smart_retries: Option<u64>,
     pub total_payment_processed_amount: Option<u64>,
     pub total_payment_processed_amount_without_smart_retries: Option<u64>,
+    pub total_smart_retried_amount_in_usd: Option<u64>,
+    pub total_smart_retried_amount_without_smart_retries_in_usd: Option<u64>,
+    pub total_payment_processed_amount_in_usd: Option<u64>,
+    pub total_payment_processed_amount_without_smart_retries_in_usd: Option<u64>,
     pub total_payment_processed_count: Option<u64>,
     pub total_payment_processed_count_without_smart_retries: Option<u64>,
 }
diff --git a/crates/api_models/src/analytics/payment_intents.rs b/crates/api_models/src/analytics/payment_intents.rs
index 41f11c19ef8..05548245e8e 100644
--- a/crates/api_models/src/analytics/payment_intents.rs
+++ b/crates/api_models/src/analytics/payment_intents.rs
@@ -223,7 +223,9 @@ pub struct PaymentIntentMetricsBucketValue {
     pub successful_smart_retries: Option<u64>,
     pub total_smart_retries: Option<u64>,
     pub smart_retried_amount: Option<u64>,
+    pub smart_retried_amount_in_usd: Option<u64>,
     pub smart_retried_amount_without_smart_retries: Option<u64>,
+    pub smart_retried_amount_without_smart_retries_in_usd: Option<u64>,
     pub payment_intent_count: Option<u64>,
     pub successful_payments: Option<u32>,
     pub successful_payments_without_smart_retries: Option<u32>,
@@ -231,8 +233,10 @@ pub struct PaymentIntentMetricsBucketValue {
     pub payments_success_rate: Option<f64>,
     pub payments_success_rate_without_smart_retries: Option<f64>,
     pub payment_processed_amount: Option<u64>,
+    pub payment_processed_amount_in_usd: Option<u64>,
     pub payment_processed_count: Option<u64>,
     pub payment_processed_amount_without_smart_retries: Option<u64>,
+    pub payment_processed_amount_without_smart_retries_in_usd: Option<u64>,
     pub payment_processed_count_without_smart_retries: Option<u64>,
     pub payments_success_rate_distribution_without_smart_retries: Option<f64>,
     pub payments_failure_rate_distribution_without_smart_retries: Option<f64>,
diff --git a/crates/api_models/src/analytics/payments.rs b/crates/api_models/src/analytics/payments.rs
index 1120ab092d7..1faba79eb37 100644
--- a/crates/api_models/src/analytics/payments.rs
+++ b/crates/api_models/src/analytics/payments.rs
@@ -271,8 +271,10 @@ pub struct PaymentMetricsBucketValue {
     pub payment_count: Option<u64>,
     pub payment_success_count: Option<u64>,
     pub payment_processed_amount: Option<u64>,
+    pub payment_processed_amount_usd: Option<u64>,
     pub payment_processed_count: Option<u64>,
     pub payment_processed_amount_without_smart_retries: Option<u64>,
+    pub payment_processed_amount_without_smart_retries_usd: Option<u64>,
     pub payment_processed_count_without_smart_retries: Option<u64>,
     pub avg_ticket_size: Option<f64>,
     pub payment_error_message: Option<Vec<ErrorResult>>,
diff --git a/crates/router/src/analytics.rs b/crates/router/src/analytics.rs
index aa6db56bb34..fead4c720c3 100644
--- a/crates/router/src/analytics.rs
+++ b/crates/router/src/analytics.rs
@@ -32,7 +32,10 @@ pub mod routes {
 
     use crate::{
         consts::opensearch::SEARCH_INDEXES,
-        core::{api_locking, errors::user::UserErrors, verification::utils},
+        core::{
+            api_locking, currency::get_forex_exchange_rates, errors::user::UserErrors,
+            verification::utils,
+        },
         db::{user::UserInterface, user_role::ListUserRolesByUserIdPayload},
         routes::AppState,
         services::{
@@ -397,7 +400,8 @@ pub mod routes {
                     org_id: org_id.clone(),
                     merchant_ids: vec![merchant_id.clone()],
                 };
-                analytics::payments::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
@@ -436,7 +440,8 @@ pub mod routes {
                 let auth: AuthInfo = AuthInfo::OrgLevel {
                     org_id: org_id.clone(),
                 };
-                analytics::payments::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
@@ -482,7 +487,8 @@ pub mod routes {
                     merchant_id: merchant_id.clone(),
                     profile_ids: vec![profile_id.clone()],
                 };
-                analytics::payments::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
@@ -523,7 +529,8 @@ pub mod routes {
                     org_id: org_id.clone(),
                     merchant_ids: vec![merchant_id.clone()],
                 };
-                analytics::payment_intents::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
@@ -562,7 +569,8 @@ pub mod routes {
                 let auth: AuthInfo = AuthInfo::OrgLevel {
                     org_id: org_id.clone(),
                 };
-                analytics::payment_intents::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
@@ -608,7 +616,8 @@ pub mod routes {
                     merchant_id: merchant_id.clone(),
                     profile_ids: vec![profile_id.clone()],
                 };
-                analytics::payment_intents::get_metrics(&state.pool, &auth, req)
+                let ex_rates = get_forex_exchange_rates(state.clone()).await?;
+                analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
                     .await
                     .map(ApplicationResponse::Json)
             },
diff --git a/crates/router/src/core/currency.rs b/crates/router/src/core/currency.rs
index 96d75098271..912484b014a 100644
--- a/crates/router/src/core/currency.rs
+++ b/crates/router/src/core/currency.rs
@@ -1,4 +1,6 @@
+use analytics::errors::AnalyticsError;
 use common_utils::errors::CustomResult;
+use currency_conversion::types::ExchangeRates;
 use error_stack::ResultExt;
 
 use crate::{
@@ -46,3 +48,19 @@ pub async fn convert_forex(
         .change_context(ApiErrorResponse::InternalServerError)?,
     ))
 }
+
+pub async fn get_forex_exchange_rates(
+    state: SessionState,
+) -> CustomResult<ExchangeRates, AnalyticsError> {
+    let forex_api = state.conf.forex_api.get_inner();
+    let rates = get_forex_rates(
+        &state,
+        forex_api.call_delay,
+        forex_api.local_fetch_retry_delay,
+        forex_api.local_fetch_retry_count,
+    )
+    .await
+    .change_context(AnalyticsError::ForexFetchFailed)?;
+
+    Ok((*rates.data).clone())
+}
diff --git a/crates/router/src/utils/currency.rs b/crates/router/src/utils/currency.rs
index dcfe0347d6f..2173478ab67 100644
--- a/crates/router/src/utils/currency.rs
+++ b/crates/router/src/utils/currency.rs
@@ -26,7 +26,7 @@ const FALLBACK_FOREX_API_CURRENCY_PREFIX: &str = "USD";
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 pub struct FxExchangeRatesCacheEntry {
-    data: Arc<ExchangeRates>,
+    pub data: Arc<ExchangeRates>,
     timestamp: i64,
 }
 
@@ -421,7 +421,13 @@ pub async fn fallback_fetch_forex_rates(
                 conversions.insert(enum_curr, currency_factors);
             }
             None => {
-                logger::error!("Rates for {} not received from API", &enum_curr);
+                if enum_curr == enums::Currency::USD {
+                    let currency_factors =
+                        CurrencyFactors::new(Decimal::new(1, 0), Decimal::new(1, 0));
+                    conversions.insert(enum_curr, currency_factors);
+                } else {
+                    logger::error!("Rates for {} not received from API", &enum_curr);
+                }
             }
         };
     }

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

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

## Description
- implemented currency_conversion for calculating values of amount related metrics from their currency (i.e `INR`) to 
`USD` and store it in a separate field variant I created `<fieldname>_in_usd`
- Implemented a total_amount_in_usd variant for all the total fields related to amounts in query Metadata

### Fixes #6416 
### 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).
-->
The total amount calculated for all payment analytics buckets related to amounts is incorrect because currency conversion is not being applied before summing the values. As a result, amounts in different currencies are being added together, leading to an inaccurate total.

### Dependency

This functionality depends on `currency_conversion` crate directly to fetch exchange rates.

## 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)?
-->
### API request for metrics

```bash
curl --location 'http://localhost:8080/analytics/v1/org/metrics/payments' \
--header 'Accept: */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:9000' \
--header 'QueryType: SingleStat' \
--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/130.0.0.0 Safari/537.36' \
--header 'api-key: dev_r3kVZ4URj3fMiHhJJuhHOVqteaXwKyEMGCaFo6PiCS6S9vZsb0ErV4kCVqogM60H' \
--header 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiMDZlOGRmZWYtMzI0Zi00Yjc0LTg2MTItYzdkZjNhOGZmZTcyIiwibWVyY2hhbnRfaWQiOiJtZXJjaGFudF8xNzI3NDM0NTkzIiwicm9sZV9pZCI6Im9yZ19hZG1pbiIsImV4cCI6MTcyOTgzNzUwNywib3JnX2lkIjoib3JnXzl3Y0kxQ2hTOGVEdnRRQmRXak9IIiwicHJvZmlsZV9pZCI6InByb19FNW5lek43YjZUbVB1WlUzbEU1VSJ9.KlBRpu8DpGZ43u9hsB4xE2oSXNM21HC0RadRdfMe2VI' \
--header 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
--header 'sec-ch-ua-mobile: ?0' \
--header 'sec-ch-ua-platform: "macOS"' \
--data '[
    {
        "timeRange": {
            "startTime": "2024-10-13T18:30:00Z",
            "endTime": "2024-10-21T12:54:00Z"
        },
        "mode": "ORDER",
        "source": "BATCH",
        "metrics": [
            
            
            
            
            "payment_processed_amount"
        ],
        "delta": true
    }
]'
```

### API response
```json
{
    "queryData": [
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 150000,
            "payment_processed_amount_usd": 1782,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": 0,
            "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_failure_rate_distribution": null,
            "payments_failure_rate_distribution_without_smart_retries": null,
            "failure_reason_count": 0,
            "failure_reason_count_without_smart_retries": 0,
            "currency": "INR",
            "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-10-10T18:30:00.000Z",
                "end_time": "2024-10-18T08:24:00.000Z"
            },
            "time_bucket": "2024-10-10 18:30:00"
        },
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 1794006540,
            "payment_processed_amount_usd": 1794006540,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": 0,
            "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_failure_rate_distribution": null,
            "payments_failure_rate_distribution_without_smart_retries": null,
            "failure_reason_count": 0,
            "failure_reason_count_without_smart_retries": 0,
            "currency": "USD",
            "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-10-10T18:30:00.000Z",
                "end_time": "2024-10-18T08:24:00.000Z"
            },
            "time_bucket": "2024-10-10 18:30:00"
        },
        {
            "payment_success_rate": null,
            "payment_count": null,
            "payment_success_count": null,
            "payment_processed_amount": 6540,
            "payment_processed_amount_usd": 7118,
            "payment_processed_count": null,
            "payment_processed_amount_without_smart_retries": 0,
            "payment_processed_amount_without_smart_retries_usd": 0,
            "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_failure_rate_distribution": null,
            "payments_failure_rate_distribution_without_smart_retries": null,
            "failure_reason_count": 0,
            "failure_reason_count_without_smart_retries": 0,
            "currency": "EUR",
            "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-10-10T18:30:00.000Z",
                "end_time": "2024-10-18T08:24:00.000Z"
            },
            "time_bucket": "2024-10-10 18:30:00"
        }
    ],
    "metaData": [
        {
            "total_payment_processed_amount": 1794163080,
            "total_payment_processed_amount_usd": 1794015440,
            "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
        }
    ]
}
```
## Testing for various other scenarios

### by tweaking `development.toml` we can create these scenarios 
```toml
[forex_api]
call_delay = 30
local_fetch_retry_count = 5
local_fetch_retry_delay = 1000
api_timeout = 20000
api_key = "YOUR PRIMARY API KEY HER" # set this value to anything else to invalidate it
fallback_api_key = "YOUR FALLBACK API KEY HER" # set this value to anything else to invalidate it
redis_lock_timeout = 2600
```

by setting `call_delay` to 30s we can check for the fetch cycle and redis updates.

1. When primary api isn't working (invalidating the primary api key)
<img width="1722" alt="Screenshot 2024-10-29 at 12 08 11 AM" src="https://github.com/user-attachments/assets/9b5fe452-d339-4dca-b65d-f3fd3e510037">

 #### API response : 200

2.  when both keys are invalidated 
<img width="1709" alt="Screenshot 2024-10-29 at 12 10 32 AM" src="https://github.com/user-attachments/assets/6dcaa6be-236c-40af-b3a1-0d40f789b99c">

<img width="1282" alt="Screenshot 2024-10-29 at 12 32 15 AM" src="https://github.com/user-attachments/assets/0c13bdee-e076-47a7-943b-642765d09804">

#### API response : 500

## 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
