feat(router): Add Smart Routing to route payments efficiently (#2665)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shashank_attarde <shashank.attarde@juspay.in>
Co-authored-by: Aprabhat19 <amishaprabhat@gmail.com>
Co-authored-by: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com>
This commit is contained in:
Prajjwal Kumar
2023-11-03 18:37:31 +05:30
committed by GitHub
parent 6c5de9cee4
commit 9b618d2447
96 changed files with 15376 additions and 233 deletions

View File

@ -1,6 +1,8 @@
use std::str::FromStr;
use api_models::{
admin::{self as admin_types},
enums as api_enums,
enums as api_enums, routing as routing_types,
};
use common_utils::{
crypto::{generate_cryptographically_secure_random_string, OptionalSecretValue},
@ -18,6 +20,7 @@ use crate::{
core::{
errors::{self, RouterResponse, RouterResult, StorageErrorExt},
payments::helpers,
routing::helpers as routing_helpers,
utils as core_utils,
},
db::StorageInterface,
@ -89,7 +92,7 @@ pub async fn create_merchant_account(
.transpose()?;
if let Some(ref routing_algorithm) = req.routing_algorithm {
let _: api::RoutingAlgorithm = routing_algorithm
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {
@ -178,7 +181,10 @@ pub async fn create_merchant_account(
.await?,
return_url: req.return_url.map(|a| a.to_string()),
webhook_details,
routing_algorithm: req.routing_algorithm,
routing_algorithm: Some(serde_json::json!({
"algorithm_id": null,
"timestamp": 0
})),
sub_merchants_enabled: req.sub_merchants_enabled,
parent_merchant_id,
enable_payment_response_hash,
@ -470,7 +476,7 @@ pub async fn merchant_account_update(
}
if let Some(ref routing_algorithm) = req.routing_algorithm {
let _: api::RoutingAlgorithm = routing_algorithm
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {
@ -756,6 +762,9 @@ pub async fn create_payment_connector(
)
.await?;
let routable_connector =
api_enums::RoutableConnectors::from_str(&req.connector_name.to_string()).ok();
let business_profile = state
.store
.find_business_profile_by_profile_id(&profile_id)
@ -828,6 +837,37 @@ pub async fn create_payment_connector(
let frm_configs = get_frm_config_as_secret(req.frm_configs);
// The purpose of this merchant account update is just to update the
// merchant account `modified_at` field for KGraph cache invalidation
let merchant_account_update = storage::MerchantAccountUpdate::Update {
merchant_name: None,
merchant_details: None,
return_url: None,
webhook_details: None,
sub_merchants_enabled: None,
parent_merchant_id: None,
enable_payment_response_hash: None,
locker_id: None,
payment_response_hash_key: None,
primary_business_details: None,
metadata: None,
publishable_key: None,
redirect_to_merchant_with_http_post: None,
routing_algorithm: None,
intent_fulfillment_time: None,
frm_routing_algorithm: None,
payout_routing_algorithm: None,
default_profile: None,
payment_link_config: None,
};
state
.store
.update_specific_fields_in_merchant(merchant_id, merchant_account_update, &key_store)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error updating the merchant account when creating payment connector")?;
let merchant_connector_account = domain::MerchantConnectorAccount {
merchant_id: merchant_id.to_string(),
connector_type: req.connector_type,
@ -852,7 +892,7 @@ pub async fn create_payment_connector(
connector_label: Some(connector_label),
business_country: req.business_country,
business_label: req.business_label.clone(),
business_sub_label: req.business_sub_label,
business_sub_label: req.business_sub_label.clone(),
created_at: common_utils::date_time::now(),
modified_at: common_utils::date_time::now(),
id: None,
@ -873,6 +913,9 @@ pub async fn create_payment_connector(
pm_auth_config: req.pm_auth_config.clone(),
};
let mut default_routing_config =
routing_helpers::get_merchant_default_config(&*state.store, merchant_id).await?;
let mca = state
.store
.insert_merchant_connector_account(merchant_connector_account, &key_store)
@ -884,6 +927,28 @@ pub async fn create_payment_connector(
},
)?;
if let Some(routable_connector_val) = routable_connector {
let choice = routing_types::RoutableConnectorChoice {
#[cfg(feature = "backwards_compatibility")]
choice_kind: routing_types::RoutableChoiceKind::FullStruct,
connector: routable_connector_val,
#[cfg(feature = "connector_choice_mca_id")]
merchant_connector_id: Some(mca.merchant_connector_id.clone()),
#[cfg(not(feature = "connector_choice_mca_id"))]
sub_label: req.business_sub_label,
};
if !default_routing_config.contains(&choice) {
default_routing_config.push(choice);
routing_helpers::update_merchant_default_config(
&*state.store,
merchant_id,
default_routing_config,
)
.await?;
}
}
metrics::MCA_CREATE.add(
&metrics::CONTEXT,
1,
@ -1248,7 +1313,7 @@ pub async fn create_business_profile(
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
if let Some(ref routing_algorithm) = request.routing_algorithm {
let _: api::RoutingAlgorithm = routing_algorithm
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {
@ -1360,7 +1425,7 @@ pub async fn update_business_profile(
.transpose()?;
if let Some(ref routing_algorithm) = request.routing_algorithm {
let _: api::RoutingAlgorithm = routing_algorithm
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {