Skip to content

Commit 262ab0e

Browse files
committed
feat(analytics): add payment_processed_amount as metric for payment_intents table
1 parent f034cab commit 262ab0e

File tree

4 files changed

+170
-1
lines changed

4 files changed

+170
-1
lines changed

crates/analytics/src/payment_intents/core.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ pub async fn get_metrics(
205205
| PaymentIntentMetrics::SessionizedPaymentsSuccessRate => metrics_builder
206206
.payments_success_rate
207207
.add_metrics_bucket(&value),
208-
PaymentIntentMetrics::SessionizedPaymentProcessedAmount => metrics_builder
208+
PaymentIntentMetrics::SessionizedPaymentProcessedAmount
209+
| PaymentIntentMetrics::PaymentProcessedAmount => metrics_builder
209210
.payment_processed_amount
210211
.add_metrics_bucket(&value),
211212
PaymentIntentMetrics::SessionizedPaymentsDistribution => metrics_builder

crates/analytics/src/payment_intents/metrics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ use crate::{
1717
};
1818

1919
mod payment_intent_count;
20+
mod payment_processed_amount;
2021
mod payments_success_rate;
2122
mod sessionized_metrics;
2223
mod smart_retried_amount;
2324
mod successful_smart_retries;
2425
mod total_smart_retries;
2526

2627
use payment_intent_count::PaymentIntentCount;
28+
use payment_processed_amount::PaymentProcessedAmount;
2729
use payments_success_rate::PaymentsSuccessRate;
2830
use smart_retried_amount::SmartRetriedAmount;
2931
use successful_smart_retries::SuccessfulSmartRetries;
@@ -116,6 +118,11 @@ where
116118
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
117119
.await
118120
}
121+
Self::PaymentProcessedAmount => {
122+
PaymentProcessedAmount
123+
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
124+
.await
125+
}
119126
Self::SessionizedSuccessfulSmartRetries => {
120127
sessionized_metrics::SuccessfulSmartRetries
121128
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use std::collections::HashSet;
2+
3+
use api_models::analytics::{
4+
payment_intents::{
5+
PaymentIntentDimensions, PaymentIntentFilters, PaymentIntentMetricsBucketIdentifier,
6+
},
7+
Granularity, TimeRange,
8+
};
9+
use common_utils::errors::ReportSwitchExt;
10+
use diesel_models::enums as storage_enums;
11+
use error_stack::ResultExt;
12+
use time::PrimitiveDateTime;
13+
14+
use super::PaymentIntentMetricRow;
15+
use crate::{
16+
enums::AuthInfo,
17+
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
18+
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
19+
};
20+
21+
#[derive(Default)]
22+
pub(super) struct PaymentProcessedAmount;
23+
24+
#[async_trait::async_trait]
25+
impl<T> super::PaymentIntentMetric<T> for PaymentProcessedAmount
26+
where
27+
T: AnalyticsDataSource + super::PaymentIntentMetricAnalytics,
28+
PrimitiveDateTime: ToSql<T>,
29+
AnalyticsCollection: ToSql<T>,
30+
Granularity: GroupByClause<T>,
31+
Aggregate<&'static str>: ToSql<T>,
32+
Window<&'static str>: ToSql<T>,
33+
{
34+
async fn load_metrics(
35+
&self,
36+
dimensions: &[PaymentIntentDimensions],
37+
auth: &AuthInfo,
38+
filters: &PaymentIntentFilters,
39+
granularity: &Option<Granularity>,
40+
time_range: &TimeRange,
41+
pool: &T,
42+
) -> MetricsResult<HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>>
43+
{
44+
let mut query_builder: QueryBuilder<T> =
45+
QueryBuilder::new(AnalyticsCollection::PaymentIntent);
46+
47+
let mut dimensions = dimensions.to_vec();
48+
49+
dimensions.push(PaymentIntentDimensions::PaymentIntentStatus);
50+
51+
for dim in dimensions.iter() {
52+
query_builder.add_select_column(dim).switch()?;
53+
}
54+
query_builder
55+
.add_select_column(Aggregate::Count {
56+
field: None,
57+
alias: Some("count"),
58+
})
59+
.switch()?;
60+
61+
query_builder
62+
.add_select_column("attempt_count == 1 as first_attempt")
63+
.switch()?;
64+
65+
query_builder
66+
.add_select_column(Aggregate::Sum {
67+
field: "amount",
68+
alias: Some("total"),
69+
})
70+
.switch()?;
71+
query_builder
72+
.add_select_column(Aggregate::Min {
73+
field: "created_at",
74+
alias: Some("start_bucket"),
75+
})
76+
.switch()?;
77+
query_builder
78+
.add_select_column(Aggregate::Max {
79+
field: "created_at",
80+
alias: Some("end_bucket"),
81+
})
82+
.switch()?;
83+
84+
filters.set_filter_clause(&mut query_builder).switch()?;
85+
86+
auth.set_filter_clause(&mut query_builder).switch()?;
87+
88+
time_range
89+
.set_filter_clause(&mut query_builder)
90+
.attach_printable("Error filtering time range")
91+
.switch()?;
92+
93+
for dim in dimensions.iter() {
94+
query_builder
95+
.add_group_by_clause(dim)
96+
.attach_printable("Error grouping by dimensions")
97+
.switch()?;
98+
}
99+
100+
query_builder
101+
.add_group_by_clause("attempt_count")
102+
.attach_printable("Error grouping by attempt_count")
103+
.switch()?;
104+
105+
if let Some(granularity) = granularity.as_ref() {
106+
granularity
107+
.set_group_by_clause(&mut query_builder)
108+
.attach_printable("Error adding granularity")
109+
.switch()?;
110+
}
111+
112+
query_builder
113+
.add_filter_clause(
114+
PaymentIntentDimensions::PaymentIntentStatus,
115+
storage_enums::IntentStatus::Succeeded,
116+
)
117+
.switch()?;
118+
119+
query_builder
120+
.execute_query::<PaymentIntentMetricRow, _>(pool)
121+
.await
122+
.change_context(MetricsError::QueryBuildingError)?
123+
.change_context(MetricsError::QueryExecutionFailure)?
124+
.into_iter()
125+
.map(|i| {
126+
Ok((
127+
PaymentIntentMetricsBucketIdentifier::new(
128+
None,
129+
i.currency.as_ref().map(|i| i.0),
130+
i.profile_id.clone(),
131+
i.connector.clone(),
132+
i.authentication_type.as_ref().map(|i| i.0),
133+
i.payment_method.clone(),
134+
i.payment_method_type.clone(),
135+
i.card_network.clone(),
136+
i.merchant_id.clone(),
137+
i.card_last_4.clone(),
138+
i.card_issuer.clone(),
139+
i.error_reason.clone(),
140+
TimeRange {
141+
start_time: match (granularity, i.start_bucket) {
142+
(Some(g), Some(st)) => g.clip_to_start(st)?,
143+
_ => time_range.start_time,
144+
},
145+
end_time: granularity.as_ref().map_or_else(
146+
|| Ok(time_range.end_time),
147+
|g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
148+
)?,
149+
},
150+
),
151+
i,
152+
))
153+
})
154+
.collect::<error_stack::Result<
155+
HashSet<(PaymentIntentMetricsBucketIdentifier, PaymentIntentMetricRow)>,
156+
crate::query::PostProcessingError,
157+
>>()
158+
.change_context(MetricsError::PostProcessingFailure)
159+
}
160+
}

crates/api_models/src/analytics/payment_intents.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub enum PaymentIntentMetrics {
9393
SmartRetriedAmount,
9494
PaymentIntentCount,
9595
PaymentsSuccessRate,
96+
PaymentProcessedAmount,
9697
SessionizedSuccessfulSmartRetries,
9798
SessionizedTotalSmartRetries,
9899
SessionizedSmartRetriedAmount,

0 commit comments

Comments
 (0)