mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
chore: introduce dedicated function to check for should continue after preprocessing
This commit is contained in:
@ -2286,7 +2286,7 @@ impl ConnectorSpecifications for Cybersource {
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowDetails> {
|
||||
) -> Option<api::PreProcessingFlowName> {
|
||||
match current_flow_info {
|
||||
api::CurrentFlowInfo::Authorize { .. } => {
|
||||
// during authorize flow, there is no pre processing flow. Only alternate PreAuthenticate flow
|
||||
@ -2297,45 +2297,43 @@ impl ConnectorSpecifications for Cybersource {
|
||||
let redirect_response = request_data.redirect_response.as_ref()?;
|
||||
match redirect_response.params.as_ref() {
|
||||
Some(param) if !param.peek().is_empty() => {
|
||||
let flow_name = api::PreProcessingFlowName::Authenticate;
|
||||
let should_continue =
|
||||
|authn_result: &api::PreProcessingFlowResponse<'_>| -> bool {
|
||||
(matches!(
|
||||
authn_result.response,
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
ref redirection_data,
|
||||
..
|
||||
}) if redirection_data.is_none()
|
||||
) && authn_result.attempt_status
|
||||
!= common_enums::AttemptStatus::AuthenticationFailed)
|
||||
};
|
||||
Some(api::PreProcessingFlowDetails {
|
||||
flow_name,
|
||||
should_continue: Box::new(should_continue),
|
||||
})
|
||||
}
|
||||
Some(_) | None => {
|
||||
let flow_name = api::PreProcessingFlowName::PostAuthenticate;
|
||||
let should_continue =
|
||||
|authn_result: &api::PreProcessingFlowResponse<'_>| -> bool {
|
||||
(matches!(
|
||||
authn_result.response,
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
ref redirection_data,
|
||||
..
|
||||
}) if redirection_data.is_none()
|
||||
) && authn_result.attempt_status
|
||||
!= common_enums::AttemptStatus::AuthenticationFailed)
|
||||
};
|
||||
Some(api::PreProcessingFlowDetails {
|
||||
flow_name,
|
||||
should_continue: Box::new(should_continue),
|
||||
})
|
||||
Some(api::PreProcessingFlowName::Authenticate)
|
||||
}
|
||||
Some(_) | None => Some(api::PreProcessingFlowName::PostAuthenticate),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn decide_should_continue_after_preprocessing(
|
||||
&self,
|
||||
current_flow: api::CurrentFlowInfo<'_>,
|
||||
pre_processing_flow_name: api::PreProcessingFlowName,
|
||||
preprocessing_flow_response: api::PreProcessingFlowResponse<'_>,
|
||||
) -> bool {
|
||||
match (current_flow, pre_processing_flow_name) {
|
||||
(api::CurrentFlowInfo::Authorize { .. }, _) => {
|
||||
// during authorize flow, there is no pre processing flow. Only alternate PreAuthenticate flow
|
||||
true
|
||||
}
|
||||
(
|
||||
api::CurrentFlowInfo::CompleteAuthorize { .. },
|
||||
api::PreProcessingFlowName::Authenticate,
|
||||
)
|
||||
| (
|
||||
api::CurrentFlowInfo::CompleteAuthorize { .. },
|
||||
api::PreProcessingFlowName::PostAuthenticate,
|
||||
) => {
|
||||
(matches!(
|
||||
preprocessing_flow_response.response,
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
ref redirection_data,
|
||||
..
|
||||
}) if redirection_data.is_none()
|
||||
) && preprocessing_flow_response.attempt_status
|
||||
!= common_enums::AttemptStatus::AuthenticationFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_alternate_flow_if_needed(
|
||||
&self,
|
||||
current_flow: api::CurrentFlowInfo<'_>,
|
||||
|
||||
@ -429,43 +429,26 @@ pub struct PreProcessingFlowResponse<'a> {
|
||||
pub attempt_status: enums::AttemptStatus,
|
||||
}
|
||||
|
||||
/// Details related to preprocessing flow
|
||||
pub struct PreProcessingFlowDetails {
|
||||
/// Name of the preprocessing flow
|
||||
pub flow_name: PreProcessingFlowName,
|
||||
|
||||
/// Based on the response of the preprocessing flow, decide whether to continue with the current flow or not
|
||||
pub should_continue: Box<dyn Fn(&PreProcessingFlowResponse<'_>) -> bool>,
|
||||
}
|
||||
|
||||
/// Custom Debug implementation for PreProcessingFlowDetails
|
||||
///
|
||||
/// Since Closure does not implement Debug trait
|
||||
impl Debug for PreProcessingFlowDetails {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self {
|
||||
flow_name,
|
||||
should_continue: _,
|
||||
} = self;
|
||||
f.debug_struct("PreProcessingFlowDetails")
|
||||
.field("flow_name", flow_name)
|
||||
.field(
|
||||
"should_continue",
|
||||
&"<closure: fn(&PreProcessingFlowResponse) -> bool>",
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The trait that provides specifications about the connector
|
||||
pub trait ConnectorSpecifications {
|
||||
/// Preprocessing flow name if any, that must be made before the current flow.
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
_current_flow: CurrentFlowInfo<'_>,
|
||||
) -> Option<PreProcessingFlowDetails> {
|
||||
) -> Option<PreProcessingFlowName> {
|
||||
None
|
||||
}
|
||||
/// Based on the current flow and preprocessing_flow_response, decide if the main flow must be called or not
|
||||
///
|
||||
/// By default, always continue with the main flow after the preprocessing flow.
|
||||
fn decide_should_continue_after_preprocessing(
|
||||
&self,
|
||||
_current_flow: CurrentFlowInfo<'_>,
|
||||
_pre_processing_flow_name: PreProcessingFlowName,
|
||||
_preprocessing_flow_response: PreProcessingFlowResponse<'_>,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
/// If Some is returned, the returned api flow must be made instead of the current flow.
|
||||
fn get_alternate_flow_if_needed(
|
||||
&self,
|
||||
|
||||
@ -511,7 +511,7 @@ impl ConnectorSpecifications for ConnectorEnum {
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowDetails> {
|
||||
) -> 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),
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
Currently in hyperswitch, under authorize flow, there can be a lot of flows that must be called before actually calling the authorize. Like session/access token, customer create, order create etc.
|
||||
Currently these are scattered all across the handler function 'pub async fn payments_operation_core<F, Req, Op, FData, D>(' .
|
||||
I want to standardize the flows like this.
|
||||
|
||||
PrimaryFlows and Secondary Flows.
|
||||
PrimaryFlows: The actual flow.
|
||||
SecondaryFlow: These flows might come as prerequisites before the Primary Flows. There can be multiple Secondary Flows for a PrimaryFlow.
|
||||
|
||||
PrimaryFlows can be defined as:
|
||||
1. A flow where the response is returned to the client.
|
||||
Eg: Authorize, Capture
|
||||
Authorize can have SessionToken, OrderCreate etc.
|
||||
|
||||
If Authorize is a PrimaryFlow, Then SessionTokena and OrderCreate will be SecondaryFlow.
|
||||
The order will be like SessionToken(2ndary) -> OrderCreate(2ndary) -> Authorize(Primary).
|
||||
Similarly,
|
||||
* SessionToken(2ndary) -> PreAuthN(Primary).
|
||||
* SessionToken(2ndary) -> AuthN(Primary).
|
||||
* PostAuthN(2ndary) -> Authorization(Primary)
|
||||
|
||||
Lets have marker trait for Primary and Secondary Flows. Feel free to come up with a better nomenclature for things.
|
||||
|
||||
Execution order must be known at compile time.
|
||||
@ -219,43 +219,55 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
||||
|
||||
async fn call_preprocessing_through_unified_connector_service<'a>(
|
||||
self,
|
||||
_state: &SessionState,
|
||||
_header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
|
||||
_lineage_ids: &grpc_client::LineageIds,
|
||||
#[cfg(feature = "v1")] _merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
state: &SessionState,
|
||||
header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
|
||||
lineage_ids: &grpc_client::LineageIds,
|
||||
#[cfg(feature = "v1")] merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
#[cfg(feature = "v2")]
|
||||
_merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
_merchant_context: &domain::MerchantContext,
|
||||
merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
merchant_context: &domain::MerchantContext,
|
||||
connector_data: &api::ConnectorData,
|
||||
_unified_connector_service_execution_mode: common_enums::ExecutionMode,
|
||||
_merchant_order_reference_id: Option<String>,
|
||||
unified_connector_service_execution_mode: common_enums::ExecutionMode,
|
||||
merchant_order_reference_id: Option<String>,
|
||||
) -> RouterResult<(Self, bool)> {
|
||||
let current_flow = api_interface::CurrentFlowInfo::CompleteAuthorize {
|
||||
request_data: &self.request,
|
||||
};
|
||||
if let Some(preprocessing_flow_details) = connector_data
|
||||
let optional_preprocessing_flow = connector_data
|
||||
.connector
|
||||
.get_preprocessing_flow_if_needed(current_flow)
|
||||
{
|
||||
let updated_router_data = match preprocessing_flow_details.flow_name {
|
||||
api_interface::PreProcessingFlowName::Authenticate => {
|
||||
// Call UCS for Authenticate flow
|
||||
self
|
||||
}
|
||||
api_interface::PreProcessingFlowName::PostAuthenticate => {
|
||||
// Call UCS for PostAuthenticate flow
|
||||
self
|
||||
}
|
||||
};
|
||||
let pre_processing_flow_response = api_interface::PreProcessingFlowResponse {
|
||||
response: &updated_router_data.response,
|
||||
attempt_status: updated_router_data.status,
|
||||
};
|
||||
let should_continue =
|
||||
(preprocessing_flow_details.should_continue)(&pre_processing_flow_response);
|
||||
Ok((updated_router_data, should_continue))
|
||||
} else {
|
||||
Ok((self, true))
|
||||
.get_preprocessing_flow_if_needed(current_flow);
|
||||
match optional_preprocessing_flow {
|
||||
Some(preprocessing_flow) => {
|
||||
let updated_router_data = handle_preprocessing_through_unified_connector_service(
|
||||
self,
|
||||
state,
|
||||
header_payload,
|
||||
lineage_ids,
|
||||
merchant_connector_account.clone(),
|
||||
merchant_context,
|
||||
connector_data,
|
||||
unified_connector_service_execution_mode,
|
||||
merchant_order_reference_id.clone(),
|
||||
preprocessing_flow,
|
||||
)
|
||||
.await?;
|
||||
let pre_processing_flow_response = api_interface::PreProcessingFlowResponse {
|
||||
response: &updated_router_data.response,
|
||||
attempt_status: updated_router_data.status,
|
||||
};
|
||||
let current_flow = api_interface::CurrentFlowInfo::CompleteAuthorize {
|
||||
request_data: &updated_router_data.request,
|
||||
};
|
||||
let should_continue = connector_data
|
||||
.connector
|
||||
.decide_should_continue_after_preprocessing(
|
||||
current_flow,
|
||||
preprocessing_flow,
|
||||
pre_processing_flow_response,
|
||||
);
|
||||
Ok((updated_router_data, should_continue))
|
||||
}
|
||||
None => Ok((self, true)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,6 +289,41 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_preprocessing_through_unified_connector_service(
|
||||
router_data: types::RouterData<
|
||||
api::CompleteAuthorize,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
_state: &SessionState,
|
||||
_header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
|
||||
_lineage_ids: &grpc_client::LineageIds,
|
||||
#[cfg(feature = "v1")] _merchant_connector_account: helpers::MerchantConnectorAccountType,
|
||||
#[cfg(feature = "v2")] _merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
_merchant_context: &domain::MerchantContext,
|
||||
_connector_data: &api::ConnectorData,
|
||||
_unified_connector_service_execution_mode: common_enums::ExecutionMode,
|
||||
_merchant_order_reference_id: Option<String>,
|
||||
preprocessing_flow_name: api_interface::PreProcessingFlowName,
|
||||
) -> RouterResult<
|
||||
types::RouterData<
|
||||
api::CompleteAuthorize,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> {
|
||||
match preprocessing_flow_name {
|
||||
api_interface::PreProcessingFlowName::Authenticate => {
|
||||
// Call UCS for Authenticate flow
|
||||
Ok(router_data)
|
||||
}
|
||||
api_interface::PreProcessingFlowName::PostAuthenticate => {
|
||||
// Call UCS for PostAuthenticate flow
|
||||
Ok(router_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn complete_authorize_preprocessing_steps<F: Clone>(
|
||||
state: &SessionState,
|
||||
router_data: &types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
|
||||
|
||||
Reference in New Issue
Block a user