Skip to content

feat(router): Add support for storing multiple surcharge configurations at profile level #7570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a837ddd
Add column algorithm_type to routing_algorithm and add new profile le…
Mar 5, 2025
aa59b81
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Mar 5, 2025
08d6c2f
Add active_surcharge_algorithm_id to business_profile and make corres…
Mar 6, 2025
596e77e
Add surcharge_algorithm_id to payment_attempt and make corresponding …
Mar 6, 2025
3da0d7e
Add endpoint to insert new surcharge config into routing_algorithm table
Mar 10, 2025
805311c
Merge branch main into multi-surcharge-configs
Mar 10, 2025
7a18da6
Add endpoint to retrieve linked surcharge config
Mar 18, 2025
383ea30
Merge main into multi-surcharge-configs
Mar 18, 2025
90e67c6
Add endpoint to update active_surcharge_algorithm_id in business_prof…
Mar 19, 2025
2fcbcfa
Address clippy warnings
Mar 19, 2025
60f4de6
resolve errors occurring due to v2 migrations
Mar 20, 2025
536ace3
Resolve comments
Mar 24, 2025
43281ad
Merge branch 'main' into multi-surcharge-configs
Mar 24, 2025
054c812
chore: run formatter
hyperswitch-bot[bot] Mar 24, 2025
1b7c209
Addressed comments and failing clippy for v2
Mar 24, 2025
12ca4e9
Merge branch 'multi-surcharge-configs' of https://github.com/juspay/h…
Mar 24, 2025
8b7809b
Fix OpenAPI schema
Mar 24, 2025
9e0f344
Merge branch 'main' into multi-surcharge-configs
spritianeja03 Mar 25, 2025
733f4d3
Fix clippy_v2 warnings
Mar 25, 2025
e4d7487
Merge branch 'main' into multi-surcharge-configs
spritianeja03 Mar 25, 2025
f88487a
Address comments
Mar 28, 2025
1aacceb
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Mar 28, 2025
cff5be1
Fix v2 errors
Mar 28, 2025
e3deaf1
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Apr 4, 2025
2d93935
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Apr 4, 2025
ccbff83
resolve comments
Apr 8, 2025
abe36df
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Apr 8, 2025
90518b4
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Apr 11, 2025
39f7901
rename endpoints
Apr 16, 2025
8b1d4a2
Merge branch 'main' of https://github.com/juspay/hyperswitch into mul…
Apr 17, 2025
63bb09b
resolve errors
Apr 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -19728,6 +19728,14 @@
"description": "Indicates if clear pan retries is enabled or not.",
"nullable": true
},
"active_surcharge_algorithm_id": {
"allOf": [
{
"$ref": "#/components/schemas/SurchargeRoutingId"
}
],
"nullable": true
},
"is_debit_routing_enabled": {
"type": "boolean",
"description": "Indicates if debit routing is enabled or not",
Expand Down Expand Up @@ -22601,6 +22609,10 @@
"propertyName": "type"
}
},
"SurchargeRoutingId": {
"type": "string",
"description": "A wrapper type for `RoutingId` that can be used for surcharge routing ids"
},
"SwishQrData": {
"type": "object"
},
Expand Down
20 changes: 20 additions & 0 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -24118,6 +24118,14 @@
"description": "Indicates if 3ds challenge is forced",
"nullable": true
},
"active_surcharge_algorithm_id": {
"allOf": [
{
"$ref": "#/components/schemas/SurchargeRoutingId"
}
],
"nullable": true
},
"is_debit_routing_enabled": {
"type": "boolean",
"description": "Indicates if debit routing is enabled or not",
Expand Down Expand Up @@ -24395,6 +24403,14 @@
"type": "boolean",
"description": "Indicates if 3ds challenge is forced"
},
"active_surcharge_algorithm_id": {
"allOf": [
{
"$ref": "#/components/schemas/SurchargeRoutingId"
}
],
"nullable": true
},
"is_debit_routing_enabled": {
"type": "boolean",
"description": "Indicates if debit routing is enabled or not",
Expand Down Expand Up @@ -27053,6 +27069,10 @@
"propertyName": "type"
}
},
"SurchargeRoutingId": {
"type": "string",
"description": "A wrapper type for `RoutingId` that can be used for surcharge routing ids"
},
"SwishQrData": {
"type": "object"
},
Expand Down
16 changes: 16 additions & 0 deletions crates/api_models/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,6 +1986,10 @@ pub struct ProfileCreate {
/// Indicates if 3ds challenge is forced
pub force_3ds_challenge: Option<bool>,

/// Active surcharge config id
#[schema(value_type = Option<SurchargeRoutingId>)]
pub active_surcharge_algorithm_id: Option<id_type::SurchargeRoutingId>,

/// Indicates if debit routing is enabled or not
#[schema(value_type = Option<bool>)]
pub is_debit_routing_enabled: Option<bool>,
Expand Down Expand Up @@ -2118,6 +2122,10 @@ pub struct ProfileCreate {
///Indicates if clear pan retries is enabled or not.
pub is_clear_pan_retries_enabled: Option<bool>,

/// Active surcharge algorithm id
#[schema(value_type = Option<SurchargeRoutingId>)]
pub active_surcharge_algorithm_id: Option<id_type::SurchargeRoutingId>,

/// Indicates if debit routing is enabled or not
#[schema(value_type = Option<bool>)]
pub is_debit_routing_enabled: Option<bool>,
Expand Down Expand Up @@ -2277,6 +2285,10 @@ pub struct ProfileResponse {
/// Indicates if 3ds challenge is forced
pub force_3ds_challenge: bool,

/// Active surcharge config id
#[schema(value_type = Option<SurchargeRoutingId>)]
pub active_surcharge_algorithm_id: Option<id_type::SurchargeRoutingId>,

/// Indicates if debit routing is enabled or not
#[schema(value_type = Option<bool>)]
pub is_debit_routing_enabled: Option<bool>,
Expand Down Expand Up @@ -2565,6 +2577,10 @@ pub struct ProfileUpdate {
/// Indicates if 3ds challenge is forced
pub force_3ds_challenge: Option<bool>,

/// Active surcharge config id
#[schema(value_type = Option<SurchargeRoutingId>)]
pub active_surcharge_algorithm_id: Option<id_type::SurchargeRoutingId>,

/// Indicates if debit routing is enabled or not
#[schema(value_type = Option<bool>)]
pub is_debit_routing_enabled: Option<bool>,
Expand Down
16 changes: 14 additions & 2 deletions crates/api_models/src/events/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::routing::{
ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord,
RoutingKind, RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitWrapper,
SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingQuery,
ToggleDynamicRoutingWrapper,
SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, SurchargeRetrieveLinkQuery,
SurchargeRetrieveQuery, ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper,
};

impl ApiEventMetric for RoutingKind {
Expand Down Expand Up @@ -57,6 +57,12 @@ impl ApiEventMetric for RoutingRetrieveQuery {
}
}

impl ApiEventMetric for SurchargeRetrieveQuery {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for RoutingConfigRequest {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
Expand All @@ -69,6 +75,12 @@ impl ApiEventMetric for RoutingRetrieveLinkQuery {
}
}

impl ApiEventMetric for SurchargeRetrieveLinkQuery {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
}
}

impl ApiEventMetric for RoutingLinkWrapper {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Routing)
Expand Down
11 changes: 11 additions & 0 deletions crates/api_models/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,22 @@ pub struct RoutingRetrieveQuery {
pub offset: Option<u8>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SurchargeRetrieveQuery {
pub limit: Option<i64>,
pub offset: Option<i64>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct RoutingRetrieveLinkQuery {
pub profile_id: Option<common_utils::id_type::ProfileId>,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SurchargeRetrieveLinkQuery {
pub profile_id: common_utils::id_type::ProfileId,
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct RoutingRetrieveLinkQueryWrapper {
pub routing_query: RoutingRetrieveQuery,
Expand Down
49 changes: 49 additions & 0 deletions crates/api_models/src/surcharge_decision_configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,32 @@ impl events::ApiEventMetric for SurchargeDecisionManagerRecord {
Some(events::ApiEventsType::Routing)
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SurchargeRecord {
pub name: String,
pub algorithm_id: common_utils::id_type::SurchargeRoutingId,
pub merchant_surcharge_configs: MerchantSurchargeConfigs,
pub algorithm: Program<SurchargeDecisionConfigs>,
pub description: Option<String>,
#[serde(with = "common_utils::custom_serde::iso8601")]
pub created_at: time::PrimitiveDateTime,
#[serde(with = "common_utils::custom_serde::iso8601")]
pub modified_at: time::PrimitiveDateTime,
}

impl events::ApiEventMetric for SurchargeRecord {
fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
Some(events::ApiEventsType::Routing)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SurchargeDecisionConfigReq {
pub name: Option<String>,
pub merchant_surcharge_configs: MerchantSurchargeConfigs,
pub algorithm: Option<Program<SurchargeDecisionConfigs>>,
pub description: Option<String>,
}

impl events::ApiEventMetric for SurchargeDecisionConfigReq {
Expand All @@ -73,9 +93,38 @@ impl events::ApiEventMetric for SurchargeDecisionConfigReq {
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SurchargeDecisionManagerReq {
pub name: String,
pub merchant_surcharge_configs: MerchantSurchargeConfigs,
pub algorithm: Program<SurchargeDecisionConfigs>,
pub description: Option<String>,
}

impl events::ApiEventMetric for SurchargeDecisionManagerReq {
fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
Some(events::ApiEventsType::Routing)
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SurchargeDecisionManagerConfig {
pub merchant_surcharge_configs: MerchantSurchargeConfigs,
pub algorithm: Program<SurchargeDecisionConfigs>,
}

impl events::ApiEventMetric for SurchargeDecisionManagerConfig {
fn get_api_event_type(&self) -> Option<events::ApiEventsType> {
Some(events::ApiEventsType::Routing)
}
}
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
pub struct MerchantSurchargeConfigs {
pub show_surcharge_breakup_screen: Option<bool>,
}

pub type SurchargeDecisionManagerResponse = SurchargeDecisionManagerRecord;

pub type SurchargeConfigResponse = SurchargeRecord;
23 changes: 23 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7017,6 +7017,29 @@ pub enum TransactionType {
Payout,
}

#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
strum::Display,
strum::EnumString,
serde::Deserialize,
serde::Serialize,
ToSchema,
Default,
)]
#[router_derive::diesel_enum(storage_type = "text")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum AlgorithmType {
#[default]
Routing,
Surcharge,
ThreeDs,
}

#[derive(
Clone,
Copy,
Expand Down
8 changes: 8 additions & 0 deletions crates/common_utils/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,11 @@ pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY_IN_SECS: i32 = 3600;

/// SOAP 1.1 Envelope Namespace
pub const SOAP_ENV_NAMESPACE: &str = "http://schemas.xmlsoap.org/soap/envelope/";

/// Algorithm type for Routing
pub const ALGORITHM_TYPE_ROUTING: common_enums::AlgorithmType =
common_enums::AlgorithmType::Routing;

/// Algorithm Type for Surcharge
pub const ALGORITHM_TYPE_SURCHARGE: common_enums::AlgorithmType =
common_enums::AlgorithmType::Surcharge;
2 changes: 1 addition & 1 deletion crates/common_utils/src/id_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub use self::{
profile::ProfileId,
refunds::RefundReferenceId,
relay::RelayId,
routing::RoutingId,
routing::{RoutingId, SurchargeRoutingId},
tenant::TenantId,
};
use crate::{fp_utils::when, generate_id_with_default_len};
Expand Down
74 changes: 73 additions & 1 deletion crates/common_utils/src/id_type/routing.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::ops::Deref;

use crate::events::ApiEventMetric;

crate::id_type!(
RoutingId,
" A type for routing_id that can be used for routing ids"
Expand All @@ -14,8 +18,76 @@ crate::impl_serializable_secret_id_type!(RoutingId);
crate::impl_queryable_id_type!(RoutingId);
crate::impl_to_sql_from_sql_id_type!(RoutingId);

impl crate::events::ApiEventMetric for RoutingId {
impl ApiEventMetric for RoutingId {
fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
Some(crate::events::ApiEventsType::Routing)
}
}

#[derive(
Clone,
Hash,
Debug,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
diesel::expression::AsExpression,
utoipa::ToSchema,
)]
#[diesel(sql_type = diesel::sql_types::Text,)]
#[schema(value_type = String)]
/// A wrapper type for `RoutingId` that can be used for surcharge routing ids
pub struct SurchargeRoutingId(RoutingId);

impl SurchargeRoutingId {
/// Creates a new `SurchargeRoutingId` from a `RoutingId`
pub fn new(routing_id: RoutingId) -> Self {
Self(routing_id)
}

/// Returns the inner `RoutingId`
pub fn inner(&self) -> &RoutingId {
&self.0
}
}

impl Deref for SurchargeRoutingId {
type Target = RoutingId;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl ApiEventMetric for SurchargeRoutingId {
fn get_api_event_type(&self) -> Option<crate::events::ApiEventsType> {
Some(crate::events::ApiEventsType::Routing)
}
}
crate::impl_serializable_secret_id_type!(SurchargeRoutingId);
crate::impl_queryable_id_type!(SurchargeRoutingId);

impl<DB> diesel::serialize::ToSql<diesel::sql_types::Text, DB> for SurchargeRoutingId
where
DB: diesel::backend::Backend,
RoutingId: diesel::serialize::ToSql<diesel::sql_types::Text, DB>,
{
fn to_sql<'b>(
&'b self,
out: &mut diesel::serialize::Output<'b, '_, DB>,
) -> diesel::serialize::Result {
self.0.to_sql(out)
}
}

impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Text, DB> for SurchargeRoutingId
where
DB: diesel::backend::Backend,
RoutingId: diesel::deserialize::FromSql<diesel::sql_types::Text, DB>,
{
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
let val = RoutingId::from_sql(value)?;
Ok(Self(val))
}
}
Loading
Loading