mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
chore: add logic to use pre decide flow
This commit is contained in:
@ -348,6 +348,12 @@ pub enum AuthenticationType {
|
||||
NoThreeDs,
|
||||
}
|
||||
|
||||
impl AuthenticationType {
|
||||
pub fn is_three_ds(&self) -> bool {
|
||||
matches!(self, Self::ThreeDs)
|
||||
}
|
||||
}
|
||||
|
||||
/// The status of the capture
|
||||
#[derive(
|
||||
Clone,
|
||||
|
||||
@ -2283,4 +2283,58 @@ impl ConnectorSpecifications for Cybersource {
|
||||
fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
|
||||
Some(&CYBERSOURCE_SUPPORTED_WEBHOOK_FLOWS)
|
||||
}
|
||||
fn should_call_connector_service_with_pre_decide_flow(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> bool {
|
||||
match current_flow_info {
|
||||
api::CurrentFlowInfo::Authorize {
|
||||
request_data,
|
||||
auth_type,
|
||||
} => self.is_3ds_setup_required(request_data, *auth_type),
|
||||
}
|
||||
}
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowName> {
|
||||
match current_flow_info {
|
||||
api::CurrentFlowInfo::Authorize { .. } => {
|
||||
// during authorize flow, there is no pre processing flow. Only alternate PreAuthenticate flow
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_alternate_flow_if_needed(
|
||||
&self,
|
||||
current_flow: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::AlternateFlow> {
|
||||
match current_flow {
|
||||
api::CurrentFlowInfo::Authorize {
|
||||
request_data,
|
||||
auth_type,
|
||||
} => {
|
||||
if self.is_3ds_setup_required(request_data, *auth_type) {
|
||||
Some(api::AlternateFlow::PreAuthenticate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cybersource {
|
||||
pub fn is_3ds_setup_required(
|
||||
&self,
|
||||
request: &PaymentsAuthorizeData,
|
||||
auth_type: common_enums::AuthenticationType,
|
||||
) -> bool {
|
||||
router_env::logger::info!(router_data_request=?request, auth_type=?auth_type, "Checking if 3DS setup is required for Cybersource");
|
||||
auth_type.is_three_ds()
|
||||
&& request.is_card()
|
||||
&& (request.connector_mandate_id().is_none()
|
||||
&& request.get_optional_network_transaction_id().is_none())
|
||||
&& request.authentication_data.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,7 +249,7 @@ macro_rules! default_imp_for_new_connector_integration_payment {
|
||||
PaymentsTaxCalculationData,
|
||||
TaxCalculationResponseData,
|
||||
> for $path::$connector{}
|
||||
impl ConnectorIntegrationV2<
|
||||
impl ConnectorIntegrationV2<
|
||||
SdkSessionUpdate,
|
||||
PaymentFlowData,
|
||||
SdkPaymentsSessionUpdateData,
|
||||
|
||||
@ -415,6 +415,13 @@ pub enum PreProcessingFlowName {
|
||||
|
||||
/// The trait that provides specifications about the connector
|
||||
pub trait ConnectorSpecifications {
|
||||
/// indicates whether the new pre-decide flow should be called
|
||||
fn should_call_connector_service_with_pre_decide_flow(
|
||||
&self,
|
||||
_current_flow: CurrentFlowInfo<'_>,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
/// Preprocessing flow name if any, that must be made before the current flow.
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
|
||||
@ -625,6 +625,38 @@ impl ConnectorValidation for ConnectorEnum {
|
||||
}
|
||||
|
||||
impl ConnectorSpecifications for ConnectorEnum {
|
||||
fn should_call_connector_service_with_pre_decide_flow(
|
||||
&self,
|
||||
current_flow: api::CurrentFlowInfo<'_>,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::Old(connector) => {
|
||||
connector.should_call_connector_service_with_pre_decide_flow(current_flow)
|
||||
}
|
||||
Self::New(connector) => {
|
||||
connector.should_call_connector_service_with_pre_decide_flow(current_flow)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowName> {
|
||||
match self {
|
||||
Self::Old(connector) => connector.get_preprocessing_flow_if_needed(current_flow_info),
|
||||
Self::New(connector) => connector.get_preprocessing_flow_if_needed(current_flow_info),
|
||||
}
|
||||
}
|
||||
fn get_alternate_flow_if_needed(
|
||||
&self,
|
||||
current_flow: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::AlternateFlow> {
|
||||
match self {
|
||||
Self::Old(connector) => connector.get_alternate_flow_if_needed(current_flow),
|
||||
Self::New(connector) => connector.get_alternate_flow_if_needed(current_flow),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
|
||||
match self {
|
||||
Self::Old(connector) => connector.get_supported_payment_methods(),
|
||||
|
||||
@ -66,6 +66,7 @@ use hyperswitch_domain_models::{
|
||||
payments::{self, payment_intent::CustomerData, ClickToPayMetaData},
|
||||
router_data::AccessToken,
|
||||
};
|
||||
use hyperswitch_interfaces::api::ConnectorSpecifications;
|
||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||
#[cfg(feature = "v2")]
|
||||
use operations::ValidateStatusForOperation;
|
||||
@ -4080,7 +4081,7 @@ where
|
||||
#[cfg(feature = "v1")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn _call_connector_service<F, RouterDReq, ApiRequest, D>(
|
||||
pub async fn call_connector_service_with_pre_decide_flow<F, RouterDReq, ApiRequest, D>(
|
||||
state: &SessionState,
|
||||
req_state: ReqState,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
@ -4097,7 +4098,7 @@ pub async fn _call_connector_service<F, RouterDReq, ApiRequest, D>(
|
||||
is_retry_payment: bool,
|
||||
return_raw_connector_response: Option<bool>,
|
||||
merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
tokenization_action: TokenizationAction,
|
||||
) -> RouterResult<(
|
||||
RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||
@ -4728,27 +4729,61 @@ where
|
||||
// Update feature metadata to track Direct routing usage for stickiness
|
||||
update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;
|
||||
|
||||
call_connector_service(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await
|
||||
let should_call_connector_service_with_pre_decide_flow = router_data
|
||||
.get_current_flow_info()
|
||||
.map(|current_flow_info| {
|
||||
connector
|
||||
.connector
|
||||
.should_call_connector_service_with_pre_decide_flow(current_flow_info)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if should_call_connector_service_with_pre_decide_flow {
|
||||
logger::info!("Calling call_connector_service_with_pre_decide_flow");
|
||||
call_connector_service_with_pre_decide_flow(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
call_connector_service(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
@ -4815,28 +4850,61 @@ where
|
||||
// Update feature metadata to track Direct routing usage for stickiness
|
||||
update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;
|
||||
|
||||
let should_call_connector_service_with_pre_decide_flow = router_data
|
||||
.get_current_flow_info()
|
||||
.map(|current_flow_info| {
|
||||
connector
|
||||
.connector
|
||||
.should_call_connector_service_with_pre_decide_flow(current_flow_info)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// Call Direct connector service
|
||||
let result = call_connector_service(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await?;
|
||||
let result = if should_call_connector_service_with_pre_decide_flow {
|
||||
logger::info!("Calling call_connector_service_with_pre_decide_flow");
|
||||
call_connector_service_with_pre_decide_flow(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
call_connector_service(
|
||||
state,
|
||||
req_state,
|
||||
merchant_context,
|
||||
connector,
|
||||
operation,
|
||||
payment_data,
|
||||
customer,
|
||||
call_connector_action,
|
||||
validate_result,
|
||||
schedule_time,
|
||||
header_payload,
|
||||
frm_suggestion,
|
||||
business_profile,
|
||||
is_retry_payment,
|
||||
all_keys_required,
|
||||
merchant_connector_account,
|
||||
router_data,
|
||||
tokenization_action,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
// Spawn shadow UCS call in background
|
||||
let direct_router_data = result.0.clone();
|
||||
|
||||
@ -26,6 +26,7 @@ use hyperswitch_domain_models::router_flow_types::{
|
||||
use hyperswitch_domain_models::{
|
||||
payments as domain_payments, router_request_types::PaymentsCaptureData,
|
||||
};
|
||||
use hyperswitch_interfaces::api as api_interfaces;
|
||||
|
||||
use crate::{
|
||||
core::{
|
||||
@ -112,10 +113,10 @@ pub trait Feature<F, T> {
|
||||
|
||||
async fn pre_decide_flows<'a>(
|
||||
self,
|
||||
state: &SessionState,
|
||||
connector: &api::ConnectorData,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
pre_decide_inputs: PreDecideFlowInputs<'a>,
|
||||
_state: &SessionState,
|
||||
_connector: &api::ConnectorData,
|
||||
_merchant_context: &domain::MerchantContext,
|
||||
_pre_decide_inputs: PreDecideFlowInputs<'a>,
|
||||
) -> RouterResult<(PreDecideFlowOutput, Self)>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -246,6 +247,9 @@ pub trait Feature<F, T> {
|
||||
) {
|
||||
}
|
||||
|
||||
fn get_current_flow_info(&self) -> Option<api_interfaces::CurrentFlowInfo<'_>> {
|
||||
None
|
||||
}
|
||||
async fn call_unified_connector_service<'a>(
|
||||
&mut self,
|
||||
_state: &SessionState,
|
||||
|
||||
@ -240,7 +240,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
);
|
||||
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
let should_continue = matches!(
|
||||
let should_continue_further = matches!(
|
||||
router_data.response,
|
||||
Ok(types::PaymentsResponseData::TransactionResponse {
|
||||
ref redirection_data,
|
||||
@ -259,6 +259,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
None
|
||||
};
|
||||
|
||||
// TODO add logic to do connector specific pre tasks if any
|
||||
|
||||
// In case of authorize flow, pre-task and post-tasks are being called in build request
|
||||
// if we do not want to proceed further, then the function will return Ok(None, false)
|
||||
let (connector_request, should_continue_further) = if should_continue_further {
|
||||
@ -278,10 +280,16 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
connector_response_reference_id,
|
||||
session_token,
|
||||
connector_request,
|
||||
should_continue_further: should_continue,
|
||||
should_continue_further,
|
||||
};
|
||||
Ok((pre_decide_output, router_data))
|
||||
}
|
||||
fn get_current_flow_info(&self) -> Option<api_interface::CurrentFlowInfo<'_>> {
|
||||
Some(api_interface::CurrentFlowInfo::Authorize {
|
||||
auth_type: &self.auth_type,
|
||||
request_data: &self.request,
|
||||
})
|
||||
}
|
||||
async fn decide_flows<'a>(
|
||||
mut self,
|
||||
state: &SessionState,
|
||||
@ -476,10 +484,10 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
||||
.connector
|
||||
.get_preprocessing_flow_if_needed(current_flow_info);
|
||||
match preprocessing_flow_name {
|
||||
Some(api_interface::PreProcessingFlowName::PreProcessing) | None => {
|
||||
// Calling this function for None as well to maintain backward compatibility
|
||||
Some(api_interface::PreProcessingFlowName::PreProcessing) => {
|
||||
authorize_preprocessing_steps(state, &self, true, connector).await
|
||||
}
|
||||
None => Ok(self),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user