mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat(core): [Retry] MIT Retries (#8628)
This commit is contained in:
@ -14,6 +14,8 @@ use common_utils::{
|
|||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use time::PrimitiveDateTime;
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use crate::router_data::RecurringMandatePaymentData;
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MandateDetails {
|
pub struct MandateDetails {
|
||||||
@ -174,6 +176,20 @@ pub struct PaymentsMandateReferenceRecord {
|
|||||||
pub connector_mandate_request_reference_id: Option<String>,
|
pub connector_mandate_request_reference_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
impl From<&PaymentsMandateReferenceRecord> for RecurringMandatePaymentData {
|
||||||
|
fn from(mandate_reference_record: &PaymentsMandateReferenceRecord) -> Self {
|
||||||
|
Self {
|
||||||
|
payment_method_type: mandate_reference_record.payment_method_type,
|
||||||
|
original_payment_authorized_amount: mandate_reference_record
|
||||||
|
.original_payment_authorized_amount,
|
||||||
|
original_payment_authorized_currency: mandate_reference_record
|
||||||
|
.original_payment_authorized_currency,
|
||||||
|
mandate_metadata: mandate_reference_record.mandate_metadata.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct ConnectorTokenReferenceRecord {
|
pub struct ConnectorTokenReferenceRecord {
|
||||||
|
|||||||
@ -693,7 +693,7 @@ fn process_connector_for_networks(
|
|||||||
let matching_networks = find_matching_networks(
|
let matching_networks = find_matching_networks(
|
||||||
&merchant_debit_networks,
|
&merchant_debit_networks,
|
||||||
fee_sorted_debit_networks,
|
fee_sorted_debit_networks,
|
||||||
&connector_data.connector_data,
|
connector_data,
|
||||||
debit_routing_config,
|
debit_routing_config,
|
||||||
has_us_local_network,
|
has_us_local_network,
|
||||||
);
|
);
|
||||||
@ -715,13 +715,13 @@ fn find_merchant_connector_account(
|
|||||||
fn find_matching_networks(
|
fn find_matching_networks(
|
||||||
merchant_debit_networks: &HashSet<common_enums::CardNetwork>,
|
merchant_debit_networks: &HashSet<common_enums::CardNetwork>,
|
||||||
fee_sorted_debit_networks: &[common_enums::CardNetwork],
|
fee_sorted_debit_networks: &[common_enums::CardNetwork],
|
||||||
connector_data: &api::ConnectorData,
|
connector_routing_data: &api::ConnectorRoutingData,
|
||||||
debit_routing_config: &settings::DebitRoutingConfig,
|
debit_routing_config: &settings::DebitRoutingConfig,
|
||||||
has_us_local_network: &mut bool,
|
has_us_local_network: &mut bool,
|
||||||
) -> Vec<api::ConnectorRoutingData> {
|
) -> Vec<api::ConnectorRoutingData> {
|
||||||
let is_routing_enabled = debit_routing_config
|
let is_routing_enabled = debit_routing_config
|
||||||
.supported_connectors
|
.supported_connectors
|
||||||
.contains(&connector_data.connector_name);
|
.contains(&connector_routing_data.connector_data.connector_name.clone());
|
||||||
|
|
||||||
fee_sorted_debit_networks
|
fee_sorted_debit_networks
|
||||||
.iter()
|
.iter()
|
||||||
@ -733,8 +733,9 @@ fn find_matching_networks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
api::ConnectorRoutingData {
|
api::ConnectorRoutingData {
|
||||||
connector_data: connector_data.clone(),
|
connector_data: connector_routing_data.connector_data.clone(),
|
||||||
network: Some(network.clone()),
|
network: Some(network.clone()),
|
||||||
|
action_type: connector_routing_data.action_type.clone(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@ -8981,88 +8981,64 @@ where
|
|||||||
.get_required_value("payment_method_info")?
|
.get_required_value("payment_method_info")?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
//fetch connectors that support ntid flow
|
let retryable_connectors =
|
||||||
let ntid_supported_connectors = &state
|
join_all(connectors.into_iter().map(|connector_routing_data| {
|
||||||
.conf
|
let payment_method = payment_method_info.clone();
|
||||||
.network_transaction_id_supported_connectors
|
async move {
|
||||||
.connector_list;
|
let action_types = get_all_action_types(
|
||||||
//filered connectors list with ntid_supported_connectors
|
state,
|
||||||
let filtered_ntid_supported_connectors =
|
is_connector_agnostic_mit_enabled,
|
||||||
filter_ntid_supported_connectors(connectors.clone(), ntid_supported_connectors);
|
is_network_tokenization_enabled,
|
||||||
|
&payment_method.clone(),
|
||||||
|
connector_routing_data.connector_data.clone(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
//fetch connectors that support network tokenization flow
|
action_types
|
||||||
let network_tokenization_supported_connectors = &state
|
.into_iter()
|
||||||
.conf
|
.map(|action_type| api::ConnectorRoutingData {
|
||||||
.network_tokenization_supported_connectors
|
connector_data: connector_routing_data.connector_data.clone(),
|
||||||
.connector_list;
|
action_type: Some(action_type),
|
||||||
//filered connectors list with ntid_supported_connectors and network_tokenization_supported_connectors
|
network: connector_routing_data.network.clone(),
|
||||||
let filtered_nt_supported_connectors = filter_network_tokenization_supported_connectors(
|
})
|
||||||
filtered_ntid_supported_connectors,
|
.collect::<Vec<_>>()
|
||||||
network_tokenization_supported_connectors,
|
}
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let chosen_connector_routing_data = retryable_connectors
|
||||||
|
.first()
|
||||||
|
.ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
|
||||||
|
.attach_printable("no eligible connector found for token-based MIT payment")?;
|
||||||
|
|
||||||
|
let mandate_reference_id = get_mandate_reference_id(
|
||||||
|
chosen_connector_routing_data.action_type.clone(),
|
||||||
|
chosen_connector_routing_data.clone(),
|
||||||
|
payment_data,
|
||||||
|
&payment_method_info,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
routing_data.routed_through = Some(
|
||||||
|
chosen_connector_routing_data
|
||||||
|
.connector_data
|
||||||
|
.connector_name
|
||||||
|
.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let action_type = decide_action_type(
|
routing_data.merchant_connector_id.clone_from(
|
||||||
state,
|
&chosen_connector_routing_data
|
||||||
is_connector_agnostic_mit_enabled,
|
.connector_data
|
||||||
is_network_tokenization_enabled,
|
.merchant_connector_id,
|
||||||
&payment_method_info,
|
);
|
||||||
filtered_nt_supported_connectors.clone(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match action_type {
|
payment_data.set_mandate_id(payments_api::MandateIds {
|
||||||
Some(ActionType::NetworkTokenWithNetworkTransactionId(nt_data)) => {
|
mandate_id: None,
|
||||||
logger::info!(
|
mandate_reference_id,
|
||||||
"using network_tokenization with network_transaction_id for MIT flow"
|
});
|
||||||
);
|
Ok(ConnectorCallType::Retryable(retryable_connectors))
|
||||||
|
|
||||||
let mandate_reference_id =
|
|
||||||
Some(payments_api::MandateReferenceId::NetworkTokenWithNTI(
|
|
||||||
payments_api::NetworkTokenWithNTIRef {
|
|
||||||
network_transaction_id: nt_data.network_transaction_id.to_string(),
|
|
||||||
token_exp_month: nt_data.token_exp_month,
|
|
||||||
token_exp_year: nt_data.token_exp_year,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
let chosen_connector_data = filtered_nt_supported_connectors
|
|
||||||
.first()
|
|
||||||
.ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
|
|
||||||
.attach_printable(
|
|
||||||
"no eligible connector found for token-based MIT payment",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
routing_data.routed_through = Some(
|
|
||||||
chosen_connector_data
|
|
||||||
.connector_data
|
|
||||||
.connector_name
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
routing_data
|
|
||||||
.merchant_connector_id
|
|
||||||
.clone_from(&chosen_connector_data.connector_data.merchant_connector_id);
|
|
||||||
|
|
||||||
payment_data.set_mandate_id(payments_api::MandateIds {
|
|
||||||
mandate_id: None,
|
|
||||||
mandate_reference_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(ConnectorCallType::PreDetermined(
|
|
||||||
chosen_connector_data.clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
decide_connector_for_normal_or_recurring_payment(
|
|
||||||
state,
|
|
||||||
payment_data,
|
|
||||||
routing_data,
|
|
||||||
connectors,
|
|
||||||
is_connector_agnostic_mit_enabled,
|
|
||||||
&payment_method_info,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
@ -9112,6 +9088,78 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
pub fn get_mandate_reference_id<F: Clone, D>(
|
||||||
|
action_type: Option<ActionType>,
|
||||||
|
connector_routing_data: api::ConnectorRoutingData,
|
||||||
|
payment_data: &mut D,
|
||||||
|
payment_method_info: &domain::PaymentMethod,
|
||||||
|
) -> RouterResult<Option<api_models::payments::MandateReferenceId>>
|
||||||
|
where
|
||||||
|
D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
|
||||||
|
{
|
||||||
|
let mandate_reference_id = match action_type {
|
||||||
|
Some(ActionType::NetworkTokenWithNetworkTransactionId(network_token_data)) => {
|
||||||
|
logger::info!("using network token with network_transaction_id for MIT flow");
|
||||||
|
|
||||||
|
Some(payments_api::MandateReferenceId::NetworkTokenWithNTI(
|
||||||
|
network_token_data.into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(ActionType::CardWithNetworkTransactionId(network_transaction_id)) => {
|
||||||
|
logger::info!("using card with network_transaction_id for MIT flow");
|
||||||
|
|
||||||
|
Some(payments_api::MandateReferenceId::NetworkMandateId(
|
||||||
|
network_transaction_id,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(ActionType::ConnectorMandate(connector_mandate_details)) => {
|
||||||
|
logger::info!("using connector_mandate_id for MIT flow");
|
||||||
|
let merchant_connector_id = connector_routing_data
|
||||||
|
.connector_data
|
||||||
|
.merchant_connector_id
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
|
||||||
|
.attach_printable("No eligible connector found for token-based MIT flow: no connector mandate details")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mandate_reference_record = connector_mandate_details
|
||||||
|
.get(merchant_connector_id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
report!(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)
|
||||||
|
.attach_printable("No mandate record found for merchant connector ID")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(mandate_currency) =
|
||||||
|
mandate_reference_record.original_payment_authorized_currency
|
||||||
|
{
|
||||||
|
if mandate_currency != payment_data.get_currency() {
|
||||||
|
return Err(report!(errors::ApiErrorResponse::MandateValidationFailed {
|
||||||
|
reason: "Cross currency mandates not supported".into(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payment_data.set_recurring_mandate_payment_data(mandate_reference_record.into());
|
||||||
|
|
||||||
|
Some(payments_api::MandateReferenceId::ConnectorMandateId(
|
||||||
|
api_models::payments::ConnectorMandateReferenceId::new(
|
||||||
|
Some(mandate_reference_record.connector_mandate_id.clone()),
|
||||||
|
Some(payment_method_info.get_id().clone()),
|
||||||
|
None,
|
||||||
|
mandate_reference_record.mandate_metadata.clone(),
|
||||||
|
mandate_reference_record
|
||||||
|
.connector_mandate_request_reference_id
|
||||||
|
.clone(),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
Ok(mandate_reference_id)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
|
pub async fn decide_connector_for_normal_or_recurring_payment<F: Clone, D>(
|
||||||
@ -9178,16 +9226,8 @@ where
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
payment_data.set_recurring_mandate_payment_data(
|
payment_data.set_recurring_mandate_payment_data(
|
||||||
hyperswitch_domain_models::router_data::RecurringMandatePaymentData {
|
mandate_reference_record.into(),
|
||||||
payment_method_type: mandate_reference_record
|
);
|
||||||
.payment_method_type,
|
|
||||||
original_payment_authorized_amount: mandate_reference_record
|
|
||||||
.original_payment_authorized_amount,
|
|
||||||
original_payment_authorized_currency: mandate_reference_record
|
|
||||||
.original_payment_authorized_currency,
|
|
||||||
mandate_metadata: mandate_reference_record
|
|
||||||
.mandate_metadata.clone()
|
|
||||||
});
|
|
||||||
connector_choice = Some((connector_data, mandate_reference_id.clone()));
|
connector_choice = Some((connector_data, mandate_reference_id.clone()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -9260,9 +9300,23 @@ pub struct NTWithNTIRef {
|
|||||||
pub token_exp_year: Option<Secret<String>>,
|
pub token_exp_year: Option<Secret<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)]
|
impl From<NTWithNTIRef> for payments_api::NetworkTokenWithNTIRef {
|
||||||
|
fn from(network_token_data: NTWithNTIRef) -> Self {
|
||||||
|
Self {
|
||||||
|
network_transaction_id: network_token_data.network_transaction_id,
|
||||||
|
token_exp_month: network_token_data.token_exp_month,
|
||||||
|
token_exp_year: network_token_data.token_exp_year,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This represents the recurring details of a connector which will be used for retries
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub enum ActionType {
|
pub enum ActionType {
|
||||||
NetworkTokenWithNetworkTransactionId(NTWithNTIRef),
|
NetworkTokenWithNetworkTransactionId(NTWithNTIRef),
|
||||||
|
CardWithNetworkTransactionId(String), // Network Transaction Id
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
ConnectorMandate(hyperswitch_domain_models::mandates::PaymentsMandateReference),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_network_tokenization_supported_connectors(
|
pub fn filter_network_tokenization_supported_connectors(
|
||||||
@ -9278,40 +9332,154 @@ pub fn filter_network_tokenization_supported_connectors(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
pub async fn decide_action_type(
|
#[derive(Default)]
|
||||||
|
pub struct ActionTypesBuilder {
|
||||||
|
action_types: Vec<ActionType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
impl ActionTypesBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
action_types: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_mandate_flow(
|
||||||
|
mut self,
|
||||||
|
is_mandate_flow: bool,
|
||||||
|
connector_mandate_details: Option<
|
||||||
|
hyperswitch_domain_models::mandates::PaymentsMandateReference,
|
||||||
|
>,
|
||||||
|
) -> Self {
|
||||||
|
if is_mandate_flow {
|
||||||
|
self.action_types.extend(
|
||||||
|
connector_mandate_details
|
||||||
|
.map(|details| ActionType::ConnectorMandate(details.to_owned())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn with_network_tokenization(
|
||||||
|
mut self,
|
||||||
|
state: &SessionState,
|
||||||
|
is_network_token_with_ntid_flow: IsNtWithNtiFlow,
|
||||||
|
is_nt_with_ntid_supported_connector: bool,
|
||||||
|
payment_method_info: &domain::PaymentMethod,
|
||||||
|
) -> Self {
|
||||||
|
match is_network_token_with_ntid_flow {
|
||||||
|
IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id)
|
||||||
|
if is_nt_with_ntid_supported_connector =>
|
||||||
|
{
|
||||||
|
self.action_types.extend(
|
||||||
|
network_tokenization::do_status_check_for_network_token(
|
||||||
|
state,
|
||||||
|
payment_method_info,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
logger::error!("Status check for network token failed: {:?}", e)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.map(|(token_exp_month, token_exp_year)| {
|
||||||
|
ActionType::NetworkTokenWithNetworkTransactionId(NTWithNTIRef {
|
||||||
|
token_exp_month,
|
||||||
|
token_exp_year,
|
||||||
|
network_transaction_id,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_card_network_transaction_id(
|
||||||
|
mut self,
|
||||||
|
is_card_with_ntid_flow: bool,
|
||||||
|
payment_method_info: &domain::PaymentMethod,
|
||||||
|
) -> Self {
|
||||||
|
if is_card_with_ntid_flow {
|
||||||
|
self.action_types.extend(
|
||||||
|
payment_method_info
|
||||||
|
.network_transaction_id
|
||||||
|
.as_ref()
|
||||||
|
.map(|ntid| ActionType::CardWithNetworkTransactionId(ntid.clone())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> Vec<ActionType> {
|
||||||
|
self.action_types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
pub async fn get_all_action_types(
|
||||||
state: &SessionState,
|
state: &SessionState,
|
||||||
is_connector_agnostic_mit_enabled: Option<bool>,
|
is_connector_agnostic_mit_enabled: Option<bool>,
|
||||||
is_network_tokenization_enabled: bool,
|
is_network_tokenization_enabled: bool,
|
||||||
payment_method_info: &domain::PaymentMethod,
|
payment_method_info: &domain::PaymentMethod,
|
||||||
filtered_nt_supported_connectors: Vec<api::ConnectorRoutingData>, //network tokenization supported connectors
|
connector: api::ConnectorData,
|
||||||
) -> Option<ActionType> {
|
) -> Vec<ActionType> {
|
||||||
match (
|
let merchant_connector_id = connector.merchant_connector_id.as_ref();
|
||||||
is_network_token_with_network_transaction_id_flow(
|
|
||||||
is_connector_agnostic_mit_enabled,
|
//fetch connectors that support ntid flow
|
||||||
is_network_tokenization_enabled,
|
let ntid_supported_connectors = &state
|
||||||
|
.conf
|
||||||
|
.network_transaction_id_supported_connectors
|
||||||
|
.connector_list;
|
||||||
|
|
||||||
|
//fetch connectors that support network tokenization flow
|
||||||
|
let network_tokenization_supported_connectors = &state
|
||||||
|
.conf
|
||||||
|
.network_tokenization_supported_connectors
|
||||||
|
.connector_list;
|
||||||
|
|
||||||
|
let is_network_token_with_ntid_flow = is_network_token_with_network_transaction_id_flow(
|
||||||
|
is_connector_agnostic_mit_enabled,
|
||||||
|
is_network_tokenization_enabled,
|
||||||
|
payment_method_info,
|
||||||
|
);
|
||||||
|
let is_card_with_ntid_flow = is_network_transaction_id_flow(
|
||||||
|
state,
|
||||||
|
is_connector_agnostic_mit_enabled,
|
||||||
|
connector.connector_name,
|
||||||
|
payment_method_info,
|
||||||
|
);
|
||||||
|
let payments_mandate_reference = payment_method_info
|
||||||
|
.get_common_mandate_reference()
|
||||||
|
.map_err(|err| {
|
||||||
|
logger::warn!("Error getting connector mandate details: {:?}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.and_then(|details| details.payments);
|
||||||
|
|
||||||
|
let is_mandate_flow = payments_mandate_reference
|
||||||
|
.clone()
|
||||||
|
.zip(merchant_connector_id)
|
||||||
|
.map(|(details, merchant_connector_id)| details.contains_key(merchant_connector_id))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let is_nt_with_ntid_supported_connector = ntid_supported_connectors
|
||||||
|
.contains(&connector.connector_name)
|
||||||
|
&& network_tokenization_supported_connectors.contains(&connector.connector_name);
|
||||||
|
|
||||||
|
ActionTypesBuilder::new()
|
||||||
|
.with_mandate_flow(is_mandate_flow, payments_mandate_reference)
|
||||||
|
.with_network_tokenization(
|
||||||
|
state,
|
||||||
|
is_network_token_with_ntid_flow,
|
||||||
|
is_nt_with_ntid_supported_connector,
|
||||||
payment_method_info,
|
payment_method_info,
|
||||||
),
|
)
|
||||||
!filtered_nt_supported_connectors.is_empty(),
|
.await
|
||||||
) {
|
.with_card_network_transaction_id(is_card_with_ntid_flow, payment_method_info)
|
||||||
(IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id), true) => {
|
.build()
|
||||||
if let Ok((token_exp_month, token_exp_year)) =
|
|
||||||
network_tokenization::do_status_check_for_network_token(state, payment_method_info)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Some(ActionType::NetworkTokenWithNetworkTransactionId(
|
|
||||||
NTWithNTIRef {
|
|
||||||
token_exp_month,
|
|
||||||
token_exp_year,
|
|
||||||
network_transaction_id,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(IsNtWithNtiFlow::NtWithNtiSupported(_), false)
|
|
||||||
| (IsNtWithNtiFlow::NTWithNTINotSupported, _) => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_network_transaction_id_flow(
|
pub fn is_network_transaction_id_flow(
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::vec::IntoIter;
|
|||||||
use common_utils::{ext_traits::Encode, types::MinorUnit};
|
use common_utils::{ext_traits::Encode, types::MinorUnit};
|
||||||
use diesel_models::enums as storage_enums;
|
use diesel_models::enums as storage_enums;
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
|
use hyperswitch_domain_models::ext_traits::OptionExt;
|
||||||
use router_env::{
|
use router_env::{
|
||||||
logger,
|
logger,
|
||||||
tracing::{self, instrument},
|
tracing::{self, instrument},
|
||||||
@ -159,7 +160,31 @@ where
|
|||||||
&& clear_pan_possible
|
&& clear_pan_possible
|
||||||
&& business_profile.is_clear_pan_retries_enabled;
|
&& business_profile.is_clear_pan_retries_enabled;
|
||||||
|
|
||||||
let (connector, routing_decision) = if should_retry_with_pan {
|
// Currently we are taking off_session as a source of truth to identify MIT payments.
|
||||||
|
let is_mit_payment = payment_data
|
||||||
|
.get_payment_intent()
|
||||||
|
.off_session
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let (connector, routing_decision) = if is_mit_payment {
|
||||||
|
let connector_routing_data =
|
||||||
|
super::get_connector_data(&mut connector_routing_data)?;
|
||||||
|
let payment_method_info = payment_data
|
||||||
|
.get_payment_method_info()
|
||||||
|
.get_required_value("payment_method_info")?
|
||||||
|
.clone();
|
||||||
|
let mandate_reference_id = payments::get_mandate_reference_id(
|
||||||
|
connector_routing_data.action_type.clone(),
|
||||||
|
connector_routing_data.clone(),
|
||||||
|
payment_data,
|
||||||
|
&payment_method_info,
|
||||||
|
)?;
|
||||||
|
payment_data.set_mandate_id(api_models::payments::MandateIds {
|
||||||
|
mandate_id: None,
|
||||||
|
mandate_reference_id, //mandate_ref_id
|
||||||
|
});
|
||||||
|
(connector_routing_data.connector_data, None)
|
||||||
|
} else if should_retry_with_pan {
|
||||||
// If should_retry_with_pan is true, it indicates that we are retrying with PAN using the same connector.
|
// If should_retry_with_pan is true, it indicates that we are retrying with PAN using the same connector.
|
||||||
(original_connector_data.clone(), None)
|
(original_connector_data.clone(), None)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -107,6 +107,7 @@ impl From<ConnectorData> for ConnectorRoutingData {
|
|||||||
Self {
|
Self {
|
||||||
connector_data,
|
connector_data,
|
||||||
network: None,
|
network: None,
|
||||||
|
action_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,11 @@ pub use super::fraud_check_v2::{
|
|||||||
FraudCheckTransactionV2, FraudCheckV2,
|
FraudCheckTransactionV2, FraudCheckV2,
|
||||||
};
|
};
|
||||||
use super::{ConnectorData, SessionConnectorDatas};
|
use super::{ConnectorData, SessionConnectorDatas};
|
||||||
use crate::{connector, core::errors, services::connector_integration_interface::ConnectorEnum};
|
use crate::{
|
||||||
|
connector,
|
||||||
|
core::{errors, payments::ActionType},
|
||||||
|
services::connector_integration_interface::ConnectorEnum,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FraudCheckConnectorData {
|
pub struct FraudCheckConnectorData {
|
||||||
@ -33,6 +37,8 @@ pub enum ConnectorCallType {
|
|||||||
pub struct ConnectorRoutingData {
|
pub struct ConnectorRoutingData {
|
||||||
pub connector_data: ConnectorData,
|
pub connector_data: ConnectorData,
|
||||||
pub network: Option<common_enums::CardNetwork>,
|
pub network: Option<common_enums::CardNetwork>,
|
||||||
|
// action_type is used for mandates currently
|
||||||
|
pub action_type: Option<ActionType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FraudCheckConnectorData {
|
impl FraudCheckConnectorData {
|
||||||
|
|||||||
Reference in New Issue
Block a user