mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +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:
@ -969,21 +969,25 @@ impl AnalyticsProvider {
|
||||
tenant: &dyn storage_impl::config::TenantConfig,
|
||||
) -> Self {
|
||||
match config {
|
||||
AnalyticsConfig::Sqlx { sqlx } => {
|
||||
AnalyticsConfig::Sqlx { sqlx, .. } => {
|
||||
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()),
|
||||
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,
|
||||
ClickhouseClient {
|
||||
config: Arc::new(clickhouse.clone()),
|
||||
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,
|
||||
ClickhouseClient {
|
||||
config: Arc::new(clickhouse.clone()),
|
||||
@ -1000,20 +1004,35 @@ impl AnalyticsProvider {
|
||||
pub enum AnalyticsConfig {
|
||||
Sqlx {
|
||||
sqlx: Database,
|
||||
forex_enabled: bool,
|
||||
},
|
||||
Clickhouse {
|
||||
clickhouse: ClickhouseConfig,
|
||||
forex_enabled: bool,
|
||||
},
|
||||
CombinedCkh {
|
||||
sqlx: Database,
|
||||
clickhouse: ClickhouseConfig,
|
||||
forex_enabled: bool,
|
||||
},
|
||||
CombinedSqlx {
|
||||
sqlx: Database,
|
||||
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]
|
||||
impl SecretsHandler for AnalyticsConfig {
|
||||
async fn convert_to_raw_secret(
|
||||
@ -1024,7 +1043,7 @@ impl SecretsHandler for AnalyticsConfig {
|
||||
let decrypted_password = match analytics_config {
|
||||
// Todo: Perform kms decryption of clickhouse password
|
||||
Self::Clickhouse { .. } => masking::Secret::new(String::default()),
|
||||
Self::Sqlx { sqlx }
|
||||
Self::Sqlx { sqlx, .. }
|
||||
| Self::CombinedCkh { sqlx, .. }
|
||||
| Self::CombinedSqlx { sqlx, .. } => {
|
||||
secret_management_client
|
||||
@ -1034,26 +1053,46 @@ impl SecretsHandler for AnalyticsConfig {
|
||||
};
|
||||
|
||||
Ok(value.transition_state(|conf| match conf {
|
||||
Self::Sqlx { sqlx } => Self::Sqlx {
|
||||
Self::Sqlx {
|
||||
sqlx,
|
||||
forex_enabled,
|
||||
} => Self::Sqlx {
|
||||
sqlx: Database {
|
||||
password: decrypted_password,
|
||||
..sqlx
|
||||
},
|
||||
forex_enabled,
|
||||
},
|
||||
Self::Clickhouse { clickhouse } => Self::Clickhouse { clickhouse },
|
||||
Self::CombinedCkh { sqlx, clickhouse } => Self::CombinedCkh {
|
||||
Self::Clickhouse {
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
} => Self::Clickhouse {
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
},
|
||||
Self::CombinedCkh {
|
||||
sqlx,
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
} => Self::CombinedCkh {
|
||||
sqlx: Database {
|
||||
password: decrypted_password,
|
||||
..sqlx
|
||||
},
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
},
|
||||
Self::CombinedSqlx { sqlx, clickhouse } => Self::CombinedSqlx {
|
||||
Self::CombinedSqlx {
|
||||
sqlx,
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
} => Self::CombinedSqlx {
|
||||
sqlx: Database {
|
||||
password: decrypted_password,
|
||||
..sqlx
|
||||
},
|
||||
clickhouse,
|
||||
forex_enabled,
|
||||
},
|
||||
}))
|
||||
}
|
||||
@ -1063,6 +1102,7 @@ impl Default for AnalyticsConfig {
|
||||
fn default() -> Self {
|
||||
Self::Sqlx {
|
||||
sqlx: Database::default(),
|
||||
forex_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ pub async fn get_sankey(
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_metrics(
|
||||
pool: &AnalyticsProvider,
|
||||
ex_rates: &ExchangeRates,
|
||||
ex_rates: &Option<ExchangeRates>,
|
||||
auth: &AuthInfo,
|
||||
req: GetPaymentIntentMetricRequest,
|
||||
) -> AnalyticsResult<PaymentIntentsMetricsResponse<MetricsBucketResponse>> {
|
||||
@ -201,22 +201,25 @@ 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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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);
|
||||
@ -224,44 +227,50 @@ pub async fn get_metrics(
|
||||
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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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;
|
||||
@ -270,22 +279,25 @@ pub async fn get_metrics(
|
||||
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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
collected_values.payment_processed_amount_without_smart_retries_in_usd =
|
||||
amount_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,
|
||||
),
|
||||
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_smart_retried_amount_in_usd: if ex_rates.is_some() {
|
||||
Some(total_smart_retried_amount_in_usd)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
total_smart_retried_amount_without_smart_retries_in_usd: if ex_rates.is_some() {
|
||||
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_without_smart_retries: Some(
|
||||
total_payment_processed_count_without_smart_retries,
|
||||
|
||||
@ -48,7 +48,7 @@ pub enum TaskType {
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_metrics(
|
||||
pool: &AnalyticsProvider,
|
||||
ex_rates: &ExchangeRates,
|
||||
ex_rates: &Option<ExchangeRates>,
|
||||
auth: &AuthInfo,
|
||||
req: GetPaymentMetricRequest,
|
||||
) -> AnalyticsResult<PaymentsMetricsResponse<MetricsBucketResponse>> {
|
||||
@ -234,22 +234,25 @@ pub async fn get_metrics(
|
||||
.map(|(id, val)| {
|
||||
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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
collected_values.payment_processed_amount_in_usd = amount_in_usd;
|
||||
total_payment_processed_amount += amount;
|
||||
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;
|
||||
}
|
||||
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();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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 +=
|
||||
@ -298,13 +304,19 @@ pub async fn get_metrics(
|
||||
query_data,
|
||||
meta_data: [PaymentsAnalyticsMetadata {
|
||||
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,
|
||||
),
|
||||
total_payment_processed_amount_without_smart_retries_usd: Some(
|
||||
total_payment_processed_amount_without_smart_retries_usd,
|
||||
),
|
||||
total_payment_processed_amount_without_smart_retries_usd: if ex_rates.is_some() {
|
||||
Some(total_payment_processed_amount_without_smart_retries_usd)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
total_payment_processed_count: Some(total_payment_processed_count),
|
||||
total_payment_processed_count_without_smart_retries: Some(
|
||||
total_payment_processed_count_without_smart_retries,
|
||||
|
||||
@ -47,7 +47,7 @@ pub enum TaskType {
|
||||
|
||||
pub async fn get_metrics(
|
||||
pool: &AnalyticsProvider,
|
||||
ex_rates: &ExchangeRates,
|
||||
ex_rates: &Option<ExchangeRates>,
|
||||
auth: &AuthInfo,
|
||||
req: GetRefundMetricRequest,
|
||||
) -> AnalyticsResult<RefundsMetricsResponse<RefundMetricsBucketResponse>> {
|
||||
@ -217,22 +217,25 @@ pub async fn get_metrics(
|
||||
total += total_count;
|
||||
}
|
||||
if let Some(amount) = collected_values.refund_processed_amount {
|
||||
let amount_in_usd = id
|
||||
.currency
|
||||
.and_then(|currency| {
|
||||
i64::try_from(amount)
|
||||
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
|
||||
.ok()
|
||||
.and_then(|amount_i64| {
|
||||
convert(ex_rates, currency, Currency::USD, amount_i64)
|
||||
.inspect_err(|e| {
|
||||
logger::error!("Currency conversion error: {:?}", e)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
})
|
||||
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
|
||||
.unwrap_or_default();
|
||||
let amount_in_usd = if let Some(ex_rates) = ex_rates {
|
||||
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()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
collected_values.refund_processed_amount_in_usd = amount_in_usd;
|
||||
total_refund_processed_amount += amount;
|
||||
total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
|
||||
@ -261,7 +264,11 @@ pub async fn get_metrics(
|
||||
meta_data: [RefundsAnalyticsMetadata {
|
||||
total_refund_success_rate,
|
||||
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_reason_count: Some(total_refund_reason_count),
|
||||
total_refund_error_message_count: Some(total_refund_error_message_count),
|
||||
|
||||
Reference in New Issue
Block a user