mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(router): include the pre-routing connectors in Apple Pay retries (#4952)
This commit is contained in:
@ -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)
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user