// File: crates/router/src/core/payments/flows.rs

pub mod approve_flow;
pub mod authorize_flow;
pub mod cancel_flow;
pub mod cancel_post_capture_flow;
pub mod capture_flow;
pub mod complete_authorize_flow;
pub mod extend_authorization_flow;
#[cfg(feature = "v2")]
pub mod external_proxy_flow;
pub mod incremental_authorization_flow;
pub mod post_session_tokens_flow;
pub mod psync_flow;
pub mod reject_flow;
pub mod session_flow;
pub mod session_update_flow;
pub mod setup_mandate_flow;
pub mod update_metadata_flow;

use async_trait::async_trait;
use common_enums::{self, ExecutionMode};
use common_types::payments::CustomerAcceptance;
use external_services::grpc_client;
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
use hyperswitch_domain_models::router_flow_types::{
    BillingConnectorInvoiceSync, BillingConnectorPaymentsSync, InvoiceRecordBack,
};
use hyperswitch_domain_models::{
    payments as domain_payments, router_request_types::PaymentsCaptureData,
};
use hyperswitch_interfaces::api as api_interfaces;

use crate::{
    core::{
        errors::{ApiErrorResponse, RouterResult},
        payments::{self, gateway::context as gateway_context, helpers},
    },
    logger,
    routes::SessionState,
    services, types as router_types,
    types::{self, api, api::enums as api_enums, domain},
};

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait ConstructFlowSpecificData<F, Req, Res> {
    #[cfg(feature = "v1")]
    async fn construct_router_data<'a>(
        &self,
        state: &SessionState,
        connector_id: &str,
        platform: &domain::Platform,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        merchant_recipient_data: Option<types::MerchantRecipientData>,
        header_payload: Option<domain_payments::HeaderPayload>,
        payment_method: Option<common_enums::PaymentMethod>,
        payment_method_type: Option<common_enums::PaymentMethodType>,
    ) -> RouterResult<types::RouterData<F, Req, Res>>;

    #[cfg(feature = "v2")]
    async fn construct_router_data<'a>(
        &self,
        _state: &SessionState,
        _connector_id: &str,
        _platform: &domain::Platform,
        _customer: &Option<domain::Customer>,
        _merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        _merchant_recipient_data: Option<types::MerchantRecipientData>,
        _header_payload: Option<domain_payments::HeaderPayload>,
    ) -> RouterResult<types::RouterData<F, Req, Res>>;

    async fn get_merchant_recipient_data<'a>(
        &self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _merchant_connector_account: &helpers::MerchantConnectorAccountType,
        _connector: &api::ConnectorData,
    ) -> RouterResult<Option<types::MerchantRecipientData>> {
        Ok(None)
    }
}

#[allow(clippy::too_many_arguments)]
#[async_trait]
pub trait Feature<F, T> {
    async fn decide_flows<'a>(
        self,
        state: &SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
        connector_request: Option<services::Request>,
        business_profile: &domain::Profile,
        header_payload: domain_payments::HeaderPayload,
        return_raw_connector_response: Option<bool>,
        gateway_context: gateway_context::RouterGatewayContext,
    ) -> RouterResult<Self>
    where
        Self: Sized,
        F: Clone,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;

    async fn add_access_token<'a>(
        &self,
        state: &SessionState,
        connector: &api::ConnectorData,
        platform: &domain::Platform,
        creds_identifier: Option<&str>,
    ) -> RouterResult<types::AddAccessTokenResult>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>;

    async fn add_session_token<'a>(
        self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
    ) -> RouterResult<Self>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(self)
    }

    async fn add_payment_method_token<'a>(
        &mut self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
        _tokenization_action: &payments::TokenizationAction,
        _should_continue_payment: bool,
    ) -> RouterResult<types::PaymentMethodTokenResult>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(types::PaymentMethodTokenResult {
            payment_method_token_result: Ok(None),
            is_payment_method_tokenization_performed: false,
            connector_response: None,
        })
    }

    async fn preprocessing_steps<'a>(
        self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
    ) -> RouterResult<Self>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(self)
    }

    async fn postprocessing_steps<'a>(
        self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
    ) -> RouterResult<Self>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(self)
    }

    async fn create_connector_customer<'a>(
        &self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
    ) -> RouterResult<Option<String>>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(None)
    }

    /// Returns the connector request and a bool which specifies whether to proceed with further
    async fn build_flow_specific_connector_request(
        &mut self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
        _call_connector_action: payments::CallConnectorAction,
    ) -> RouterResult<(Option<services::Request>, bool)> {
        Ok((None, true))
    }

    async fn create_order_at_connector(
        &mut self,
        _state: &SessionState,
        _connector: &api::ConnectorData,
        _should_continue_payment: bool,
    ) -> RouterResult<Option<types::CreateOrderResult>>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(None)
    }

    fn update_router_data_with_create_order_response(
        &mut self,
        _create_order_result: types::CreateOrderResult,
    ) {
    }

    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,
        _platform: &domain::Platform,
        _connector_data: &api::ConnectorData,
        _unified_connector_service_execution_mode: ExecutionMode,
        _merchant_order_reference_id: Option<String>,
    ) -> 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,
        _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,
        _platform: &domain::Platform,
        _connector_data: &api::ConnectorData,
        _unified_connector_service_execution_mode: ExecutionMode,
        _merchant_order_reference_id: Option<String>,
        _call_connector_action: common_enums::CallConnectorAction,
        _creds_identifier: Option<String>,
    ) -> RouterResult<()>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(())
    }

    #[cfg(feature = "v2")]
    async fn call_unified_connector_service_with_external_vault_proxy<'a>(
        &mut self,
        _state: &SessionState,
        _header_payload: &domain_payments::HeaderPayload,
        _lineage_ids: grpc_client::LineageIds,
        _merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
        _external_vault_merchant_connector_account: domain::MerchantConnectorAccountTypeDetails,
        _platform: &domain::Platform,
        _unified_connector_service_execution_mode: ExecutionMode,
        _merchant_order_reference_id: Option<String>,
    ) -> RouterResult<()>
    where
        F: Clone,
        Self: Sized,
        dyn api::Connector: services::ConnectorIntegration<F, T, types::PaymentsResponseData>,
    {
        Ok(())
    }
}

/// Determines whether a capture API call should be made for a payment attempt
/// This function evaluates whether an authorized payment should proceed with a capture API call
/// based on various payment parameters. It's primarily used in two-step (auth + capture) payment flows for CaptureMethod SequentialAutomatic
///
pub fn should_initiate_capture_flow(
    connector_name: &router_types::Connector,
    customer_acceptance: Option<CustomerAcceptance>,
    capture_method: Option<api_enums::CaptureMethod>,
    setup_future_usage: Option<api_enums::FutureUsage>,
    status: common_enums::AttemptStatus,
) -> bool {
    match status {
        common_enums::AttemptStatus::Authorized => {
            if let Some(api_enums::CaptureMethod::SequentialAutomatic) = capture_method {
                match connector_name {
                    router_types::Connector::Paybox => {
                        // Check CIT conditions for Paybox
                        setup_future_usage == Some(api_enums::FutureUsage::OffSession)
                            && customer_acceptance.is_some()
                    }
                    _ => false,
                }
            } else {
                false
            }
        }
        _ => false,
    }
}

/// Executes a capture request by building a connector-specific request and deciding
/// the appropriate flow to send it to the payment connector.
pub async fn call_capture_request(
    mut capture_router_data: types::RouterData<
        api::Capture,
        PaymentsCaptureData,
        types::PaymentsResponseData,
    >,
    state: &SessionState,
    connector: &api::ConnectorData,
    call_connector_action: payments::CallConnectorAction,
    business_profile: &domain::Profile,
    header_payload: domain_payments::HeaderPayload,
    context: gateway_context::RouterGatewayContext,
) -> RouterResult<types::RouterData<api::Capture, PaymentsCaptureData, types::PaymentsResponseData>>
{
    // Build capture-specific connector request
    let (connector_request, _should_continue_further) = capture_router_data
        .build_flow_specific_connector_request(state, connector, call_connector_action.clone())
        .await?;

    // Execute capture flow
    capture_router_data
        .decide_flows(
            state,
            connector,
            call_connector_action,
            connector_request,
            business_profile,
            header_payload.clone(),
            None,
            context, // gateway_context
        )
        .await
}

/// Processes the response from the capture flow and determines the final status and the response.
fn handle_post_capture_response(
    authorize_router_data_response: types::PaymentsResponseData,
    post_capture_router_data: Result<
        types::RouterData<api::Capture, PaymentsCaptureData, types::PaymentsResponseData>,
        error_stack::Report<ApiErrorResponse>,
    >,
) -> RouterResult<(common_enums::AttemptStatus, types::PaymentsResponseData)> {
    match post_capture_router_data {
        Err(err) => {
            logger::error!(
                "Capture flow encountered an error: {:?}. Proceeding without updating.",
                err
            );
            Ok((
                common_enums::AttemptStatus::Authorized,
                authorize_router_data_response,
            ))
        }
        Ok(post_capture_router_data) => {
            match (
                &post_capture_router_data.response,
                post_capture_router_data.status,
            ) {
                (Ok(post_capture_resp), common_enums::AttemptStatus::Charged) => Ok((
                    common_enums::AttemptStatus::Charged,
                    types::PaymentsResponseData::merge_transaction_responses(
                        &authorize_router_data_response,
                        post_capture_resp,
                    )?,
                )),
                _ => {
                    logger::error!(
                        "Error in post capture_router_data response: {:?}, Current Status: {:?}. Proceeding without updating.",
                        post_capture_router_data.response,
                        post_capture_router_data.status,
                    );
                    Ok((
                        common_enums::AttemptStatus::Authorized,
                        authorize_router_data_response,
                    ))
                }
            }
        }
    }
}


// File: crates/router/src/core/payments/operations.rs

#[cfg(feature = "v1")]
pub mod payment_approve;
#[cfg(feature = "v1")]
pub mod payment_cancel;
#[cfg(feature = "v1")]
pub mod payment_cancel_post_capture;
#[cfg(feature = "v1")]
pub mod payment_capture;
#[cfg(feature = "v1")]
pub mod payment_complete_authorize;
#[cfg(feature = "v1")]
pub mod payment_confirm;
#[cfg(feature = "v1")]
pub mod payment_create;
#[cfg(feature = "v1")]
pub mod payment_post_session_tokens;
#[cfg(feature = "v1")]
pub mod payment_reject;
pub mod payment_response;
#[cfg(feature = "v1")]
pub mod payment_session;
#[cfg(feature = "v2")]
pub mod payment_session_intent;
#[cfg(feature = "v1")]
pub mod payment_start;
#[cfg(feature = "v1")]
pub mod payment_status;
#[cfg(feature = "v1")]
pub mod payment_update;
#[cfg(feature = "v1")]
pub mod payment_update_metadata;
#[cfg(feature = "v1")]
pub mod payments_extend_authorization;
#[cfg(feature = "v1")]
pub mod payments_incremental_authorization;
#[cfg(feature = "v1")]
pub mod tax_calculation;

#[cfg(feature = "v2")]
pub mod payment_attempt_list;
#[cfg(feature = "v2")]
pub mod payment_attempt_record;
#[cfg(feature = "v2")]
pub mod payment_confirm_intent;
#[cfg(feature = "v2")]
pub mod payment_create_intent;
#[cfg(feature = "v2")]
pub mod payment_get_intent;
#[cfg(feature = "v2")]
pub mod payment_update_intent;
#[cfg(feature = "v2")]
pub mod proxy_payments_intent;

#[cfg(feature = "v2")]
pub mod external_vault_proxy_payment_intent;

#[cfg(feature = "v2")]
pub mod payment_get;

#[cfg(feature = "v2")]
pub mod payment_capture_v2;

#[cfg(feature = "v2")]
pub mod payment_cancel_v2;

use api_models::enums::FrmSuggestion;
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
use api_models::routing::RoutableConnectorChoice;
use async_trait::async_trait;
use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};

#[cfg(feature = "v2")]
pub use self::payment_attempt_list::PaymentGetListAttempts;
#[cfg(feature = "v2")]
pub use self::payment_get::PaymentGet;
#[cfg(feature = "v2")]
pub use self::payment_get_intent::PaymentGetIntent;
pub use self::payment_response::PaymentResponse;
#[cfg(feature = "v2")]
pub use self::payment_update_intent::PaymentUpdateIntent;
#[cfg(feature = "v1")]
pub use self::{
    payment_approve::PaymentApprove, payment_cancel::PaymentCancel,
    payment_cancel_post_capture::PaymentCancelPostCapture, payment_capture::PaymentCapture,
    payment_confirm::PaymentConfirm, payment_create::PaymentCreate,
    payment_post_session_tokens::PaymentPostSessionTokens, payment_reject::PaymentReject,
    payment_session::PaymentSession, payment_start::PaymentStart, payment_status::PaymentStatus,
    payment_update::PaymentUpdate, payment_update_metadata::PaymentUpdateMetadata,
    payments_extend_authorization::PaymentExtendAuthorization,
    payments_incremental_authorization::PaymentIncrementalAuthorization,
    tax_calculation::PaymentSessionUpdate,
};
#[cfg(feature = "v2")]
pub use self::{
    payment_confirm_intent::PaymentIntentConfirm, payment_create_intent::PaymentIntentCreate,
    payment_session_intent::PaymentSessionIntent,
};
use super::{helpers, CustomerDetails, OperationSessionGetters, OperationSessionSetters};
#[cfg(feature = "v2")]
use crate::core::payments;
use crate::{
    core::errors::{self, CustomResult, RouterResult},
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self,
        api::{self, ConnectorCallType},
        domain,
        storage::{self, enums},
        PaymentsResponseData,
    },
};

pub type BoxedOperation<'a, F, T, D> = Box<dyn Operation<F, T, Data = D> + Send + Sync + 'a>;

pub trait Operation<F: Clone, T>: Send + std::fmt::Debug {
    type Data;
    fn to_validate_request(
        &self,
    ) -> RouterResult<&(dyn ValidateRequest<F, T, Self::Data> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("validate request interface not found for {self:?}"))
    }

    fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}"))
    }

    fn to_domain(&self) -> RouterResult<&dyn Domain<F, T, Self::Data>> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("domain interface not found for {self:?}"))
    }

    fn to_update_tracker(
        &self,
    ) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| format!("update tracker interface not found for {self:?}"))
    }

    fn to_post_update_tracker(
        &self,
    ) -> RouterResult<&(dyn PostUpdateTracker<F, Self::Data, T> + Send + Sync)> {
        Err(report!(errors::ApiErrorResponse::InternalServerError)).attach_printable_lazy(|| {
            format!("post connector update tracker not found for {self:?}")
        })
    }
}

#[cfg(feature = "v1")]
#[derive(Clone)]
pub struct ValidateResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub payment_id: api::PaymentIdType,
    pub storage_scheme: enums::MerchantStorageScheme,
    pub requeue: bool,
}

#[cfg(feature = "v2")]
#[derive(Clone)]
pub struct ValidateResult {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub storage_scheme: enums::MerchantStorageScheme,
    pub requeue: bool,
}

#[cfg(feature = "v1")]
#[allow(clippy::type_complexity)]
pub trait ValidateRequest<F, R, D> {
    fn validate_request<'b>(
        &'b self,
        request: &R,
        platform: &domain::Platform,
    ) -> RouterResult<(BoxedOperation<'b, F, R, D>, ValidateResult)>;
}

#[cfg(feature = "v2")]
pub trait ValidateRequest<F, R, D> {
    fn validate_request(
        &self,
        request: &R,
        platform: &domain::Platform,
    ) -> RouterResult<ValidateResult>;
}

#[cfg(feature = "v2")]
pub struct GetTrackerResponse<D> {
    pub payment_data: D,
}

#[cfg(feature = "v1")]
pub struct GetTrackerResponse<'a, F: Clone, R, D> {
    pub operation: BoxedOperation<'a, F, R, D>,
    pub customer_details: Option<CustomerDetails>,
    pub payment_data: D,
    pub business_profile: domain::Profile,
    pub mandate_type: Option<api::MandateTransactionType>,
}

/// This trait is used to fetch / create all the tracker related information for a payment
/// This functions returns the session data that is used by subsequent functions
#[async_trait]
pub trait GetTracker<F: Clone, D, R>: Send {
    #[cfg(feature = "v1")]
    #[allow(clippy::too_many_arguments)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &R,
        platform: &domain::Platform,
        auth_flow: services::AuthFlow,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<GetTrackerResponse<'a, F, R, D>>;

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &common_utils::id_type::GlobalPaymentId,
        request: &R,
        platform: &domain::Platform,
        profile: &domain::Profile,
        header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<GetTrackerResponse<D>>;

    #[cfg(feature = "v2")]
    #[allow(clippy::too_many_arguments)]
    async fn get_trackers_for_split_payments<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_id: &common_utils::id_type::GlobalPaymentId,
        _request: &R,
        _merchant_context: &domain::Platform,
        _profile: &domain::Profile,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
        _split_amount_data: (
            api_models::payments::PaymentMethodData,
            common_utils::types::MinorUnit,
        ),
        _attempts_group_id: &common_utils::id_type::GlobalAttemptGroupId,
    ) -> RouterResult<GetTrackerResponse<D>> {
        Err(errors::ApiErrorResponse::NotImplemented {
            message: errors::NotImplementedMessage::Default,
        })?
    }

    async fn validate_request_with_state(
        &self,
        _state: &SessionState,
        _request: &R,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> RouterResult<()> {
        Ok(())
    }
}

#[async_trait]
pub trait Domain<F: Clone, R, D>: Send + Sync {
    #[cfg(feature = "v1")]
    /// This will fetch customer details, (this operation is flow specific)
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<(BoxedOperation<'a, F, R, D>, Option<domain::Customer>), errors::StorageError>;

    #[cfg(feature = "v2")]
    /// This will fetch customer details, (this operation is flow specific)
    async fn get_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<(BoxedOperation<'a, F, R, D>, Option<domain::Customer>), errors::StorageError>;

    #[cfg(feature = "v2")]
    /// This will run the decision manager for the payment
    async fn run_decision_manager<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn make_pm_data<'a>(
        &'a self,
        state: &'a SessionState,
        payment_data: &mut D,
        storage_scheme: enums::MerchantStorageScheme,
        merchant_key_store: &domain::MerchantKeyStore,
        customer: &Option<domain::Customer>,
        business_profile: &domain::Profile,
        should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, R, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )>;

    async fn add_task_to_process_tracker<'a>(
        &'a self,
        _db: &'a SessionState,
        _payment_attempt: &storage::PaymentAttempt,
        _requeue: bool,
        _schedule_time: Option<time::PrimitiveDateTime>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[cfg(feature = "v1")]
    async fn get_connector<'a>(
        &'a self,
        platform: &domain::Platform,
        state: &SessionState,
        request: &R,
        payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse>;

    #[cfg(feature = "v2")]
    async fn get_connector_from_request<'a>(
        &'a self,
        state: &SessionState,
        request: &R,
        payment_data: &mut D,
    ) -> CustomResult<api::ConnectorData, errors::ApiErrorResponse> {
        Err(report!(errors::ApiErrorResponse::InternalServerError))
            .attach_printable_lazy(|| "get connector for tunnel not implemented".to_string())
    }

    #[cfg(feature = "v2")]
    async fn perform_routing<'a>(
        &'a self,
        platform: &domain::Platform,
        business_profile: &domain::Profile,
        state: &SessionState,
        // TODO: do not take the whole payment data here
        payment_data: &mut D,
    ) -> CustomResult<ConnectorCallType, errors::ApiErrorResponse>;

    async fn populate_payment_data<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _platform: &domain::Platform,
        _business_profile: &domain::Profile,
        _connector_data: &api::ConnectorData,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn call_external_three_ds_authentication_if_eligible<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _should_continue_confirm_transaction: &mut bool,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _key_store: &domain::MerchantKeyStore,
        _mandate_type: Option<api_models::payments::MandateTransactionType>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn call_unified_authentication_service_if_eligible<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _should_continue_confirm_transaction: &mut bool,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _key_store: &domain::MerchantKeyStore,
        _mandate_type: Option<api_models::payments::MandateTransactionType>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[allow(clippy::too_many_arguments)]
    async fn payments_dynamic_tax_calculation<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _connector_call_type: &ConnectorCallType,
        _business_profile: &domain::Profile,
        _platform: &domain::Platform,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }

    async fn store_extended_card_info_temporarily<'a>(
        &'a self,
        _state: &SessionState,
        _payment_id: &common_utils::id_type::PaymentId,
        _business_profile: &domain::Profile,
        _payment_method_data: Option<&domain::PaymentMethodData>,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    #[cfg(feature = "v2")]
    async fn create_or_fetch_payment_method<'a>(
        &'a self,
        state: &SessionState,
        platform: &domain::Platform,
        business_profile: &domain::Profile,
        payment_data: &mut D,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    // does not propagate error to not affect the payment flow
    // must add debugger in case of internal error
    #[cfg(feature = "v2")]
    async fn update_payment_method<'a>(
        &'a self,
        state: &SessionState,
        platform: &domain::Platform,
        payment_data: &mut D,
    ) {
    }

    /// This function is used to apply the 3DS authentication strategy
    async fn apply_three_ds_authentication_strategy<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse> {
        Ok(())
    }

    /// Get connector tokenization action
    #[cfg(feature = "v2")]
    async fn get_connector_tokenization_action<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &D,
    ) -> RouterResult<(payments::TokenizationAction)> {
        Ok(payments::TokenizationAction::SkipConnectorTokenization)
    }

    // #[cfg(feature = "v2")]
    // async fn call_connector<'a, RouterDataReq>(
    //     &'a self,
    //     _state: &SessionState,
    //     _req_state: ReqState,
    //     _platform: &domain::Platform,
    //     _business_profile: &domain::Profile,
    //     _payment_method_data: Option<&domain::PaymentMethodData>,
    //     _connector: api::ConnectorData,
    //     _customer: &Option<domain::Customer>,
    //     _payment_data: &mut D,
    //     _call_connector_action: common_enums::CallConnectorAction,
    // ) -> CustomResult<
    //     hyperswitch_domain_models::router_data::RouterData<F, RouterDataReq, PaymentsResponseData>,
    //     errors::ApiErrorResponse,
    // > {
    //     // TODO: raise an error here
    //     todo!();
    // }
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait UpdateTracker<F, D, Req>: Send {
    /// Update the tracker information with the new data from request or calculated by the operations performed after get trackers
    /// This will persist the SessionData ( PaymentData ) in the database
    ///
    /// In case we are calling a processor / connector, we persist all the data in the database and then call the connector
    async fn update_trackers<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        payment_data: D,
        customer: Option<domain::Customer>,
        storage_scheme: enums::MerchantStorageScheme,
        updated_customer: Option<storage::CustomerUpdate>,
        mechant_key_store: &domain::MerchantKeyStore,
        frm_suggestion: Option<FrmSuggestion>,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(BoxedOperation<'b, F, Req, D>, D)>
    where
        F: 'b + Send;
}

#[cfg(feature = "v2")]
#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait CallConnector<F, D, RouterDReq: Send>: Send {
    async fn call_connector<'b>(
        &'b self,
        db: &'b SessionState,
        req_state: ReqState,
        payment_data: D,
        key_store: &domain::MerchantKeyStore,
        call_connector_action: common_enums::CallConnectorAction,
        connector_data: api::ConnectorData,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<types::RouterData<F, RouterDReq, PaymentsResponseData>>
    where
        F: 'b + Send + Sync,
        D: super::flows::ConstructFlowSpecificData<F, RouterDReq, PaymentsResponseData>,
        types::RouterData<F, RouterDReq, PaymentsResponseData>:
            super::flows::Feature<F, RouterDReq> + Send;
}

#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait PostUpdateTracker<F, D, R: Send>: Send {
    /// Update the tracker information with the response from the connector
    /// The response from routerdata is used to update paymentdata and also persist this in the database
    #[cfg(feature = "v1")]
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        payment_data: D,
        response: types::RouterData<F, R, PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
        locale: &Option<String>,
        #[cfg(feature = "dynamic_routing")] routable_connector: Vec<RoutableConnectorChoice>,
        #[cfg(feature = "dynamic_routing")] business_profile: &domain::Profile,
    ) -> RouterResult<D>
    where
        F: 'b + Send + Sync;

    #[cfg(feature = "v2")]
    async fn update_tracker<'b>(
        &'b self,
        db: &'b SessionState,
        payment_data: D,
        response: types::RouterData<F, R, PaymentsResponseData>,
        key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<D>
    where
        F: 'b + Send + Sync,
        types::RouterData<F, R, PaymentsResponseData>:
            hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<F, R, D>;

    async fn save_pm_and_mandate<'b>(
        &self,
        _state: &SessionState,
        _resp: &types::RouterData<F, R, PaymentsResponseData>,
        _platform: &domain::Platform,
        _payment_data: &mut D,
        _business_profile: &domain::Profile,
    ) -> CustomResult<(), errors::ApiErrorResponse>
    where
        F: 'b + Clone + Send + Sync,
    {
        Ok(())
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<
        D,
        F: Clone + Send,
        Op: Send + Sync + Operation<F, api::PaymentsRetrieveRequest, Data = D>,
    > Domain<F, api::PaymentsRetrieveRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsRetrieveRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRetrieveRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;
        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                // This function is to retrieve customer details. If the customer is deleted, it returns
                // customer details that contains the fields as Redacted
                db.find_customer_optional_with_redacted_customer_details_by_customer_id_merchant_id(
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    async fn get_connector<'a>(
        &'a self,
        _platform: &domain::Platform,
        state: &SessionState,
        _request: &api::PaymentsRetrieveRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsRetrieveRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsCaptureRequest, Data = D>>
    Domain<F, api::PaymentsCaptureRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsCaptureRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;

        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                db.find_customer_optional_by_customer_id_merchant_id(
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v2")]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        todo!()
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsCaptureRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _platform: &domain::Platform,
        state: &SessionState,
        _request: &api::PaymentsCaptureRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsCancelRequest, Data = D>>
    Domain<F, api::PaymentsCancelRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsCancelRequest, Data = D>,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send,
{
    #[instrument(skip_all)]
    #[cfg(feature = "v1")]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        state: &SessionState,
        payment_data: &mut D,
        _request: Option<CustomerDetails>,
        merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        let db = &*state.store;

        let customer = match payment_data.get_payment_intent().customer_id.as_ref() {
            None => None,
            Some(customer_id) => {
                db.find_customer_optional_by_customer_id_merchant_id(
                    customer_id,
                    &merchant_key_store.merchant_id,
                    merchant_key_store,
                    storage_scheme,
                )
                .await?
            }
        };

        if let Some(email) = customer.as_ref().and_then(|inner| inner.email.clone()) {
            payment_data.set_email_if_not_present(email.into());
        }

        Ok((Box::new(self), customer))
    }

    #[instrument(skip_all)]
    #[cfg(feature = "v2")]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        todo!()
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsCancelRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _platform: &domain::Platform,
        state: &SessionState,
        _request: &api::PaymentsCancelRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl<D, F: Clone + Send, Op: Send + Sync + Operation<F, api::PaymentsRejectRequest, Data = D>>
    Domain<F, api::PaymentsRejectRequest, D> for Op
where
    for<'a> &'a Op: Operation<F, api::PaymentsRejectRequest, Data = D>,
{
    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _request: Option<CustomerDetails>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    async fn get_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut D,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> CustomResult<
        (
            BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut D,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        BoxedOperation<'a, F, api::PaymentsRejectRequest, D>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _platform: &domain::Platform,
        state: &SessionState,
        _request: &api::PaymentsRejectRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _platform: &domain::Platform,
        _payment_data: &mut D,
    ) -> CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

/// Validate if a particular operation can be performed for the given intent status
pub trait ValidateStatusForOperation {
    fn validate_status_for_operation(
        &self,
        intent_status: common_enums::IntentStatus,
    ) -> Result<(), errors::ApiErrorResponse>;
}

/// Should the connector be called for this operation
pub trait ShouldCallConnector {
    fn should_call_connector(
        &self,
        intent_status: common_enums::IntentStatus,
        force_sync: Option<bool>,
    ) -> bool;
}


// File: crates/router/src/core/payments/retry.rs

use std::vec::IntoIter;

use common_utils::{ext_traits::Encode, types::MinorUnit};
use diesel_models::enums as storage_enums;
use error_stack::ResultExt;
use hyperswitch_domain_models::ext_traits::OptionExt;
use router_env::{
    logger,
    tracing::{self, instrument},
};

use crate::{
    core::{
        errors::{self, RouterResult, StorageErrorExt},
        payments::{
            self,
            flows::{ConstructFlowSpecificData, Feature},
            operations,
        },
        routing::helpers as routing_helpers,
    },
    db::StorageInterface,
    routes::{
        self,
        app::{self, ReqState},
        metrics,
    },
    services,
    types::{self, api, domain, storage, transformers::ForeignFrom},
};

#[instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
#[cfg(feature = "v1")]
pub async fn do_gsm_actions<'a, F, ApiRequest, FData, D>(
    state: &app::SessionState,
    req_state: ReqState,
    payment_data: &mut D,
    mut connector_routing_data: IntoIter<api::ConnectorRoutingData>,
    original_connector_data: &api::ConnectorData,
    mut router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    platform: &domain::Platform,
    operation: &operations::BoxedOperation<'_, F, ApiRequest, D>,
    customer: &Option<domain::Customer>,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
) -> RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
where
    F: Clone + Send + Sync + std::fmt::Debug + 'static,
    FData: Send + Sync + types::Capturable + Clone + 'static + serde::Serialize,
    payments::PaymentResponse: operations::Operation<F, FData>,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
    D: ConstructFlowSpecificData<F, FData, types::PaymentsResponseData>,
    types::RouterData<F, FData, types::PaymentsResponseData>: Feature<F, FData>,
    dyn api::Connector: services::api::ConnectorIntegration<F, FData, types::PaymentsResponseData>,
{
    let mut retries = None;

    metrics::AUTO_RETRY_ELIGIBLE_REQUEST_COUNT.add(1, &[]);

    let mut initial_gsm = get_gsm(state, &router_data).await?;

    let step_up_possible = initial_gsm
        .as_ref()
        .and_then(|data| data.feature_data.get_retry_feature_data())
        .map(|data| data.is_step_up_possible())
        .unwrap_or(false);

    #[cfg(feature = "v1")]
    let is_no_three_ds_payment = matches!(
        payment_data.get_payment_attempt().authentication_type,
        Some(storage_enums::AuthenticationType::NoThreeDs)
    );

    #[cfg(feature = "v2")]
    let is_no_three_ds_payment = matches!(
        payment_data.get_payment_attempt().authentication_type,
        storage_enums::AuthenticationType::NoThreeDs
    );

    let should_step_up = if step_up_possible && is_no_three_ds_payment {
        is_step_up_enabled_for_merchant_connector(
            state,
            platform.get_processor().get_account().get_id(),
            original_connector_data.connector_name,
        )
        .await
    } else {
        false
    };

    if should_step_up {
        router_data = do_retry(
            &state.clone(),
            req_state.clone(),
            original_connector_data,
            operation,
            customer,
            platform,
            payment_data,
            router_data,
            validate_result,
            schedule_time,
            true,
            frm_suggestion,
            business_profile,
            false, //should_retry_with_pan is not applicable for step-up
            None,
        )
        .await?;
    }
    // Step up is not applicable so proceed with auto retries flow
    else {
        loop {
            // Use initial_gsm for first time alone
            let gsm = match initial_gsm.as_ref() {
                Some(gsm) => Some(gsm.clone()),
                None => get_gsm(state, &router_data).await?,
            };

            match get_gsm_decision(gsm) {
                storage_enums::GsmDecision::Retry => {
                    retries = get_retries(
                        state,
                        retries,
                        platform.get_processor().get_account().get_id(),
                        business_profile,
                    )
                    .await;

                    if retries.is_none() || retries == Some(0) {
                        metrics::AUTO_RETRY_EXHAUSTED_COUNT.add(1, &[]);
                        logger::info!("retries exhausted for auto_retry payment");
                        break;
                    }

                    if connector_routing_data.len() == 0 {
                        logger::info!("connectors exhausted for auto_retry payment");
                        metrics::AUTO_RETRY_EXHAUSTED_COUNT.add(1, &[]);
                        break;
                    }

                    let is_network_token = payment_data
                        .get_payment_method_data()
                        .map(|pmd| pmd.is_network_token_payment_method_data())
                        .unwrap_or(false);

                    let clear_pan_possible = initial_gsm
                        .and_then(|data| data.feature_data.get_retry_feature_data())
                        .map(|data| data.is_clear_pan_possible())
                        .unwrap_or(false);

                    let should_retry_with_pan = is_network_token
                        && clear_pan_possible
                        && business_profile.is_clear_pan_retries_enabled;

                    // 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.
                        (original_connector_data.clone(), None)
                    } else {
                        let connector_routing_data =
                            super::get_connector_data(&mut connector_routing_data)?;
                        let routing_decision = connector_routing_data.network.map(|card_network| {
                            routing_helpers::RoutingDecisionData::get_debit_routing_decision_data(
                                card_network,
                                None,
                            )
                        });

                        (connector_routing_data.connector_data, routing_decision)
                    };

                    router_data = do_retry(
                        &state.clone(),
                        req_state.clone(),
                        &connector,
                        operation,
                        customer,
                        platform,
                        payment_data,
                        router_data,
                        validate_result,
                        schedule_time,
                        //this is an auto retry payment, but not step-up
                        false,
                        frm_suggestion,
                        business_profile,
                        should_retry_with_pan,
                        routing_decision,
                    )
                    .await?;

                    retries = retries.map(|i| i - 1);
                }
                storage_enums::GsmDecision::DoDefault => break,
            }
            initial_gsm = None;
        }
    }
    Ok(router_data)
}

#[instrument(skip_all)]
pub async fn is_step_up_enabled_for_merchant_connector(
    state: &app::SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    connector_name: types::Connector,
) -> bool {
    let key = merchant_id.get_step_up_enabled_key();
    let db = &*state.store;
    db.find_config_by_key_unwrap_or(key.as_str(), Some("[]".to_string()))
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .and_then(|step_up_config| {
            serde_json::from_str::<Vec<types::Connector>>(&step_up_config.config)
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Step-up config parsing failed")
        })
        .map_err(|err| {
            logger::error!(step_up_config_error=?err);
        })
        .ok()
        .map(|connectors_enabled| connectors_enabled.contains(&connector_name))
        .unwrap_or(false)
}

#[cfg(feature = "v1")]
pub async fn get_merchant_max_auto_retries_enabled(
    db: &dyn StorageInterface,
    merchant_id: &common_utils::id_type::MerchantId,
) -> Option<i32> {
    let key = merchant_id.get_max_auto_retries_enabled();

    db.find_config_by_key(key.as_str())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .and_then(|retries_config| {
            retries_config
                .config
                .parse::<i32>()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Retries config parsing failed")
        })
        .map_err(|err| {
            logger::error!(retries_error=?err);
            None::<i32>
        })
        .ok()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn get_retries(
    state: &app::SessionState,
    retries: Option<i32>,
    merchant_id: &common_utils::id_type::MerchantId,
    profile: &domain::Profile,
) -> Option<i32> {
    match retries {
        Some(retries) => Some(retries),
        None => get_merchant_max_auto_retries_enabled(state.store.as_ref(), merchant_id)
            .await
            .or(profile.max_auto_retries_enabled.map(i32::from)),
    }
}

#[instrument(skip_all)]
pub async fn get_gsm<F, FData>(
    state: &app::SessionState,
    router_data: &types::RouterData<F, FData, types::PaymentsResponseData>,
) -> RouterResult<Option<hyperswitch_domain_models::gsm::GatewayStatusMap>> {
    let error_response = router_data.response.as_ref().err();
    let error_code = error_response.map(|err| err.code.to_owned());
    let error_message = error_response.map(|err| err.message.to_owned());
    let connector_name = router_data.connector.to_string();
    let flow = get_flow_name::<F>()?;
    Ok(
        payments::helpers::get_gsm_record(state, error_code, error_message, connector_name, flow)
            .await,
    )
}

#[instrument(skip_all)]
pub fn get_gsm_decision(
    option_gsm: Option<hyperswitch_domain_models::gsm::GatewayStatusMap>,
) -> storage_enums::GsmDecision {
    let option_gsm_decision = option_gsm
        .as_ref()
        .map(|gsm| gsm.feature_data.get_decision());

    if option_gsm_decision.is_some() {
        metrics::AUTO_RETRY_GSM_MATCH_COUNT.add(1, &[]);
    }
    option_gsm_decision.unwrap_or_default()
}

#[inline]
fn get_flow_name<F>() -> RouterResult<String> {
    Ok(std::any::type_name::<F>()
        .to_string()
        .rsplit("::")
        .next()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Flow stringify failed")?
        .to_string())
}

#[cfg(feature = "v1")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn do_retry<'a, F, ApiRequest, FData, D>(
    state: &'a routes::SessionState,
    req_state: ReqState,
    connector: &'a api::ConnectorData,
    operation: &'a operations::BoxedOperation<'a, F, ApiRequest, D>,
    customer: &'a Option<domain::Customer>,
    platform: &domain::Platform,
    payment_data: &'a mut D,
    router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    is_step_up: bool,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    should_retry_with_pan: bool,
    routing_decision: Option<routing_helpers::RoutingDecisionData>,
) -> RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
where
    F: Clone + Send + Sync + std::fmt::Debug + 'static,
    FData: Send + Sync + types::Capturable + Clone + 'static + serde::Serialize,
    payments::PaymentResponse: operations::Operation<F, FData>,
    D: payments::OperationSessionGetters<F>
        + payments::OperationSessionSetters<F>
        + Send
        + Sync
        + Clone,
    D: ConstructFlowSpecificData<F, FData, types::PaymentsResponseData>,
    types::RouterData<F, FData, types::PaymentsResponseData>: Feature<F, FData>,
    dyn api::Connector: services::api::ConnectorIntegration<F, FData, types::PaymentsResponseData>,
{
    metrics::AUTO_RETRY_PAYMENT_COUNT.add(1, &[]);

    modify_trackers(
        state,
        connector.connector_name.to_string(),
        payment_data,
        platform.get_processor().get_key_store(),
        platform.get_processor().get_account().storage_scheme,
        router_data,
        is_step_up,
    )
    .await?;

    let (merchant_connector_account, router_data, tokenization_action) =
        payments::call_connector_service_prerequisites(
            state,
            platform,
            connector.clone(),
            operation,
            payment_data,
            customer,
            validate_result,
            business_profile,
            should_retry_with_pan,
            routing_decision,
        )
        .await?;

    let (router_data, _mca) = payments::decide_unified_connector_service_call(
        state,
        req_state,
        platform,
        connector.clone(),
        operation,
        payment_data,
        customer,
        payments::CallConnectorAction::Trigger,
        None,
        validate_result,
        schedule_time,
        hyperswitch_domain_models::payments::HeaderPayload::default(),
        frm_suggestion,
        business_profile,
        true,
        None,
        merchant_connector_account,
        router_data,
        tokenization_action,
    )
    .await?;

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn modify_trackers<F, FData, D>(
    state: &routes::SessionState,
    connector: String,
    payment_data: &mut D,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: storage_enums::MerchantStorageScheme,
    router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    is_step_up: bool,
) -> RouterResult<()>
where
    F: Clone + Send,
    FData: Send,
    D: payments::OperationSessionGetters<F> + payments::OperationSessionSetters<F> + Send + Sync,
{
    todo!()
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn modify_trackers<F, FData, D>(
    state: &routes::SessionState,
    connector: String,
    payment_data: &mut D,
    key_store: &domain::MerchantKeyStore,
    storage_scheme: storage_enums::MerchantStorageScheme,
    router_data: types::RouterData<F, FData, types::PaymentsResponseData>,
    is_step_up: bool,
) -> RouterResult<()>
where
    F: Clone + Send,
    FData: Send + types::Capturable,
    D: payments::OperationSessionGetters<F> + payments::OperationSessionSetters<F> + Send + Sync,
{
    let new_attempt_count = payment_data.get_payment_intent().attempt_count + 1;
    let new_payment_attempt = make_new_payment_attempt(
        connector,
        payment_data.get_payment_attempt().clone(),
        new_attempt_count,
        is_step_up,
        payment_data.get_payment_intent().setup_future_usage,
    );

    let db = &*state.store;
    let additional_payment_method_data =
        payments::helpers::update_additional_payment_data_with_connector_response_pm_data(
            payment_data
                .get_payment_attempt()
                .payment_method_data
                .clone(),
            router_data
                .connector_response
                .clone()
                .and_then(|connector_response| connector_response.additional_payment_method_data),
        )?;

    let debit_routing_savings = payment_data.get_payment_method_data().and_then(|data| {
        payments::helpers::get_debit_routing_savings_amount(
            data,
            payment_data.get_payment_attempt(),
        )
    });

    match router_data.response {
        Ok(types::PaymentsResponseData::TransactionResponse {
            resource_id,
            connector_metadata,
            redirection_data,
            charges,
            ..
        }) => {
            let encoded_data = payment_data.get_payment_attempt().encoded_data.clone();

            let authentication_data = (*redirection_data)
                .as_ref()
                .map(Encode::encode_to_value)
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Could not parse the connector response")?;

            let payment_attempt_update = storage::PaymentAttemptUpdate::ResponseUpdate {
                status: router_data.status,
                connector: None,
                connector_transaction_id: match resource_id {
                    types::ResponseId::NoResponseId => None,
                    types::ResponseId::ConnectorTransactionId(id)
                    | types::ResponseId::EncodedData(id) => Some(id),
                },
                connector_response_reference_id: payment_data
                    .get_payment_attempt()
                    .connector_response_reference_id
                    .clone(),
                authentication_type: None,
                payment_method_id: payment_data.get_payment_attempt().payment_method_id.clone(),
                mandate_id: payment_data
                    .get_mandate_id()
                    .and_then(|mandate| mandate.mandate_id.clone()),
                connector_metadata,
                payment_token: None,
                error_code: None,
                error_message: None,
                error_reason: None,
                amount_capturable: if router_data.status.is_terminal_status() {
                    Some(MinorUnit::new(0))
                } else {
                    None
                },
                updated_by: storage_scheme.to_string(),
                authentication_data,
                encoded_data,
                unified_code: None,
                unified_message: None,
                capture_before: None,
                extended_authorization_applied: None,
                extended_authorization_last_applied_at: None,
                payment_method_data: additional_payment_method_data,
                connector_mandate_detail: None,
                charges,
                setup_future_usage_applied: None,
                debit_routing_savings,
                network_transaction_id: payment_data
                    .get_payment_attempt()
                    .network_transaction_id
                    .clone(),
                is_overcapture_enabled: None,
                authorized_amount: router_data.authorized_amount,
                tokenization: None,
            };

            #[cfg(feature = "v1")]
            db.update_payment_attempt_with_attempt_id(
                payment_data.get_payment_attempt().clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            #[cfg(feature = "v2")]
            db.update_payment_attempt_with_attempt_id(
                key_manager_state,
                key_store,
                payment_data.get_payment_attempt().clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        }
        Ok(_) => {
            logger::error!("unexpected response: this response was not expected in Retry flow");
            return Ok(());
        }
        Err(ref error_response) => {
            let option_gsm = get_gsm(state, &router_data).await?;
            let auth_update = if Some(router_data.auth_type)
                != payment_data.get_payment_attempt().authentication_type
            {
                Some(router_data.auth_type)
            } else {
                None
            };

            let payment_attempt_update = storage::PaymentAttemptUpdate::ErrorUpdate {
                connector: None,
                error_code: Some(Some(error_response.code.clone())),
                error_message: Some(Some(error_response.message.clone())),
                status: storage_enums::AttemptStatus::Failure,
                error_reason: Some(error_response.reason.clone()),
                amount_capturable: Some(MinorUnit::new(0)),
                updated_by: storage_scheme.to_string(),
                unified_code: option_gsm.clone().map(|gsm| gsm.unified_code),
                unified_message: option_gsm.map(|gsm| gsm.unified_message),
                connector_transaction_id: error_response.connector_transaction_id.clone(),
                payment_method_data: additional_payment_method_data,
                authentication_type: auth_update,
                issuer_error_code: error_response.network_decline_code.clone(),
                issuer_error_message: error_response.network_error_message.clone(),
                network_details: Some(ForeignFrom::foreign_from(error_response)),
            };

            #[cfg(feature = "v1")]
            db.update_payment_attempt_with_attempt_id(
                payment_data.get_payment_attempt().clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

            #[cfg(feature = "v2")]
            db.update_payment_attempt_with_attempt_id(
                key_manager_state,
                key_store,
                payment_data.get_payment_attempt().clone(),
                payment_attempt_update,
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
        }
    }

    #[cfg(feature = "v1")]
    let payment_attempt = db
        .insert_payment_attempt(new_payment_attempt, storage_scheme)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error inserting payment attempt")?;

    #[cfg(feature = "v2")]
    let payment_attempt = db
        .insert_payment_attempt(key_store, new_payment_attempt, storage_scheme)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error inserting payment attempt")?;

    // update payment_attempt, connector_response and payment_intent in payment_data
    payment_data.set_payment_attempt(payment_attempt);

    let payment_intent = db
        .update_payment_intent(
            payment_data.get_payment_intent().clone(),
            storage::PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate {
                active_attempt_id: payment_data.get_payment_attempt().get_id().to_owned(),
                attempt_count: new_attempt_count,
                updated_by: storage_scheme.to_string(),
            },
            key_store,
            storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

    payment_data.set_payment_intent(payment_intent);

    Ok(())
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub fn make_new_payment_attempt(
    connector: String,
    old_payment_attempt: storage::PaymentAttempt,
    new_attempt_count: i16,
    is_step_up: bool,
    setup_future_usage_intent: Option<storage_enums::FutureUsage>,
) -> storage::PaymentAttemptNew {
    let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
    storage::PaymentAttemptNew {
        connector: Some(connector),
        attempt_id: old_payment_attempt
            .payment_id
            .get_attempt_id(new_attempt_count),
        payment_id: old_payment_attempt.payment_id,
        merchant_id: old_payment_attempt.merchant_id,
        status: old_payment_attempt.status,
        currency: old_payment_attempt.currency,
        save_to_locker: old_payment_attempt.save_to_locker,
        offer_amount: old_payment_attempt.offer_amount,
        payment_method_id: old_payment_attempt.payment_method_id,
        payment_method: old_payment_attempt.payment_method,
        payment_method_type: old_payment_attempt.payment_method_type,
        capture_method: old_payment_attempt.capture_method,
        capture_on: old_payment_attempt.capture_on,
        confirm: old_payment_attempt.confirm,
        authentication_type: if is_step_up {
            Some(storage_enums::AuthenticationType::ThreeDs)
        } else {
            old_payment_attempt.authentication_type
        },
        amount_to_capture: old_payment_attempt.amount_to_capture,
        mandate_id: old_payment_attempt.mandate_id,
        browser_info: old_payment_attempt.browser_info,
        payment_token: old_payment_attempt.payment_token,
        client_source: old_payment_attempt.client_source,
        client_version: old_payment_attempt.client_version,
        created_at,
        modified_at,
        last_synced,
        profile_id: old_payment_attempt.profile_id,
        organization_id: old_payment_attempt.organization_id,
        net_amount: old_payment_attempt.net_amount,
        error_message: Default::default(),
        cancellation_reason: Default::default(),
        error_code: Default::default(),
        connector_metadata: Default::default(),
        payment_experience: Default::default(),
        payment_method_data: Default::default(),
        business_sub_label: Default::default(),
        straight_through_algorithm: Default::default(),
        preprocessing_step_id: Default::default(),
        mandate_details: Default::default(),
        error_reason: Default::default(),
        connector_response_reference_id: Default::default(),
        multiple_capture_count: Default::default(),
        amount_capturable: Default::default(),
        updated_by: Default::default(),
        authentication_data: Default::default(),
        encoded_data: Default::default(),
        merchant_connector_id: Default::default(),
        unified_code: Default::default(),
        unified_message: Default::default(),
        external_three_ds_authentication_attempted: Default::default(),
        authentication_connector: Default::default(),
        authentication_id: Default::default(),
        mandate_data: Default::default(),
        payment_method_billing_address_id: Default::default(),
        fingerprint_id: Default::default(),
        customer_acceptance: Default::default(),
        connector_mandate_detail: Default::default(),
        request_extended_authorization: Default::default(),
        extended_authorization_applied: Default::default(),
        extended_authorization_last_applied_at: Default::default(),
        capture_before: Default::default(),
        card_discovery: old_payment_attempt.card_discovery,
        processor_merchant_id: old_payment_attempt.processor_merchant_id,
        created_by: old_payment_attempt.created_by,
        setup_future_usage_applied: setup_future_usage_intent, // setup future usage is picked from intent for new payment attempt
        routing_approach: old_payment_attempt.routing_approach,
        connector_request_reference_id: Default::default(),
        network_transaction_id: old_payment_attempt.network_transaction_id,
        network_details: Default::default(),
        is_stored_credential: old_payment_attempt.is_stored_credential,
        authorized_amount: old_payment_attempt.authorized_amount,
        tokenization: Default::default(),
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub fn make_new_payment_attempt(
    _connector: String,
    _old_payment_attempt: storage::PaymentAttempt,
    _new_attempt_count: i16,
    _is_step_up: bool,
) -> storage::PaymentAttempt {
    todo!()
}

pub async fn get_merchant_config_for_gsm(
    db: &dyn StorageInterface,
    merchant_id: &common_utils::id_type::MerchantId,
) -> bool {
    let config = db
        .find_config_by_key_unwrap_or(
            &merchant_id.get_should_call_gsm_key(),
            Some("false".to_string()),
        )
        .await;
    match config {
        Ok(conf) => conf.config == "true",
        Err(error) => {
            logger::error!(?error);
            false
        }
    }
}

#[cfg(feature = "v1")]
pub async fn config_should_call_gsm(
    db: &dyn StorageInterface,
    merchant_id: &common_utils::id_type::MerchantId,
    profile: &domain::Profile,
) -> bool {
    let merchant_config_gsm = get_merchant_config_for_gsm(db, merchant_id).await;
    let profile_config_gsm = profile.is_auto_retries_enabled;
    merchant_config_gsm || profile_config_gsm
}

pub trait GsmValidation<F: Send + Clone + Sync, FData: Send + Sync, Resp> {
    // TODO : move this function to appropriate place later.
    fn should_call_gsm(&self) -> bool;
}

impl<F: Send + Clone + Sync, FData: Send + Sync>
    GsmValidation<F, FData, types::PaymentsResponseData>
    for types::RouterData<F, FData, types::PaymentsResponseData>
{
    #[inline(always)]
    fn should_call_gsm(&self) -> bool {
        if self.response.is_err() {
            true
        } else {
            match self.status {
                storage_enums::AttemptStatus::Started
                | storage_enums::AttemptStatus::AuthenticationPending
                | storage_enums::AttemptStatus::AuthenticationSuccessful
                | storage_enums::AttemptStatus::Authorized
                | storage_enums::AttemptStatus::Charged
                | storage_enums::AttemptStatus::Authorizing
                | storage_enums::AttemptStatus::CodInitiated
                | storage_enums::AttemptStatus::Voided
                | storage_enums::AttemptStatus::VoidedPostCharge
                | storage_enums::AttemptStatus::VoidInitiated
                | storage_enums::AttemptStatus::CaptureInitiated
                | storage_enums::AttemptStatus::RouterDeclined
                | storage_enums::AttemptStatus::VoidFailed
                | storage_enums::AttemptStatus::AutoRefunded
                | storage_enums::AttemptStatus::CaptureFailed
                | storage_enums::AttemptStatus::PartialCharged
                | storage_enums::AttemptStatus::PartialChargedAndChargeable
                | storage_enums::AttemptStatus::Pending
                | storage_enums::AttemptStatus::PaymentMethodAwaited
                | storage_enums::AttemptStatus::ConfirmationAwaited
                | storage_enums::AttemptStatus::Unresolved
                | storage_enums::AttemptStatus::DeviceDataCollectionPending
                | storage_enums::AttemptStatus::IntegrityFailure
                | storage_enums::AttemptStatus::Expired
                | storage_enums::AttemptStatus::PartiallyAuthorized => false,

                storage_enums::AttemptStatus::AuthenticationFailed
                | storage_enums::AttemptStatus::AuthorizationFailed
                | storage_enums::AttemptStatus::Failure => true,
            }
        }
    }
}