From 2142adaacc57cc1093a2e09ffea3bec20793a9b7 Mon Sep 17 00:00:00 2001 From: "harsith a.k" Date: Wed, 18 Jun 2025 15:38:31 +0530 Subject: [PATCH 1/3] Added support --- src/decider/gatewaydecider/gw_filter.rs | 51 ++++++++++++++++++- src/decider/gatewaydecider/utils.rs | 9 ++++ src/storage/types.rs | 1 + .../merchant/merchant_gateway_account.rs | 23 +++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/decider/gatewaydecider/gw_filter.rs b/src/decider/gatewaydecider/gw_filter.rs index 8276b94c..2431cb01 100644 --- a/src/decider/gatewaydecider/gw_filter.rs +++ b/src/decider/gatewaydecider/gw_filter.rs @@ -1692,13 +1692,62 @@ pub async fn filterGatewaysForValidationType( let amount = Utils::effective_amount_with_txn_amount(txn_detail.clone()).await; + // New Work + + let m_pf = Utils::get_pf_from_validation_type(&validation_type); + + let (empty_csi_mgas , gpmf_source_mgas): (Vec<_>, Vec<_>,) = enabled_gateway_accounts.clone().into_iter().partition(|mga| mga.config_source_info.is_none()); + + // --- Second Partition on gpmfSourceMgas --- + // (gpmfBasedMgas, nonGpmfInferredMgas) = DL.partition (...) gpmfSourceMgas + let (gpmf_based_mgas, non_gpmf_inferred_mgas): (Vec<_>, Vec<_>) = + gpmf_source_mgas + .into_iter() + .partition(|mga| { + let config_info_opt = &mga.config_source_info; // This will be Some(ConfigSourceInfo) at this point + + // Access gpmf_inferred_flows within the Some(config_info) + // Equivalent to `(.gpmfInferredFlows) =<< mga.configSourceInfo` + let gpmf_flows_opt: Option<&Vec> = config_info_opt + .as_ref() // Get a reference to the inner ConfigSourceInfo + .and_then(|info| info.gpmf_inferred_flows.as_ref()); // Get a reference to the inner Vec> + + // isNothing ((.gpmfInferredFlows) =<< mga.configSourceInfo) + let is_gpmf_flows_none = gpmf_flows_opt.is_none(); + + // (null $ fromMaybe [] ((.gpmfInferredFlows) =<< mga.configSourceInfo)) + let is_gpmf_flows_empty = gpmf_flows_opt.map_or(true, |flows| flows.is_empty()); + // `map_or(true, ...)` means: if `gpmf_flows_opt` is `None`, return `true` (it's "empty" in this context). + // If it's `Some(flows)`, check if `flows.is_empty()`. + + // (isJust mPf && elem mPf (maybe [] ((<$>) Just) ((.gpmfInferredFlows) =<< mga.configSourceInfo))) + let m_pf_is_present_in_gpmf_flows = m_pf.as_ref().map_or(false, |pf_val| { + gpmf_flows_opt.map_or(false, |flows_vec_opt| { flows_vec_opt.contains(pf_val)}) + }); + // Detailed breakdown of the above line: + // m_pf.as_ref().map_or(false, |pf_val| { ... }): Only proceeds if m_pf is Some(Flow), otherwise false. + // gpmf_flows_opt.map_or(false, |flows_vec_opt| { ... }): Only proceeds if gpmf_inferred_flows is Some(Vec<...>), otherwise false. + // flows_vec_opt.iter().any(|opt_flow| { ... }): Iterates through each Option in the inner Vec. + // opt_flow.as_ref().map_or(false, |flow_val| flow_val == pf_val): Checks if the Option is Some(Flow) and if that inner Flow matches our m_pf value. + + is_gpmf_flows_none || is_gpmf_flows_empty || m_pf_is_present_in_gpmf_flows + }); + + // --- Concatenation --- + // gciBasedMgas = emptyCSIMgas <> nonGpmfInferredMgas + // Using `chain` for efficient concatenation of iterators without collecting intermediate vecs + let gci_based_mgas: Vec = empty_csi_mgas + .into_iter() + .chain(non_gpmf_inferred_mgas.into_iter()) + .collect(); + // Filter gateways for payment method and validation type let merchant_gateway_card_infos = SETMCI::filter_gateways_for_payment_method_and_validation_type( this.state(), macc, txn_card_info.clone(), - enabled_gateway_accounts.clone(), + gci_based_mgas.clone(), validation_type, transaction_id_to_text(txn_detail.txnId.clone()), ) diff --git a/src/decider/gatewaydecider/utils.rs b/src/decider/gatewaydecider/utils.rs index 2e35a2bf..ce1df524 100644 --- a/src/decider/gatewaydecider/utils.rs +++ b/src/decider/gatewaydecider/utils.rs @@ -2727,6 +2727,15 @@ pub async fn get_penality_factor_( } } +pub fn get_pf_from_validation_type(vt: &ValidationType) -> Option { + match vt { + ValidationType::Tpv => Some(PaymentFlow::TPV), + ValidationType::TpvEmandate => Some(PaymentFlow::TPV_EMANDATE), + ValidationType::Emandate => Some(PaymentFlow::EMANDATE), + _ => None, + } +} + // fn push_to_stream(decided_gateway: OptionETG::Gateway, final_decider_approach: types::GatewayDeciderApproach, m_priority_logic_tag: Option, current_gateway_score_map: GatewayScoreMap) -> DeciderFlow<()> { // if let Some(decided_gateway) = decided_gateway { diff --git a/src/storage/types.rs b/src/storage/types.rs index af24889c..9044ccbd 100644 --- a/src/storage/types.rs +++ b/src/storage/types.rs @@ -332,6 +332,7 @@ pub struct MerchantGatewayAccount { pub gateway_identifier: Option, pub gateway_type: Option, pub supported_txn_type: Option, + pub config_source_info: Option, } #[derive(Debug, Clone, Identifiable, Queryable)] diff --git a/src/types/merchant/merchant_gateway_account.rs b/src/types/merchant/merchant_gateway_account.rs index aecc9eb3..e2049099 100644 --- a/src/types/merchant/merchant_gateway_account.rs +++ b/src/types/merchant/merchant_gateway_account.rs @@ -25,6 +25,7 @@ use crate::storage::schema_pg::merchant_gateway_account::dsl; use diesel::associations::HasTable; use diesel::*; use std::fmt::Debug; +use crate::types::payment_flow::PaymentFlow // #[derive(Debug, PartialEq, Serialize, Deserialize)] // pub struct EulerAccountDetails { @@ -86,6 +87,25 @@ pub fn to_mga_reference_id(id: String) -> MgaReferenceId { } } + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +enum FlowConfigSource { GPMF} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ConfigSourceInfo { + #[serde(rename = "flowConfigSource")] + pub flow_config_source: Vec, + #[serde(rename = "gpmfInferredFlows")] + pub gpmf_inferred_flows: Option>, +} + +fn to_config_source_info(csi: String) -> Result { + match serde_json::from_str::(&csi) { + Ok(res) => Ok(res), + Err(_) => Err(ApiError::ParsingError("Invalid ConfigSourceInfo value")), + } +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MerchantGatewayAccount { #[serde(rename = "id")] @@ -112,6 +132,8 @@ pub struct MerchantGatewayAccount { pub gatewayType: Option, #[serde(rename = "supportedTxnType")] pub supportedTxnType: Option, + #[serde(rename = "configSourceInfo")] + pub config_source_info: Option, } impl TryFrom for MerchantGatewayAccount { @@ -134,6 +156,7 @@ impl TryFrom for MerchantGatewayAccount { gatewayIdentifier: value.gateway_identifier, gatewayType: value.gateway_type, supportedTxnType: value.supported_txn_type, + config_source_info: value.config_source_info.map(|csi| to_config_source_info(csi)).transpose()?, }) } } From 6795e35daf61d010da63720e9d8deaf568ad1bf4 Mon Sep 17 00:00:00 2001 From: "harsith a.k" Date: Fri, 20 Jun 2025 19:43:34 +0530 Subject: [PATCH 2/3] Added decider changes --- src/decider/gatewaydecider/gw_filter.rs | 99 ++++++++++++-- src/decider/gatewaydecider/utils.rs | 67 +++++++++- src/storage/schema.rs | 16 +++ src/storage/types.rs | 13 ++ src/types.rs | 1 + .../merchant/merchant_gateway_account.rs | 2 +- src/types/micro_payment_flow.rs | 124 ++++++++++++++++++ src/types/payment_flow.rs | 79 +++++++++++ 8 files changed, 391 insertions(+), 10 deletions(-) create mode 100644 src/types/micro_payment_flow.rs diff --git a/src/decider/gatewaydecider/gw_filter.rs b/src/decider/gatewaydecider/gw_filter.rs index 2431cb01..f4bbb618 100644 --- a/src/decider/gatewaydecider/gw_filter.rs +++ b/src/decider/gatewaydecider/gw_filter.rs @@ -1757,9 +1757,9 @@ pub async fn filterGatewaysForValidationType( let merchant_gateway_card_infos = Utils::filter_gateway_card_info_for_max_register_amount( txn_detail.clone(), - txn_card_info, + txn_card_info.clone(), merchant_gateway_card_infos, - amount, + amount.clone(), ); // Extract gateway card info IDs @@ -1783,10 +1783,10 @@ pub async fn filterGatewaysForValidationType( let new_st = catMaybes(&nst); - // Update gateway list and MGAs - setGwsAndMgas( - this, - enabled_gateway_accounts + + // NEW LOGIC + + let mgci_matched_final_mgas: Vec = gci_based_mgas .into_iter() .filter(|mga| { new_st.contains(&mga.gateway) @@ -1796,8 +1796,91 @@ pub async fn filterGatewaysForValidationType( .collect::>() .contains(&Some(mga.id.clone())) }) - .collect(), - ); + .collect(); + + let mgpmf_matched_final_mgas = match (m_pf.as_ref(), gpmf_based_mgas.is_empty()) { + (Some(pf), false) => { + let maybe_jbc = find_bank_code(txn_card_info.paymentMethod).await; + match maybe_jbc { + None => vec![], + Some(jbc) => { + let gw_list: Vec = gpmf_based_mgas + .iter() + .map(|mga| mga.gateway.clone()) + .collect(); + + // let all_gpmf_entries = GPMF::find_all_by_country_code_gw_pf_id_pmt_jbcid( + // CountryCode::IND, + // &gw_list, + // pf.clone(), + // txn_card_info.payment_method_type.clone(), + // jbc.id.clone(), + // ) + // .await?; + + let all_gpmf_entries = GPMF::find_all_gpmf_by_country_code_gw_pf_id_pmt_jbcid_db( + crate::types::country::country_iso::CountryISO::IND, + gw_list, + pf.clone(), + txn_card_info.paymentMethodType.clone(), + jbc.id, + ) + .await + .unwrap_or_default(); + // Extract MGA IDs and GPMF IDs for further filtering + let mga_ids = gpmf_based_mgas + .iter() + .map(|mga| mga.id.merchantGwAccId) + .collect::>(); + + let gpmf_ids: Vec = all_gpmf_entries + .iter() + .map(|entry| to_gateway_payment_method_flow_id(entry.id.clone())) + .collect(); + + // Get merchant gateway payment method flows that match both MGA and GPMF + let mgpmf_entries = + MGPMF::get_all_mgpmf_by_mga_id_and_gpmf_ids(mga_ids, gpmf_ids).await; + + + // let mga_ids: Vec = gpmf_based_mgas + // .iter() + // .map(|mga| mga.id.merchant_gw_acc_id.clone()) + // .collect(); + + // let gpmf_ids: Vec = + // all_gpmf_entries.iter().map(|e| e.id.clone()).collect(); + + // let mgpmf_entries = MGPMF::get_all_by_mga_and_gpmf_ids(&mga_ids, &gpmf_ids).await?; + + let max_amount_filtered_entries = Utils::filter_mgpmf_for_max_register_amount( + pf.clone(), + &txn_card_info.paymentMethodType, + mgpmf_entries.clone(), + amount, + ); + + // Extract merchant gateway account IDs that have matching payment method flows + let mgpmf_mga_id_entries = max_amount_filtered_entries + .await.iter() + .map(|entry| entry.merchantGatewayAccountId) + .collect::>(); + + // Final filtering of MGAs to only those with matching payment flows + gpmf_based_mgas + .into_iter() + .filter(|mga| mgpmf_mga_id_entries.contains(&mga.id.merchantGwAccId)) + .collect() + } + } + } + _ => vec![], + }; + + // Update gateway list and MGAs + + let new_gws = [mgci_matched_final_mgas, mgpmf_matched_final_mgas].concat(); + setGwsAndMgas(this, new_gws); } } // Handle other transaction types diff --git a/src/decider/gatewaydecider/utils.rs b/src/decider/gatewaydecider/utils.rs index ce1df524..5a43cdc2 100644 --- a/src/decider/gatewaydecider/utils.rs +++ b/src/decider/gatewaydecider/utils.rs @@ -6,7 +6,7 @@ use crate::types::card::card_type::card_type_to_text; use crate::types::merchant::id::{merchant_id_to_text, MerchantId}; use crate::types::merchant::merchant_gateway_account::MerchantGatewayAccount; use crate::types::money::internal::Money; -use crate::types::payment_flow::{payment_flows_to_text, PaymentFlow}; +use crate::types::payment_flow::{payment_flows_to_text, to_flow_level_id, FlowLevel, FlowLevelId, MicroPaymentFlowName, PaymentFlow}; use crate::types::user_eligibility_info::{ get_eligibility_info, identifier_name_to_text, IdentifierName, }; @@ -85,6 +85,8 @@ use crate::types::isin_routes as ETIsinR; // // use configs::env_vars as ENV; use crate::types::gateway_card_info::ValidationType; use crate::error::StorageError; +use crate::types::merchant_gateway_payment_method_flow::{ MerchantGatewayPaymentMethodFlow}; +use crate::types::micro_payment_flow:: {find_mpf_by_flow_level_flow_level_ids_mpf_name, MicroPaymentFlowF}; pub fn either_decode_t Deserialize<'de>>(text: &str) -> Result { from_slice(text.as_bytes()).map_err(|e| e.to_string()) @@ -616,6 +618,10 @@ pub fn is_emandate_register_transaction(txn_detail: &ETTD::TxnDetail) -> bool { txn_detail.txnObjectType == ETTD::TxnObjectType::EmandateRegister } +fn is_pmt_eligible_for_emandate_amount_filter(pmt: &PaymentMethodType) -> bool { + matches!(pmt, PaymentMethodType::Card | PaymentMethodType::NB | PaymentMethodType::Aadhaar | PaymentMethodType::PAN) +} + pub async fn get_card_brand(decider_flow: &mut DeciderFlow<'_>) -> Option { let c_card_brand = decider_flow.writer.cardBrand.clone(); if let Some(cb) = c_card_brand { @@ -2737,6 +2743,65 @@ pub fn get_pf_from_validation_type(vt: &ValidationType) -> Option { } +pub async fn filter_mgpmf_for_max_register_amount( + pf: PaymentFlow, + payment_method_type: &PaymentMethodType, + merchant_gateway_payment_method_flows: Vec, + txn_amount: Money, +) -> Vec { + if matches!(pf, PaymentFlow::EMANDATE | PaymentFlow::TPV_EMANDATE) + && is_pmt_eligible_for_emandate_amount_filter(payment_method_type) + { + let flow_level_ids = extract_mgpmf_ids_as_flow_level_ids(&merchant_gateway_payment_method_flows); + + let min_amount = Money::from_double(10000.0); + + let mpfs: Vec = find_mpf_by_flow_level_flow_level_ids_mpf_name(FlowLevel::MerchantGatewayPaymentMethodFlow, flow_level_ids, MicroPaymentFlowName::RegisterMaxAmount).await; + + let filtered_ids: Vec = mpfs + .iter() + .filter_map(|mpf| { + let val= from_str(&mpf.value).ok().unwrap_or(min_amount.clone()); + if txn_amount <= val { + Some(mpf.flowLevelId.clone()) + } else { + None + } + }) + .collect(); + + merchant_gateway_payment_method_flows + .into_iter() + // .filter(|mgpmf| filtered_ids.contains(&mgpmf.id)) + .filter_map(|mgpmf| { + match mgpmf.id.map(|id| to_flow_level_id(id.to_string())) { + Some(value) => if filtered_ids.contains(&value) { + Some(mgpmf) + } else { + None + } + _ => None + } + + + }) + .collect() + } else { + merchant_gateway_payment_method_flows + } +} + + +fn extract_mgpmf_ids_as_flow_level_ids( + flows: &[MerchantGatewayPaymentMethodFlow], +) -> Vec { + flows + .iter() + .filter_map(|flow| flow.id.map(|id| to_flow_level_id(id.to_string()))) + .collect() +} + + // fn push_to_stream(decided_gateway: OptionETG::Gateway, final_decider_approach: types::GatewayDeciderApproach, m_priority_logic_tag: Option, current_gateway_score_map: GatewayScoreMap) -> DeciderFlow<()> { // if let Some(decided_gateway) = decided_gateway { // let merchant = asks(|ctx| ctx.dp_merchant_account); diff --git a/src/storage/schema.rs b/src/storage/schema.rs index bd3cbfe4..feaf6955 100644 --- a/src/storage/schema.rs +++ b/src/storage/schema.rs @@ -253,6 +253,7 @@ diesel::table! { gateway_identifier -> Nullable, gateway_type -> Nullable, supported_txn_type -> Nullable, + config_source_info -> Nullable, } } @@ -293,6 +294,20 @@ diesel::table! { } } +diesel::table! { + micro_payment_flow (id) { + id -> Text, + flow_level -> Text, + flow_level_id -> Text, + micro_payment_flow_name -> Text, + #[sql_name = "type"] + value_type -> Text, + value -> Text, + date_created -> Datetime, + last_updated -> Datetime, + } +} + diesel::table! { merchant_iframe_preferences (id) { id -> Bigint, @@ -546,3 +561,4 @@ diesel::allow_tables_to_appear_in_same_query!( txn_offer_detail, user_eligibility_info, ); + diff --git a/src/storage/types.rs b/src/storage/types.rs index 9044ccbd..64be4ff1 100644 --- a/src/storage/types.rs +++ b/src/storage/types.rs @@ -374,6 +374,19 @@ pub struct MerchantGatewayPaymentMethodFlow { pub gateway_bank_code: Option, } +#[derive(Debug, Clone, Identifiable, Queryable)] +#[cfg_attr(feature = "mysql", diesel(table_name = schema::micro_payment_flow))] +#[cfg_attr(feature = "postgres", diesel(table_name = schema_pg::micro_payment_flow))] +pub struct MicroPaymentFlow { + pub id: String, + pub flow_level: String, + pub flow_level_id: String, + pub micro_payment_flow_name: String, + pub value_type: String, + pub value: String, + pub date_created: PrimitiveDateTime, + pub last_updated: PrimitiveDateTime, +} #[derive(Debug, Clone, PartialEq, FromSqlRow, AsExpression, Serialize)] #[cfg_attr(feature = "mysql", diesel(sql_type = Bit))] diff --git a/src/types.rs b/src/types.rs index 47f6d310..81cdd381 100644 --- a/src/types.rs +++ b/src/types.rs @@ -38,3 +38,4 @@ pub mod txn_offer; pub mod txn_offer_detail; pub mod txn_offer_info; pub mod user_eligibility_info; +pub mod micro_payment_flow; diff --git a/src/types/merchant/merchant_gateway_account.rs b/src/types/merchant/merchant_gateway_account.rs index e2049099..440c28e6 100644 --- a/src/types/merchant/merchant_gateway_account.rs +++ b/src/types/merchant/merchant_gateway_account.rs @@ -25,7 +25,7 @@ use crate::storage::schema_pg::merchant_gateway_account::dsl; use diesel::associations::HasTable; use diesel::*; use std::fmt::Debug; -use crate::types::payment_flow::PaymentFlow +use crate::types::payment_flow::PaymentFlow; // #[derive(Debug, PartialEq, Serialize, Deserialize)] // pub struct EulerAccountDetails { diff --git a/src/types/micro_payment_flow.rs b/src/types/micro_payment_flow.rs new file mode 100644 index 00000000..98585a72 --- /dev/null +++ b/src/types/micro_payment_flow.rs @@ -0,0 +1,124 @@ +use crate::app::get_tenant_app_state; +use crate::types::payment_flow::*; +use serde::{Deserialize, Serialize}; +use std::string::String; +use std::time::SystemTime; +use std::vec::Vec; +use crate::storage::types::{MicroPaymentFlow as DBMicroPaymentFlow}; + +use super::payment_flow::{FlowLevel, MicroPaymentFlowName, FlowLevelId, MicroPaymentFlowType, to_flow_level_id, text_to_flow_level, text_to_micro_payment_flow_name, text_to_micro_payment_flow_type + }; +#[cfg(feature = "mysql")] +use crate::storage::schema::micro_payment_flow::dsl; +#[cfg(feature = "postgres")] +use crate::storage::schema_pg::micro_payment_flow::dsl; +use diesel::associations::HasTable; +use diesel::*; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MicroPaymentFlowId { + pub microPaymentFlowId: String, +} + +pub fn to_micro_payment_flow_id(id: String) -> MicroPaymentFlowId { + MicroPaymentFlowId { + microPaymentFlowId: id, + } +} + +pub fn micro_payment_flow_id_text(id: MicroPaymentFlowId) -> String { + id.microPaymentFlowId +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MicroPaymentFlowF { + #[serde(rename = "id")] + pub id: MicroPaymentFlowId, + #[serde(rename = "flowLevel")] + pub flowLevel: FlowLevel, + #[serde(rename = "flowLevelId")] + pub flowLevelId: FlowLevelId, + #[serde(rename = "value")] + pub value: String, + #[serde(rename = "microPaymentFlowName")] + pub microPaymentFlowName: MicroPaymentFlowName, + #[serde(rename = "valueType")] + pub valueType: MicroPaymentFlowType, + #[serde(rename = "dateCreated")] + pub dateCreated: SystemTime, + #[serde(rename = "lastUpdated")] + pub lastUpdated: SystemTime, +} + +impl TryFrom for MicroPaymentFlowF { + type Error = crate::error::ApiError; + + fn try_from(db: DBMicroPaymentFlow) -> Result { + // Convert string IDs to domain types + let id = to_micro_payment_flow_id(db.id); + let flow_level = text_to_flow_level(db.flow_level)?; + let flow_level_id = to_flow_level_id(db.flow_level_id); + let micro_payment_flow_name = text_to_micro_payment_flow_name(db.micro_payment_flow_name)?; + let value_type = text_to_micro_payment_flow_type(db.value_type)?; + + // Construct the GatewayPaymentMethodFlow instance + Ok(Self { + id, + flowLevel:flow_level, + flowLevelId:flow_level_id, + dateCreated: db.date_created.assume_utc().into(), + lastUpdated: db.last_updated.assume_utc().into(), + microPaymentFlowName:micro_payment_flow_name, + valueType:value_type, + value: db.value, + }) + } +} + +pub async fn find_mpf_by_flow_level_flow_level_ids_mpf_name_db( + flow_level: FlowLevel, + flow_level_ids: Vec, + mpf_name: MicroPaymentFlowName, +) -> Result, crate::generics::MeshError> { + let app_state = get_tenant_app_state().await; + + // Extract and convert various IDs to their string representations + let flow_level_text = flow_level_to_text(&flow_level); + let mpfn_text = micro_payment_flow_name_to_text(&mpf_name); + let flow_level_ids_text: Vec = flow_level_ids.into_iter().map(|fl_id| fl_id.flowLevelId).collect(); + + + // Use Diesel's query builder with multiple conditions + crate::generics::generic_find_all::< + ::Table, + _, + DBMicroPaymentFlow, + >( + &app_state.db, + dsl::flow_level_id + .eq_any(flow_level_ids_text) + .and(dsl::flow_level.eq(flow_level_text)) + .and(dsl::micro_payment_flow_name.eq(mpfn_text)), + ) + .await +} + +pub async fn find_mpf_by_flow_level_flow_level_ids_mpf_name( + flow_level: FlowLevel, + flow_level_ids: Vec, + mpf_name: MicroPaymentFlowName, +) -> Vec { + // Call the DB function and handle the results + match find_mpf_by_flow_level_flow_level_ids_mpf_name_db(flow_level, flow_level_ids, mpf_name) + .await + { + Ok(db_results) => db_results + .into_iter() + .filter_map(|db_record: DBMicroPaymentFlow| { + MicroPaymentFlowF::try_from(db_record).ok() + }) + .collect(), + Err(_) => Vec::new(), // Silently handle any errors by returning empty vec + } +} + diff --git a/src/types/payment_flow.rs b/src/types/payment_flow.rs index df01244c..884d98a3 100644 --- a/src/types/payment_flow.rs +++ b/src/types/payment_flow.rs @@ -504,3 +504,82 @@ pub struct AddressVerification { #[serde(rename = "collectAvsInfo")] pub collectAvsInfo: Option, } + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum FlowLevel { + PaymentFlow, + GatewayPaymentFlow, + GatewayPaymentMethodFlow, + MerchantGatewayPaymentMethodFlow, +} + +pub fn text_to_flow_level(text: String) -> Result { + match text.as_str() { + "PAYMENT_FLOW" => Ok(FlowLevel::PaymentFlow), + "GATEWAY_PAYMENT_FLOW" => Ok(FlowLevel::GatewayPaymentFlow), + "GATEWAY_PAYMENT_METHOD_FLOW" => Ok(FlowLevel::GatewayPaymentMethodFlow), + "MERCHANT_GATEWAY_PAYMENT_METHOD_FLOW" => Ok(FlowLevel::MerchantGatewayPaymentMethodFlow), + _ => Err(ApiError::ParsingError("Invalid FlowLevel")), + } +} + + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + + +pub struct FlowLevelId { + pub flowLevelId: String, +} + +pub fn to_flow_level_id(id: String) -> FlowLevelId { + FlowLevelId { + flowLevelId: id, + } +} + + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum MicroPaymentFlowName { + RegisterMaxAmount, + SupportedFrequencies, + BankAccountDetailsSupportMode, +} + +pub fn text_to_micro_payment_flow_name(text: String) -> Result { + match text.as_str() { + "REGISTER_MAX_AMOUNT" => Ok(MicroPaymentFlowName::RegisterMaxAmount), + "SUPPORTED_FREQUENCIES" => Ok(MicroPaymentFlowName::SupportedFrequencies), + "BANK_ACCOUNT_DETAILS_SUPPORT_MODE" => Ok(MicroPaymentFlowName::BankAccountDetailsSupportMode), + _ => Err(ApiError::ParsingError("Invalid MicroPaymentFlowName")), + } +} + +pub fn text_to_micro_payment_flow_type(text: String) -> Result { + match text.as_str() { + "ARRAY" => Ok(MicroPaymentFlowType::ARRAY), + "BOOLEAN" => Ok(MicroPaymentFlowType::BOOLEAN), + "DOUBLE" => Ok(MicroPaymentFlowType::DOUBLE), + "OBJECT" => Ok(MicroPaymentFlowType::OBJECT), + "STRING" => Ok(MicroPaymentFlowType::STRING), + _ => Err(ApiError::ParsingError("Invalid MicroPaymentFlowType")), + } +} + +pub fn micro_payment_flow_name_to_text(mpfn: &MicroPaymentFlowName) -> String { + match mpfn { + MicroPaymentFlowName::RegisterMaxAmount => "REGISTER_MAX_AMOUNT".to_string(), + MicroPaymentFlowName::SupportedFrequencies => "SUPPORTED_FREQUENCIES".to_string(), + MicroPaymentFlowName::BankAccountDetailsSupportMode => "BANK_ACCOUNT_DETAILS_SUPPORT_MODE".to_string(), + } +} + +pub fn flow_level_to_text(mpfn: &FlowLevel) -> String { + match mpfn { + FlowLevel::PaymentFlow => "PAYMENT_FLOW".to_string(), + FlowLevel::GatewayPaymentFlow => "GATEWAY_PAYMENT_FLOW".to_string(), + FlowLevel::GatewayPaymentMethodFlow => "GATEWAY_PAYMENT_METHOD_FLOW".to_string(), + FlowLevel::MerchantGatewayPaymentMethodFlow => "MERCHANT_GATEWAY_PAYMENT_METHOD_FLOW".to_string(), + } +} \ No newline at end of file From e5a32328c5d2d15d7c4eafa3e118f0317459560b Mon Sep 17 00:00:00 2001 From: "harsith a.k" Date: Mon, 23 Jun 2025 19:28:45 +0530 Subject: [PATCH 3/3] Tested gci to gpmf migration changes --- src/decider/gatewaydecider/gw_filter.rs | 69 +++---------------- src/decider/gatewaydecider/utils.rs | 4 -- .../merchant/merchant_gateway_account.rs | 2 +- src/types/micro_payment_flow.rs | 6 +- 4 files changed, 17 insertions(+), 64 deletions(-) diff --git a/src/decider/gatewaydecider/gw_filter.rs b/src/decider/gatewaydecider/gw_filter.rs index f4bbb618..cb855d5f 100644 --- a/src/decider/gatewaydecider/gw_filter.rs +++ b/src/decider/gatewaydecider/gw_filter.rs @@ -1669,6 +1669,7 @@ pub async fn filterGatewaysForValidationType( (ETGCI::ValidationType::Tpv, e_mgas) }; + if matches!(validation_type,ETGCI::ValidationType::TpvEmandate | ETGCI::ValidationType::Emandate) && is_otm_flow { return Ok(returnGwListWithLog( this, @@ -1692,50 +1693,31 @@ pub async fn filterGatewaysForValidationType( let amount = Utils::effective_amount_with_txn_amount(txn_detail.clone()).await; - // New Work - let m_pf = Utils::get_pf_from_validation_type(&validation_type); let (empty_csi_mgas , gpmf_source_mgas): (Vec<_>, Vec<_>,) = enabled_gateway_accounts.clone().into_iter().partition(|mga| mga.config_source_info.is_none()); // --- Second Partition on gpmfSourceMgas --- - // (gpmfBasedMgas, nonGpmfInferredMgas) = DL.partition (...) gpmfSourceMgas let (gpmf_based_mgas, non_gpmf_inferred_mgas): (Vec<_>, Vec<_>) = gpmf_source_mgas .into_iter() .partition(|mga| { - let config_info_opt = &mga.config_source_info; // This will be Some(ConfigSourceInfo) at this point - - // Access gpmf_inferred_flows within the Some(config_info) - // Equivalent to `(.gpmfInferredFlows) =<< mga.configSourceInfo` - let gpmf_flows_opt: Option<&Vec> = config_info_opt - .as_ref() // Get a reference to the inner ConfigSourceInfo - .and_then(|info| info.gpmf_inferred_flows.as_ref()); // Get a reference to the inner Vec> + let config_info_opt = &mga.config_source_info; - // isNothing ((.gpmfInferredFlows) =<< mga.configSourceInfo) - let is_gpmf_flows_none = gpmf_flows_opt.is_none(); + let gpmf_flows_opt: Option<&Vec> = config_info_opt + .as_ref() + .and_then(|info| info.gpmf_inferred_flows.as_ref()); - // (null $ fromMaybe [] ((.gpmfInferredFlows) =<< mga.configSourceInfo)) let is_gpmf_flows_empty = gpmf_flows_opt.map_or(true, |flows| flows.is_empty()); - // `map_or(true, ...)` means: if `gpmf_flows_opt` is `None`, return `true` (it's "empty" in this context). - // If it's `Some(flows)`, check if `flows.is_empty()`. - // (isJust mPf && elem mPf (maybe [] ((<$>) Just) ((.gpmfInferredFlows) =<< mga.configSourceInfo))) - let m_pf_is_present_in_gpmf_flows = m_pf.as_ref().map_or(false, |pf_val| { + let m_pf_is_present_in_gpmf_flows = m_pf + .as_ref() + .map_or(false, |pf_val| { gpmf_flows_opt.map_or(false, |flows_vec_opt| { flows_vec_opt.contains(pf_val)}) }); - // Detailed breakdown of the above line: - // m_pf.as_ref().map_or(false, |pf_val| { ... }): Only proceeds if m_pf is Some(Flow), otherwise false. - // gpmf_flows_opt.map_or(false, |flows_vec_opt| { ... }): Only proceeds if gpmf_inferred_flows is Some(Vec<...>), otherwise false. - // flows_vec_opt.iter().any(|opt_flow| { ... }): Iterates through each Option in the inner Vec. - // opt_flow.as_ref().map_or(false, |flow_val| flow_val == pf_val): Checks if the Option is Some(Flow) and if that inner Flow matches our m_pf value. - - is_gpmf_flows_none || is_gpmf_flows_empty || m_pf_is_present_in_gpmf_flows + is_gpmf_flows_empty || m_pf_is_present_in_gpmf_flows }); - // --- Concatenation --- - // gciBasedMgas = emptyCSIMgas <> nonGpmfInferredMgas - // Using `chain` for efficient concatenation of iterators without collecting intermediate vecs let gci_based_mgas: Vec = empty_csi_mgas .into_iter() .chain(non_gpmf_inferred_mgas.into_iter()) @@ -1783,9 +1765,6 @@ pub async fn filterGatewaysForValidationType( let new_st = catMaybes(&nst); - - // NEW LOGIC - let mgci_matched_final_mgas: Vec = gci_based_mgas .into_iter() .filter(|mga| { @@ -1808,15 +1787,6 @@ pub async fn filterGatewaysForValidationType( .iter() .map(|mga| mga.gateway.clone()) .collect(); - - // let all_gpmf_entries = GPMF::find_all_by_country_code_gw_pf_id_pmt_jbcid( - // CountryCode::IND, - // &gw_list, - // pf.clone(), - // txn_card_info.payment_method_type.clone(), - // jbc.id.clone(), - // ) - // .await?; let all_gpmf_entries = GPMF::find_all_gpmf_by_country_code_gw_pf_id_pmt_jbcid_db( crate::types::country::country_iso::CountryISO::IND, @@ -1827,7 +1797,7 @@ pub async fn filterGatewaysForValidationType( ) .await .unwrap_or_default(); - // Extract MGA IDs and GPMF IDs for further filtering + let mga_ids = gpmf_based_mgas .iter() .map(|mga| mga.id.merchantGwAccId) @@ -1838,20 +1808,8 @@ pub async fn filterGatewaysForValidationType( .map(|entry| to_gateway_payment_method_flow_id(entry.id.clone())) .collect(); - // Get merchant gateway payment method flows that match both MGA and GPMF let mgpmf_entries = MGPMF::get_all_mgpmf_by_mga_id_and_gpmf_ids(mga_ids, gpmf_ids).await; - - - // let mga_ids: Vec = gpmf_based_mgas - // .iter() - // .map(|mga| mga.id.merchant_gw_acc_id.clone()) - // .collect(); - - // let gpmf_ids: Vec = - // all_gpmf_entries.iter().map(|e| e.id.clone()).collect(); - - // let mgpmf_entries = MGPMF::get_all_by_mga_and_gpmf_ids(&mga_ids, &gpmf_ids).await?; let max_amount_filtered_entries = Utils::filter_mgpmf_for_max_register_amount( pf.clone(), @@ -1860,13 +1818,11 @@ pub async fn filterGatewaysForValidationType( amount, ); - // Extract merchant gateway account IDs that have matching payment method flows let mgpmf_mga_id_entries = max_amount_filtered_entries .await.iter() .map(|entry| entry.merchantGatewayAccountId) .collect::>(); - // Final filtering of MGAs to only those with matching payment flows gpmf_based_mgas .into_iter() .filter(|mga| mgpmf_mga_id_entries.contains(&mga.id.merchantGwAccId)) @@ -1875,10 +1831,7 @@ pub async fn filterGatewaysForValidationType( } } _ => vec![], - }; - - // Update gateway list and MGAs - + }; let new_gws = [mgci_matched_final_mgas, mgpmf_matched_final_mgas].concat(); setGwsAndMgas(this, new_gws); } diff --git a/src/decider/gatewaydecider/utils.rs b/src/decider/gatewaydecider/utils.rs index 5a43cdc2..1960f600 100644 --- a/src/decider/gatewaydecider/utils.rs +++ b/src/decider/gatewaydecider/utils.rs @@ -2742,7 +2742,6 @@ pub fn get_pf_from_validation_type(vt: &ValidationType) -> Option { } } - pub async fn filter_mgpmf_for_max_register_amount( pf: PaymentFlow, payment_method_type: &PaymentMethodType, @@ -2772,7 +2771,6 @@ pub async fn filter_mgpmf_for_max_register_amount( merchant_gateway_payment_method_flows .into_iter() - // .filter(|mgpmf| filtered_ids.contains(&mgpmf.id)) .filter_map(|mgpmf| { match mgpmf.id.map(|id| to_flow_level_id(id.to_string())) { Some(value) => if filtered_ids.contains(&value) { @@ -2791,7 +2789,6 @@ pub async fn filter_mgpmf_for_max_register_amount( } } - fn extract_mgpmf_ids_as_flow_level_ids( flows: &[MerchantGatewayPaymentMethodFlow], ) -> Vec { @@ -2801,7 +2798,6 @@ fn extract_mgpmf_ids_as_flow_level_ids( .collect() } - // fn push_to_stream(decided_gateway: OptionETG::Gateway, final_decider_approach: types::GatewayDeciderApproach, m_priority_logic_tag: Option, current_gateway_score_map: GatewayScoreMap) -> DeciderFlow<()> { // if let Some(decided_gateway) = decided_gateway { // let merchant = asks(|ctx| ctx.dp_merchant_account); diff --git a/src/types/merchant/merchant_gateway_account.rs b/src/types/merchant/merchant_gateway_account.rs index 440c28e6..a3d8d324 100644 --- a/src/types/merchant/merchant_gateway_account.rs +++ b/src/types/merchant/merchant_gateway_account.rs @@ -94,7 +94,7 @@ enum FlowConfigSource { GPMF} #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ConfigSourceInfo { #[serde(rename = "flowConfigSource")] - pub flow_config_source: Vec, + pub flow_config_source: FlowConfigSource, #[serde(rename = "gpmfInferredFlows")] pub gpmf_inferred_flows: Option>, } diff --git a/src/types/micro_payment_flow.rs b/src/types/micro_payment_flow.rs index 98585a72..2875f7f5 100644 --- a/src/types/micro_payment_flow.rs +++ b/src/types/micro_payment_flow.rs @@ -14,6 +14,7 @@ use crate::storage::schema::micro_payment_flow::dsl; use crate::storage::schema_pg::micro_payment_flow::dsl; use diesel::associations::HasTable; use diesel::*; +use crate::logger; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MicroPaymentFlowId { @@ -118,7 +119,10 @@ pub async fn find_mpf_by_flow_level_flow_level_ids_mpf_name( MicroPaymentFlowF::try_from(db_record).ok() }) .collect(), - Err(_) => Vec::new(), // Silently handle any errors by returning empty vec + Err(err) => { + logger::info!("Error in find_mpf_by_flow_level_flow_level_ids_mpf_name: {:?}", err); + Vec::new() // Silently handle any errors by returning empty vec + } } }