feat(router): Add revenue recovery retry algorithm type and data columns to business_profile table (#7772)

Co-authored-by: Aniket Burman <aniket.burman@Aniket-Burman-JDXHW2PH34.local>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com>
This commit is contained in:
Aniket Burman
2025-04-24 15:02:01 +05:30
committed by GitHub
parent c3ac0d0bd6
commit c633b336fb
8 changed files with 185 additions and 2 deletions

View File

@ -212,6 +212,29 @@ pub enum CardDiscovery {
ClickToPay,
}
#[derive(
Clone,
Copy,
Debug,
Hash,
Eq,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
strum::EnumIter,
ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RevenueRecoveryAlgorithmType {
Monitoring,
Smart,
Cascading,
}
/// Pass this parameter to force 3DS or non 3DS auth for this payment. Some connectors will still force 3DS auth even in case of passing 'no_three_ds' here and vice versa. Default value is 'no_three_ds' if not set
#[derive(
Clone,

View File

@ -364,6 +364,8 @@ pub struct Profile {
pub three_ds_decision_manager_config: Option<common_types::payments::DecisionManagerRecord>,
pub should_collect_cvv_during_payment:
Option<primitive_wrappers::ShouldCollectCvvDuringPayment>,
pub revenue_recovery_retry_algorithm_type: Option<common_enums::RevenueRecoveryAlgorithmType>,
pub revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
}
impl Profile {
@ -432,6 +434,8 @@ pub struct ProfileNew {
pub should_collect_cvv_during_payment:
Option<primitive_wrappers::ShouldCollectCvvDuringPayment>,
pub id: common_utils::id_type::ProfileId,
pub revenue_recovery_retry_algorithm_type: Option<common_enums::RevenueRecoveryAlgorithmType>,
pub revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
}
#[cfg(feature = "v2")]
@ -484,6 +488,8 @@ pub struct ProfileUpdateInternal {
pub three_ds_decision_manager_config: Option<common_types::payments::DecisionManagerRecord>,
pub should_collect_cvv_during_payment:
Option<primitive_wrappers::ShouldCollectCvvDuringPayment>,
pub revenue_recovery_retry_algorithm_type: Option<common_enums::RevenueRecoveryAlgorithmType>,
pub revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
}
#[cfg(feature = "v2")]
@ -533,6 +539,8 @@ impl ProfileUpdateInternal {
is_clear_pan_retries_enabled,
is_debit_routing_enabled,
merchant_business_country,
revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data,
} = self;
Profile {
id: source.id,
@ -613,6 +621,10 @@ impl ProfileUpdateInternal {
is_debit_routing_enabled,
merchant_business_country: merchant_business_country
.or(source.merchant_business_country),
revenue_recovery_retry_algorithm_type: revenue_recovery_retry_algorithm_type
.or(source.revenue_recovery_retry_algorithm_type),
revenue_recovery_retry_algorithm_data: revenue_recovery_retry_algorithm_data
.or(source.revenue_recovery_retry_algorithm_data),
}
}
}
@ -738,3 +750,11 @@ pub struct BusinessGenericLinkConfig {
}
common_utils::impl_to_sql_from_sql_json!(BusinessPayoutLinkConfig);
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, diesel::AsExpression)]
#[diesel(sql_type = diesel::sql_types::Jsonb)]
pub struct RevenueRecoveryAlgorithmData {
pub monitoring_configured_timestamp: time::PrimitiveDateTime,
}
common_utils::impl_to_sql_from_sql_json!(RevenueRecoveryAlgorithmData);

View File

@ -20,8 +20,8 @@ pub mod diesel_exports {
DbRefundStatus as RefundStatus, DbRefundType as RefundType, DbRelayStatus as RelayStatus,
DbRelayType as RelayType,
DbRequestIncrementalAuthorization as RequestIncrementalAuthorization,
DbRoleScope as RoleScope, DbRoutingAlgorithmKind as RoutingAlgorithmKind,
DbScaExemptionType as ScaExemptionType,
DbRevenueRecoveryAlgorithmType as RevenueRecoveryAlgorithmType, DbRoleScope as RoleScope,
DbRoutingAlgorithmKind as RoutingAlgorithmKind, DbScaExemptionType as ScaExemptionType,
DbSuccessBasedRoutingConclusiveState as SuccessBasedRoutingConclusiveState,
DbTotpStatus as TotpStatus, DbTransactionType as TransactionType,
DbUserRoleVersion as UserRoleVersion, DbUserStatus as UserStatus,

View File

@ -232,6 +232,8 @@ diesel::table! {
default_fallback_routing -> Nullable<Jsonb>,
three_ds_decision_manager_config -> Nullable<Jsonb>,
should_collect_cvv_during_payment -> Nullable<Bool>,
revenue_recovery_retry_algorithm_type -> Nullable<RevenueRecoveryAlgorithmType>,
revenue_recovery_retry_algorithm_data -> Nullable<Jsonb>,
}
}

View File

@ -8,6 +8,8 @@ use common_utils::{
pii, type_name,
types::keymanager,
};
#[cfg(feature = "v2")]
use diesel_models::business_profile::RevenueRecoveryAlgorithmData;
use diesel_models::business_profile::{
AuthenticationConnectorDetails, BusinessPaymentLinkConfig, BusinessPayoutLinkConfig,
CardTestingGuardConfig, ProfileUpdateInternal, WebhookDetails,
@ -901,6 +903,8 @@ pub struct Profile {
pub is_clear_pan_retries_enabled: bool,
pub is_debit_routing_enabled: bool,
pub merchant_business_country: Option<api_enums::CountryAlpha2>,
pub revenue_recovery_retry_algorithm_type: Option<common_enums::RevenueRecoveryAlgorithmType>,
pub revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
}
#[cfg(feature = "v2")]
@ -951,6 +955,8 @@ pub struct ProfileSetter {
pub is_clear_pan_retries_enabled: bool,
pub is_debit_routing_enabled: bool,
pub merchant_business_country: Option<api_enums::CountryAlpha2>,
pub revenue_recovery_retry_algorithm_type: Option<common_enums::RevenueRecoveryAlgorithmType>,
pub revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
}
#[cfg(feature = "v2")]
@ -1006,6 +1012,8 @@ impl From<ProfileSetter> for Profile {
is_clear_pan_retries_enabled: value.is_clear_pan_retries_enabled,
is_debit_routing_enabled: value.is_debit_routing_enabled,
merchant_business_country: value.merchant_business_country,
revenue_recovery_retry_algorithm_type: value.revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data: value.revenue_recovery_retry_algorithm_data,
}
}
}
@ -1095,6 +1103,10 @@ pub enum ProfileUpdate {
CardTestingSecretKeyUpdate {
card_testing_secret_key: OptionalEncryptableName,
},
RevenueRecoveryAlgorithmUpdate {
revenue_recovery_retry_algorithm_type: common_enums::RevenueRecoveryAlgorithmType,
revenue_recovery_retry_algorithm_data: Option<RevenueRecoveryAlgorithmData>,
},
}
#[cfg(feature = "v2")]
@ -1181,6 +1193,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled,
merchant_business_country,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
}
}
ProfileUpdate::RoutingAlgorithmUpdate {
@ -1230,6 +1244,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::ExtendedCardInfoUpdate {
is_extended_card_info_enabled,
@ -1277,6 +1293,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::ConnectorAgnosticMitUpdate {
is_connector_agnostic_mit_enabled,
@ -1324,6 +1342,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::DefaultRoutingFallbackUpdate {
default_fallback_routing,
@ -1371,6 +1391,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::NetworkTokenizationUpdate {
is_network_tokenization_enabled,
@ -1418,6 +1440,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::CollectCvvDuringPaymentUpdate {
should_collect_cvv_during_payment,
@ -1465,6 +1489,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::DecisionManagerRecordUpdate {
three_ds_decision_manager_config,
@ -1512,6 +1538,8 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::CardTestingSecretKeyUpdate {
card_testing_secret_key,
@ -1559,6 +1587,58 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
},
ProfileUpdate::RevenueRecoveryAlgorithmUpdate {
revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data,
} => Self {
profile_name: None,
modified_at: now,
return_url: None,
enable_payment_response_hash: None,
payment_response_hash_key: None,
redirect_to_merchant_with_http_post: None,
webhook_details: None,
metadata: None,
is_recon_enabled: None,
applepay_verified_domains: None,
payment_link_config: None,
session_expiry: None,
authentication_connector_details: None,
payout_link_config: None,
is_extended_card_info_enabled: None,
extended_card_info_config: None,
is_connector_agnostic_mit_enabled: None,
use_billing_as_payment_method_billing: None,
collect_shipping_details_from_wallet_connector: None,
collect_billing_details_from_wallet_connector: None,
outgoing_webhook_custom_http_headers: None,
always_collect_billing_details_from_wallet_connector: None,
always_collect_shipping_details_from_wallet_connector: None,
routing_algorithm_id: None,
payout_routing_algorithm_id: None,
order_fulfillment_time: None,
order_fulfillment_time_origin: None,
frm_routing_algorithm_id: None,
default_fallback_routing: None,
should_collect_cvv_during_payment: None,
tax_connector_id: None,
is_tax_connector_enabled: None,
is_network_tokenization_enabled: None,
is_auto_retries_enabled: None,
max_auto_retries_enabled: None,
is_click_to_pay_enabled: None,
authentication_product_ids: None,
three_ds_decision_manager_config: None,
card_testing_guard_config: None,
card_testing_secret_key: None,
is_clear_pan_retries_enabled: None,
is_debit_routing_enabled: false,
merchant_business_country: None,
revenue_recovery_retry_algorithm_type: Some(revenue_recovery_retry_algorithm_type),
revenue_recovery_retry_algorithm_data,
},
}
}
@ -1628,6 +1708,8 @@ impl super::behaviour::Conversion for Profile {
force_3ds_challenge: None,
is_debit_routing_enabled: self.is_debit_routing_enabled,
merchant_business_country: self.merchant_business_country,
revenue_recovery_retry_algorithm_type: self.revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data: self.revenue_recovery_retry_algorithm_data,
})
}
@ -1717,6 +1799,8 @@ impl super::behaviour::Conversion for Profile {
is_clear_pan_retries_enabled: item.is_clear_pan_retries_enabled,
is_debit_routing_enabled: item.is_debit_routing_enabled,
merchant_business_country: item.merchant_business_country,
revenue_recovery_retry_algorithm_type: item.revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data: item.revenue_recovery_retry_algorithm_data,
})
}
.await
@ -1780,6 +1864,8 @@ impl super::behaviour::Conversion for Profile {
is_clear_pan_retries_enabled: Some(self.is_clear_pan_retries_enabled),
is_debit_routing_enabled: self.is_debit_routing_enabled,
merchant_business_country: self.merchant_business_country,
revenue_recovery_retry_algorithm_type: self.revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data: self.revenue_recovery_retry_algorithm_data,
})
}
}

View File

@ -2913,6 +2913,17 @@ pub async fn create_connector(
.validate_and_get_business_profile(&merchant_account, store, key_manager_state, &key_store)
.await?;
#[cfg(feature = "v2")]
if req.connector_type == common_enums::ConnectorType::BillingProcessor {
update_revenue_recovery_algorithm_under_profile(
business_profile.clone(),
store,
key_manager_state,
&key_store,
common_enums::RevenueRecoveryAlgorithmType::Monitoring,
)
.await?;
}
core_utils::validate_profile_id_from_auth_layer(auth_profile_id, &business_profile)?;
let pm_auth_config_validation = PMAuthConfigValidation {
@ -3941,6 +3952,8 @@ impl ProfileCreateBridge for api::ProfileCreate {
is_clear_pan_retries_enabled: self.is_clear_pan_retries_enabled.unwrap_or_default(),
is_debit_routing_enabled: self.is_debit_routing_enabled.unwrap_or_default(),
merchant_business_country: self.merchant_business_country,
revenue_recovery_retry_algorithm_type: None,
revenue_recovery_retry_algorithm_data: None,
}))
}
}
@ -4546,6 +4559,33 @@ impl ProfileWrapper {
Ok(())
}
}
#[cfg(feature = "v2")]
pub async fn update_revenue_recovery_algorithm_under_profile(
profile: domain::Profile,
db: &dyn StorageInterface,
key_manager_state: &KeyManagerState,
merchant_key_store: &domain::MerchantKeyStore,
revenue_recovery_retry_algorithm_type: common_enums::RevenueRecoveryAlgorithmType,
) -> RouterResult<()> {
let recovery_algorithm_data = diesel_models::business_profile::RevenueRecoveryAlgorithmData {
monitoring_configured_timestamp: date_time::now(),
};
let profile_update = domain::ProfileUpdate::RevenueRecoveryAlgorithmUpdate {
revenue_recovery_retry_algorithm_type,
revenue_recovery_retry_algorithm_data: Some(recovery_algorithm_data),
};
db.update_profile_by_profile_id(
key_manager_state,
merchant_key_store,
profile,
profile_update,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to update revenue recovery retry algorithm in business profile")?;
Ok(())
}
pub async fn extended_card_info_toggle(
state: SessionState,

View File

@ -0,0 +1,6 @@
-- This file should undo anything in `up.sql`
ALTER TABLE business_profile
DROP COLUMN IF EXISTS revenue_recovery_retry_algorithm_type,
DROP COLUMN IF EXISTS revenue_recovery_retry_algorithm_data;
DROP TYPE IF EXISTS "RevenueRecoveryAlgorithmType";

View File

@ -0,0 +1,6 @@
-- Your SQL goes here
CREATE TYPE "RevenueRecoveryAlgorithmType" AS ENUM ('monitoring', 'smart', 'cascading');
ALTER TABLE business_profile
ADD COLUMN IF NOT EXISTS revenue_recovery_retry_algorithm_type "RevenueRecoveryAlgorithmType",
ADD COLUMN IF NOT EXISTS revenue_recovery_retry_algorithm_data JSONB;