mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
chore: add logic to use pre decide flow
This commit is contained in:
@ -348,6 +348,12 @@ pub enum AuthenticationType {
|
|||||||
NoThreeDs,
|
NoThreeDs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AuthenticationType {
|
||||||
|
pub fn is_three_ds(&self) -> bool {
|
||||||
|
matches!(self, Self::ThreeDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The status of the capture
|
/// The status of the capture
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
|||||||
@ -2283,4 +2283,58 @@ impl ConnectorSpecifications for Cybersource {
|
|||||||
fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
|
fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
|
||||||
Some(&CYBERSOURCE_SUPPORTED_WEBHOOK_FLOWS)
|
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,
|
PaymentsTaxCalculationData,
|
||||||
TaxCalculationResponseData,
|
TaxCalculationResponseData,
|
||||||
> for $path::$connector{}
|
> for $path::$connector{}
|
||||||
impl ConnectorIntegrationV2<
|
impl ConnectorIntegrationV2<
|
||||||
SdkSessionUpdate,
|
SdkSessionUpdate,
|
||||||
PaymentFlowData,
|
PaymentFlowData,
|
||||||
SdkPaymentsSessionUpdateData,
|
SdkPaymentsSessionUpdateData,
|
||||||
|
|||||||
@ -415,6 +415,13 @@ pub enum PreProcessingFlowName {
|
|||||||
|
|
||||||
/// The trait that provides specifications about the connector
|
/// The trait that provides specifications about the connector
|
||||||
pub trait ConnectorSpecifications {
|
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.
|
/// Preprocessing flow name if any, that must be made before the current flow.
|
||||||
fn get_preprocessing_flow_if_needed(
|
fn get_preprocessing_flow_if_needed(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@ -625,6 +625,38 @@ impl ConnectorValidation for ConnectorEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectorSpecifications 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> {
|
fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
|
||||||
match self {
|
match self {
|
||||||
Self::Old(connector) => connector.get_supported_payment_methods(),
|
Self::Old(connector) => connector.get_supported_payment_methods(),
|
||||||
|
|||||||
@ -66,6 +66,7 @@ use hyperswitch_domain_models::{
|
|||||||
payments::{self, payment_intent::CustomerData, ClickToPayMetaData},
|
payments::{self, payment_intent::CustomerData, ClickToPayMetaData},
|
||||||
router_data::AccessToken,
|
router_data::AccessToken,
|
||||||
};
|
};
|
||||||
|
use hyperswitch_interfaces::api::ConnectorSpecifications;
|
||||||
use masking::{ExposeInterface, PeekInterface, Secret};
|
use masking::{ExposeInterface, PeekInterface, Secret};
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use operations::ValidateStatusForOperation;
|
use operations::ValidateStatusForOperation;
|
||||||
@ -4080,7 +4081,7 @@ where
|
|||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[instrument(skip_all)]
|
#[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,
|
state: &SessionState,
|
||||||
req_state: ReqState,
|
req_state: ReqState,
|
||||||
merchant_context: &domain::MerchantContext,
|
merchant_context: &domain::MerchantContext,
|
||||||
@ -4097,7 +4098,7 @@ pub async fn _call_connector_service<F, RouterDReq, ApiRequest, D>(
|
|||||||
is_retry_payment: bool,
|
is_retry_payment: bool,
|
||||||
return_raw_connector_response: Option<bool>,
|
return_raw_connector_response: Option<bool>,
|
||||||
merchant_connector_account: helpers::MerchantConnectorAccountType,
|
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,
|
tokenization_action: TokenizationAction,
|
||||||
) -> RouterResult<(
|
) -> RouterResult<(
|
||||||
RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
|
||||||
@ -4728,27 +4729,61 @@ where
|
|||||||
// Update feature metadata to track Direct routing usage for stickiness
|
// Update feature metadata to track Direct routing usage for stickiness
|
||||||
update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;
|
update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;
|
||||||
|
|
||||||
call_connector_service(
|
let should_call_connector_service_with_pre_decide_flow = router_data
|
||||||
state,
|
.get_current_flow_info()
|
||||||
req_state,
|
.map(|current_flow_info| {
|
||||||
merchant_context,
|
connector
|
||||||
connector,
|
.connector
|
||||||
operation,
|
.should_call_connector_service_with_pre_decide_flow(current_flow_info)
|
||||||
payment_data,
|
})
|
||||||
customer,
|
.unwrap_or(false);
|
||||||
call_connector_action,
|
|
||||||
validate_result,
|
if should_call_connector_service_with_pre_decide_flow {
|
||||||
schedule_time,
|
logger::info!("Calling call_connector_service_with_pre_decide_flow");
|
||||||
header_payload,
|
call_connector_service_with_pre_decide_flow(
|
||||||
frm_suggestion,
|
state,
|
||||||
business_profile,
|
req_state,
|
||||||
is_retry_payment,
|
merchant_context,
|
||||||
all_keys_required,
|
connector,
|
||||||
merchant_connector_account,
|
operation,
|
||||||
router_data,
|
payment_data,
|
||||||
tokenization_action,
|
customer,
|
||||||
)
|
call_connector_action,
|
||||||
.await
|
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")]
|
#[cfg(feature = "v1")]
|
||||||
@ -4815,28 +4850,61 @@ where
|
|||||||
// Update feature metadata to track Direct routing usage for stickiness
|
// Update feature metadata to track Direct routing usage for stickiness
|
||||||
update_gateway_system_in_feature_metadata(payment_data, GatewaySystem::Direct)?;
|
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
|
// Call Direct connector service
|
||||||
let result = call_connector_service(
|
let result = if should_call_connector_service_with_pre_decide_flow {
|
||||||
state,
|
logger::info!("Calling call_connector_service_with_pre_decide_flow");
|
||||||
req_state,
|
call_connector_service_with_pre_decide_flow(
|
||||||
merchant_context,
|
state,
|
||||||
connector,
|
req_state,
|
||||||
operation,
|
merchant_context,
|
||||||
payment_data,
|
connector,
|
||||||
customer,
|
operation,
|
||||||
call_connector_action,
|
payment_data,
|
||||||
validate_result,
|
customer,
|
||||||
schedule_time,
|
call_connector_action,
|
||||||
header_payload,
|
validate_result,
|
||||||
frm_suggestion,
|
schedule_time,
|
||||||
business_profile,
|
header_payload,
|
||||||
is_retry_payment,
|
frm_suggestion,
|
||||||
all_keys_required,
|
business_profile,
|
||||||
merchant_connector_account,
|
is_retry_payment,
|
||||||
router_data,
|
all_keys_required,
|
||||||
tokenization_action,
|
merchant_connector_account,
|
||||||
)
|
router_data,
|
||||||
.await?;
|
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
|
// Spawn shadow UCS call in background
|
||||||
let direct_router_data = result.0.clone();
|
let direct_router_data = result.0.clone();
|
||||||
|
|||||||
@ -26,6 +26,7 @@ use hyperswitch_domain_models::router_flow_types::{
|
|||||||
use hyperswitch_domain_models::{
|
use hyperswitch_domain_models::{
|
||||||
payments as domain_payments, router_request_types::PaymentsCaptureData,
|
payments as domain_payments, router_request_types::PaymentsCaptureData,
|
||||||
};
|
};
|
||||||
|
use hyperswitch_interfaces::api as api_interfaces;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
@ -112,10 +113,10 @@ pub trait Feature<F, T> {
|
|||||||
|
|
||||||
async fn pre_decide_flows<'a>(
|
async fn pre_decide_flows<'a>(
|
||||||
self,
|
self,
|
||||||
state: &SessionState,
|
_state: &SessionState,
|
||||||
connector: &api::ConnectorData,
|
_connector: &api::ConnectorData,
|
||||||
merchant_context: &domain::MerchantContext,
|
_merchant_context: &domain::MerchantContext,
|
||||||
pre_decide_inputs: PreDecideFlowInputs<'a>,
|
_pre_decide_inputs: PreDecideFlowInputs<'a>,
|
||||||
) -> RouterResult<(PreDecideFlowOutput, Self)>
|
) -> RouterResult<(PreDecideFlowOutput, Self)>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
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>(
|
async fn call_unified_connector_service<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_state: &SessionState,
|
_state: &SessionState,
|
||||||
|
|||||||
@ -240,7 +240,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
);
|
);
|
||||||
|
|
||||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||||
let should_continue = matches!(
|
let should_continue_further = matches!(
|
||||||
router_data.response,
|
router_data.response,
|
||||||
Ok(types::PaymentsResponseData::TransactionResponse {
|
Ok(types::PaymentsResponseData::TransactionResponse {
|
||||||
ref redirection_data,
|
ref redirection_data,
|
||||||
@ -259,6 +259,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
None
|
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
|
// 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)
|
// 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 {
|
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,
|
connector_response_reference_id,
|
||||||
session_token,
|
session_token,
|
||||||
connector_request,
|
connector_request,
|
||||||
should_continue_further: should_continue,
|
should_continue_further,
|
||||||
};
|
};
|
||||||
Ok((pre_decide_output, router_data))
|
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>(
|
async fn decide_flows<'a>(
|
||||||
mut self,
|
mut self,
|
||||||
state: &SessionState,
|
state: &SessionState,
|
||||||
@ -476,10 +484,10 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
|
|||||||
.connector
|
.connector
|
||||||
.get_preprocessing_flow_if_needed(current_flow_info);
|
.get_preprocessing_flow_if_needed(current_flow_info);
|
||||||
match preprocessing_flow_name {
|
match preprocessing_flow_name {
|
||||||
Some(api_interface::PreProcessingFlowName::PreProcessing) | None => {
|
Some(api_interface::PreProcessingFlowName::PreProcessing) => {
|
||||||
// Calling this function for None as well to maintain backward compatibility
|
|
||||||
authorize_preprocessing_steps(state, &self, true, connector).await
|
authorize_preprocessing_steps(state, &self, true, connector).await
|
||||||
}
|
}
|
||||||
|
None => Ok(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user