mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	Refactor(core): Inclusion of constraint graph for merchant Payment Method list (#4845)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: shashank_attarde <shashank.attarde@juspay.in>
This commit is contained in:
		| @ -79,6 +79,8 @@ pub enum MandateAcceptanceType { | ||||
| pub enum PaymentType { | ||||
|     SetupMandate, | ||||
|     NonMandate, | ||||
|     NewMandate, | ||||
|     UpdateMandate, | ||||
| } | ||||
|  | ||||
| #[derive( | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| pub mod cards; | ||||
| pub mod surcharge_decision_configs; | ||||
| pub mod transformers; | ||||
| pub mod utils; | ||||
| pub mod vault; | ||||
|  | ||||
| pub use api_models::enums::Connector; | ||||
|  | ||||
| @ -5,7 +5,7 @@ use std::{ | ||||
| }; | ||||
|  | ||||
| use api_models::{ | ||||
|     admin::{self, PaymentMethodsEnabled}, | ||||
|     admin::PaymentMethodsEnabled, | ||||
|     enums::{self as api_enums}, | ||||
|     payment_methods::{ | ||||
|         BankAccountTokenData, Card, CardDetailUpdate, CardDetailsPaymentMethod, CardNetworkTypes, | ||||
| @ -19,18 +19,22 @@ use api_models::{ | ||||
|     pm_auth::PaymentMethodAuthConfig, | ||||
|     surcharge_decision_configs as api_surcharge_decision_configs, | ||||
| }; | ||||
| use cgraph::ConstraintGraph; | ||||
| use common_enums::enums::MerchantStorageScheme; | ||||
| use common_utils::{ | ||||
|     consts, | ||||
|     ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, | ||||
|     generate_id, id_type, | ||||
| }; | ||||
| use diesel_models::{ | ||||
|     business_profile::BusinessProfile, encryption::Encryption, enums as storage_enums, | ||||
|     payment_method, | ||||
| }; | ||||
| use diesel_models::{business_profile::BusinessProfile, encryption::Encryption, payment_method}; | ||||
| use domain::CustomerUpdate; | ||||
| use error_stack::{report, ResultExt}; | ||||
| use euclid::{ | ||||
|     dssa::graph::{AnalysisContext, CgraphExt}, | ||||
|     frontend::dir, | ||||
| }; | ||||
| use hyperswitch_constraint_graph as cgraph; | ||||
| use kgraph_utils::transformers::IntoDirValue; | ||||
| use masking::Secret; | ||||
| use router_env::{instrument, tracing}; | ||||
| use strum::IntoEnumIterator; | ||||
| @ -45,7 +49,11 @@ use crate::{ | ||||
|     configs::settings, | ||||
|     core::{ | ||||
|         errors::{self, StorageErrorExt}, | ||||
|         payment_methods::{transformers as payment_methods, vault}, | ||||
|         payment_methods::{ | ||||
|             transformers as payment_methods, | ||||
|             utils::{get_merchant_pm_filter_graph, make_pm_graph, refresh_pm_filters_cache}, | ||||
|             vault, | ||||
|         }, | ||||
|         payments::{ | ||||
|             helpers, | ||||
|             routing::{self, SessionFlowRoutingInput}, | ||||
| @ -1877,18 +1885,40 @@ pub async fn list_payment_methods( | ||||
|     .await?; | ||||
|  | ||||
|     // filter out connectors based on the business country | ||||
|     let filtered_mcas = helpers::filter_mca_based_on_business_profile(all_mcas, profile_id); | ||||
|     let filtered_mcas = | ||||
|         helpers::filter_mca_based_on_business_profile(all_mcas.clone(), profile_id.clone()); | ||||
|  | ||||
|     logger::debug!(mca_before_filtering=?filtered_mcas); | ||||
|  | ||||
|     let mut response: Vec<ResponsePaymentMethodIntermediate> = vec![]; | ||||
|     for mca in &filtered_mcas { | ||||
|         let payment_methods = match &mca.payment_methods_enabled { | ||||
|             Some(pm) => pm.clone(), | ||||
|             None => continue, | ||||
|  | ||||
|     // Key creation for storing PM_FILTER_CGRAPH | ||||
|     #[cfg(feature = "business_profile_routing")] | ||||
|     let key = { | ||||
|         let profile_id = profile_id | ||||
|             .clone() | ||||
|             .get_required_value("profile_id") | ||||
|             .change_context(errors::ApiErrorResponse::GenericNotFoundError { | ||||
|                 message: "Profile id not found".to_string(), | ||||
|             })?; | ||||
|         format!( | ||||
|             "pm_filters_cgraph_{}_{}", | ||||
|             &merchant_account.merchant_id, profile_id | ||||
|         ) | ||||
|     }; | ||||
|  | ||||
|     #[cfg(not(feature = "business_profile_routing"))] | ||||
|     let key = { format!("pm_filters_cgraph_{}", &merchant_account.merchant_id) }; | ||||
|  | ||||
|     if let Some(graph) = get_merchant_pm_filter_graph(&state, &key).await { | ||||
|         // Derivation of PM_FILTER_CGRAPH from MokaCache successful | ||||
|         for mca in &filtered_mcas { | ||||
|             let payment_methods = match &mca.payment_methods_enabled { | ||||
|                 Some(pm) => pm, | ||||
|                 None => continue, | ||||
|             }; | ||||
|             filter_payment_methods( | ||||
|                 &graph, | ||||
|                 payment_methods, | ||||
|                 &mut req, | ||||
|                 &mut response, | ||||
| @ -1896,13 +1926,54 @@ pub async fn list_payment_methods( | ||||
|                 payment_attempt.as_ref(), | ||||
|                 billing_address.as_ref(), | ||||
|                 mca.connector_name.clone(), | ||||
|             pm_config_mapping, | ||||
|             &state.conf.mandates.supported_payment_methods, | ||||
|             &state.conf.mandates.update_mandate_supported, | ||||
|                 &state.conf.saved_payment_methods, | ||||
|             ) | ||||
|             .await?; | ||||
|         } | ||||
|     } else { | ||||
|         // No PM_FILTER_CGRAPH Cache present in MokaCache | ||||
|         let mut builder = cgraph::ConstraintGraphBuilder::<'static, _>::new(); | ||||
|         for mca in &filtered_mcas { | ||||
|             let payment_methods = match &mca.payment_methods_enabled { | ||||
|                 Some(pm) => pm, | ||||
|                 None => continue, | ||||
|             }; | ||||
|             if let Err(e) = make_pm_graph( | ||||
|                 &mut builder, | ||||
|                 payment_methods, | ||||
|                 mca.connector_name.clone(), | ||||
|                 pm_config_mapping, | ||||
|                 &state.conf.mandates.supported_payment_methods, | ||||
|                 &state.conf.mandates.update_mandate_supported, | ||||
|             ) { | ||||
|                 logger::error!( | ||||
|                     "Failed to construct constraint graph for list payment methods {e:?}" | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Refreshing our CGraph cache | ||||
|         let graph = refresh_pm_filters_cache(&state, &key, builder.build()).await; | ||||
|  | ||||
|         for mca in &filtered_mcas { | ||||
|             let payment_methods = match &mca.payment_methods_enabled { | ||||
|                 Some(pm) => pm, | ||||
|                 None => continue, | ||||
|             }; | ||||
|             filter_payment_methods( | ||||
|                 &graph, | ||||
|                 payment_methods, | ||||
|                 &mut req, | ||||
|                 &mut response, | ||||
|                 payment_intent.as_ref(), | ||||
|                 payment_attempt.as_ref(), | ||||
|                 billing_address.as_ref(), | ||||
|                 mca.connector_name.clone(), | ||||
|                 &state.conf.saved_payment_methods, | ||||
|             ) | ||||
|             .await?; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Filter out wallet payment method from mca if customer has already saved it | ||||
|     customer | ||||
| @ -2763,20 +2834,18 @@ pub async fn call_surcharge_decision_management_for_saved_card( | ||||
|  | ||||
| #[allow(clippy::too_many_arguments)] | ||||
| pub async fn filter_payment_methods( | ||||
|     payment_methods: Vec<serde_json::Value>, | ||||
|     graph: &ConstraintGraph<'_, dir::DirValue>, | ||||
|     payment_methods: &[serde_json::Value], | ||||
|     req: &mut api::PaymentMethodListRequest, | ||||
|     resp: &mut Vec<ResponsePaymentMethodIntermediate>, | ||||
|     payment_intent: Option<&storage::PaymentIntent>, | ||||
|     payment_attempt: Option<&storage::PaymentAttempt>, | ||||
|     address: Option<&domain::Address>, | ||||
|     connector: String, | ||||
|     config: &settings::ConnectorFilters, | ||||
|     supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     saved_payment_methods: &settings::EligiblePaymentMethods, | ||||
| ) -> errors::CustomResult<(), errors::ApiErrorResponse> { | ||||
|     for payment_method in payment_methods.into_iter() { | ||||
|         let parse_result = serde_json::from_value::<PaymentMethodsEnabled>(payment_method); | ||||
|     for payment_method in payment_methods.iter() { | ||||
|         let parse_result = serde_json::from_value::<PaymentMethodsEnabled>(payment_method.clone()); | ||||
|         if let Ok(payment_methods_enabled) = parse_result { | ||||
|             let payment_method = payment_methods_enabled.payment_method; | ||||
|  | ||||
| @ -2807,57 +2876,13 @@ pub async fn filter_payment_methods( | ||||
|                             .map(|minor_amount| minor_amount.get_amount_as_i64()), | ||||
|                     ) | ||||
|                 { | ||||
|                     let mut payment_method_object = payment_method_type_info; | ||||
|                     let payment_method_object = payment_method_type_info.clone(); | ||||
|  | ||||
|                     let filter; | ||||
|                     ( | ||||
|                         payment_method_object.accepted_countries, | ||||
|                         req.accepted_countries, | ||||
|                         filter, | ||||
|                     ) = filter_pm_country_based( | ||||
|                         &payment_method_object.accepted_countries, | ||||
|                         &req.accepted_countries, | ||||
|                     ); | ||||
|                     let filter2; | ||||
|                     ( | ||||
|                         payment_method_object.accepted_currencies, | ||||
|                         req.accepted_currencies, | ||||
|                         filter2, | ||||
|                     ) = filter_pm_currencies_based( | ||||
|                         &payment_method_object.accepted_currencies, | ||||
|                         &req.accepted_currencies, | ||||
|                     ); | ||||
|  | ||||
|                     let filter4 = filter_pm_card_network_based( | ||||
|                         payment_method_object.card_networks.as_ref(), | ||||
|                         req.card_networks.as_ref(), | ||||
|                         &payment_method_object.payment_method_type, | ||||
|                     ); | ||||
|  | ||||
|                     let filter3 = if let Some(payment_intent) = payment_intent { | ||||
|                         filter_payment_country_based(&payment_method_object, address).await? | ||||
|                             && filter_payment_currency_based(payment_intent, &payment_method_object) | ||||
|                             && filter_payment_amount_based(payment_intent, &payment_method_object) | ||||
|                             && filter_payment_mandate_based(payment_attempt, &payment_method_object) | ||||
|                                 .await? | ||||
|                     } else { | ||||
|                         true | ||||
|                     }; | ||||
|  | ||||
|                     let filter5 = filter_pm_based_on_config( | ||||
|                         config, | ||||
|                         &connector, | ||||
|                         &payment_method_object.payment_method_type, | ||||
|                         payment_attempt, | ||||
|                         &mut payment_method_object.card_networks, | ||||
|                         &address.and_then(|inner| inner.country), | ||||
|                         payment_attempt.and_then(|value| value.currency), | ||||
|                     ); | ||||
|  | ||||
|                     let filter6 = filter_pm_based_on_allowed_types( | ||||
|                         allowed_payment_method_types.as_ref(), | ||||
|                         &payment_method_object.payment_method_type, | ||||
|                     ); | ||||
|                     let pm_dir_value: dir::DirValue = | ||||
|                         (payment_method_type_info.payment_method_type, payment_method) | ||||
|                             .into_dir_value() | ||||
|                             .change_context(errors::ApiErrorResponse::InternalServerError) | ||||
|                             .attach_printable("pm_value_node not created")?; | ||||
|  | ||||
|                     let connector_variant = api_enums::Connector::from_str(connector.as_str()) | ||||
|                         .change_context(errors::ConnectorError::InvalidConnectorName) | ||||
| @ -2867,35 +2892,85 @@ pub async fn filter_payment_methods( | ||||
|                         .attach_printable_lazy(|| { | ||||
|                             format!("unable to parse connector name {connector:?}") | ||||
|                         })?; | ||||
|                     let filter7 = payment_attempt | ||||
|                         .and_then(|attempt| attempt.mandate_details.as_ref()) | ||||
|                         .map(|_mandate_details| { | ||||
|                             filter_pm_based_on_supported_payments_for_mandate( | ||||
|                                 supported_payment_methods_for_mandate, | ||||
|                                 &payment_method, | ||||
|                                 &payment_method_object.payment_method_type, | ||||
|                                 connector_variant, | ||||
|                             ) | ||||
|                         }) | ||||
|                         .unwrap_or(true); | ||||
|  | ||||
|                     let filter8 = payment_attempt | ||||
|                     let mut context_values: Vec<dir::DirValue> = Vec::new(); | ||||
|                     context_values.push(pm_dir_value.clone()); | ||||
|  | ||||
|                     payment_intent.map(|intent| { | ||||
|                         intent.currency.map(|currency| { | ||||
|                             context_values.push(dir::DirValue::PaymentCurrency(currency)) | ||||
|                         }) | ||||
|                     }); | ||||
|                     address.map(|address| { | ||||
|                         address.country.map(|country| { | ||||
|                             context_values.push(dir::DirValue::BillingCountry( | ||||
|                                 common_enums::Country::from_alpha2(country), | ||||
|                             )) | ||||
|                         }) | ||||
|                     }); | ||||
|  | ||||
|                     // Addition of Connector to context | ||||
|                     if let Ok(connector) = api_enums::RoutableConnectors::from_str( | ||||
|                         connector_variant.to_string().as_str(), | ||||
|                     ) { | ||||
|                         context_values.push(dir::DirValue::Connector(Box::new( | ||||
|                             api_models::routing::ast::ConnectorChoice { | ||||
|                                 connector, | ||||
|                                 #[cfg(not(feature = "connector_choice_mca_id"))] | ||||
|                                 sub_label: None, | ||||
|                             }, | ||||
|                         ))); | ||||
|                     }; | ||||
|  | ||||
|                     let filter_pm_based_on_allowed_types = filter_pm_based_on_allowed_types( | ||||
|                         allowed_payment_method_types.as_ref(), | ||||
|                         &payment_method_object.payment_method_type, | ||||
|                     ); | ||||
|  | ||||
|                     if payment_attempt | ||||
|                         .and_then(|attempt| attempt.mandate_details.as_ref()) | ||||
|                         .is_some() | ||||
|                     { | ||||
|                         context_values.push(dir::DirValue::PaymentType( | ||||
|                             euclid::enums::PaymentType::NewMandate, | ||||
|                         )); | ||||
|                     }; | ||||
|  | ||||
|                     payment_attempt | ||||
|                         .and_then(|attempt| attempt.mandate_data.as_ref()) | ||||
|                         .map(|mandate_detail| { | ||||
|                             if mandate_detail.update_mandate_id.is_some() { | ||||
|                                 filter_pm_based_on_update_mandate_support_for_connector( | ||||
|                                     supported_payment_methods_for_update_mandate, | ||||
|                                     &payment_method, | ||||
|                                     &payment_method_object.payment_method_type, | ||||
|                                     connector_variant, | ||||
|                                 ) | ||||
|                             } else { | ||||
|                                 true | ||||
|                                 context_values.push(dir::DirValue::PaymentType( | ||||
|                                     euclid::enums::PaymentType::UpdateMandate, | ||||
|                                 )); | ||||
|                             } | ||||
|                         }) | ||||
|                         .unwrap_or(true); | ||||
|                         }); | ||||
|  | ||||
|                     let filter9 = req | ||||
|                     payment_attempt | ||||
|                         .map(|attempt| { | ||||
|                             attempt.mandate_data.is_none() && attempt.mandate_details.is_none() | ||||
|                         }) | ||||
|                         .and_then(|res| { | ||||
|                             res.then(|| { | ||||
|                                 context_values.push(dir::DirValue::PaymentType( | ||||
|                                     euclid::enums::PaymentType::NonMandate, | ||||
|                                 )) | ||||
|                             }) | ||||
|                         }); | ||||
|  | ||||
|                     payment_attempt | ||||
|                         .and_then(|inner| inner.capture_method) | ||||
|                         .map(|capture_method| { | ||||
|                             context_values.push(dir::DirValue::CaptureMethod(capture_method)); | ||||
|                         }); | ||||
|  | ||||
|                     let filter_pm_card_network_based = filter_pm_card_network_based( | ||||
|                         payment_method_object.card_networks.as_ref(), | ||||
|                         req.card_networks.as_ref(), | ||||
|                         &payment_method_object.payment_method_type, | ||||
|                     ); | ||||
|  | ||||
|                     let saved_payment_methods_filter = req | ||||
|                         .client_secret | ||||
|                         .as_ref() | ||||
|                         .map(|cs| { | ||||
| @ -2909,25 +2984,28 @@ pub async fn filter_payment_methods( | ||||
|                         }) | ||||
|                         .unwrap_or(true); | ||||
|  | ||||
|                     let connector = connector.clone(); | ||||
|                     let context = AnalysisContext::from_dir_values(context_values.clone()); | ||||
|  | ||||
|                     let result = graph.key_value_analysis( | ||||
|                         pm_dir_value.clone(), | ||||
|                         &context, | ||||
|                         &mut cgraph::Memoization::new(), | ||||
|                         &mut cgraph::CycleCheck::new(), | ||||
|                         None, | ||||
|                     ); | ||||
|                     if filter_pm_based_on_allowed_types | ||||
|                         && filter_pm_card_network_based | ||||
|                         && saved_payment_methods_filter | ||||
|                         && matches!(result, Ok(())) | ||||
|                     { | ||||
|                         let response_pm_type = ResponsePaymentMethodIntermediate::new( | ||||
|                             payment_method_object, | ||||
|                         connector, | ||||
|                             connector.clone(), | ||||
|                             payment_method, | ||||
|                         ); | ||||
|  | ||||
|                     if filter | ||||
|                         && filter2 | ||||
|                         && filter3 | ||||
|                         && filter4 | ||||
|                         && filter5 | ||||
|                         && filter6 | ||||
|                         && filter7 | ||||
|                         && filter8 | ||||
|                         && filter9 | ||||
|                     { | ||||
|                         resp.push(response_pm_type); | ||||
|                     } else { | ||||
|                         logger::error!("Filtering Payment Methods Failed"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @ -2935,157 +3013,46 @@ pub async fn filter_payment_methods( | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| pub fn filter_pm_based_on_update_mandate_support_for_connector( | ||||
|     supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     payment_method: &api_enums::PaymentMethod, | ||||
|     payment_method_type: &api_enums::PaymentMethodType, | ||||
|     connector: api_enums::Connector, | ||||
| ) -> bool { | ||||
|     if payment_method == &api_enums::PaymentMethod::Card { | ||||
|         supported_payment_methods_for_mandate | ||||
|             .0 | ||||
|             .get(payment_method) | ||||
|             .map(|payment_method_type_hm| { | ||||
|                 let pm_credit = payment_method_type_hm | ||||
|                     .0 | ||||
|                     .get(&api_enums::PaymentMethodType::Credit) | ||||
|                     .map(|conn| conn.connector_list.clone()) | ||||
|                     .unwrap_or_default(); | ||||
|                 let pm_debit = payment_method_type_hm | ||||
|                     .0 | ||||
|                     .get(&api_enums::PaymentMethodType::Debit) | ||||
|                     .map(|conn| conn.connector_list.clone()) | ||||
|                     .unwrap_or_default(); | ||||
|                 &pm_credit | &pm_debit | ||||
|             }) | ||||
|             .map(|supported_connectors| supported_connectors.contains(&connector)) | ||||
|             .unwrap_or(false) | ||||
|     } else { | ||||
|         supported_payment_methods_for_mandate | ||||
|             .0 | ||||
|             .get(payment_method) | ||||
|             .and_then(|payment_method_type_hm| payment_method_type_hm.0.get(payment_method_type)) | ||||
|             .map(|supported_connectors| supported_connectors.connector_list.contains(&connector)) | ||||
|             .unwrap_or(false) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn filter_pm_based_on_supported_payments_for_mandate( | ||||
|     supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     payment_method: &api_enums::PaymentMethod, | ||||
|     payment_method_type: &api_enums::PaymentMethodType, | ||||
|     connector: api_enums::Connector, | ||||
| ) -> bool { | ||||
|     supported_payment_methods_for_mandate | ||||
|         .0 | ||||
|         .get(payment_method) | ||||
|         .and_then(|payment_method_type_hm| payment_method_type_hm.0.get(payment_method_type)) | ||||
|         .map(|supported_connectors| supported_connectors.connector_list.contains(&connector)) | ||||
|         .unwrap_or(false) | ||||
| } | ||||
|  | ||||
| fn filter_pm_based_on_config<'a>( | ||||
|     config: &'a settings::ConnectorFilters, | ||||
|     connector: &'a str, | ||||
|     payment_method_type: &'a api_enums::PaymentMethodType, | ||||
|     payment_attempt: Option<&storage::PaymentAttempt>, | ||||
|     card_network: &mut Option<Vec<api_enums::CardNetwork>>, | ||||
|     country: &Option<api_enums::CountryAlpha2>, | ||||
|     currency: Option<api_enums::Currency>, | ||||
| ) -> bool { | ||||
|     config | ||||
|         .0 | ||||
|         .get(connector) | ||||
|         .or_else(|| config.0.get("default")) | ||||
|         .and_then(|inner| match payment_method_type { | ||||
|             api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { | ||||
|                 let country_currency_filter = inner | ||||
|                     .0 | ||||
|                     .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|                         *payment_method_type, | ||||
|                     )) | ||||
|                     .map(|value| global_country_currency_filter(value, country, currency)); | ||||
|  | ||||
|                 card_network_filter(country, currency, card_network, inner); | ||||
|  | ||||
|                 let capture_method_filter = payment_attempt | ||||
|                     .and_then(|inner| inner.capture_method) | ||||
|                     .and_then(|capture_method| { | ||||
|                         (capture_method == storage_enums::CaptureMethod::Manual).then(|| { | ||||
|                             filter_pm_based_on_capture_method_used(inner, payment_method_type) | ||||
|                         }) | ||||
|                     }); | ||||
|  | ||||
|                 Some( | ||||
|                     country_currency_filter.unwrap_or(true) | ||||
|                         && capture_method_filter.unwrap_or(true), | ||||
|                 ) | ||||
|             } | ||||
|             payment_method_type => inner | ||||
|                 .0 | ||||
|                 .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|                     *payment_method_type, | ||||
|                 )) | ||||
|                 .map(|value| global_country_currency_filter(value, country, currency)), | ||||
|         }) | ||||
|         .unwrap_or(true) | ||||
| } | ||||
|  | ||||
| ///Filters the payment method list on basis of Capture methods, checks whether the connector issues Manual payments using cards or not if not it won't be visible in payment methods list | ||||
| fn filter_pm_based_on_capture_method_used( | ||||
|     payment_method_filters: &settings::PaymentMethodFilters, | ||||
| fn filter_pm_based_on_allowed_types( | ||||
|     allowed_types: Option<&Vec<api_enums::PaymentMethodType>>, | ||||
|     payment_method_type: &api_enums::PaymentMethodType, | ||||
| ) -> bool { | ||||
|     payment_method_filters | ||||
|         .0 | ||||
|         .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|             *payment_method_type, | ||||
|         )) | ||||
|         .and_then(|v| v.not_available_flows) | ||||
|         .and_then(|v| v.capture_method) | ||||
|         .map(|v| !matches!(v, api_enums::CaptureMethod::Manual)) | ||||
|         .unwrap_or(true) | ||||
|     allowed_types.map_or(true, |pm| pm.contains(payment_method_type)) | ||||
| } | ||||
|  | ||||
| fn card_network_filter( | ||||
|     country: &Option<api_enums::CountryAlpha2>, | ||||
|     currency: Option<api_enums::Currency>, | ||||
|     card_network: &mut Option<Vec<api_enums::CardNetwork>>, | ||||
|     payment_method_filters: &settings::PaymentMethodFilters, | ||||
| ) { | ||||
|     if let Some(value) = card_network.as_mut() { | ||||
|         let filtered_card_networks = value | ||||
|             .iter() | ||||
|             .filter(|&element| { | ||||
|                 let key = settings::PaymentMethodFilterKey::CardNetwork(element.clone()); | ||||
|                 payment_method_filters | ||||
|                     .0 | ||||
|                     .get(&key) | ||||
|                     .map(|value| global_country_currency_filter(value, country, currency)) | ||||
|                     .unwrap_or(true) | ||||
| fn filter_amount_based(payment_method: &RequestPaymentMethodTypes, amount: Option<i64>) -> bool { | ||||
|     let min_check = amount | ||||
|         .and_then(|amt| { | ||||
|             payment_method | ||||
|                 .minimum_amount | ||||
|                 .map(|min_amt| amt >= min_amt.into()) | ||||
|         }) | ||||
|             .cloned() | ||||
|             .collect::<Vec<_>>(); | ||||
|         *value = filtered_card_networks; | ||||
|     } | ||||
|         .unwrap_or(true); | ||||
|     let max_check = amount | ||||
|         .and_then(|amt| { | ||||
|             payment_method | ||||
|                 .maximum_amount | ||||
|                 .map(|max_amt| amt <= max_amt.into()) | ||||
|         }) | ||||
|         .unwrap_or(true); | ||||
|     (min_check && max_check) || amount == Some(0) | ||||
| } | ||||
|  | ||||
| fn global_country_currency_filter( | ||||
|     item: &settings::CurrencyCountryFlowFilter, | ||||
|     country: &Option<api_enums::CountryAlpha2>, | ||||
|     currency: Option<api_enums::Currency>, | ||||
| fn filter_recurring_based( | ||||
|     payment_method: &RequestPaymentMethodTypes, | ||||
|     recurring_enabled: Option<bool>, | ||||
| ) -> bool { | ||||
|     let country_condition = item | ||||
|         .country | ||||
|         .as_ref() | ||||
|         .zip(country.as_ref()) | ||||
|         .map(|(lhs, rhs)| lhs.contains(rhs)); | ||||
|     let currency_condition = item | ||||
|         .currency | ||||
|         .as_ref() | ||||
|         .zip(currency) | ||||
|         .map(|(lhs, rhs)| lhs.contains(&rhs)); | ||||
|     country_condition.unwrap_or(true) && currency_condition.unwrap_or(true) | ||||
|     recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled) | ||||
| } | ||||
|  | ||||
| fn filter_installment_based( | ||||
|     payment_method: &RequestPaymentMethodTypes, | ||||
|     installment_payment_enabled: Option<bool>, | ||||
| ) -> bool { | ||||
|     installment_payment_enabled.map_or(true, |enabled| { | ||||
|         payment_method.installment_payment_enabled == enabled | ||||
|     }) | ||||
| } | ||||
|  | ||||
| fn filter_pm_card_network_based( | ||||
| @ -3106,216 +3073,6 @@ fn filter_pm_card_network_based( | ||||
|         _ => true, | ||||
|     } | ||||
| } | ||||
| fn filter_pm_country_based( | ||||
|     accepted_countries: &Option<admin::AcceptedCountries>, | ||||
|     req_country_list: &Option<Vec<api_enums::CountryAlpha2>>, | ||||
| ) -> ( | ||||
|     Option<admin::AcceptedCountries>, | ||||
|     Option<Vec<api_enums::CountryAlpha2>>, | ||||
|     bool, | ||||
| ) { | ||||
|     match (accepted_countries, req_country_list) { | ||||
|         (None, None) => (None, None, true), | ||||
|         (None, Some(ref r)) => ( | ||||
|             Some(admin::AcceptedCountries::EnableOnly(r.to_vec())), | ||||
|             Some(r.to_vec()), | ||||
|             true, | ||||
|         ), | ||||
|         (Some(l), None) => (Some(l.to_owned()), None, true), | ||||
|         (Some(l), Some(ref r)) => { | ||||
|             let updated = match l { | ||||
|                 admin::AcceptedCountries::EnableOnly(acc) => { | ||||
|                     filter_accepted_enum_based(&Some(acc.clone()), &Some(r.to_owned())) | ||||
|                         .map(admin::AcceptedCountries::EnableOnly) | ||||
|                 } | ||||
|  | ||||
|                 admin::AcceptedCountries::DisableOnly(den) => { | ||||
|                     filter_disabled_enum_based(&Some(den.clone()), &Some(r.to_owned())) | ||||
|                         .map(admin::AcceptedCountries::DisableOnly) | ||||
|                 } | ||||
|  | ||||
|                 admin::AcceptedCountries::AllAccepted => { | ||||
|                     Some(admin::AcceptedCountries::AllAccepted) | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             (updated, Some(r.to_vec()), true) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn filter_pm_currencies_based( | ||||
|     accepted_currency: &Option<admin::AcceptedCurrencies>, | ||||
|     req_currency_list: &Option<Vec<api_enums::Currency>>, | ||||
| ) -> ( | ||||
|     Option<admin::AcceptedCurrencies>, | ||||
|     Option<Vec<api_enums::Currency>>, | ||||
|     bool, | ||||
| ) { | ||||
|     match (accepted_currency, req_currency_list) { | ||||
|         (None, None) => (None, None, true), | ||||
|         (None, Some(ref r)) => ( | ||||
|             Some(admin::AcceptedCurrencies::EnableOnly(r.to_vec())), | ||||
|             Some(r.to_vec()), | ||||
|             true, | ||||
|         ), | ||||
|         (Some(l), None) => (Some(l.to_owned()), None, true), | ||||
|         (Some(l), Some(ref r)) => { | ||||
|             let updated = match l { | ||||
|                 admin::AcceptedCurrencies::EnableOnly(acc) => { | ||||
|                     filter_accepted_enum_based(&Some(acc.clone()), &Some(r.to_owned())) | ||||
|                         .map(admin::AcceptedCurrencies::EnableOnly) | ||||
|                 } | ||||
|  | ||||
|                 admin::AcceptedCurrencies::DisableOnly(den) => { | ||||
|                     filter_disabled_enum_based(&Some(den.clone()), &Some(r.to_owned())) | ||||
|                         .map(admin::AcceptedCurrencies::DisableOnly) | ||||
|                 } | ||||
|  | ||||
|                 admin::AcceptedCurrencies::AllAccepted => { | ||||
|                     Some(admin::AcceptedCurrencies::AllAccepted) | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             (updated, Some(r.to_vec()), true) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn filter_accepted_enum_based<T: Eq + std::hash::Hash + Clone>( | ||||
|     left: &Option<Vec<T>>, | ||||
|     right: &Option<Vec<T>>, | ||||
| ) -> Option<Vec<T>> { | ||||
|     match (left, right) { | ||||
|         (Some(ref l), Some(ref r)) => { | ||||
|             let a: HashSet<&T> = HashSet::from_iter(l.iter()); | ||||
|             let b: HashSet<&T> = HashSet::from_iter(r.iter()); | ||||
|  | ||||
|             let y: Vec<T> = a.intersection(&b).map(|&i| i.to_owned()).collect(); | ||||
|             Some(y) | ||||
|         } | ||||
|         (Some(ref l), None) => Some(l.to_vec()), | ||||
|         (_, _) => None, | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn filter_disabled_enum_based<T: Eq + std::hash::Hash + Clone>( | ||||
|     left: &Option<Vec<T>>, | ||||
|     right: &Option<Vec<T>>, | ||||
| ) -> Option<Vec<T>> { | ||||
|     match (left, right) { | ||||
|         (Some(ref l), Some(ref r)) => { | ||||
|             let mut enabled = Vec::new(); | ||||
|             for element in r { | ||||
|                 if !l.contains(element) { | ||||
|                     enabled.push(element.to_owned()); | ||||
|                 } | ||||
|             } | ||||
|             Some(enabled) | ||||
|         } | ||||
|         (None, Some(r)) => Some(r.to_vec()), | ||||
|         (_, _) => None, | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn filter_amount_based(payment_method: &RequestPaymentMethodTypes, amount: Option<i64>) -> bool { | ||||
|     let min_check = amount | ||||
|         .and_then(|amt| { | ||||
|             payment_method | ||||
|                 .minimum_amount | ||||
|                 .map(|min_amt| amt >= min_amt.into()) | ||||
|         }) | ||||
|         .unwrap_or(true); | ||||
|     let max_check = amount | ||||
|         .and_then(|amt| { | ||||
|             payment_method | ||||
|                 .maximum_amount | ||||
|                 .map(|max_amt| amt <= max_amt.into()) | ||||
|         }) | ||||
|         .unwrap_or(true); | ||||
|     // let min_check = match (amount, payment_method.minimum_amount) { | ||||
|     //     (Some(amt), Some(min_amt)) => amt >= min_amt, | ||||
|     //     (_, _) => true, | ||||
|     // }; | ||||
|     // let max_check = match (amount, payment_method.maximum_amount) { | ||||
|     //     (Some(amt), Some(max_amt)) => amt <= max_amt, | ||||
|     //     (_, _) => true, | ||||
|     // }; | ||||
|     (min_check && max_check) || amount == Some(0) | ||||
| } | ||||
|  | ||||
| fn filter_pm_based_on_allowed_types( | ||||
|     allowed_types: Option<&Vec<api_enums::PaymentMethodType>>, | ||||
|     payment_method_type: &api_enums::PaymentMethodType, | ||||
| ) -> bool { | ||||
|     allowed_types.map_or(true, |pm| pm.contains(payment_method_type)) | ||||
| } | ||||
|  | ||||
| fn filter_recurring_based( | ||||
|     payment_method: &RequestPaymentMethodTypes, | ||||
|     recurring_enabled: Option<bool>, | ||||
| ) -> bool { | ||||
|     recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled) | ||||
| } | ||||
|  | ||||
| fn filter_installment_based( | ||||
|     payment_method: &RequestPaymentMethodTypes, | ||||
|     installment_payment_enabled: Option<bool>, | ||||
| ) -> bool { | ||||
|     installment_payment_enabled.map_or(true, |enabled| { | ||||
|         payment_method.installment_payment_enabled == enabled | ||||
|     }) | ||||
| } | ||||
|  | ||||
| async fn filter_payment_country_based( | ||||
|     pm: &RequestPaymentMethodTypes, | ||||
|     address: Option<&domain::Address>, | ||||
| ) -> errors::CustomResult<bool, errors::ApiErrorResponse> { | ||||
|     Ok(address.map_or(true, |address| { | ||||
|         address.country.as_ref().map_or(true, |country| { | ||||
|             pm.accepted_countries.as_ref().map_or(true, |ac| match ac { | ||||
|                 admin::AcceptedCountries::EnableOnly(acc) => acc.contains(country), | ||||
|                 admin::AcceptedCountries::DisableOnly(den) => !den.contains(country), | ||||
|                 admin::AcceptedCountries::AllAccepted => true, | ||||
|             }) | ||||
|         }) | ||||
|     })) | ||||
| } | ||||
|  | ||||
| fn filter_payment_currency_based( | ||||
|     payment_intent: &storage::PaymentIntent, | ||||
|     pm: &RequestPaymentMethodTypes, | ||||
| ) -> bool { | ||||
|     payment_intent.currency.map_or(true, |currency| { | ||||
|         pm.accepted_currencies.as_ref().map_or(true, |ac| match ac { | ||||
|             admin::AcceptedCurrencies::EnableOnly(acc) => acc.contains(¤cy), | ||||
|             admin::AcceptedCurrencies::DisableOnly(den) => !den.contains(¤cy), | ||||
|             admin::AcceptedCurrencies::AllAccepted => true, | ||||
|         }) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| fn filter_payment_amount_based( | ||||
|     payment_intent: &storage::PaymentIntent, | ||||
|     pm: &RequestPaymentMethodTypes, | ||||
| ) -> bool { | ||||
|     let amount = payment_intent.amount.get_amount_as_i64(); | ||||
|     (pm.maximum_amount.map_or(true, |amt| amount <= amt.into()) | ||||
|         && pm.minimum_amount.map_or(true, |amt| amount >= amt.into())) | ||||
|         || payment_intent.amount.get_amount_as_i64() == 0 | ||||
| } | ||||
|  | ||||
| async fn filter_payment_mandate_based( | ||||
|     payment_attempt: Option<&storage::PaymentAttempt>, | ||||
|     pm: &RequestPaymentMethodTypes, | ||||
| ) -> errors::CustomResult<bool, errors::ApiErrorResponse> { | ||||
|     let recurring_filter = if !pm.recurring_enabled { | ||||
|         payment_attempt.map_or(true, |pa| pa.mandate_id.is_none()) | ||||
|     } else { | ||||
|         true | ||||
|     }; | ||||
|     Ok(recurring_filter) | ||||
| } | ||||
|  | ||||
| pub async fn do_list_customer_pm_fetch_customer_if_not_passed( | ||||
|     state: routes::SessionState, | ||||
|  | ||||
							
								
								
									
										733
									
								
								crates/router/src/core/payment_methods/utils.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										733
									
								
								crates/router/src/core/payment_methods/utils.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,733 @@ | ||||
| use std::{str::FromStr, sync::Arc}; | ||||
|  | ||||
| use api_models::{ | ||||
|     admin::{self, PaymentMethodsEnabled}, | ||||
|     enums as api_enums, | ||||
|     payment_methods::RequestPaymentMethodTypes, | ||||
| }; | ||||
| use common_enums::enums; | ||||
| use euclid::frontend::dir; | ||||
| use hyperswitch_constraint_graph as cgraph; | ||||
| use kgraph_utils::{error::KgraphError, transformers::IntoDirValue}; | ||||
| use storage_impl::redis::cache::{CacheKey, PM_FILTERS_CGRAPH_CACHE}; | ||||
|  | ||||
| use crate::{configs::settings, routes::SessionState}; | ||||
|  | ||||
| pub fn make_pm_graph( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     payment_methods: &[serde_json::value::Value], | ||||
|     connector: String, | ||||
|     pm_config_mapping: &settings::ConnectorFilters, | ||||
|     supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
| ) -> Result<(), KgraphError> { | ||||
|     for payment_method in payment_methods.iter() { | ||||
|         let pm_enabled = serde_json::from_value::<PaymentMethodsEnabled>(payment_method.clone()); | ||||
|         if let Ok(payment_methods_enabled) = pm_enabled { | ||||
|             compile_pm_graph( | ||||
|                 builder, | ||||
|                 payment_methods_enabled.clone(), | ||||
|                 connector.clone(), | ||||
|                 pm_config_mapping, | ||||
|                 supported_payment_methods_for_mandate, | ||||
|                 supported_payment_methods_for_update_mandate, | ||||
|             )?; | ||||
|         }; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub async fn get_merchant_pm_filter_graph<'a>( | ||||
|     state: &SessionState, | ||||
|     key: &str, | ||||
| ) -> Option<Arc<hyperswitch_constraint_graph::ConstraintGraph<'a, dir::DirValue>>> { | ||||
|     PM_FILTERS_CGRAPH_CACHE | ||||
|         .get_val::<Arc<hyperswitch_constraint_graph::ConstraintGraph<'_, dir::DirValue>>>( | ||||
|             CacheKey { | ||||
|                 key: key.to_string(), | ||||
|                 prefix: state.tenant.clone(), | ||||
|             }, | ||||
|         ) | ||||
|         .await | ||||
| } | ||||
|  | ||||
| pub async fn refresh_pm_filters_cache( | ||||
|     state: &SessionState, | ||||
|     key: &str, | ||||
|     graph: cgraph::ConstraintGraph<'static, dir::DirValue>, | ||||
| ) -> Arc<hyperswitch_constraint_graph::ConstraintGraph<'static, dir::DirValue>> { | ||||
|     let pm_filter_graph = Arc::new(graph); | ||||
|     PM_FILTERS_CGRAPH_CACHE | ||||
|         .push( | ||||
|             CacheKey { | ||||
|                 key: key.to_string(), | ||||
|                 prefix: state.tenant.clone(), | ||||
|             }, | ||||
|             pm_filter_graph.clone(), | ||||
|         ) | ||||
|         .await; | ||||
|     pm_filter_graph | ||||
| } | ||||
|  | ||||
| fn compile_pm_graph( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     pm_enabled: PaymentMethodsEnabled, | ||||
|     connector: String, | ||||
|     config: &settings::ConnectorFilters, | ||||
|     supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
| ) -> Result<(), KgraphError> { | ||||
|     if let Some(payment_method_types) = pm_enabled.payment_method_types { | ||||
|         for pmt in payment_method_types { | ||||
|             let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = | ||||
|                 Vec::new(); | ||||
|             let mut agg_or_nodes_for_mandate_filters: Vec<( | ||||
|                 cgraph::NodeId, | ||||
|                 cgraph::Relation, | ||||
|                 cgraph::Strength, | ||||
|             )> = Vec::new(); | ||||
|  | ||||
|             // Connector supported for Update mandate filter | ||||
|             let res = construct_supported_connectors_for_update_mandate_node( | ||||
|                 builder, | ||||
|                 supported_payment_methods_for_update_mandate, | ||||
|                 pmt.clone(), | ||||
|                 &pm_enabled.payment_method, | ||||
|             ); | ||||
|             if let Ok(Some(connector_eligible_for_update_mandates_node)) = res { | ||||
|                 agg_or_nodes_for_mandate_filters.push(( | ||||
|                     connector_eligible_for_update_mandates_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )) | ||||
|             } | ||||
|  | ||||
|             // Connector supported for mandates filter | ||||
|             if let Some(supported_pm_for_mandates) = supported_payment_methods_for_mandate | ||||
|                 .0 | ||||
|                 .get(&pm_enabled.payment_method) | ||||
|             { | ||||
|                 if let Some(supported_connector_for_mandates) = | ||||
|                     supported_pm_for_mandates.0.get(&pmt.payment_method_type) | ||||
|                 { | ||||
|                     let supported_connectors: Vec<api_enums::Connector> = | ||||
|                         supported_connector_for_mandates | ||||
|                             .connector_list | ||||
|                             .clone() | ||||
|                             .into_iter() | ||||
|                             .collect(); | ||||
|                     if let Ok(Some(connector_eligible_for_mandates_node)) = | ||||
|                         construct_supported_connectors_for_mandate_node( | ||||
|                             builder, | ||||
|                             supported_connectors, | ||||
|                         ) | ||||
|                     { | ||||
|                         agg_or_nodes_for_mandate_filters.push(( | ||||
|                             connector_eligible_for_mandates_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         )) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Non Prominent Mandate flows | ||||
|             let payment_type_non_mandate_value_node = builder.make_value_node( | ||||
|                 cgraph::NodeValue::Value(dir::DirValue::PaymentType( | ||||
|                     euclid::enums::PaymentType::NonMandate, | ||||
|                 )), | ||||
|                 None, | ||||
|                 None::<()>, | ||||
|             ); | ||||
|             let payment_type_setup_mandate_value_node = builder.make_value_node( | ||||
|                 cgraph::NodeValue::Value(dir::DirValue::PaymentType( | ||||
|                     euclid::enums::PaymentType::SetupMandate, | ||||
|                 )), | ||||
|                 None, | ||||
|                 None::<()>, | ||||
|             ); | ||||
|  | ||||
|             let non_major_mandate_any_node = builder | ||||
|                 .make_any_aggregator( | ||||
|                     &[ | ||||
|                         ( | ||||
|                             payment_type_non_mandate_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             payment_type_setup_mandate_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                     ], | ||||
|                     None, | ||||
|                     None::<()>, | ||||
|                     None, | ||||
|                 ) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             agg_or_nodes_for_mandate_filters.push(( | ||||
|                 non_major_mandate_any_node, | ||||
|                 cgraph::Relation::Positive, | ||||
|                 cgraph::Strength::Strong, | ||||
|             )); | ||||
|  | ||||
|             let agg_or_node = builder | ||||
|                 .make_any_aggregator(&agg_or_nodes_for_mandate_filters, None, None::<()>, None) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             agg_nodes.push(( | ||||
|                 agg_or_node, | ||||
|                 cgraph::Relation::Positive, | ||||
|                 cgraph::Strength::Strong, | ||||
|             )); | ||||
|  | ||||
|             // Capture Method filter | ||||
|             config | ||||
|                 .0 | ||||
|                 .get(connector.as_str()) | ||||
|                 .or_else(|| config.0.get("default")) | ||||
|                 .map(|inner| { | ||||
|                     if let Ok(Some(capture_method_filter)) = | ||||
|                         construct_capture_method_node(builder, inner, &pmt.payment_method_type) | ||||
|                     { | ||||
|                         agg_nodes.push(( | ||||
|                             capture_method_filter, | ||||
|                             cgraph::Relation::Negative, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         )) | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|             // Country filter | ||||
|             if let Ok(Some(country_node)) = compile_accepted_countries_for_mca( | ||||
|                 builder, | ||||
|                 &pmt.payment_method_type, | ||||
|                 pmt.accepted_countries, | ||||
|                 config, | ||||
|                 connector.clone(), | ||||
|             ) { | ||||
|                 agg_nodes.push(( | ||||
|                     country_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )) | ||||
|             } | ||||
|  | ||||
|             // Currency filter | ||||
|             if let Ok(Some(currency_node)) = compile_accepted_currency_for_mca( | ||||
|                 builder, | ||||
|                 &pmt.payment_method_type, | ||||
|                 pmt.accepted_currencies, | ||||
|                 config, | ||||
|                 connector.clone(), | ||||
|             ) { | ||||
|                 agg_nodes.push(( | ||||
|                     currency_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )) | ||||
|             } | ||||
|  | ||||
|             let and_node_for_all_the_filters = builder | ||||
|                 .make_all_aggregator(&agg_nodes, None, None::<()>, None) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             // Making our output node | ||||
|             let pmt_info = "PaymentMethodType"; | ||||
|             let dir_node: cgraph::NodeValue<dir::DirValue> = | ||||
|                 (pmt.payment_method_type, pm_enabled.payment_method) | ||||
|                     .into_dir_value() | ||||
|                     .map(Into::into)?; | ||||
|             let payment_method_type_value_node = | ||||
|                 builder.make_value_node(dir_node, Some(pmt_info), None::<()>); | ||||
|  | ||||
|             builder | ||||
|                 .make_edge( | ||||
|                     and_node_for_all_the_filters, | ||||
|                     payment_method_type_value_node, | ||||
|                     cgraph::Strength::Strong, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     None::<cgraph::DomainId>, | ||||
|                 ) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn construct_capture_method_node( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     payment_method_filters: &settings::PaymentMethodFilters, | ||||
|     payment_method_type: &api_enums::PaymentMethodType, | ||||
| ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
|     if !payment_method_filters | ||||
|         .0 | ||||
|         .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|             *payment_method_type, | ||||
|         )) | ||||
|         .and_then(|v| v.not_available_flows) | ||||
|         .and_then(|v| v.capture_method) | ||||
|         .map(|v| !matches!(v, api_enums::CaptureMethod::Manual)) | ||||
|         .unwrap_or(true) | ||||
|     { | ||||
|         return Ok(Some(builder.make_value_node( | ||||
|             cgraph::NodeValue::Value(dir::DirValue::CaptureMethod( | ||||
|                 common_enums::CaptureMethod::Manual, | ||||
|             )), | ||||
|             None, | ||||
|             None::<()>, | ||||
|         ))); | ||||
|     } | ||||
|     Ok(None) | ||||
| } | ||||
|  | ||||
| fn construct_supported_connectors_for_update_mandate_node( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, | ||||
|     pmt: RequestPaymentMethodTypes, | ||||
|     payment_method: &enums::PaymentMethod, | ||||
| ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
|     let card_value_node = builder.make_value_node( | ||||
|         cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)), | ||||
|         None, | ||||
|         None::<()>, | ||||
|     ); | ||||
|  | ||||
|     let payment_type_value_node = builder.make_value_node( | ||||
|         cgraph::NodeValue::Value(dir::DirValue::PaymentType( | ||||
|             euclid::enums::PaymentType::UpdateMandate, | ||||
|         )), | ||||
|         None, | ||||
|         None::<()>, | ||||
|     ); | ||||
|  | ||||
|     let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); | ||||
|     let mut card_dir_values = Vec::new(); | ||||
|     let mut non_card_dir_values = Vec::new(); | ||||
|  | ||||
|     if let Some(supported_pm_for_mandates) = supported_payment_methods_for_update_mandate | ||||
|         .0 | ||||
|         .get(payment_method) | ||||
|     { | ||||
|         if payment_method == &enums::PaymentMethod::Card { | ||||
|             if let Some(credit_connector_list) = supported_pm_for_mandates | ||||
|                 .0 | ||||
|                 .get(&api_enums::PaymentMethodType::Credit) | ||||
|             { | ||||
|                 card_dir_values.extend( | ||||
|                     credit_connector_list | ||||
|                         .connector_list | ||||
|                         .clone() | ||||
|                         .into_iter() | ||||
|                         .filter_map(|connector| { | ||||
|                             api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) | ||||
|                                 .ok() | ||||
|                                 .map(|connector| { | ||||
|                                     dir::DirValue::Connector(Box::new( | ||||
|                                         api_models::routing::ast::ConnectorChoice { | ||||
|                                             connector, | ||||
|                                             #[cfg(not(feature = "connector_choice_mca_id"))] | ||||
|                                             sub_label: None, | ||||
|                                         }, | ||||
|                                     )) | ||||
|                                 }) | ||||
|                         }), | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             if let Some(debit_connector_list) = supported_pm_for_mandates | ||||
|                 .0 | ||||
|                 .get(&api_enums::PaymentMethodType::Debit) | ||||
|             { | ||||
|                 card_dir_values.extend( | ||||
|                     debit_connector_list | ||||
|                         .connector_list | ||||
|                         .clone() | ||||
|                         .into_iter() | ||||
|                         .filter_map(|connector| { | ||||
|                             api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) | ||||
|                                 .ok() | ||||
|                                 .map(|connector| { | ||||
|                                     dir::DirValue::Connector(Box::new( | ||||
|                                         api_models::routing::ast::ConnectorChoice { | ||||
|                                             connector, | ||||
|                                             #[cfg(not(feature = "connector_choice_mca_id"))] | ||||
|                                             sub_label: None, | ||||
|                                         }, | ||||
|                                     )) | ||||
|                                 }) | ||||
|                         }), | ||||
|                 ); | ||||
|             } | ||||
|             let card_in_node = builder | ||||
|                 .make_in_aggregator(card_dir_values, None, None::<()>) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             let card_and_node = builder | ||||
|                 .make_all_aggregator( | ||||
|                     &[ | ||||
|                         ( | ||||
|                             card_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             payment_type_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             card_in_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                     ], | ||||
|                     None, | ||||
|                     None::<()>, | ||||
|                     None, | ||||
|                 ) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             agg_nodes.push(( | ||||
|                 card_and_node, | ||||
|                 cgraph::Relation::Positive, | ||||
|                 cgraph::Strength::Strong, | ||||
|             )); | ||||
|         } else if let Some(connector_list) = | ||||
|             supported_pm_for_mandates.0.get(&pmt.payment_method_type) | ||||
|         { | ||||
|             non_card_dir_values.extend( | ||||
|                 connector_list | ||||
|                     .connector_list | ||||
|                     .clone() | ||||
|                     .into_iter() | ||||
|                     .filter_map(|connector| { | ||||
|                         api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) | ||||
|                             .ok() | ||||
|                             .map(|connector| { | ||||
|                                 dir::DirValue::Connector(Box::new( | ||||
|                                     api_models::routing::ast::ConnectorChoice { | ||||
|                                         connector, | ||||
|                                         #[cfg(not(feature = "connector_choice_mca_id"))] | ||||
|                                         sub_label: None, | ||||
|                                     }, | ||||
|                                 )) | ||||
|                             }) | ||||
|                     }), | ||||
|             ); | ||||
|             let non_card_mandate_in_node = builder | ||||
|                 .make_in_aggregator(non_card_dir_values, None, None::<()>) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             let non_card_and_node = builder | ||||
|                 .make_all_aggregator( | ||||
|                     &[ | ||||
|                         ( | ||||
|                             card_value_node, | ||||
|                             cgraph::Relation::Negative, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             payment_type_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             non_card_mandate_in_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                     ], | ||||
|                     None, | ||||
|                     None::<()>, | ||||
|                     None, | ||||
|                 ) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|             agg_nodes.push(( | ||||
|                 non_card_and_node, | ||||
|                 cgraph::Relation::Positive, | ||||
|                 cgraph::Strength::Strong, | ||||
|             )); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(Some( | ||||
|         builder | ||||
|             .make_any_aggregator( | ||||
|                 &agg_nodes, | ||||
|                 Some("any node for card and non card pm"), | ||||
|                 None::<()>, | ||||
|                 None, | ||||
|             ) | ||||
|             .map_err(KgraphError::GraphConstructionError)?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| fn construct_supported_connectors_for_mandate_node( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     eligible_connectors: Vec<api_enums::Connector>, | ||||
| ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
|     let payment_type_value_node = builder.make_value_node( | ||||
|         cgraph::NodeValue::Value(dir::DirValue::PaymentType( | ||||
|             euclid::enums::PaymentType::NewMandate, | ||||
|         )), | ||||
|         None, | ||||
|         None::<()>, | ||||
|     ); | ||||
|     let connectors_from_config: Vec<dir::DirValue> = eligible_connectors | ||||
|         .into_iter() | ||||
|         .filter_map(|connector| { | ||||
|             match api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) { | ||||
|                 Ok(connector) => Some(dir::DirValue::Connector(Box::new( | ||||
|                     api_models::routing::ast::ConnectorChoice { | ||||
|                         connector, | ||||
|                         #[cfg(not(feature = "connector_choice_mca_id"))] | ||||
|                         sub_label: None, | ||||
|                     }, | ||||
|                 ))), | ||||
|                 Err(_) => None, | ||||
|             } | ||||
|         }) | ||||
|         .collect(); | ||||
|  | ||||
|     if connectors_from_config.is_empty() { | ||||
|         Ok(None) | ||||
|     } else { | ||||
|         let connector_in_aggregator = builder | ||||
|             .make_in_aggregator(connectors_from_config, None, None::<()>) | ||||
|             .map_err(KgraphError::GraphConstructionError)?; | ||||
|         Ok(Some( | ||||
|             builder | ||||
|                 .make_all_aggregator( | ||||
|                     &[ | ||||
|                         ( | ||||
|                             payment_type_value_node, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                         ( | ||||
|                             connector_in_aggregator, | ||||
|                             cgraph::Relation::Positive, | ||||
|                             cgraph::Strength::Strong, | ||||
|                         ), | ||||
|                     ], | ||||
|                     None, | ||||
|                     None::<()>, | ||||
|                     None, | ||||
|                 ) | ||||
|                 .map_err(KgraphError::GraphConstructionError)?, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // fn construct_card_network_nodes( | ||||
| //     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
| //     mca_card_networks: Vec<api_enums::CardNetwork>, | ||||
| // ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
| //     Ok(Some( | ||||
| //         builder | ||||
| //             .make_in_aggregator( | ||||
| //                 mca_card_networks | ||||
| //                     .into_iter() | ||||
| //                     .map(dir::DirValue::CardNetwork) | ||||
| //                     .collect(), | ||||
| //                 None, | ||||
| //                 None::<()>, | ||||
| //             ) | ||||
| //             .map_err(KgraphError::GraphConstructionError)?, | ||||
| //     )) | ||||
| // } | ||||
|  | ||||
| fn compile_accepted_countries_for_mca( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     payment_method_type: &enums::PaymentMethodType, | ||||
|     pm_countries: Option<admin::AcceptedCountries>, | ||||
|     config: &settings::ConnectorFilters, | ||||
|     connector: String, | ||||
| ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
|     let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); | ||||
|  | ||||
|     // Country from the MCA | ||||
|     if let Some(pm_obj_countries) = pm_countries { | ||||
|         match pm_obj_countries { | ||||
|             admin::AcceptedCountries::EnableOnly(countries) => { | ||||
|                 let pm_object_country_value_node = builder | ||||
|                     .make_in_aggregator( | ||||
|                         countries | ||||
|                             .into_iter() | ||||
|                             .map(|country| { | ||||
|                                 dir::DirValue::BillingCountry(common_enums::Country::from_alpha2( | ||||
|                                     country, | ||||
|                                 )) | ||||
|                             }) | ||||
|                             .collect(), | ||||
|                         None, | ||||
|                         None::<()>, | ||||
|                     ) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|                 agg_nodes.push(( | ||||
|                     pm_object_country_value_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|             admin::AcceptedCountries::DisableOnly(countries) => { | ||||
|                 let pm_object_country_value_node = builder | ||||
|                     .make_in_aggregator( | ||||
|                         countries | ||||
|                             .into_iter() | ||||
|                             .map(|country| { | ||||
|                                 dir::DirValue::BillingCountry(common_enums::Country::from_alpha2( | ||||
|                                     country, | ||||
|                                 )) | ||||
|                             }) | ||||
|                             .collect(), | ||||
|                         None, | ||||
|                         None::<()>, | ||||
|                     ) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|                 agg_nodes.push(( | ||||
|                     pm_object_country_value_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|             admin::AcceptedCountries::AllAccepted => return Ok(None), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // country from config | ||||
|     if let Some(config) = config | ||||
|         .0 | ||||
|         .get(connector.as_str()) | ||||
|         .or_else(|| config.0.get("default")) | ||||
|     { | ||||
|         if let Some(value) = config | ||||
|             .0 | ||||
|             .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|                 *payment_method_type, | ||||
|             )) | ||||
|         { | ||||
|             if let Some(config_countries) = value.country.as_ref() { | ||||
|                 let config_countries: Vec<common_enums::Country> = Vec::from_iter(config_countries) | ||||
|                     .into_iter() | ||||
|                     .map(|country| common_enums::Country::from_alpha2(*country)) | ||||
|                     .collect(); | ||||
|                 let dir_countries: Vec<dir::DirValue> = config_countries | ||||
|                     .into_iter() | ||||
|                     .map(dir::DirValue::BillingCountry) | ||||
|                     .collect(); | ||||
|  | ||||
|                 let config_country_agg_node = builder | ||||
|                     .make_in_aggregator(dir_countries, None, None::<()>) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|                 agg_nodes.push(( | ||||
|                     config_country_agg_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     Ok(Some( | ||||
|         builder | ||||
|             .make_all_aggregator(&agg_nodes, None, None::<()>, None) | ||||
|             .map_err(KgraphError::GraphConstructionError)?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| fn compile_accepted_currency_for_mca( | ||||
|     builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, | ||||
|     payment_method_type: &enums::PaymentMethodType, | ||||
|     pm_currency: Option<admin::AcceptedCurrencies>, | ||||
|     config: &settings::ConnectorFilters, | ||||
|     connector: String, | ||||
| ) -> Result<Option<cgraph::NodeId>, KgraphError> { | ||||
|     let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); | ||||
|     // Currency from the MCA | ||||
|     if let Some(pm_obj_currency) = pm_currency { | ||||
|         match pm_obj_currency { | ||||
|             admin::AcceptedCurrencies::EnableOnly(currency) => { | ||||
|                 let pm_object_currency_value_node = builder | ||||
|                     .make_in_aggregator( | ||||
|                         currency | ||||
|                             .into_iter() | ||||
|                             .map(dir::DirValue::PaymentCurrency) | ||||
|                             .collect(), | ||||
|                         None, | ||||
|                         None::<()>, | ||||
|                     ) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|                 agg_nodes.push(( | ||||
|                     pm_object_currency_value_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|             admin::AcceptedCurrencies::DisableOnly(currency) => { | ||||
|                 let pm_object_currency_value_node = builder | ||||
|                     .make_in_aggregator( | ||||
|                         currency | ||||
|                             .into_iter() | ||||
|                             .map(dir::DirValue::PaymentCurrency) | ||||
|                             .collect(), | ||||
|                         None, | ||||
|                         None::<()>, | ||||
|                     ) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|                 agg_nodes.push(( | ||||
|                     pm_object_currency_value_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|             admin::AcceptedCurrencies::AllAccepted => return Ok(None), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // country from config | ||||
|     if let Some(config) = config | ||||
|         .0 | ||||
|         .get(connector.as_str()) | ||||
|         .or_else(|| config.0.get("default")) | ||||
|     { | ||||
|         if let Some(value) = config | ||||
|             .0 | ||||
|             .get(&settings::PaymentMethodFilterKey::PaymentMethodType( | ||||
|                 *payment_method_type, | ||||
|             )) | ||||
|         { | ||||
|             if let Some(config_currencies) = value.currency.as_ref() { | ||||
|                 let config_currency: Vec<common_enums::Currency> = | ||||
|                     Vec::from_iter(config_currencies) | ||||
|                         .into_iter() | ||||
|                         .cloned() | ||||
|                         .collect(); | ||||
|  | ||||
|                 let dir_currencies: Vec<dir::DirValue> = config_currency | ||||
|                     .into_iter() | ||||
|                     .map(dir::DirValue::PaymentCurrency) | ||||
|                     .collect(); | ||||
|  | ||||
|                 let config_currency_agg_node = builder | ||||
|                     .make_in_aggregator(dir_currencies, None, None::<()>) | ||||
|                     .map_err(KgraphError::GraphConstructionError)?; | ||||
|  | ||||
|                 agg_nodes.push(( | ||||
|                     config_currency_agg_node, | ||||
|                     cgraph::Relation::Positive, | ||||
|                     cgraph::Strength::Strong, | ||||
|                 )); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     Ok(Some( | ||||
|         builder | ||||
|             .make_all_aggregator(&agg_nodes, None, None::<()>, None) | ||||
|             .map_err(KgraphError::GraphConstructionError)?, | ||||
|     )) | ||||
| } | ||||
| @ -430,6 +430,9 @@ impl MerchantConnectorAccountInterface for Store { | ||||
|                     cache::CacheKind::CGraph( | ||||
|                         format!("cgraph_{}_{_profile_id}", _merchant_id).into(), | ||||
|                     ), | ||||
|                     cache::CacheKind::PmFiltersCGraph( | ||||
|                         format!("pm_filters_cgraph_{}_{_profile_id}", _merchant_id).into(), | ||||
|                     ), | ||||
|                 ], | ||||
|                 update_call, | ||||
|             ) | ||||
| @ -487,6 +490,9 @@ impl MerchantConnectorAccountInterface for Store { | ||||
|                     cache::CacheKind::CGraph( | ||||
|                         format!("cgraph_{}_{_profile_id}", mca.merchant_id).into(), | ||||
|                     ), | ||||
|                     cache::CacheKind::PmFiltersCGraph( | ||||
|                         format!("pm_filters_cgraph_{}_{_profile_id}", mca.merchant_id).into(), | ||||
|                     ), | ||||
|                 ], | ||||
|                 delete_call, | ||||
|             ) | ||||
|  | ||||
| @ -31,6 +31,9 @@ const ROUTING_CACHE_PREFIX: &str = "routing"; | ||||
| /// Prefix for cgraph cache key | ||||
| const CGRAPH_CACHE_PREFIX: &str = "cgraph"; | ||||
|  | ||||
| /// Prefix for PM Filter cgraph cache key | ||||
| const PM_FILTERS_CGRAPH_CACHE_PREFIX: &str = "pm_filters_cgraph"; | ||||
|  | ||||
| /// Prefix for all kinds of cache key | ||||
| const ALL_CACHE_PREFIX: &str = "all_cache_kind"; | ||||
|  | ||||
| @ -58,6 +61,10 @@ pub static ROUTING_CACHE: Lazy<Cache> = | ||||
| pub static CGRAPH_CACHE: Lazy<Cache> = | ||||
|     Lazy::new(|| Cache::new(CACHE_TTL, CACHE_TTI, Some(MAX_CAPACITY))); | ||||
|  | ||||
| /// PM Filter CGraph Cache | ||||
| pub static PM_FILTERS_CGRAPH_CACHE: Lazy<Cache> = | ||||
|     Lazy::new(|| Cache::new(CACHE_TTL, CACHE_TTI, Some(MAX_CAPACITY))); | ||||
|  | ||||
| /// Trait which defines the behaviour of types that's gonna be stored in Cache | ||||
| pub trait Cacheable: Any + Send + Sync + DynClone { | ||||
|     fn as_any(&self) -> &dyn Any; | ||||
| @ -68,6 +75,7 @@ pub enum CacheKind<'a> { | ||||
|     Accounts(Cow<'a, str>), | ||||
|     Routing(Cow<'a, str>), | ||||
|     CGraph(Cow<'a, str>), | ||||
|     PmFiltersCGraph(Cow<'a, str>), | ||||
|     All(Cow<'a, str>), | ||||
| } | ||||
|  | ||||
| @ -78,6 +86,7 @@ impl<'a> From<CacheKind<'a>> for RedisValue { | ||||
|             CacheKind::Accounts(s) => format!("{ACCOUNTS_CACHE_PREFIX},{s}"), | ||||
|             CacheKind::Routing(s) => format!("{ROUTING_CACHE_PREFIX},{s}"), | ||||
|             CacheKind::CGraph(s) => format!("{CGRAPH_CACHE_PREFIX},{s}"), | ||||
|             CacheKind::PmFiltersCGraph(s) => format!("{PM_FILTERS_CGRAPH_CACHE_PREFIX},{s}"), | ||||
|             CacheKind::All(s) => format!("{ALL_CACHE_PREFIX},{s}"), | ||||
|         }; | ||||
|         Self::from_string(value) | ||||
| @ -97,6 +106,9 @@ impl<'a> TryFrom<RedisValue> for CacheKind<'a> { | ||||
|             CONFIG_CACHE_PREFIX => Ok(Self::Config(Cow::Owned(split.1.to_string()))), | ||||
|             ROUTING_CACHE_PREFIX => Ok(Self::Routing(Cow::Owned(split.1.to_string()))), | ||||
|             CGRAPH_CACHE_PREFIX => Ok(Self::CGraph(Cow::Owned(split.1.to_string()))), | ||||
|             PM_FILTERS_CGRAPH_CACHE_PREFIX => { | ||||
|                 Ok(Self::PmFiltersCGraph(Cow::Owned(split.1.to_string()))) | ||||
|             } | ||||
|             ALL_CACHE_PREFIX => Ok(Self::All(Cow::Owned(split.1.to_string()))), | ||||
|             _ => Err(validation_err.into()), | ||||
|         } | ||||
|  | ||||
| @ -3,7 +3,8 @@ use redis_interface::{errors as redis_errors, PubsubInterface, RedisValue}; | ||||
| use router_env::{logger, tracing::Instrument}; | ||||
|  | ||||
| use crate::redis::cache::{ | ||||
|     CacheKey, CacheKind, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE, ROUTING_CACHE, | ||||
|     CacheKey, CacheKind, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE, PM_FILTERS_CGRAPH_CACHE, | ||||
|     ROUTING_CACHE, | ||||
| }; | ||||
|  | ||||
| #[async_trait::async_trait] | ||||
| @ -99,6 +100,16 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> { | ||||
|                         .await; | ||||
|                     key | ||||
|                 } | ||||
|                 CacheKind::PmFiltersCGraph(key) => { | ||||
|                     PM_FILTERS_CGRAPH_CACHE | ||||
|                         .remove(CacheKey { | ||||
|                             key: key.to_string(), | ||||
|                             prefix: self.key_prefix.clone(), | ||||
|                         }) | ||||
|                         .await; | ||||
|  | ||||
|                     key | ||||
|                 } | ||||
|                 CacheKind::Routing(key) => { | ||||
|                     ROUTING_CACHE | ||||
|                         .remove(CacheKey { | ||||
| @ -127,6 +138,12 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> { | ||||
|                             prefix: self.key_prefix.clone(), | ||||
|                         }) | ||||
|                         .await; | ||||
|                     PM_FILTERS_CGRAPH_CACHE | ||||
|                         .remove(CacheKey { | ||||
|                             key: key.to_string(), | ||||
|                             prefix: self.key_prefix.clone(), | ||||
|                         }) | ||||
|                         .await; | ||||
|                     ROUTING_CACHE | ||||
|                         .remove(CacheKey { | ||||
|                             key: key.to_string(), | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Prajjwal Kumar
					Prajjwal Kumar