mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 13:30:39 +08:00
chore: introduce placeholders for complete authorize through ucs
This commit is contained in:
@ -2286,12 +2286,54 @@ impl ConnectorSpecifications for Cybersource {
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowName> {
|
||||
) -> Option<api::PreProcessingFlowDetails> {
|
||||
match current_flow_info {
|
||||
api::CurrentFlowInfo::Authorize { .. } => {
|
||||
// during authorize flow, there is no pre processing flow. Only alternate PreAuthenticate flow
|
||||
None
|
||||
}
|
||||
api::CurrentFlowInfo::CompleteAuthorize { request_data } => {
|
||||
// TODO: add logic before deciding the pre processing flow Authenticate or PostAuthenticate
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_alternate_flow_if_needed(
|
||||
@ -2309,6 +2351,8 @@ impl ConnectorSpecifications for Cybersource {
|
||||
None
|
||||
}
|
||||
}
|
||||
// No alternate flow for complete authorize
|
||||
api::CurrentFlowInfo::CompleteAuthorize { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,12 +655,12 @@ pub struct PaymentsAuthenticateData {
|
||||
pub amount: Option<i64>,
|
||||
pub email: Option<pii::Email>,
|
||||
pub currency: Option<storage_enums::Currency>,
|
||||
pub payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||
pub router_return_url: Option<String>,
|
||||
// pub payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||
// pub router_return_url: Option<String>,
|
||||
pub complete_authorize_url: Option<String>,
|
||||
pub browser_info: Option<BrowserInformation>,
|
||||
pub connector_transaction_id: Option<String>,
|
||||
pub enrolled_for_3ds: bool,
|
||||
// pub connector_transaction_id: Option<String>,
|
||||
// pub enrolled_for_3ds: bool,
|
||||
pub redirect_response: Option<CompleteAuthorizeRedirectResponse>,
|
||||
|
||||
// New amount for amount frame work
|
||||
@ -677,13 +677,30 @@ impl TryFrom<PaymentsAuthorizeData> for PaymentsAuthenticateData {
|
||||
minor_amount: Some(data.minor_amount),
|
||||
email: data.email,
|
||||
currency: Some(data.currency),
|
||||
payment_method_type: data.payment_method_type,
|
||||
router_return_url: data.router_return_url,
|
||||
// payment_method_type: data.payment_method_type,
|
||||
// router_return_url: data.router_return_url,
|
||||
complete_authorize_url: data.complete_authorize_url,
|
||||
browser_info: data.browser_info,
|
||||
connector_transaction_id: None,
|
||||
// connector_transaction_id: None,
|
||||
redirect_response: None,
|
||||
enrolled_for_3ds: data.enrolled_for_3ds,
|
||||
// enrolled_for_3ds: data.enrolled_for_3ds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<CompleteAuthorizeData> for PaymentsAuthenticateData {
|
||||
type Error = error_stack::Report<ApiErrorResponse>;
|
||||
|
||||
fn try_from(data: CompleteAuthorizeData) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
payment_method_data: data.payment_method_data,
|
||||
amount: Some(data.amount),
|
||||
minor_amount: Some(data.minor_amount),
|
||||
email: data.email,
|
||||
currency: Some(data.currency),
|
||||
complete_authorize_url: data.complete_authorize_url,
|
||||
browser_info: data.browser_info,
|
||||
redirect_response: data.redirect_response,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,17 +57,18 @@ use hyperswitch_domain_models::{
|
||||
AuthenticationConfirmation, PostAuthenticate, PreAuthenticate, VerifyWebhookSource,
|
||||
},
|
||||
router_request_types::{
|
||||
self,
|
||||
unified_authentication_service::{
|
||||
UasAuthenticationRequestData, UasAuthenticationResponseData,
|
||||
UasConfirmationRequestData, UasPostAuthenticationRequestData,
|
||||
UasPreAuthenticationRequestData,
|
||||
},
|
||||
AccessTokenAuthenticationRequestData, AccessTokenRequestData, MandateRevokeRequestData,
|
||||
PaymentsAuthorizeData, VerifyWebhookSourceRequestData,
|
||||
VerifyWebhookSourceRequestData,
|
||||
},
|
||||
router_response_types::{
|
||||
ConnectorInfo, MandateRevokeResponseData, PaymentMethodDetails, SupportedPaymentMethods,
|
||||
VerifyWebhookSourceResponseData,
|
||||
self, ConnectorInfo, MandateRevokeResponseData, PaymentMethodDetails,
|
||||
SupportedPaymentMethods, VerifyWebhookSourceResponseData,
|
||||
},
|
||||
};
|
||||
use masking::Maskable;
|
||||
@ -390,7 +391,12 @@ pub enum CurrentFlowInfo<'a> {
|
||||
/// The authentication type being used
|
||||
auth_type: &'a enums::AuthenticationType,
|
||||
/// The payment authorize request data
|
||||
request_data: &'a PaymentsAuthorizeData,
|
||||
request_data: &'a router_request_types::PaymentsAuthorizeData,
|
||||
},
|
||||
/// CompleteAuthorize flow information
|
||||
CompleteAuthorize {
|
||||
/// The payment authorize request data
|
||||
request_data: &'a router_request_types::CompleteAuthorizeData,
|
||||
},
|
||||
}
|
||||
|
||||
@ -406,11 +412,49 @@ pub enum AlternateFlow {
|
||||
///
|
||||
/// For example, PreProcessing flow must be made before Authorize flow.
|
||||
/// Or PostAuthenticate flow must be made before CompleteAuthorize flow for cybersource.
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PreProcessingFlowName {
|
||||
#[default]
|
||||
/// Pre-processing flow
|
||||
PreProcessing,
|
||||
/// Authentication flow must be made before the actual flow
|
||||
Authenticate,
|
||||
/// Post-authentication flow must be made before the actual flow
|
||||
PostAuthenticate,
|
||||
}
|
||||
|
||||
/// Response of the preprocessing flow
|
||||
#[derive(Debug)]
|
||||
pub struct PreProcessingFlowResponse<'a> {
|
||||
/// Payment response data from the preprocessing flow
|
||||
pub response: &'a Result<router_response_types::PaymentsResponseData, ErrorResponse>,
|
||||
/// Attempt status after the preprocessing flow
|
||||
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
|
||||
@ -419,8 +463,8 @@ pub trait ConnectorSpecifications {
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
_current_flow: CurrentFlowInfo<'_>,
|
||||
) -> Option<PreProcessingFlowName> {
|
||||
Some(PreProcessingFlowName::default())
|
||||
) -> Option<PreProcessingFlowDetails> {
|
||||
None
|
||||
}
|
||||
/// If Some is returned, the returned api flow must be made instead of the current flow.
|
||||
fn get_alternate_flow_if_needed(
|
||||
@ -766,7 +810,7 @@ pub trait ConnectorValidation: ConnectorCommon + ConnectorSpecifications {
|
||||
/// fn validate_psync_reference_id
|
||||
fn validate_psync_reference_id(
|
||||
&self,
|
||||
data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData,
|
||||
data: &router_request_types::PaymentsSyncData,
|
||||
_is_three_ds: bool,
|
||||
_status: enums::AttemptStatus,
|
||||
_connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
|
||||
|
||||
@ -511,7 +511,7 @@ impl ConnectorSpecifications for ConnectorEnum {
|
||||
fn get_preprocessing_flow_if_needed(
|
||||
&self,
|
||||
current_flow_info: api::CurrentFlowInfo<'_>,
|
||||
) -> Option<api::PreProcessingFlowName> {
|
||||
) -> Option<api::PreProcessingFlowDetails> {
|
||||
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),
|
||||
|
||||
@ -79,19 +79,19 @@ pub trait ConstructFlowSpecificData<F, Req, Res> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PreDecideFlowOutput {
|
||||
pub connector_response_reference_id: Option<String>,
|
||||
pub session_token: Option<api::SessionToken>,
|
||||
pub connector_request: Option<services::Request>,
|
||||
pub should_continue_further: bool,
|
||||
}
|
||||
// pub struct PreDecideFlowOutput {
|
||||
// pub connector_response_reference_id: Option<String>,
|
||||
// pub session_token: Option<api::SessionToken>,
|
||||
// pub connector_request: Option<services::Request>,
|
||||
// pub should_continue_further: bool,
|
||||
// }
|
||||
|
||||
pub struct PreDecideFlowInputs<'a> {
|
||||
pub call_connector_action: &'a payments::CallConnectorAction,
|
||||
pub tokenization_action: &'a payments::TokenizationAction,
|
||||
pub is_retry_payment: bool,
|
||||
pub creds_identifier: Option<&'a str>,
|
||||
}
|
||||
// pub struct PreDecideFlowInputs<'a> {
|
||||
// pub call_connector_action: &'a payments::CallConnectorAction,
|
||||
// pub tokenization_action: &'a payments::TokenizationAction,
|
||||
// pub is_retry_payment: bool,
|
||||
// pub creds_identifier: Option<&'a str>,
|
||||
// }
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[async_trait]
|
||||
@ -227,6 +227,28 @@ pub trait Feature<F, T> {
|
||||
fn get_current_flow_info(&self) -> Option<api_interfaces::CurrentFlowInfo<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn call_preprocessing_through_unified_connector_service<'a>(
|
||||
self,
|
||||
_state: &SessionState,
|
||||
_header_payload: &domain_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: ExecutionMode,
|
||||
) -> RouterResult<(Self, bool)>
|
||||
where
|
||||
F: Clone,
|
||||
Self: Sized,
|
||||
dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
|
||||
{
|
||||
// Default behaviour is to do nothing and continue further
|
||||
Ok((self, true))
|
||||
}
|
||||
|
||||
async fn call_unified_connector_service<'a>(
|
||||
&mut self,
|
||||
_state: &SessionState,
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use external_services::grpc_client;
|
||||
use hyperswitch_interfaces::{api as api_interface, api::ConnectorSpecifications};
|
||||
use masking::ExposeInterface;
|
||||
|
||||
use super::{ConstructFlowSpecificData, Feature};
|
||||
@ -214,6 +216,63 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
|
||||
) -> RouterResult<Self> {
|
||||
complete_authorize_preprocessing_steps(state, &self, true, connector).await
|
||||
}
|
||||
|
||||
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,
|
||||
#[cfg(feature = "v2")]
|
||||
_merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
|
||||
_merchant_context: &domain::MerchantContext,
|
||||
connector_data: &api::ConnectorData,
|
||||
_unified_connector_service_execution_mode: common_enums::ExecutionMode,
|
||||
) -> RouterResult<(Self, bool)> {
|
||||
let current_flow = api_interface::CurrentFlowInfo::CompleteAuthorize {
|
||||
request_data: &self.request,
|
||||
};
|
||||
if let Some(preprocessing_flow_details) = 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))
|
||||
}
|
||||
}
|
||||
|
||||
async fn call_unified_connector_service<'a>(
|
||||
&mut self,
|
||||
_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,
|
||||
) -> RouterResult<()> {
|
||||
// Call UCS for Authorize flow
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn complete_authorize_preprocessing_steps<F: Clone>(
|
||||
|
||||
@ -7873,7 +7873,7 @@ pub async fn process_through_ucs<'a, F, RouterDReq, ApiRequest, D>(
|
||||
business_profile: &'a domain::Profile,
|
||||
merchant_connector_account: MerchantConnectorAccountType,
|
||||
connector_data: &api::ConnectorData,
|
||||
mut router_data: RouterData<F, RouterDReq, PaymentsResponseData>,
|
||||
router_data: RouterData<F, RouterDReq, PaymentsResponseData>,
|
||||
) -> RouterResult<(
|
||||
RouterData<F, RouterDReq, PaymentsResponseData>,
|
||||
MerchantConnectorAccountType,
|
||||
@ -7917,6 +7917,22 @@ where
|
||||
GatewaySystem::UnifiedConnectorService,
|
||||
)?;
|
||||
|
||||
let lineage_ids = grpc_client::LineageIds::new(
|
||||
business_profile.merchant_id.clone(),
|
||||
business_profile.get_id().clone(),
|
||||
);
|
||||
let (mut router_data, should_continue) = router_data
|
||||
.call_preprocessing_through_unified_connector_service(
|
||||
state,
|
||||
&header_payload,
|
||||
&lineage_ids,
|
||||
merchant_connector_account.clone(),
|
||||
merchant_context,
|
||||
connector_data,
|
||||
ExecutionMode::Primary, // UCS is called in primary mode
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Update trackers
|
||||
(_, *payment_data) = operation
|
||||
.to_update_tracker()?
|
||||
@ -7933,23 +7949,20 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Call UCS
|
||||
let lineage_ids = grpc_client::LineageIds::new(
|
||||
business_profile.merchant_id.clone(),
|
||||
business_profile.get_id().clone(),
|
||||
);
|
||||
|
||||
router_data
|
||||
.call_unified_connector_service(
|
||||
state,
|
||||
&header_payload,
|
||||
lineage_ids,
|
||||
merchant_connector_account.clone(),
|
||||
merchant_context,
|
||||
connector_data,
|
||||
ExecutionMode::Primary, // UCS is called in primary mode
|
||||
)
|
||||
.await?;
|
||||
// Based on the preprocessing response, decide whether to continue with UCS call
|
||||
if should_continue {
|
||||
router_data
|
||||
.call_unified_connector_service(
|
||||
state,
|
||||
&header_payload,
|
||||
lineage_ids,
|
||||
merchant_connector_account.clone(),
|
||||
merchant_context,
|
||||
connector_data,
|
||||
ExecutionMode::Primary, // UCS is called in primary mode
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok((router_data, merchant_connector_account))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user