diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 0641c84239..04983ed8b1 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2085,7 +2085,15 @@ pub async fn list_payment_methods( } if let Some(choice) = result.get(&intermediate.payment_method_type) { - intermediate.connector == choice.connector.connector_name.to_string() + if let Some(first_routable_connector) = choice.first() { + intermediate.connector + == first_routable_connector + .connector + .connector_name + .to_string() + } else { + false + } } else { false } @@ -2105,26 +2113,32 @@ pub async fn list_payment_methods( let mut pre_routing_results: HashMap< api_enums::PaymentMethodType, - routing_types::RoutableConnectorChoice, + storage::PreRoutingConnectorChoice, > = HashMap::new(); - for (pm_type, choice) in result { - let routable_choice = routing_types::RoutableConnectorChoice { - #[cfg(feature = "backwards_compatibility")] - choice_kind: routing_types::RoutableChoiceKind::FullStruct, - connector: choice - .connector - .connector_name - .to_string() - .parse::() - .change_context(errors::ApiErrorResponse::InternalServerError)?, - #[cfg(feature = "connector_choice_mca_id")] - merchant_connector_id: choice.connector.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: choice.sub_label, - }; - - pre_routing_results.insert(pm_type, routable_choice); + for (pm_type, routing_choice) in result { + let mut routable_choice_list = vec![]; + for choice in routing_choice { + let routable_choice = routing_types::RoutableConnectorChoice { + #[cfg(feature = "backwards_compatibility")] + choice_kind: routing_types::RoutableChoiceKind::FullStruct, + connector: choice + .connector + .connector_name + .to_string() + .parse::() + .change_context(errors::ApiErrorResponse::InternalServerError)?, + #[cfg(feature = "connector_choice_mca_id")] + merchant_connector_id: choice.connector.merchant_connector_id.clone(), + #[cfg(not(feature = "connector_choice_mca_id"))] + sub_label: choice.sub_label, + }; + routable_choice_list.push(routable_choice); + } + pre_routing_results.insert( + pm_type, + storage::PreRoutingConnectorChoice::Multiple(routable_choice_list), + ); } let redis_conn = db @@ -2135,15 +2149,28 @@ pub async fn list_payment_methods( let mut val = Vec::new(); for (payment_method_type, routable_connector_choice) in &pre_routing_results { + let routable_connector_list = match routable_connector_choice { + storage::PreRoutingConnectorChoice::Single(routable_connector) => { + vec![routable_connector.clone()] + } + storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => { + routable_connector_list.clone() + } + }; + + let first_routable_connector = routable_connector_list + .first() + .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; + #[cfg(not(feature = "connector_choice_mca_id"))] let connector_label = get_connector_label( payment_intent.business_country, payment_intent.business_label.as_ref(), #[cfg(not(feature = "connector_choice_mca_id"))] - routable_connector_choice.sub_label.as_ref(), + first_routable_connector.sub_label.as_ref(), #[cfg(feature = "connector_choice_mca_id")] None, - routable_connector_choice.connector.to_string().as_str(), + first_routable_connector.connector.to_string().as_str(), ); #[cfg(not(feature = "connector_choice_mca_id"))] let matched_mca = filtered_mcas @@ -2152,7 +2179,7 @@ pub async fn list_payment_methods( #[cfg(feature = "connector_choice_mca_id")] let matched_mca = filtered_mcas.iter().find(|m| { - routable_connector_choice.merchant_connector_id.as_ref() + first_routable_connector.merchant_connector_id.as_ref() == Some(&m.merchant_connector_id) }); diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index e1290637db..50eedcfb56 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3182,32 +3182,51 @@ where .as_ref() .zip(payment_data.payment_attempt.payment_method_type.as_ref()) { - if let (Some(choice), None) = ( + if let (Some(routable_connector_choice), None) = ( pre_routing_results.get(storage_pm_type), &payment_data.token_data, ) { - let connector_data = api::ConnectorData::get_connector_by_name( - &state.conf.connectors, - &choice.connector.to_string(), - api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] - choice.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, - ) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Invalid connector name received")?; + let routable_connector_list = match routable_connector_choice { + storage::PreRoutingConnectorChoice::Single(routable_connector) => { + vec![routable_connector.clone()] + } + storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => { + routable_connector_list.clone() + } + }; - routing_data.routed_through = Some(choice.connector.to_string()); + let mut pre_routing_connector_data_list = vec![]; + + let first_routable_connector = routable_connector_list + .first() + .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; + + routing_data.routed_through = Some(first_routable_connector.connector.to_string()); #[cfg(feature = "connector_choice_mca_id")] { routing_data .merchant_connector_id - .clone_from(&choice.merchant_connector_id); + .clone_from(&first_routable_connector.merchant_connector_id); } #[cfg(not(feature = "connector_choice_mca_id"))] { - routing_data.business_sub_label = choice.sub_label.clone(); + routing_data.business_sub_label = first_routable_connector.sub_label.clone(); + } + + for connector_choice in routable_connector_list.clone() { + let connector_data = api::ConnectorData::get_connector_by_name( + &state.conf.connectors, + &connector_choice.connector.to_string(), + api::GetToken::Connector, + #[cfg(feature = "connector_choice_mca_id")] + connector_choice.merchant_connector_id.clone(), + #[cfg(not(feature = "connector_choice_mca_id"))] + None, + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Invalid connector name received")?; + + pre_routing_connector_data_list.push(connector_data); } #[cfg(all(feature = "retry", feature = "connector_choice_mca_id"))] @@ -3224,8 +3243,11 @@ where merchant_account, payment_data, key_store, - connector_data.clone(), - choice.merchant_connector_id.clone().as_ref(), + &pre_routing_connector_data_list, + first_routable_connector + .merchant_connector_id + .clone() + .as_ref(), ) .await?; @@ -3237,7 +3259,13 @@ where } } - return Ok(ConnectorCallType::PreDetermined(connector_data)); + let first_pre_routing_connector_data_list = pre_routing_connector_data_list + .first() + .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; + + return Ok(ConnectorCallType::PreDetermined( + first_pre_routing_connector_data_list.clone(), + )); } } @@ -3614,11 +3642,22 @@ where })); let mut final_list: Vec = Vec::new(); - for (routed_pm_type, choice) in pre_routing_results.into_iter() { - if let Some(session_connector_data) = - payment_methods.remove(&(choice.to_string(), routed_pm_type)) - { - final_list.push(session_connector_data); + for (routed_pm_type, pre_routing_choice) in pre_routing_results.into_iter() { + let routable_connector_list = match pre_routing_choice { + storage::PreRoutingConnectorChoice::Single(routable_connector) => { + vec![routable_connector.clone()] + } + storage::PreRoutingConnectorChoice::Multiple(routable_connector_list) => { + routable_connector_list.clone() + } + }; + for routable_connector in routable_connector_list { + if let Some(session_connector_data) = + payment_methods.remove(&(routable_connector.to_string(), routed_pm_type)) + { + final_list.push(session_connector_data); + break; + } } } @@ -3666,8 +3705,11 @@ where if !routing_enabled_pms.contains(&connector_data.payment_method_type) { final_list.push(connector_data); } else if let Some(choice) = result.get(&connector_data.payment_method_type) { - if connector_data.connector.connector_name == choice.connector.connector_name { - connector_data.business_sub_label = choice.sub_label.clone(); + let routing_choice = choice + .first() + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + if connector_data.connector.connector_name == routing_choice.connector.connector_name { + connector_data.business_sub_label = routing_choice.sub_label.clone(); final_list.push(connector_data); } } @@ -3678,7 +3720,10 @@ where if !routing_enabled_pms.contains(&connector_data.payment_method_type) { final_list.push(connector_data); } else if let Some(choice) = result.get(&connector_data.payment_method_type) { - if connector_data.connector.connector_name == choice.connector.connector_name { + let routing_choice = choice + .first() + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + if connector_data.connector.connector_name == routing_choice.connector.connector_name { final_list.push(connector_data); } } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 052c4b1928..7eada733ae 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -4033,7 +4033,7 @@ pub async fn get_apple_pay_retryable_connectors( merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, key_store: &domain::MerchantKeyStore, - decided_connector_data: api::ConnectorData, + pre_routing_connector_data_list: &[api::ConnectorData], merchant_connector_id: Option<&String>, ) -> CustomResult>, errors::ApiErrorResponse> where @@ -4048,13 +4048,17 @@ where field_name: "profile_id", })?; + let pre_decided_connector_data_first = pre_routing_connector_data_list + .first() + .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; + let merchant_connector_account_type = get_merchant_connector_account( &state, merchant_account.merchant_id.as_str(), payment_data.creds_identifier.to_owned(), key_store, profile_id, - &decided_connector_data.connector_name.to_string(), + &pre_decided_connector_data_first.connector_name.to_string(), merchant_connector_id, ) .await?; @@ -4081,7 +4085,7 @@ where Some(profile_id.to_string()), ); - let mut connector_data_list = vec![decided_connector_data.clone()]; + let mut connector_data_list = vec![pre_decided_connector_data_first.clone()]; for merchant_connector_account in profile_specific_merchant_connector_account_list { if is_apple_pay_simplified_flow( @@ -4118,16 +4122,31 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get merchant default fallback connectors config")?; + let mut routing_connector_data_list = Vec::new(); + + pre_routing_connector_data_list.iter().for_each(|pre_val| { + routing_connector_data_list.push(pre_val.merchant_connector_id.clone()) + }); + + fallback_connetors_list.iter().for_each(|fallback_val| { + routing_connector_data_list + .iter() + .all(|val| *val != fallback_val.merchant_connector_id) + .then(|| { + routing_connector_data_list.push(fallback_val.merchant_connector_id.clone()) + }); + }); + // connector_data_list is the list of connectors for which Apple Pay simplified flow is configured. - // This list is arranged in the same order as the merchant's default fallback connectors configuration. - let mut ordered_connector_data_list = vec![decided_connector_data.clone()]; - fallback_connetors_list + // This list is arranged in the same order as the merchant's connectors routingconfiguration. + + let mut ordered_connector_data_list = Vec::new(); + + routing_connector_data_list .iter() - .for_each(|fallback_connector| { + .for_each(|merchant_connector_id| { let connector_data = connector_data_list.iter().find(|connector_data| { - fallback_connector.merchant_connector_id == connector_data.merchant_connector_id - && fallback_connector.merchant_connector_id - != decided_connector_data.merchant_connector_id + *merchant_connector_id == connector_data.merchant_connector_id }); if let Some(connector_data_details) = connector_data { ordered_connector_data_list.push(connector_data_details.clone()); diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 4394637d99..3afb933d46 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -855,7 +855,8 @@ pub async fn perform_eligibility_analysis_with_fallback( pub async fn perform_session_flow_routing( session_input: SessionFlowRoutingInput<'_>, transaction_type: &api_enums::TransactionType, -) -> RoutingResult> { +) -> RoutingResult>> +{ let mut pm_type_map: FxHashMap> = FxHashMap::default(); @@ -962,8 +963,10 @@ pub async fn perform_session_flow_routing( ); } - let mut result: FxHashMap = - FxHashMap::default(); + let mut result: FxHashMap< + api_enums::PaymentMethodType, + Vec, + > = FxHashMap::default(); for (pm_type, allowed_connectors) in pm_type_map { let euclid_pmt: euclid_enums::PaymentMethodType = pm_type; @@ -985,20 +988,37 @@ pub async fn perform_session_flow_routing( ))] profile_id: session_input.payment_intent.profile_id.clone(), }; - let maybe_choice = - perform_session_routing_for_pm_type(session_pm_input, transaction_type).await?; + let routable_connector_choice_option = + perform_session_routing_for_pm_type(&session_pm_input, transaction_type).await?; - // (connector, sub_label) - if let Some(data) = maybe_choice { - result.insert( - pm_type, - routing_types::SessionRoutingChoice { - connector: data.0, + if let Some(routable_connector_choice) = routable_connector_choice_option { + let mut session_routing_choice: Vec = Vec::new(); + + for selection in routable_connector_choice { + let connector_name = selection.connector.to_string(); + if let Some(get_token) = session_pm_input.allowed_connectors.get(&connector_name) { + let connector_data = api::ConnectorData::get_connector_by_name( + &session_pm_input.state.clone().conf.connectors, + &connector_name, + get_token.clone(), + #[cfg(feature = "connector_choice_mca_id")] + selection.merchant_connector_id, + #[cfg(not(feature = "connector_choice_mca_id"))] + None, + ) + .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?; #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: data.1, - payment_method_type: pm_type, - }, - ); + let sub_label = selection.sub_label; + + session_routing_choice.push(routing_types::SessionRoutingChoice { + connector: connector_data, + #[cfg(not(feature = "connector_choice_mca_id"))] + sub_label: sub_label, + payment_method_type: pm_type, + }); + } + } + result.insert(pm_type, session_routing_choice); } } @@ -1006,9 +1026,9 @@ pub async fn perform_session_flow_routing( } async fn perform_session_routing_for_pm_type( - session_pm_input: SessionRoutingPmTypeInput<'_>, + session_pm_input: &SessionRoutingPmTypeInput<'_>, transaction_type: &api_enums::TransactionType, -) -> RoutingResult)>> { +) -> RoutingResult>> { let merchant_id = &session_pm_input.key_store.merchant_id; let chosen_connectors = match session_pm_input.routing_algorithm { @@ -1091,7 +1111,7 @@ async fn perform_session_routing_for_pm_type( &session_pm_input.state.clone(), session_pm_input.key_store, fallback, - session_pm_input.backend_input, + session_pm_input.backend_input.clone(), None, #[cfg(feature = "business_profile_routing")] session_pm_input.profile_id.clone(), @@ -1100,32 +1120,11 @@ async fn perform_session_routing_for_pm_type( .await?; } - let mut final_choice: Option<(api::ConnectorData, Option)> = None; - - for selection in final_selection { - let connector_name = selection.connector.to_string(); - if let Some(get_token) = session_pm_input.allowed_connectors.get(&connector_name) { - let connector_data = api::ConnectorData::get_connector_by_name( - &session_pm_input.state.clone().conf.connectors, - &connector_name, - get_token.clone(), - #[cfg(feature = "connector_choice_mca_id")] - selection.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - None, - ) - .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?; - #[cfg(not(feature = "connector_choice_mca_id"))] - let sub_label = selection.sub_label; - #[cfg(feature = "connector_choice_mca_id")] - let sub_label = None; - - final_choice = Some((connector_data, sub_label)); - break; - } + if final_selection.is_empty() { + Ok(None) + } else { + Ok(Some(final_selection)) } - - Ok(final_choice) } pub fn make_dsl_input_for_surcharge( diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 77550bedab..79c8b2ed2f 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -81,14 +81,21 @@ pub struct RoutingData { pub struct PaymentRoutingInfo { pub algorithm: Option, pub pre_routing_results: - Option>, + Option>, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PaymentRoutingInfoInner { pub algorithm: Option, pub pre_routing_results: - Option>, + Option>, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum PreRoutingConnectorChoice { + Single(routing::RoutableConnectorChoice), + Multiple(Vec), } #[derive(Debug, serde::Serialize, serde::Deserialize)]