Skip to content

Commit

Permalink
Merge branch 'main' of github.com:juspay/hyperswitch into multiple-cr…
Browse files Browse the repository at this point in the history
…ed-cypress

* 'main' of github.com:juspay/hyperswitch:
  chore(version): 2024.11.27.0
  fix(core): add payment_id as query param in merchant return url (#6665)
  feat(connector): [Netcetera] add sca exemption (#6611)
  feat(payments): propagate additional payment method data for google pay during MIT (#6644)
  feat: Added grpc based health check (#6441)
  feat(analytics): add `sessionized_metrics` for disputes analytics (#6573)
  • Loading branch information
pixincreate committed Nov 27, 2024
2 parents 8f10947 + d4b482c commit a9b8985
Show file tree
Hide file tree
Showing 35 changed files with 893 additions and 85 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ All notable changes to HyperSwitch will be documented here.

- - -

## 2024.11.27.0

### Features

- **analytics:** Add `sessionized_metrics` for disputes analytics ([#6573](https://github.com/juspay/hyperswitch/pull/6573)) ([`8fbb766`](https://github.com/juspay/hyperswitch/commit/8fbb7663089d4790628109944e5fb5a57ccdaf00))
- **connector:**
- [INESPAY] add Connector Template Code ([#6614](https://github.com/juspay/hyperswitch/pull/6614)) ([`710186f`](https://github.com/juspay/hyperswitch/commit/710186f035c92a919e8f5a49565c6f8908f1803f))
- [Netcetera] add sca exemption ([#6611](https://github.com/juspay/hyperswitch/pull/6611)) ([`3120494`](https://github.com/juspay/hyperswitch/commit/31204941ee24fe7b23344ba9b4a2615c46f33bb0))
- **payments:** Propagate additional payment method data for google pay during MIT ([#6644](https://github.com/juspay/hyperswitch/pull/6644)) ([`75fe9c0`](https://github.com/juspay/hyperswitch/commit/75fe9c0c285f640967af33b1d969af9ce48c5b17))
- **router:** [Cybersource] add PLN to the currency config ([#6628](https://github.com/juspay/hyperswitch/pull/6628)) ([`29a0885`](https://github.com/juspay/hyperswitch/commit/29a0885a8fc7b718f8b87866e2638e8bfad3c8f3))
- **users:** Send welcome to community email in magic link signup ([#6639](https://github.com/juspay/hyperswitch/pull/6639)) ([`03423a1`](https://github.com/juspay/hyperswitch/commit/03423a1f76d324453052da985f998fd3f957ce90))
- Added grpc based health check ([#6441](https://github.com/juspay/hyperswitch/pull/6441)) ([`e922f96`](https://github.com/juspay/hyperswitch/commit/e922f96cee7e34493f0022b0c56455357eddc4f8))

### Bug Fixes

- **core:** Add payment_id as query param in merchant return url ([#6665](https://github.com/juspay/hyperswitch/pull/6665)) ([`6829478`](https://github.com/juspay/hyperswitch/commit/682947866e6afc197c71bbd255f22ae427704590))

### Refactors

- **authn:** Enable cookies in Integ ([#6599](https://github.com/juspay/hyperswitch/pull/6599)) ([`02479a1`](https://github.com/juspay/hyperswitch/commit/02479a12b18dc68e2787ae237580fcb46348374e))
- **connector:** Add amount conversion framework to Riskified ([#6359](https://github.com/juspay/hyperswitch/pull/6359)) ([`acb30ef`](https://github.com/juspay/hyperswitch/commit/acb30ef6d144eaf13b237b830d1ac534259932a3))
- **payments_v2:** Use batch encryption for intent create and confirm intent ([#6589](https://github.com/juspay/hyperswitch/pull/6589)) ([`108b160`](https://github.com/juspay/hyperswitch/commit/108b1603fa44b2a56c278196edb5a1f76f5d3d03))
- **tenant:** Use tenant id type ([#6643](https://github.com/juspay/hyperswitch/pull/6643)) ([`c9df7b0`](https://github.com/juspay/hyperswitch/commit/c9df7b0557889c88ea20392dfe56bf651e22c9a7))

**Full Changelog:** [`2024.11.26.0...2024.11.27.0`](https://github.com/juspay/hyperswitch/compare/2024.11.26.0...2024.11.27.0)

- - -

## 2024.11.26.0

### Features
Expand Down
1 change: 1 addition & 0 deletions config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -790,3 +790,4 @@ connector_list = "cybersource" # Supported connectors for network tokenization
[grpc_client.dynamic_routing_client] # Dynamic Routing Client Configuration
host = "localhost" # Client Host
port = 7000 # Client Port
service = "dynamo" # Service name
1 change: 1 addition & 0 deletions config/deployments/env_specific.toml
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,4 @@ check_token_status_url= "" # base url to check token status from token servic
[grpc_client.dynamic_routing_client] # Dynamic Routing Client Configuration
host = "localhost" # Client Host
port = 7000 # Client Port
service = "dynamo" # Service name
4 changes: 4 additions & 0 deletions crates/analytics/src/clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ impl AnalyticsDataSource for ClickhouseClient {
| AnalyticsCollection::Dispute => {
TableEngine::CollapsingMergeTree { sign: "sign_flag" }
}
AnalyticsCollection::DisputeSessionized => {
TableEngine::CollapsingMergeTree { sign: "sign_flag" }
}
AnalyticsCollection::SdkEvents
| AnalyticsCollection::SdkEventsAnalytics
| AnalyticsCollection::ApiEvents
Expand Down Expand Up @@ -439,6 +442,7 @@ impl ToSql<ClickhouseClient> for AnalyticsCollection {
Self::ConnectorEvents => Ok("connector_events_audit".to_string()),
Self::OutgoingWebhookEvent => Ok("outgoing_webhook_events_audit".to_string()),
Self::Dispute => Ok("dispute".to_string()),
Self::DisputeSessionized => Ok("sessionizer_dispute".to_string()),
Self::ActivePaymentsAnalytics => Ok("active_payments".to_string()),
}
}
Expand Down
12 changes: 6 additions & 6 deletions crates/analytics/src/disputes/accumulators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use super::metrics::DisputeMetricRow;
#[derive(Debug, Default)]
pub struct DisputeMetricsAccumulator {
pub disputes_status_rate: RateAccumulator,
pub total_amount_disputed: SumAccumulator,
pub total_dispute_lost_amount: SumAccumulator,
pub disputed_amount: DisputedAmountAccumulator,
pub dispute_lost_amount: DisputedAmountAccumulator,
}
#[derive(Debug, Default)]
pub struct RateAccumulator {
Expand All @@ -17,7 +17,7 @@ pub struct RateAccumulator {
}
#[derive(Debug, Default)]
#[repr(transparent)]
pub struct SumAccumulator {
pub struct DisputedAmountAccumulator {
pub total: Option<i64>,
}

Expand All @@ -29,7 +29,7 @@ pub trait DisputeMetricAccumulator {
fn collect(self) -> Self::MetricOutput;
}

impl DisputeMetricAccumulator for SumAccumulator {
impl DisputeMetricAccumulator for DisputedAmountAccumulator {
type MetricOutput = Option<u64>;
#[inline]
fn add_metrics_bucket(&mut self, metrics: &DisputeMetricRow) {
Expand Down Expand Up @@ -92,8 +92,8 @@ impl DisputeMetricsAccumulator {
disputes_challenged: challenge_rate,
disputes_won: won_rate,
disputes_lost: lost_rate,
total_amount_disputed: self.total_amount_disputed.collect(),
total_dispute_lost_amount: self.total_dispute_lost_amount.collect(),
disputed_amount: self.disputed_amount.collect(),
dispute_lost_amount: self.dispute_lost_amount.collect(),
total_dispute,
}
}
Expand Down
46 changes: 31 additions & 15 deletions crates/analytics/src/disputes/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use api_models::analytics::{
DisputeDimensions, DisputeMetrics, DisputeMetricsBucketIdentifier,
DisputeMetricsBucketResponse,
},
AnalyticsMetadata, DisputeFilterValue, DisputeFiltersResponse, GetDisputeFilterRequest,
GetDisputeMetricRequest, MetricsResponse,
DisputeFilterValue, DisputeFiltersResponse, DisputesAnalyticsMetadata, DisputesMetricsResponse,
GetDisputeFilterRequest, GetDisputeMetricRequest,
};
use error_stack::ResultExt;
use router_env::{
Expand All @@ -30,7 +30,7 @@ pub async fn get_metrics(
pool: &AnalyticsProvider,
auth: &AuthInfo,
req: GetDisputeMetricRequest,
) -> AnalyticsResult<MetricsResponse<DisputeMetricsBucketResponse>> {
) -> AnalyticsResult<DisputesMetricsResponse<DisputeMetricsBucketResponse>> {
let mut metrics_accumulator: HashMap<
DisputeMetricsBucketIdentifier,
DisputeMetricsAccumulator,
Expand Down Expand Up @@ -87,14 +87,17 @@ pub async fn get_metrics(
logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
let metrics_builder = metrics_accumulator.entry(id).or_default();
match metric {
DisputeMetrics::DisputeStatusMetric => metrics_builder
DisputeMetrics::DisputeStatusMetric
| DisputeMetrics::SessionizedDisputeStatusMetric => metrics_builder
.disputes_status_rate
.add_metrics_bucket(&value),
DisputeMetrics::TotalAmountDisputed => metrics_builder
.total_amount_disputed
.add_metrics_bucket(&value),
DisputeMetrics::TotalDisputeLostAmount => metrics_builder
.total_dispute_lost_amount
DisputeMetrics::TotalAmountDisputed
| DisputeMetrics::SessionizedTotalAmountDisputed => {
metrics_builder.disputed_amount.add_metrics_bucket(&value)
}
DisputeMetrics::TotalDisputeLostAmount
| DisputeMetrics::SessionizedTotalDisputeLostAmount => metrics_builder
.dispute_lost_amount
.add_metrics_bucket(&value),
}
}
Expand All @@ -105,18 +108,31 @@ pub async fn get_metrics(
metrics_accumulator
);
}
let mut total_disputed_amount = 0;
let mut total_dispute_lost_amount = 0;
let query_data: Vec<DisputeMetricsBucketResponse> = metrics_accumulator
.into_iter()
.map(|(id, val)| DisputeMetricsBucketResponse {
values: val.collect(),
dimensions: id,
.map(|(id, val)| {
let collected_values = val.collect();
if let Some(amount) = collected_values.disputed_amount {
total_disputed_amount += amount;
}
if let Some(amount) = collected_values.dispute_lost_amount {
total_dispute_lost_amount += amount;
}

DisputeMetricsBucketResponse {
values: collected_values,
dimensions: id,
}
})
.collect();

Ok(MetricsResponse {
Ok(DisputesMetricsResponse {
query_data,
meta_data: [AnalyticsMetadata {
current_time_range: req.time_range,
meta_data: [DisputesAnalyticsMetadata {
total_disputed_amount: Some(total_disputed_amount),
total_dispute_lost_amount: Some(total_dispute_lost_amount),
}],
})
}
Expand Down
16 changes: 16 additions & 0 deletions crates/analytics/src/disputes/metrics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod dispute_status_metric;
mod sessionized_metrics;
mod total_amount_disputed;
mod total_dispute_lost_amount;

Expand Down Expand Up @@ -92,6 +93,21 @@ where
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedTotalAmountDisputed => {
sessionized_metrics::TotalAmountDisputed::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedDisputeStatusMetric => {
sessionized_metrics::DisputeStatusMetric::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedTotalDisputeLostAmount => {
sessionized_metrics::TotalDisputeLostAmount::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use std::collections::HashSet;

use api_models::analytics::{
disputes::{DisputeDimensions, DisputeFilters, DisputeMetricsBucketIdentifier},
Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::DisputeMetricRow;
use crate::{
enums::AuthInfo,
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};
#[derive(Default)]
pub(crate) struct DisputeStatusMetric {}

#[async_trait::async_trait]
impl<T> super::DisputeMetric<T> for DisputeStatusMetric
where
T: AnalyticsDataSource + super::DisputeMetricAnalytics,
PrimitiveDateTime: ToSql<T>,
AnalyticsCollection: ToSql<T>,
Granularity: GroupByClause<T>,
Aggregate<&'static str>: ToSql<T>,
Window<&'static str>: ToSql<T>,
{
async fn load_metrics(
&self,
dimensions: &[DisputeDimensions],
auth: &AuthInfo,
filters: &DisputeFilters,
granularity: &Option<Granularity>,
time_range: &TimeRange,
pool: &T,
) -> MetricsResult<HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>>
where
T: AnalyticsDataSource + super::DisputeMetricAnalytics,
{
let mut query_builder = QueryBuilder::new(AnalyticsCollection::DisputeSessionized);

for dim in dimensions {
query_builder.add_select_column(dim).switch()?;
}

query_builder.add_select_column("dispute_status").switch()?;

query_builder
.add_select_column(Aggregate::Count {
field: None,
alias: Some("count"),
})
.switch()?;
query_builder
.add_select_column(Aggregate::Min {
field: "created_at",
alias: Some("start_bucket"),
})
.switch()?;
query_builder
.add_select_column(Aggregate::Max {
field: "created_at",
alias: Some("end_bucket"),
})
.switch()?;

filters.set_filter_clause(&mut query_builder).switch()?;

auth.set_filter_clause(&mut query_builder).switch()?;

time_range.set_filter_clause(&mut query_builder).switch()?;

for dim in dimensions {
query_builder.add_group_by_clause(dim).switch()?;
}

query_builder
.add_group_by_clause("dispute_status")
.switch()?;

if let Some(granularity) = granularity.as_ref() {
granularity
.set_group_by_clause(&mut query_builder)
.switch()?;
}

query_builder
.execute_query::<DisputeMetricRow, _>(pool)
.await
.change_context(MetricsError::QueryBuildingError)?
.change_context(MetricsError::QueryExecutionFailure)?
.into_iter()
.map(|i| {
Ok((
DisputeMetricsBucketIdentifier::new(
i.dispute_stage.as_ref().map(|i| i.0),
i.connector.clone(),
TimeRange {
start_time: match (granularity, i.start_bucket) {
(Some(g), Some(st)) => g.clip_to_start(st)?,
_ => time_range.start_time,
},
end_time: granularity.as_ref().map_or_else(
|| Ok(time_range.end_time),
|g| i.end_bucket.map(|et| g.clip_to_end(et)).transpose(),
)?,
},
),
i,
))
})
.collect::<error_stack::Result<
HashSet<(DisputeMetricsBucketIdentifier, DisputeMetricRow)>,
crate::query::PostProcessingError,
>>()
.change_context(MetricsError::PostProcessingFailure)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod dispute_status_metric;
mod total_amount_disputed;
mod total_dispute_lost_amount;
pub(super) use dispute_status_metric::DisputeStatusMetric;
pub(super) use total_amount_disputed::TotalAmountDisputed;
pub(super) use total_dispute_lost_amount::TotalDisputeLostAmount;

pub use super::{DisputeMetric, DisputeMetricAnalytics, DisputeMetricRow};
Loading

0 comments on commit a9b8985

Please sign in to comment.