feat(router): include the pre-routing connectors in Apple Pay retries (#4952)

This commit is contained in:
Shankar Singh C
2024-06-13 16:41:43 +05:30
committed by GitHub
parent 0a86cdb506
commit fb836618a6
5 changed files with 200 additions and 103 deletions

View File

@ -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::<api_enums::RoutableConnectors>()
.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::<api_enums::RoutableConnectors>()
.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)
});

View File

@ -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<api::SessionConnectorData> = 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);
}
}

View File

@ -4033,7 +4033,7 @@ pub async fn get_apple_pay_retryable_connectors<F>(
merchant_account: &domain::MerchantAccount,
payment_data: &mut PaymentData<F>,
key_store: &domain::MerchantKeyStore,
decided_connector_data: api::ConnectorData,
pre_routing_connector_data_list: &[api::ConnectorData],
merchant_connector_id: Option<&String>,
) -> CustomResult<Option<Vec<api::ConnectorData>>, 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());

View File

@ -855,7 +855,8 @@ pub async fn perform_eligibility_analysis_with_fallback<F: Clone>(
pub async fn perform_session_flow_routing(
session_input: SessionFlowRoutingInput<'_>,
transaction_type: &api_enums::TransactionType,
) -> RoutingResult<FxHashMap<api_enums::PaymentMethodType, routing_types::SessionRoutingChoice>> {
) -> RoutingResult<FxHashMap<api_enums::PaymentMethodType, Vec<routing_types::SessionRoutingChoice>>>
{
let mut pm_type_map: FxHashMap<api_enums::PaymentMethodType, FxHashMap<String, api::GetToken>> =
FxHashMap::default();
@ -962,8 +963,10 @@ pub async fn perform_session_flow_routing(
);
}
let mut result: FxHashMap<api_enums::PaymentMethodType, routing_types::SessionRoutingChoice> =
FxHashMap::default();
let mut result: FxHashMap<
api_enums::PaymentMethodType,
Vec<routing_types::SessionRoutingChoice>,
> = 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<routing_types::SessionRoutingChoice> = 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<Option<(api::ConnectorData, Option<String>)>> {
) -> RoutingResult<Option<Vec<api_models::routing::RoutableConnectorChoice>>> {
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<String>)> = 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(

View File

@ -81,14 +81,21 @@ pub struct RoutingData {
pub struct PaymentRoutingInfo {
pub algorithm: Option<routing::StraightThroughAlgorithm>,
pub pre_routing_results:
Option<HashMap<api_models::enums::PaymentMethodType, routing::RoutableConnectorChoice>>,
Option<HashMap<api_models::enums::PaymentMethodType, PreRoutingConnectorChoice>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentRoutingInfoInner {
pub algorithm: Option<routing::StraightThroughAlgorithm>,
pub pre_routing_results:
Option<HashMap<api_models::enums::PaymentMethodType, routing::RoutableConnectorChoice>>,
Option<HashMap<api_models::enums::PaymentMethodType, PreRoutingConnectorChoice>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum PreRoutingConnectorChoice {
Single(routing::RoutableConnectorChoice),
Multiple(Vec<routing::RoutableConnectorChoice>),
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]