mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(analytics): Analytics Request Validator and config driven forex feature (#6733)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sandeep Kumar <83278309+tsdk02@users.noreply.github.com>
This commit is contained in:
@ -661,6 +661,7 @@ pm_auth_key = "Some_pm_auth_key"
|
|||||||
# Analytics configuration.
|
# Analytics configuration.
|
||||||
[analytics]
|
[analytics]
|
||||||
source = "sqlx" # The Analytics source/strategy to be used
|
source = "sqlx" # The Analytics source/strategy to be used
|
||||||
|
forex_enabled = false # Enable or disable forex conversion for analytics
|
||||||
|
|
||||||
[analytics.clickhouse]
|
[analytics.clickhouse]
|
||||||
username = "" # Clickhouse username
|
username = "" # Clickhouse username
|
||||||
|
|||||||
@ -9,6 +9,7 @@ database_name = "clickhouse_db_name" # Clickhouse database name
|
|||||||
# Analytics configuration.
|
# Analytics configuration.
|
||||||
[analytics]
|
[analytics]
|
||||||
source = "sqlx" # The Analytics source/strategy to be used
|
source = "sqlx" # The Analytics source/strategy to be used
|
||||||
|
forex_enabled = false # Boolean to enable or disable forex conversion
|
||||||
|
|
||||||
[analytics.sqlx]
|
[analytics.sqlx]
|
||||||
username = "db_user" # Analytics DB Username
|
username = "db_user" # Analytics DB Username
|
||||||
|
|||||||
@ -719,6 +719,7 @@ authentication_analytics_topic = "hyperswitch-authentication-events"
|
|||||||
|
|
||||||
[analytics]
|
[analytics]
|
||||||
source = "sqlx"
|
source = "sqlx"
|
||||||
|
forex_enabled = false
|
||||||
|
|
||||||
[analytics.clickhouse]
|
[analytics.clickhouse]
|
||||||
username = "default"
|
username = "default"
|
||||||
|
|||||||
@ -104,6 +104,12 @@ To use Forex services, you need to sign up and get your API keys from the follow
|
|||||||
- It will be in dashboard, labeled as `access key`.
|
- It will be in dashboard, labeled as `access key`.
|
||||||
|
|
||||||
### Configuring Forex APIs
|
### Configuring Forex APIs
|
||||||
|
To enable Forex functionality, update the `config/development.toml` or `config/docker_compose.toml` file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[analytics]
|
||||||
|
forex_enabled = true # default set to false
|
||||||
|
```
|
||||||
|
|
||||||
To configure the Forex APIs, update the `config/development.toml` or `config/docker_compose.toml` file with your API keys:
|
To configure the Forex APIs, update the `config/development.toml` or `config/docker_compose.toml` file with your API keys:
|
||||||
|
|
||||||
|
|||||||
@ -969,21 +969,25 @@ impl AnalyticsProvider {
|
|||||||
tenant: &dyn storage_impl::config::TenantConfig,
|
tenant: &dyn storage_impl::config::TenantConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match config {
|
match config {
|
||||||
AnalyticsConfig::Sqlx { sqlx } => {
|
AnalyticsConfig::Sqlx { sqlx, .. } => {
|
||||||
Self::Sqlx(SqlxClient::from_conf(sqlx, tenant.get_schema()).await)
|
Self::Sqlx(SqlxClient::from_conf(sqlx, tenant.get_schema()).await)
|
||||||
}
|
}
|
||||||
AnalyticsConfig::Clickhouse { clickhouse } => Self::Clickhouse(ClickhouseClient {
|
AnalyticsConfig::Clickhouse { clickhouse, .. } => Self::Clickhouse(ClickhouseClient {
|
||||||
config: Arc::new(clickhouse.clone()),
|
config: Arc::new(clickhouse.clone()),
|
||||||
database: tenant.get_clickhouse_database().to_string(),
|
database: tenant.get_clickhouse_database().to_string(),
|
||||||
}),
|
}),
|
||||||
AnalyticsConfig::CombinedCkh { sqlx, clickhouse } => Self::CombinedCkh(
|
AnalyticsConfig::CombinedCkh {
|
||||||
|
sqlx, clickhouse, ..
|
||||||
|
} => Self::CombinedCkh(
|
||||||
SqlxClient::from_conf(sqlx, tenant.get_schema()).await,
|
SqlxClient::from_conf(sqlx, tenant.get_schema()).await,
|
||||||
ClickhouseClient {
|
ClickhouseClient {
|
||||||
config: Arc::new(clickhouse.clone()),
|
config: Arc::new(clickhouse.clone()),
|
||||||
database: tenant.get_clickhouse_database().to_string(),
|
database: tenant.get_clickhouse_database().to_string(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AnalyticsConfig::CombinedSqlx { sqlx, clickhouse } => Self::CombinedSqlx(
|
AnalyticsConfig::CombinedSqlx {
|
||||||
|
sqlx, clickhouse, ..
|
||||||
|
} => Self::CombinedSqlx(
|
||||||
SqlxClient::from_conf(sqlx, tenant.get_schema()).await,
|
SqlxClient::from_conf(sqlx, tenant.get_schema()).await,
|
||||||
ClickhouseClient {
|
ClickhouseClient {
|
||||||
config: Arc::new(clickhouse.clone()),
|
config: Arc::new(clickhouse.clone()),
|
||||||
@ -1000,20 +1004,35 @@ impl AnalyticsProvider {
|
|||||||
pub enum AnalyticsConfig {
|
pub enum AnalyticsConfig {
|
||||||
Sqlx {
|
Sqlx {
|
||||||
sqlx: Database,
|
sqlx: Database,
|
||||||
|
forex_enabled: bool,
|
||||||
},
|
},
|
||||||
Clickhouse {
|
Clickhouse {
|
||||||
clickhouse: ClickhouseConfig,
|
clickhouse: ClickhouseConfig,
|
||||||
|
forex_enabled: bool,
|
||||||
},
|
},
|
||||||
CombinedCkh {
|
CombinedCkh {
|
||||||
sqlx: Database,
|
sqlx: Database,
|
||||||
clickhouse: ClickhouseConfig,
|
clickhouse: ClickhouseConfig,
|
||||||
|
forex_enabled: bool,
|
||||||
},
|
},
|
||||||
CombinedSqlx {
|
CombinedSqlx {
|
||||||
sqlx: Database,
|
sqlx: Database,
|
||||||
clickhouse: ClickhouseConfig,
|
clickhouse: ClickhouseConfig,
|
||||||
|
forex_enabled: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AnalyticsConfig {
|
||||||
|
pub fn get_forex_enabled(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Sqlx { forex_enabled, .. }
|
||||||
|
| Self::Clickhouse { forex_enabled, .. }
|
||||||
|
| Self::CombinedCkh { forex_enabled, .. }
|
||||||
|
| Self::CombinedSqlx { forex_enabled, .. } => *forex_enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl SecretsHandler for AnalyticsConfig {
|
impl SecretsHandler for AnalyticsConfig {
|
||||||
async fn convert_to_raw_secret(
|
async fn convert_to_raw_secret(
|
||||||
@ -1024,7 +1043,7 @@ impl SecretsHandler for AnalyticsConfig {
|
|||||||
let decrypted_password = match analytics_config {
|
let decrypted_password = match analytics_config {
|
||||||
// Todo: Perform kms decryption of clickhouse password
|
// Todo: Perform kms decryption of clickhouse password
|
||||||
Self::Clickhouse { .. } => masking::Secret::new(String::default()),
|
Self::Clickhouse { .. } => masking::Secret::new(String::default()),
|
||||||
Self::Sqlx { sqlx }
|
Self::Sqlx { sqlx, .. }
|
||||||
| Self::CombinedCkh { sqlx, .. }
|
| Self::CombinedCkh { sqlx, .. }
|
||||||
| Self::CombinedSqlx { sqlx, .. } => {
|
| Self::CombinedSqlx { sqlx, .. } => {
|
||||||
secret_management_client
|
secret_management_client
|
||||||
@ -1034,26 +1053,46 @@ impl SecretsHandler for AnalyticsConfig {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(value.transition_state(|conf| match conf {
|
Ok(value.transition_state(|conf| match conf {
|
||||||
Self::Sqlx { sqlx } => Self::Sqlx {
|
Self::Sqlx {
|
||||||
|
sqlx,
|
||||||
|
forex_enabled,
|
||||||
|
} => Self::Sqlx {
|
||||||
sqlx: Database {
|
sqlx: Database {
|
||||||
password: decrypted_password,
|
password: decrypted_password,
|
||||||
..sqlx
|
..sqlx
|
||||||
},
|
},
|
||||||
|
forex_enabled,
|
||||||
},
|
},
|
||||||
Self::Clickhouse { clickhouse } => Self::Clickhouse { clickhouse },
|
Self::Clickhouse {
|
||||||
Self::CombinedCkh { sqlx, clickhouse } => Self::CombinedCkh {
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
|
} => Self::Clickhouse {
|
||||||
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
|
},
|
||||||
|
Self::CombinedCkh {
|
||||||
|
sqlx,
|
||||||
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
|
} => Self::CombinedCkh {
|
||||||
sqlx: Database {
|
sqlx: Database {
|
||||||
password: decrypted_password,
|
password: decrypted_password,
|
||||||
..sqlx
|
..sqlx
|
||||||
},
|
},
|
||||||
clickhouse,
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
},
|
},
|
||||||
Self::CombinedSqlx { sqlx, clickhouse } => Self::CombinedSqlx {
|
Self::CombinedSqlx {
|
||||||
|
sqlx,
|
||||||
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
|
} => Self::CombinedSqlx {
|
||||||
sqlx: Database {
|
sqlx: Database {
|
||||||
password: decrypted_password,
|
password: decrypted_password,
|
||||||
..sqlx
|
..sqlx
|
||||||
},
|
},
|
||||||
clickhouse,
|
clickhouse,
|
||||||
|
forex_enabled,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1063,6 +1102,7 @@ impl Default for AnalyticsConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Sqlx {
|
Self::Sqlx {
|
||||||
sqlx: Database::default(),
|
sqlx: Database::default(),
|
||||||
|
forex_enabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ pub async fn get_sankey(
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn get_metrics(
|
pub async fn get_metrics(
|
||||||
pool: &AnalyticsProvider,
|
pool: &AnalyticsProvider,
|
||||||
ex_rates: &ExchangeRates,
|
ex_rates: &Option<ExchangeRates>,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
req: GetPaymentIntentMetricRequest,
|
req: GetPaymentIntentMetricRequest,
|
||||||
) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
|
) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
|
||||||
@ -201,22 +201,25 @@ pub async fn get_metrics(
|
|||||||
total += total_count;
|
total += total_count;
|
||||||
}
|
}
|
||||||
if let Some(retried_amount) = collected_values.smart_retried_amount {
|
if let Some(retried_amount) = collected_values.smart_retried_amount {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(retried_amount)
|
i64::try_from(retried_amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.smart_retried_amount_in_usd = amount_in_usd;
|
collected_values.smart_retried_amount_in_usd = amount_in_usd;
|
||||||
total_smart_retried_amount += retried_amount;
|
total_smart_retried_amount += retried_amount;
|
||||||
total_smart_retried_amount_in_usd += amount_in_usd.unwrap_or(0);
|
total_smart_retried_amount_in_usd += amount_in_usd.unwrap_or(0);
|
||||||
@ -224,44 +227,50 @@ pub async fn get_metrics(
|
|||||||
if let Some(retried_amount) =
|
if let Some(retried_amount) =
|
||||||
collected_values.smart_retried_amount_without_smart_retries
|
collected_values.smart_retried_amount_without_smart_retries
|
||||||
{
|
{
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(retried_amount)
|
i64::try_from(retried_amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.smart_retried_amount_without_smart_retries_in_usd = amount_in_usd;
|
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 += retried_amount;
|
||||||
total_smart_retried_amount_without_smart_retries_in_usd +=
|
total_smart_retried_amount_without_smart_retries_in_usd +=
|
||||||
amount_in_usd.unwrap_or(0);
|
amount_in_usd.unwrap_or(0);
|
||||||
}
|
}
|
||||||
if let Some(amount) = collected_values.payment_processed_amount {
|
if let Some(amount) = collected_values.payment_processed_amount {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(amount)
|
i64::try_from(amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.payment_processed_amount_in_usd = amount_in_usd;
|
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_in_usd += amount_in_usd.unwrap_or(0);
|
||||||
total_payment_processed_amount += amount;
|
total_payment_processed_amount += amount;
|
||||||
@ -270,22 +279,25 @@ pub async fn get_metrics(
|
|||||||
total_payment_processed_count += count;
|
total_payment_processed_count += count;
|
||||||
}
|
}
|
||||||
if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
|
if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(amount)
|
i64::try_from(amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.payment_processed_amount_without_smart_retries_in_usd =
|
collected_values.payment_processed_amount_without_smart_retries_in_usd =
|
||||||
amount_in_usd;
|
amount_in_usd;
|
||||||
total_payment_processed_amount_without_smart_retries_in_usd +=
|
total_payment_processed_amount_without_smart_retries_in_usd +=
|
||||||
@ -322,14 +334,26 @@ pub async fn get_metrics(
|
|||||||
total_payment_processed_amount_without_smart_retries: Some(
|
total_payment_processed_amount_without_smart_retries: Some(
|
||||||
total_payment_processed_amount_without_smart_retries,
|
total_payment_processed_amount_without_smart_retries,
|
||||||
),
|
),
|
||||||
total_smart_retried_amount_in_usd: Some(total_smart_retried_amount_in_usd),
|
total_smart_retried_amount_in_usd: if ex_rates.is_some() {
|
||||||
total_smart_retried_amount_without_smart_retries_in_usd: Some(
|
Some(total_smart_retried_amount_in_usd)
|
||||||
total_smart_retried_amount_without_smart_retries_in_usd,
|
} else {
|
||||||
),
|
None
|
||||||
total_payment_processed_amount_in_usd: Some(total_payment_processed_amount_in_usd),
|
},
|
||||||
total_payment_processed_amount_without_smart_retries_in_usd: Some(
|
total_smart_retried_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
|
||||||
total_payment_processed_amount_without_smart_retries_in_usd,
|
Some(total_smart_retried_amount_without_smart_retries_in_usd)
|
||||||
),
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
total_payment_processed_amount_in_usd: if ex_rates.is_some() {
|
||||||
|
Some(total_payment_processed_amount_in_usd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
total_payment_processed_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
|
||||||
|
Some(total_payment_processed_amount_without_smart_retries_in_usd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
total_payment_processed_count: Some(total_payment_processed_count),
|
total_payment_processed_count: Some(total_payment_processed_count),
|
||||||
total_payment_processed_count_without_smart_retries: Some(
|
total_payment_processed_count_without_smart_retries: Some(
|
||||||
total_payment_processed_count_without_smart_retries,
|
total_payment_processed_count_without_smart_retries,
|
||||||
|
|||||||
@ -48,7 +48,7 @@ pub enum TaskType {
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn get_metrics(
|
pub async fn get_metrics(
|
||||||
pool: &AnalyticsProvider,
|
pool: &AnalyticsProvider,
|
||||||
ex_rates: &ExchangeRates,
|
ex_rates: &Option<ExchangeRates>,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
req: GetPaymentMetricRequest,
|
req: GetPaymentMetricRequest,
|
||||||
) -> AnalyticsResult<PaymentsMetricsResponse<MetricsBucketResponse>> {
|
) -> AnalyticsResult<PaymentsMetricsResponse<MetricsBucketResponse>> {
|
||||||
@ -234,22 +234,25 @@ pub async fn get_metrics(
|
|||||||
.map(|(id, val)| {
|
.map(|(id, val)| {
|
||||||
let mut collected_values = val.collect();
|
let mut collected_values = val.collect();
|
||||||
if let Some(amount) = collected_values.payment_processed_amount {
|
if let Some(amount) = collected_values.payment_processed_amount {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(amount)
|
i64::try_from(amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.payment_processed_amount_in_usd = amount_in_usd;
|
collected_values.payment_processed_amount_in_usd = amount_in_usd;
|
||||||
total_payment_processed_amount += amount;
|
total_payment_processed_amount += amount;
|
||||||
total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
|
total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
|
||||||
@ -258,22 +261,25 @@ pub async fn get_metrics(
|
|||||||
total_payment_processed_count += count;
|
total_payment_processed_count += count;
|
||||||
}
|
}
|
||||||
if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
|
if let Some(amount) = collected_values.payment_processed_amount_without_smart_retries {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(amount)
|
i64::try_from(amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.payment_processed_amount_without_smart_retries_usd = amount_in_usd;
|
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 += amount;
|
||||||
total_payment_processed_amount_without_smart_retries_usd +=
|
total_payment_processed_amount_without_smart_retries_usd +=
|
||||||
@ -298,13 +304,19 @@ pub async fn get_metrics(
|
|||||||
query_data,
|
query_data,
|
||||||
meta_data: [PaymentsAnalyticsMetadata {
|
meta_data: [PaymentsAnalyticsMetadata {
|
||||||
total_payment_processed_amount: Some(total_payment_processed_amount),
|
total_payment_processed_amount: Some(total_payment_processed_amount),
|
||||||
total_payment_processed_amount_in_usd: Some(total_payment_processed_amount_in_usd),
|
total_payment_processed_amount_in_usd: if ex_rates.is_some() {
|
||||||
|
Some(total_payment_processed_amount_in_usd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
total_payment_processed_amount_without_smart_retries: Some(
|
total_payment_processed_amount_without_smart_retries: Some(
|
||||||
total_payment_processed_amount_without_smart_retries,
|
total_payment_processed_amount_without_smart_retries,
|
||||||
),
|
),
|
||||||
total_payment_processed_amount_without_smart_retries_usd: Some(
|
total_payment_processed_amount_without_smart_retries_usd: if ex_rates.is_some() {
|
||||||
total_payment_processed_amount_without_smart_retries_usd,
|
Some(total_payment_processed_amount_without_smart_retries_usd)
|
||||||
),
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
total_payment_processed_count: Some(total_payment_processed_count),
|
total_payment_processed_count: Some(total_payment_processed_count),
|
||||||
total_payment_processed_count_without_smart_retries: Some(
|
total_payment_processed_count_without_smart_retries: Some(
|
||||||
total_payment_processed_count_without_smart_retries,
|
total_payment_processed_count_without_smart_retries,
|
||||||
|
|||||||
@ -47,7 +47,7 @@ pub enum TaskType {
|
|||||||
|
|
||||||
pub async fn get_metrics(
|
pub async fn get_metrics(
|
||||||
pool: &AnalyticsProvider,
|
pool: &AnalyticsProvider,
|
||||||
ex_rates: &ExchangeRates,
|
ex_rates: &Option<ExchangeRates>,
|
||||||
auth: &AuthInfo,
|
auth: &AuthInfo,
|
||||||
req: GetRefundMetricRequest,
|
req: GetRefundMetricRequest,
|
||||||
) -> AnalyticsResult<RefundsMetricsResponse<RefundMetricsBucketResponse>> {
|
) -> AnalyticsResult<RefundsMetricsResponse<RefundMetricsBucketResponse>> {
|
||||||
@ -217,22 +217,25 @@ pub async fn get_metrics(
|
|||||||
total += total_count;
|
total += total_count;
|
||||||
}
|
}
|
||||||
if let Some(amount) = collected_values.refund_processed_amount {
|
if let Some(amount) = collected_values.refund_processed_amount {
|
||||||
let amount_in_usd = id
|
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||||
.currency
|
id.currency
|
||||||
.and_then(|currency| {
|
.and_then(|currency| {
|
||||||
i64::try_from(amount)
|
i64::try_from(amount)
|
||||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|amount_i64| {
|
.and_then(|amount_i64| {
|
||||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||||
.inspect_err(|e| {
|
.inspect_err(|e| {
|
||||||
logger::error!("Currency conversion error: {:?}", e)
|
logger::error!("Currency conversion error: {:?}", e)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
collected_values.refund_processed_amount_in_usd = amount_in_usd;
|
collected_values.refund_processed_amount_in_usd = amount_in_usd;
|
||||||
total_refund_processed_amount += amount;
|
total_refund_processed_amount += amount;
|
||||||
total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
|
total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
|
||||||
@ -261,7 +264,11 @@ pub async fn get_metrics(
|
|||||||
meta_data: [RefundsAnalyticsMetadata {
|
meta_data: [RefundsAnalyticsMetadata {
|
||||||
total_refund_success_rate,
|
total_refund_success_rate,
|
||||||
total_refund_processed_amount: Some(total_refund_processed_amount),
|
total_refund_processed_amount: Some(total_refund_processed_amount),
|
||||||
total_refund_processed_amount_in_usd: Some(total_refund_processed_amount_in_usd),
|
total_refund_processed_amount_in_usd: if ex_rates.is_some() {
|
||||||
|
Some(total_refund_processed_amount_in_usd)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
total_refund_processed_count: Some(total_refund_processed_count),
|
total_refund_processed_count: Some(total_refund_processed_count),
|
||||||
total_refund_reason_count: Some(total_refund_reason_count),
|
total_refund_reason_count: Some(total_refund_reason_count),
|
||||||
total_refund_error_message_count: Some(total_refund_error_message_count),
|
total_refund_error_message_count: Some(total_refund_error_message_count),
|
||||||
|
|||||||
@ -62,7 +62,42 @@ pub enum Granularity {
|
|||||||
#[serde(rename = "G_ONEDAY")]
|
#[serde(rename = "G_ONEDAY")]
|
||||||
OneDay,
|
OneDay,
|
||||||
}
|
}
|
||||||
|
pub trait ForexMetric {
|
||||||
|
fn is_forex_metric(&self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AnalyticsRequest {
|
||||||
|
pub payment_intent: Option<GetPaymentIntentMetricRequest>,
|
||||||
|
pub payment_attempt: Option<GetPaymentMetricRequest>,
|
||||||
|
pub refund: Option<GetRefundMetricRequest>,
|
||||||
|
pub dispute: Option<GetDisputeMetricRequest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnalyticsRequest {
|
||||||
|
pub fn requires_forex_functionality(&self) -> bool {
|
||||||
|
self.payment_attempt
|
||||||
|
.as_ref()
|
||||||
|
.map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
|| self
|
||||||
|
.payment_intent
|
||||||
|
.as_ref()
|
||||||
|
.map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
|| self
|
||||||
|
.refund
|
||||||
|
.as_ref()
|
||||||
|
.map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
|| self
|
||||||
|
.dispute
|
||||||
|
.as_ref()
|
||||||
|
.map(|req| req.metrics.iter().any(|metric| metric.is_forex_metric()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetPaymentMetricRequest {
|
pub struct GetPaymentMetricRequest {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{NameDescription, TimeRange};
|
use super::{ForexMetric, NameDescription, TimeRange};
|
||||||
use crate::enums::DisputeStage;
|
use crate::enums::DisputeStage;
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
@ -28,6 +28,14 @@ pub enum DisputeMetrics {
|
|||||||
SessionizedTotalAmountDisputed,
|
SessionizedTotalAmountDisputed,
|
||||||
SessionizedTotalDisputeLostAmount,
|
SessionizedTotalDisputeLostAmount,
|
||||||
}
|
}
|
||||||
|
impl ForexMetric for DisputeMetrics {
|
||||||
|
fn is_forex_metric(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::TotalAmountDisputed | Self::TotalDisputeLostAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use common_utils::id_type;
|
use common_utils::id_type;
|
||||||
|
|
||||||
use super::{NameDescription, TimeRange};
|
use super::{ForexMetric, NameDescription, TimeRange};
|
||||||
use crate::enums::{
|
use crate::enums::{
|
||||||
AuthenticationType, Connector, Currency, IntentStatus, PaymentMethod, PaymentMethodType,
|
AuthenticationType, Connector, Currency, IntentStatus, PaymentMethod, PaymentMethodType,
|
||||||
};
|
};
|
||||||
@ -106,6 +106,17 @@ pub enum PaymentIntentMetrics {
|
|||||||
SessionizedPaymentProcessedAmount,
|
SessionizedPaymentProcessedAmount,
|
||||||
SessionizedPaymentsDistribution,
|
SessionizedPaymentsDistribution,
|
||||||
}
|
}
|
||||||
|
impl ForexMetric for PaymentIntentMetrics {
|
||||||
|
fn is_forex_metric(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::PaymentProcessedAmount
|
||||||
|
| Self::SmartRetriedAmount
|
||||||
|
| Self::SessionizedPaymentProcessedAmount
|
||||||
|
| Self::SessionizedSmartRetriedAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Serialize)]
|
#[derive(Debug, Default, serde::Serialize)]
|
||||||
pub struct ErrorResult {
|
pub struct ErrorResult {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use common_utils::id_type;
|
use common_utils::id_type;
|
||||||
|
|
||||||
use super::{NameDescription, TimeRange};
|
use super::{ForexMetric, NameDescription, TimeRange};
|
||||||
use crate::enums::{
|
use crate::enums::{
|
||||||
AttemptStatus, AuthenticationType, CardNetwork, Connector, Currency, PaymentMethod,
|
AttemptStatus, AuthenticationType, CardNetwork, Connector, Currency, PaymentMethod,
|
||||||
PaymentMethodType,
|
PaymentMethodType,
|
||||||
@ -119,6 +119,17 @@ pub enum PaymentMetrics {
|
|||||||
FailureReasons,
|
FailureReasons,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ForexMetric for PaymentMetrics {
|
||||||
|
fn is_forex_metric(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::PaymentProcessedAmount
|
||||||
|
| Self::AvgTicketSize
|
||||||
|
| Self::SessionizedPaymentProcessedAmount
|
||||||
|
| Self::SessionizedAvgTicketSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Debug, Default, serde::Serialize)]
|
#[derive(Debug, Default, serde::Serialize)]
|
||||||
pub struct ErrorResult {
|
pub struct ErrorResult {
|
||||||
pub reason: String,
|
pub reason: String,
|
||||||
|
|||||||
@ -30,7 +30,7 @@ pub enum RefundType {
|
|||||||
RetryRefund,
|
RetryRefund,
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::{NameDescription, TimeRange};
|
use super::{ForexMetric, NameDescription, TimeRange};
|
||||||
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct RefundFilters {
|
pub struct RefundFilters {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -137,6 +137,14 @@ pub enum RefundDistributions {
|
|||||||
#[strum(serialize = "refund_error_message")]
|
#[strum(serialize = "refund_error_message")]
|
||||||
SessionizedRefundErrorMessage,
|
SessionizedRefundErrorMessage,
|
||||||
}
|
}
|
||||||
|
impl ForexMetric for RefundMetrics {
|
||||||
|
fn is_forex_metric(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::RefundProcessedAmount | Self::SessionizedRefundProcessedAmount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod metric_behaviour {
|
pub mod metric_behaviour {
|
||||||
pub struct RefundSuccessRate;
|
pub struct RefundSuccessRate;
|
||||||
|
|||||||
@ -18,12 +18,12 @@ pub mod routes {
|
|||||||
search::{
|
search::{
|
||||||
GetGlobalSearchRequest, GetSearchRequest, GetSearchRequestWithIndex, SearchIndex,
|
GetGlobalSearchRequest, GetSearchRequest, GetSearchRequestWithIndex, SearchIndex,
|
||||||
},
|
},
|
||||||
GenerateReportRequest, GetActivePaymentsMetricRequest, GetApiEventFiltersRequest,
|
AnalyticsRequest, GenerateReportRequest, GetActivePaymentsMetricRequest,
|
||||||
GetApiEventMetricRequest, GetAuthEventMetricRequest, GetDisputeMetricRequest,
|
GetApiEventFiltersRequest, GetApiEventMetricRequest, GetAuthEventMetricRequest,
|
||||||
GetFrmFilterRequest, GetFrmMetricRequest, GetPaymentFiltersRequest,
|
GetDisputeMetricRequest, GetFrmFilterRequest, GetFrmMetricRequest,
|
||||||
GetPaymentIntentFiltersRequest, GetPaymentIntentMetricRequest, GetPaymentMetricRequest,
|
GetPaymentFiltersRequest, GetPaymentIntentFiltersRequest, GetPaymentIntentMetricRequest,
|
||||||
GetRefundFilterRequest, GetRefundMetricRequest, GetSdkEventFiltersRequest,
|
GetPaymentMetricRequest, GetRefundFilterRequest, GetRefundMetricRequest,
|
||||||
GetSdkEventMetricRequest, ReportRequest,
|
GetSdkEventFiltersRequest, GetSdkEventMetricRequest, ReportRequest,
|
||||||
};
|
};
|
||||||
use common_enums::EntityType;
|
use common_enums::EntityType;
|
||||||
use common_utils::types::TimeRange;
|
use common_utils::types::TimeRange;
|
||||||
@ -31,11 +31,9 @@ pub mod routes {
|
|||||||
use futures::{stream::FuturesUnordered, StreamExt};
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
analytics_validator::request_validator,
|
||||||
consts::opensearch::SEARCH_INDEXES,
|
consts::opensearch::SEARCH_INDEXES,
|
||||||
core::{
|
core::{api_locking, errors::user::UserErrors, verification::utils},
|
||||||
api_locking, currency::get_forex_exchange_rates, errors::user::UserErrors,
|
|
||||||
verification::utils,
|
|
||||||
},
|
|
||||||
db::{user::UserInterface, user_role::ListUserRolesByUserIdPayload},
|
db::{user::UserInterface, user_role::ListUserRolesByUserIdPayload},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services::{
|
services::{
|
||||||
@ -405,7 +403,15 @@ pub mod routes {
|
|||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
merchant_ids: vec![merchant_id.clone()],
|
merchant_ids: vec![merchant_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_attempt: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -444,7 +450,16 @@ pub mod routes {
|
|||||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_attempt: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -491,7 +506,16 @@ pub mod routes {
|
|||||||
merchant_id: merchant_id.clone(),
|
merchant_id: merchant_id.clone(),
|
||||||
profile_ids: vec![profile_id.clone()],
|
profile_ids: vec![profile_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_attempt: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payments::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -532,7 +556,16 @@ pub mod routes {
|
|||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
merchant_ids: vec![merchant_id.clone()],
|
merchant_ids: vec![merchant_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_intent: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -571,7 +604,16 @@ pub mod routes {
|
|||||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_intent: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -618,7 +660,16 @@ pub mod routes {
|
|||||||
merchant_id: merchant_id.clone(),
|
merchant_id: merchant_id.clone(),
|
||||||
profile_ids: vec![profile_id.clone()],
|
profile_ids: vec![profile_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
payment_intent: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::payment_intents::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -659,7 +710,16 @@ pub mod routes {
|
|||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
merchant_ids: vec![merchant_id.clone()],
|
merchant_ids: vec![merchant_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
refund: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -698,7 +758,16 @@ pub mod routes {
|
|||||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||||
org_id: org_id.clone(),
|
org_id: org_id.clone(),
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
refund: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
@ -745,7 +814,16 @@ pub mod routes {
|
|||||||
merchant_id: merchant_id.clone(),
|
merchant_id: merchant_id.clone(),
|
||||||
profile_ids: vec![profile_id.clone()],
|
profile_ids: vec![profile_id.clone()],
|
||||||
};
|
};
|
||||||
let ex_rates = get_forex_exchange_rates(state.clone()).await?;
|
|
||||||
|
let validator_response = request_validator(
|
||||||
|
AnalyticsRequest {
|
||||||
|
refund: Some(req.clone()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&state,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let ex_rates = validator_response;
|
||||||
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
analytics::refunds::get_metrics(&state.pool, &ex_rates, &auth, req)
|
||||||
.await
|
.await
|
||||||
.map(ApplicationResponse::Json)
|
.map(ApplicationResponse::Json)
|
||||||
|
|||||||
24
crates/router/src/analytics_validator.rs
Normal file
24
crates/router/src/analytics_validator.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use analytics::errors::AnalyticsError;
|
||||||
|
use api_models::analytics::AnalyticsRequest;
|
||||||
|
use common_utils::errors::CustomResult;
|
||||||
|
use currency_conversion::types::ExchangeRates;
|
||||||
|
use router_env::logger;
|
||||||
|
|
||||||
|
use crate::core::currency::get_forex_exchange_rates;
|
||||||
|
|
||||||
|
pub async fn request_validator(
|
||||||
|
req_type: AnalyticsRequest,
|
||||||
|
state: &crate::routes::SessionState,
|
||||||
|
) -> CustomResult<Option<ExchangeRates>, AnalyticsError> {
|
||||||
|
let forex_enabled = state.conf.analytics.get_inner().get_forex_enabled();
|
||||||
|
let require_forex_functionality = req_type.requires_forex_functionality();
|
||||||
|
|
||||||
|
let ex_rates = if forex_enabled && require_forex_functionality {
|
||||||
|
logger::info!("Fetching forex exchange rates");
|
||||||
|
Some(get_forex_exchange_rates(state.clone()).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ex_rates)
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ pub mod workflows;
|
|||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub mod analytics;
|
pub mod analytics;
|
||||||
|
pub mod analytics_validator;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
pub mod services;
|
pub mod services;
|
||||||
|
|||||||
Reference in New Issue
Block a user