mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(payouts): extend routing capabilities to payout operation (#3531)
Co-authored-by: Kashif <mohammed.kashif@juspay.in>
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
pub mod helpers;
|
||||
pub mod transformers;
|
||||
|
||||
use api_models::routing::{self as routing_types, RoutingAlgorithmId};
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery};
|
||||
use api_models::{
|
||||
enums,
|
||||
routing::{self as routing_types, RoutingAlgorithmId},
|
||||
};
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
use common_utils::ext_traits::{Encode, StringExt};
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
@ -13,6 +16,9 @@ use diesel_models::routing_algorithm::RoutingAlgorithm;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use super::payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
use super::payouts;
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use crate::types::transformers::{ForeignInto, ForeignTryInto};
|
||||
use crate::{
|
||||
@ -30,18 +36,30 @@ use crate::{core::errors, services::api as service_api, types::storage};
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
use crate::{errors, services::api as service_api};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TransactionData<'a, F>
|
||||
where
|
||||
F: Clone,
|
||||
{
|
||||
Payment(&'a payments::PaymentData<F>),
|
||||
#[cfg(feature = "payouts")]
|
||||
Payout(&'a payouts::PayoutData),
|
||||
}
|
||||
|
||||
pub async fn retrieve_merchant_routing_dictionary(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveQuery,
|
||||
#[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingKind> {
|
||||
metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE.add(&metrics::CONTEXT, 1, &[]);
|
||||
#[cfg(feature = "business_profile_routing")]
|
||||
{
|
||||
let routing_metadata = state
|
||||
.store
|
||||
.list_routing_algorithm_metadata_by_merchant_id(
|
||||
.list_routing_algorithm_metadata_by_merchant_id_transaction_type(
|
||||
&merchant_account.merchant_id,
|
||||
transaction_type,
|
||||
i64::from(query_params.limit.unwrap_or_default()),
|
||||
i64::from(query_params.offset.unwrap_or_default()),
|
||||
)
|
||||
@ -80,6 +98,7 @@ pub async fn create_routing_config(
|
||||
merchant_account: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
request: routing_types::RoutingConfigRequest,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
@ -148,6 +167,7 @@ pub async fn create_routing_config(
|
||||
algorithm_data: serde_json::json!(algorithm),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: transaction_type.to_owned(),
|
||||
};
|
||||
let record = db
|
||||
.insert_routing_algorithm(algo)
|
||||
@ -197,6 +217,7 @@ pub async fn create_routing_config(
|
||||
description: description.clone(),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: Some(*transaction_type),
|
||||
};
|
||||
merchant_dictionary.records.push(new_record.clone());
|
||||
|
||||
@ -233,6 +254,7 @@ pub async fn link_routing_config(
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore,
|
||||
algorithm_id: String,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
@ -257,14 +279,25 @@ pub async fn link_routing_config(
|
||||
id: routing_algorithm.profile_id.clone(),
|
||||
})?;
|
||||
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = business_profile
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
utils::when(routing_algorithm.algorithm_for != *transaction_type, || {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: format!(
|
||||
"Cannot use {}'s routing algorithm for {} operation",
|
||||
routing_algorithm.algorithm_for, transaction_type
|
||||
),
|
||||
})
|
||||
})?;
|
||||
|
||||
utils::when(
|
||||
routing_ref.algorithm_id == Some(algorithm_id.clone()),
|
||||
@ -277,8 +310,13 @@ pub async fn link_routing_config(
|
||||
)?;
|
||||
|
||||
routing_ref.update_algorithm_id(algorithm_id);
|
||||
helpers::update_business_profile_active_algorithm_ref(db, business_profile, routing_ref)
|
||||
.await?;
|
||||
helpers::update_business_profile_active_algorithm_ref(
|
||||
db,
|
||||
business_profile,
|
||||
routing_ref,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
@ -288,14 +326,16 @@ pub async fn link_routing_config(
|
||||
|
||||
#[cfg(not(feature = "business_profile_routing"))]
|
||||
{
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = merchant_account
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(),
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
|
||||
utils::when(
|
||||
routing_ref.algorithm_id == Some(algorithm_id.clone()),
|
||||
@ -402,6 +442,9 @@ pub async fn retrieve_routing_config(
|
||||
algorithm,
|
||||
created_at: record.created_at,
|
||||
modified_at: record.modified_at,
|
||||
algorithm_for: record
|
||||
.algorithm_for
|
||||
.unwrap_or(enums::TransactionType::Payment),
|
||||
};
|
||||
|
||||
metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
@ -413,6 +456,7 @@ pub async fn unlink_routing_config(
|
||||
merchant_account: domain::MerchantAccount,
|
||||
#[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore,
|
||||
#[cfg(feature = "business_profile_routing")] request: routing_types::RoutingConfigRequest,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_UNLINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
@ -433,16 +477,20 @@ pub async fn unlink_routing_config(
|
||||
.await?;
|
||||
match business_profile {
|
||||
Some(business_profile) => {
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = business_profile
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"unable to deserialize routing algorithm ref from merchant account",
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => {
|
||||
business_profile.payout_routing_algorithm.clone()
|
||||
}
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"unable to deserialize routing algorithm ref from merchant account",
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
|
||||
@ -468,6 +516,7 @@ pub async fn unlink_routing_config(
|
||||
db,
|
||||
business_profile,
|
||||
routing_algorithm,
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -496,14 +545,16 @@ pub async fn unlink_routing_config(
|
||||
let mut merchant_dictionary =
|
||||
helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?;
|
||||
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = merchant_account
|
||||
.routing_algorithm
|
||||
.clone()
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type {
|
||||
enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(),
|
||||
#[cfg(feature = "payouts")]
|
||||
enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(),
|
||||
}
|
||||
.map(|val| val.parse_value("RoutingAlgorithmRef"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||
.unwrap_or_default();
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
|
||||
utils::when(routing_algo_ref.algorithm_id.is_none(), || {
|
||||
@ -593,11 +644,13 @@ pub async fn update_default_routing_config(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
updated_config: Vec<routing_types::RoutableConnectorChoice>,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
metrics::ROUTING_UPDATE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
let default_config =
|
||||
helpers::get_merchant_default_config(db, &merchant_account.merchant_id).await?;
|
||||
helpers::get_merchant_default_config(db, &merchant_account.merchant_id, transaction_type)
|
||||
.await?;
|
||||
|
||||
utils::when(default_config.len() != updated_config.len(), || {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
@ -630,6 +683,7 @@ pub async fn update_default_routing_config(
|
||||
db,
|
||||
&merchant_account.merchant_id,
|
||||
updated_config.clone(),
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -640,11 +694,12 @@ pub async fn update_default_routing_config(
|
||||
pub async fn retrieve_default_routing_config(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
|
||||
helpers::get_merchant_default_config(db, &merchant_account.merchant_id)
|
||||
helpers::get_merchant_default_config(db, &merchant_account.merchant_id, transaction_type)
|
||||
.await
|
||||
.map(|conn_choice| {
|
||||
metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(
|
||||
@ -747,6 +802,9 @@ pub async fn retrieve_linked_routing_config(
|
||||
algorithm: the_algorithm,
|
||||
created_at: record.created_at,
|
||||
modified_at: record.modified_at,
|
||||
algorithm_for: record
|
||||
.algorithm_for
|
||||
.unwrap_or(enums::TransactionType::Payment),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@ -764,6 +822,7 @@ pub async fn retrieve_linked_routing_config(
|
||||
pub async fn retrieve_default_routing_config_for_profiles(
|
||||
state: AppState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<Vec<routing_types::ProfileDefaultRoutingConfig>> {
|
||||
metrics::ROUTING_RETRIEVE_CONFIG_FOR_PROFILE.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
@ -776,7 +835,7 @@ pub async fn retrieve_default_routing_config_for_profiles(
|
||||
|
||||
let retrieve_config_futures = all_profiles
|
||||
.iter()
|
||||
.map(|prof| helpers::get_merchant_default_config(db, &prof.profile_id))
|
||||
.map(|prof| helpers::get_merchant_default_config(db, &prof.profile_id, transaction_type))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let configs = futures::future::join_all(retrieve_config_futures)
|
||||
@ -804,6 +863,7 @@ pub async fn update_default_routing_config_for_profile(
|
||||
merchant_account: domain::MerchantAccount,
|
||||
updated_config: Vec<routing_types::RoutableConnectorChoice>,
|
||||
profile_id: String,
|
||||
transaction_type: &enums::TransactionType,
|
||||
) -> RouterResponse<routing_types::ProfileDefaultRoutingConfig> {
|
||||
metrics::ROUTING_UPDATE_CONFIG_FOR_PROFILE.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
@ -817,7 +877,8 @@ pub async fn update_default_routing_config_for_profile(
|
||||
.get_required_value("BusinessProfile")
|
||||
.change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })?;
|
||||
let default_config =
|
||||
helpers::get_merchant_default_config(db, &business_profile.profile_id).await?;
|
||||
helpers::get_merchant_default_config(db, &business_profile.profile_id, transaction_type)
|
||||
.await?;
|
||||
|
||||
utils::when(default_config.len() != updated_config.len(), || {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
@ -868,6 +929,7 @@ pub async fn update_default_routing_config_for_profile(
|
||||
db,
|
||||
&business_profile.profile_id,
|
||||
updated_config.clone(),
|
||||
transaction_type,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user