feat(router): add support to use signature_network and is_issuer_regulated as filters (#9033)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Shankar Singh C
2025-08-22 19:16:19 +05:30
committed by GitHub
parent cc44831c51
commit ad05dc4176
27 changed files with 158 additions and 45 deletions

View File

@ -63,8 +63,6 @@ pub struct ProcessedAmountAccumulator {
pub struct DebitRoutingAccumulator { pub struct DebitRoutingAccumulator {
pub transaction_count: u64, pub transaction_count: u64,
pub savings_amount: u64, pub savings_amount: u64,
pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -193,13 +191,7 @@ impl PaymentMetricAccumulator for SuccessRateAccumulator {
} }
impl PaymentMetricAccumulator for DebitRoutingAccumulator { impl PaymentMetricAccumulator for DebitRoutingAccumulator {
type MetricOutput = ( type MetricOutput = (Option<u64>, Option<u64>, Option<u64>);
Option<u64>,
Option<u64>,
Option<u64>,
Option<String>,
Option<bool>,
);
fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) { fn add_metrics_bucket(&mut self, metrics: &PaymentMetricRow) {
if let Some(count) = metrics.count { if let Some(count) = metrics.count {
@ -208,12 +200,6 @@ impl PaymentMetricAccumulator for DebitRoutingAccumulator {
if let Some(total) = metrics.total.as_ref().and_then(ToPrimitive::to_u64) { if let Some(total) = metrics.total.as_ref().and_then(ToPrimitive::to_u64) {
self.savings_amount += total; self.savings_amount += total;
} }
if let Some(signature_network) = &metrics.signature_network {
self.signature_network = Some(signature_network.clone());
}
if let Some(is_issuer_regulated) = metrics.is_issuer_regulated {
self.is_issuer_regulated = Some(is_issuer_regulated);
}
} }
fn collect(self) -> Self::MetricOutput { fn collect(self) -> Self::MetricOutput {
@ -221,8 +207,6 @@ impl PaymentMetricAccumulator for DebitRoutingAccumulator {
Some(self.transaction_count), Some(self.transaction_count),
Some(self.savings_amount), Some(self.savings_amount),
Some(0), Some(0),
self.signature_network,
self.is_issuer_regulated,
) )
} }
} }
@ -484,13 +468,8 @@ impl PaymentMetricsAccumulator {
) = self.payments_distribution.collect(); ) = self.payments_distribution.collect();
let (failure_reason_count, failure_reason_count_without_smart_retries) = let (failure_reason_count, failure_reason_count_without_smart_retries) =
self.failure_reasons_distribution.collect(); self.failure_reasons_distribution.collect();
let ( let (debit_routed_transaction_count, debit_routing_savings, debit_routing_savings_in_usd) =
debit_routed_transaction_count, self.debit_routing.collect();
debit_routing_savings,
debit_routing_savings_in_usd,
signature_network,
is_issuer_regulated,
) = self.debit_routing.collect();
PaymentMetricsBucketValue { PaymentMetricsBucketValue {
payment_success_rate: self.payment_success_rate.collect(), payment_success_rate: self.payment_success_rate.collect(),
@ -518,8 +497,6 @@ impl PaymentMetricsAccumulator {
debit_routed_transaction_count, debit_routed_transaction_count,
debit_routing_savings, debit_routing_savings,
debit_routing_savings_in_usd, debit_routing_savings_in_usd,
signature_network,
is_issuer_regulated,
} }
} }
} }

View File

@ -441,6 +441,9 @@ pub async fn get_filters(
PaymentDimensions::CardIssuer => fil.card_issuer, PaymentDimensions::CardIssuer => fil.card_issuer,
PaymentDimensions::ErrorReason => fil.error_reason, PaymentDimensions::ErrorReason => fil.error_reason,
PaymentDimensions::RoutingApproach => fil.routing_approach.map(|i| i.as_ref().to_string()), PaymentDimensions::RoutingApproach => fil.routing_approach.map(|i| i.as_ref().to_string()),
PaymentDimensions::SignatureNetwork => fil.signature_network,
PaymentDimensions::IsIssuerRegulated => fil.is_issuer_regulated.map(|b| b.to_string()),
PaymentDimensions::IsDebitRouted => fil.is_debit_routed.map(|b| b.to_string())
}) })
.collect::<Vec<String>>(); .collect::<Vec<String>>();
res.query_data.push(FilterValue { res.query_data.push(FilterValue {

View File

@ -38,6 +38,9 @@ pub struct PaymentDistributionRow {
pub count: Option<i64>, pub count: Option<i64>,
pub error_message: Option<String>, pub error_message: Option<String>,
pub routing_approach: Option<DBEnumWrapper<storage_enums::RoutingApproach>>, pub routing_approach: Option<DBEnumWrapper<storage_enums::RoutingApproach>>,
pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>,
pub is_debit_routed: Option<bool>,
#[serde(with = "common_utils::custom_serde::iso8601::option")] #[serde(with = "common_utils::custom_serde::iso8601::option")]
pub start_bucket: Option<PrimitiveDateTime>, pub start_bucket: Option<PrimitiveDateTime>,
#[serde(with = "common_utils::custom_serde::iso8601::option")] #[serde(with = "common_utils::custom_serde::iso8601::option")]

View File

@ -161,6 +161,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -66,4 +66,7 @@ pub struct PaymentFilterRow {
pub error_reason: Option<String>, pub error_reason: Option<String>,
pub first_attempt: Option<bool>, pub first_attempt: Option<bool>,
pub routing_approach: Option<DBEnumWrapper<RoutingApproach>>, pub routing_approach: Option<DBEnumWrapper<RoutingApproach>>,
pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>,
pub is_debit_routed: Option<bool>,
} }

View File

@ -55,6 +55,7 @@ pub struct PaymentMetricRow {
pub routing_approach: Option<DBEnumWrapper<storage_enums::RoutingApproach>>, pub routing_approach: Option<DBEnumWrapper<storage_enums::RoutingApproach>>,
pub signature_network: Option<String>, pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>, pub is_issuer_regulated: Option<bool>,
pub is_debit_routed: Option<bool>,
#[serde(with = "common_utils::custom_serde::iso8601::option")] #[serde(with = "common_utils::custom_serde::iso8601::option")]
pub start_bucket: Option<PrimitiveDateTime>, pub start_bucket: Option<PrimitiveDateTime>,
#[serde(with = "common_utils::custom_serde::iso8601::option")] #[serde(with = "common_utils::custom_serde::iso8601::option")]

View File

@ -123,6 +123,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -118,6 +118,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -57,13 +57,6 @@ where
.switch()?; .switch()?;
query_builder.add_select_column("currency").switch()?; query_builder.add_select_column("currency").switch()?;
query_builder
.add_select_column("signature_network")
.switch()?;
query_builder
.add_select_column("is_issuer_regulated")
.switch()?;
query_builder query_builder
.add_select_column(Aggregate::Min { .add_select_column(Aggregate::Min {
field: "created_at", field: "created_at",
@ -93,16 +86,6 @@ where
.switch()?; .switch()?;
} }
query_builder
.add_group_by_clause("signature_network")
.attach_printable("Error grouping by signature_network")
.switch()?;
query_builder
.add_group_by_clause("is_issuer_regulated")
.attach_printable("Error grouping by is_issuer_regulated")
.switch()?;
query_builder query_builder
.add_group_by_clause("currency") .add_group_by_clause("currency")
.attach_printable("Error grouping by currency") .attach_printable("Error grouping by currency")
@ -146,6 +129,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -109,6 +109,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -123,6 +123,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -116,6 +116,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -113,6 +113,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -124,6 +124,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -119,6 +119,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -129,6 +129,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -184,6 +184,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -110,6 +110,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -141,6 +141,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -117,6 +117,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -120,6 +120,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -113,6 +113,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -113,6 +113,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -112,6 +112,9 @@ where
i.card_issuer.clone(), i.card_issuer.clone(),
i.error_reason.clone(), i.error_reason.clone(),
i.routing_approach.as_ref().map(|i| i.0.clone()), i.routing_approach.as_ref().map(|i| i.0.clone()),
i.signature_network.clone(),
i.is_issuer_regulated,
i.is_debit_routed,
TimeRange { TimeRange {
start_time: match (granularity, i.start_bucket) { start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?, (Some(g), Some(st)) => g.clip_to_start(st)?,

View File

@ -119,6 +119,30 @@ where
.attach_printable("Error adding routing approach filter")?; .attach_printable("Error adding routing approach filter")?;
} }
if !self.signature_network.is_empty() {
builder
.add_filter_in_range_clause(
PaymentDimensions::SignatureNetwork,
&self.signature_network,
)
.attach_printable("Error adding signature network filter")?;
}
if !self.is_issuer_regulated.is_empty() {
builder
.add_filter_in_range_clause(
PaymentDimensions::IsIssuerRegulated,
&self.is_issuer_regulated,
)
.attach_printable("Error adding is issuer regulated filter")?;
}
if !self.is_debit_routed.is_empty() {
builder
.add_filter_in_range_clause(PaymentDimensions::IsDebitRouted, &self.is_debit_routed)
.attach_printable("Error adding is debit routed filter")?;
}
Ok(()) Ok(())
} }
} }

View File

@ -746,6 +746,11 @@ impl<'a> FromRow<'a, PgRow> for super::payments::metrics::PaymentMetricRow {
ColumnNotFound(_) => Ok(Default::default()), ColumnNotFound(_) => Ok(Default::default()),
e => Err(e), e => Err(e),
})?; })?;
let is_debit_routed: Option<bool> =
row.try_get("is_debit_routed").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e { let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()), ColumnNotFound(_) => Ok(Default::default()),
e => Err(e), e => Err(e),
@ -780,6 +785,7 @@ impl<'a> FromRow<'a, PgRow> for super::payments::metrics::PaymentMetricRow {
routing_approach, routing_approach,
signature_network, signature_network,
is_issuer_regulated, is_issuer_regulated,
is_debit_routed,
total, total,
count, count,
start_bucket, start_bucket,
@ -857,6 +863,22 @@ impl<'a> FromRow<'a, PgRow> for super::payments::distribution::PaymentDistributi
ColumnNotFound(_) => Ok(Default::default()), ColumnNotFound(_) => Ok(Default::default()),
e => Err(e), e => Err(e),
})?; })?;
let signature_network: Option<String> =
row.try_get("signature_network").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let is_issuer_regulated: Option<bool> =
row.try_get("is_issuer_regulated").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let is_debit_routed: Option<bool> =
row.try_get("is_debit_routed").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e { let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()), ColumnNotFound(_) => Ok(Default::default()),
e => Err(e), e => Err(e),
@ -900,6 +922,9 @@ impl<'a> FromRow<'a, PgRow> for super::payments::distribution::PaymentDistributi
count, count,
error_message, error_message,
routing_approach, routing_approach,
signature_network,
is_issuer_regulated,
is_debit_routed,
start_bucket, start_bucket,
end_bucket, end_bucket,
}) })
@ -979,6 +1004,21 @@ impl<'a> FromRow<'a, PgRow> for super::payments::filters::PaymentFilterRow {
ColumnNotFound(_) => Ok(Default::default()), ColumnNotFound(_) => Ok(Default::default()),
e => Err(e), e => Err(e),
})?; })?;
let signature_network: Option<String> =
row.try_get("signature_network").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let is_issuer_regulated: Option<bool> =
row.try_get("is_issuer_regulated").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
let is_debit_routed: Option<bool> =
row.try_get("is_debit_routed").or_else(|e| match e {
ColumnNotFound(_) => Ok(Default::default()),
e => Err(e),
})?;
Ok(Self { Ok(Self {
currency, currency,
status, status,
@ -996,6 +1036,9 @@ impl<'a> FromRow<'a, PgRow> for super::payments::filters::PaymentFilterRow {
error_reason, error_reason,
first_attempt, first_attempt,
routing_approach, routing_approach,
signature_network,
is_issuer_regulated,
is_debit_routed,
}) })
} }
} }

View File

@ -45,6 +45,12 @@ pub struct PaymentFilters {
pub first_attempt: Vec<bool>, pub first_attempt: Vec<bool>,
#[serde(default)] #[serde(default)]
pub routing_approach: Vec<RoutingApproach>, pub routing_approach: Vec<RoutingApproach>,
#[serde(default)]
pub signature_network: Vec<String>,
#[serde(default)]
pub is_issuer_regulated: Vec<bool>,
#[serde(default)]
pub is_debit_routed: Vec<bool>,
} }
#[derive( #[derive(
@ -87,6 +93,9 @@ pub enum PaymentDimensions {
CardIssuer, CardIssuer,
ErrorReason, ErrorReason,
RoutingApproach, RoutingApproach,
SignatureNetwork,
IsIssuerRegulated,
IsDebitRouted,
} }
#[derive( #[derive(
@ -208,6 +217,9 @@ pub struct PaymentMetricsBucketIdentifier {
pub card_issuer: Option<String>, pub card_issuer: Option<String>,
pub error_reason: Option<String>, pub error_reason: Option<String>,
pub routing_approach: Option<RoutingApproach>, pub routing_approach: Option<RoutingApproach>,
pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>,
pub is_debit_routed: Option<bool>,
#[serde(rename = "time_range")] #[serde(rename = "time_range")]
pub time_bucket: TimeRange, pub time_bucket: TimeRange,
// Coz FE sucks // Coz FE sucks
@ -234,6 +246,9 @@ impl PaymentMetricsBucketIdentifier {
card_issuer: Option<String>, card_issuer: Option<String>,
error_reason: Option<String>, error_reason: Option<String>,
routing_approach: Option<RoutingApproach>, routing_approach: Option<RoutingApproach>,
signature_network: Option<String>,
is_issuer_regulated: Option<bool>,
is_debit_routed: Option<bool>,
normalized_time_range: TimeRange, normalized_time_range: TimeRange,
) -> Self { ) -> Self {
Self { Self {
@ -252,6 +267,9 @@ impl PaymentMetricsBucketIdentifier {
card_issuer, card_issuer,
error_reason, error_reason,
routing_approach, routing_approach,
signature_network,
is_issuer_regulated,
is_debit_routed,
time_bucket: normalized_time_range, time_bucket: normalized_time_range,
start_time: normalized_time_range.start_time, start_time: normalized_time_range.start_time,
} }
@ -278,6 +296,9 @@ impl Hash for PaymentMetricsBucketIdentifier {
.clone() .clone()
.map(|i| i.to_string()) .map(|i| i.to_string())
.hash(state); .hash(state);
self.signature_network.hash(state);
self.is_issuer_regulated.hash(state);
self.is_debit_routed.hash(state);
self.time_bucket.hash(state); self.time_bucket.hash(state);
} }
} }
@ -319,8 +340,6 @@ pub struct PaymentMetricsBucketValue {
pub debit_routed_transaction_count: Option<u64>, pub debit_routed_transaction_count: Option<u64>,
pub debit_routing_savings: Option<u64>, pub debit_routing_savings: Option<u64>,
pub debit_routing_savings_in_usd: Option<u64>, pub debit_routing_savings_in_usd: Option<u64>,
pub signature_network: Option<String>,
pub is_issuer_regulated: Option<bool>,
} }
#[derive(Debug, serde::Serialize)] #[derive(Debug, serde::Serialize)]