    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    TokenizationAction,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let merchant_connector_account_type_details =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
            helpers::get_merchant_connector_account_v2(
                state,
                merchant_context.get_merchant_key_store(),
                connector.merchant_connector_id.as_ref(),
            )
            .await?,
        ));

    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let updated_customer = call_create_connector_customer_if_required(
        state,
        customer,
        merchant_context,
        &merchant_connector_account_type_details,
        payment_data,
    )
    .await?;

    let router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            customer,
            &merchant_connector_account_type_details,
            None,
            Some(header_payload),
        )
        .await?;

    let tokenization_action = operation
        .to_domain()?
        .get_connector_tokenization_action(state, payment_data)
        .await?;

    Ok((
        merchant_connector_account_type_details,
        updated_customer,
        router_data,
        tokenization_action,
    ))
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#[instrument(skip_all)]
pub async fn call_connector_service_prerequisites_for_external_vault_proxy<
    F,
    RouterDReq,
    ApiRequest,
    D,
>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    should_retry_with_pan: bool,
    all_keys_required: Option<bool>,
) -> RouterResult<(
    domain::MerchantConnectorAccountTypeDetails,
    domain::MerchantConnectorAccountTypeDetails,
    Option<storage::CustomerUpdate>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    // get merchant connector account related to external vault
    let external_vault_source: id_type::MerchantConnectorAccountId = business_profile
        .external_vault_connector_details
        .clone()
        .map(|connector_details| connector_details.vault_connector_id.clone())
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("mca_id not present for external vault")?;

    let external_vault_merchant_connector_account_type_details =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
            helpers::get_merchant_connector_account_v2(
                state,
                merchant_context.get_merchant_key_store(),
                Some(&external_vault_source),
            )
            .await?,
        ));

    let (
        merchant_connector_account_type_details,
        updated_customer,
        router_data,
        _tokenization_action,
    ) = call_connector_service_prerequisites(
        state,
        req_state,
        merchant_context,
        connector,
        operation,
        payment_data,
        customer,
        call_connector_action,
        schedule_time,
        header_payload,
        frm_suggestion,
        business_profile,
        is_retry_payment,
        should_retry_with_pan,
        all_keys_required,
    )
    .await?;
    Ok((
        merchant_connector_account_type_details,
        external_vault_merchant_connector_account_type_details,
        updated_customer,
        router_data,
    ))
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn internal_call_connector_service_prerequisites<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    business_profile: &domain::Profile,
) -> RouterResult<(
    domain::MerchantConnectorAccountTypeDetails,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let merchant_connector_details =
        payment_data
            .get_merchant_connector_details()
            .ok_or_else(|| {
                error_stack::report!(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Merchant connector details not found in payment data")
            })?;
    let merchant_connector_account =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(
            merchant_connector_details,
        );

    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            &None,
            &merchant_connector_account,
            None,
            None,
        )
        .await?;

    Ok((merchant_connector_account, router_data))
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn connector_service_decider<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    business_profile: &domain::Profile,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let mut router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            &None,
            &merchant_connector_account_type_details,
            None,
            Some(header_payload.clone()),
        )
        .await?;

    // do order creation
    let should_call_unified_connector_service = should_call_unified_connector_service(
        state,
        merchant_context,
        &router_data,
        Some(payment_data),
    )
    .await?;

    let (connector_request, should_continue_further) = if !should_call_unified_connector_service {
        let mut should_continue_further = true;

        let should_continue = match router_data
            .create_order_at_connector(state, &connector, should_continue_further)
            .await?
        {
            Some(create_order_response) => {
                if let Ok(order_id) = create_order_response.clone().create_order_result {
                    payment_data.set_connector_response_reference_id(Some(order_id))
                }

                // Set the response in routerdata response to carry forward
                router_data
                    .update_router_data_with_create_order_response(create_order_response.clone());
                create_order_response.create_order_result.ok().map(|_| ())
            }
            // If create order is not required, then we can proceed with further processing
            None => Some(()),
        };

        let should_continue: (Option<common_utils::request::Request>, bool) = match should_continue
        {
            Some(_) => {
                router_data
                    .build_flow_specific_connector_request(
                        state,
                        &connector,
                        call_connector_action.clone(),
                    )
                    .await?
            }
            None => (None, false),
        };
        should_continue
    } else {
        // If unified connector service is called, these values are not used
        // as the request is built in the unified connector service call
        (None, false)
    };

    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            None, // customer is not used in internal flows
            merchant_context.get_merchant_account().storage_scheme,
            None,
            merchant_context.get_merchant_key_store(),
            None, // frm_suggestion is not used in internal flows
            header_payload.clone(),
        )
        .await?;

    record_time_taken_with(|| async {
        if should_call_unified_connector_service {
            router_env::logger::info!(
                "Processing payment through UCS gateway system- payment_id={}, attempt_id={}",
                payment_data.get_payment_intent().id.get_string_repr(),
                payment_data.get_payment_attempt().id.get_string_repr()
            );
            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_type_details.clone(),
                    merchant_context,
                )
                .await?;

            Ok(router_data)
        } else {
            Err(
                errors::ApiErrorResponse::InternalServerError
            )
            .attach_printable("Unified connector service is down and traditional connector service fallback is not implemented")
        }
    })
    .await
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn decide_unified_connector_service_call<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    should_retry_with_pan: bool,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    updated_customer: Option<storage::CustomerUpdate>,
    tokenization_action: TokenizationAction,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    record_time_taken_with(|| async {
        if should_call_unified_connector_service(
            state,
            merchant_context,
            &router_data,
            Some(payment_data),
        )
        .await?
        {
            router_env::logger::info!(
                "Executing payment through UCS gateway system - payment_id={}, attempt_id={}",
                payment_data.get_payment_intent().id.get_string_repr(),
                payment_data.get_payment_attempt().id.get_string_repr()
            );
            if should_add_task_to_process_tracker(payment_data) {
                operation
                    .to_domain()?
                    .add_task_to_process_tracker(
                        state,
                        payment_data.get_payment_attempt(),
                        false,
                        schedule_time,
                    )
                    .await
                    .map_err(|error| logger::error!(process_tracker_error=?error))
                    .ok();
            }

            (_, *payment_data) = operation
                .to_update_tracker()?
                .update_trackers(
                    state,
                    req_state,
                    payment_data.clone(),
                    customer.clone(),
                    merchant_context.get_merchant_account().storage_scheme,
                    None,
                    merchant_context.get_merchant_key_store(),
                    frm_suggestion,
                    header_payload.clone(),
                )
                .await?;
            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_type_details.clone(),
                    merchant_context,
                )
                .await?;

            Ok(router_data)
        } else {
            router_env::logger::info!(
                "Processing payment through Direct gateway system - payment_id={}, attempt_id={}",
                payment_data.get_payment_intent().id.get_string_repr(),
                payment_data.get_payment_attempt().id.get_string_repr()
            );

            call_connector_service(
                state,
                req_state,
                merchant_context,
                connector,
                operation,
                payment_data,
                customer,
                call_connector_action,
                schedule_time,
                header_payload,
                frm_suggestion,
                business_profile,
                is_retry_payment,
                should_retry_with_pan,
                return_raw_connector_response,
                merchant_connector_account_type_details,
                router_data,
                updated_customer,
                tokenization_action,
            )
            .await
        }
    })
    .await
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_unified_connector_service_for_external_proxy<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    _connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    _call_connector_action: CallConnectorAction,
    _schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    _is_retry_payment: bool,
    _should_retry_with_pan: bool,
    _return_raw_connector_response: Option<bool>,
    merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
    external_vault_merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    _updated_customer: Option<storage::CustomerUpdate>,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    record_time_taken_with(|| async {
        (_, *payment_data) = operation
            .to_update_tracker()?
            .update_trackers(
                state,
                req_state,
                payment_data.clone(),
                customer.clone(),
                merchant_context.get_merchant_account().storage_scheme,
                None,
                merchant_context.get_merchant_key_store(),
                frm_suggestion,
                header_payload.clone(),
            )
            .await?;
        let lineage_ids = grpc_client::LineageIds::new(
            business_profile.merchant_id.clone(),
            business_profile.get_id().clone(),
        );
        router_data
            .call_unified_connector_service_with_external_vault_proxy(
                state,
                &header_payload,
                lineage_ids,
                merchant_connector_account_type_details.clone(),
                external_vault_merchant_connector_account_type_details.clone(),
                merchant_context,
            )
            .await?;

        Ok(router_data)
    })
    .await
}

#[cfg(feature = "v1")]
// This function does not perform the tokenization action, as the payment method is not saved in this flow.
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn proxy_for_call_connector_service<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    validate_result: &operations::ValidateResult,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,

    business_profile: &domain::Profile,
    return_raw_connector_response: Option<bool>,
) -> RouterResult<(
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    helpers::MerchantConnectorAccountType,
)>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let stime_connector = Instant::now();

    let merchant_connector_account = construct_profile_id_and_get_mca(
        state,
        merchant_context,
        payment_data,
        &connector.connector_name.to_string(),
        connector.merchant_connector_id.as_ref(),
        false,
    )
    .await?;

    if payment_data
        .get_payment_attempt()
        .merchant_connector_id
        .is_none()
    {
        payment_data.set_merchant_connector_id_in_attempt(merchant_connector_account.get_mca_id());
    }

    let merchant_recipient_data = None;

    let mut router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            customer,
            &merchant_connector_account,
            merchant_recipient_data,
            Some(header_payload.clone()),
        )
        .await?;

    let add_access_token_result = router_data
        .add_access_token(
            state,
            &connector,
            merchant_context,
            payment_data.get_creds_identifier(),
        )
        .await?;

    router_data = router_data.add_session_token(state, &connector).await?;

    let mut should_continue_further = access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &call_connector_action,
    );

    (router_data, should_continue_further) = complete_preprocessing_steps_if_required(
        state,
        &connector,
        payment_data,
        router_data,
        operation,
        should_continue_further,
    )
    .await?;

    if let Ok(router_types::PaymentsResponseData::PreProcessingResponse {
        session_token: Some(session_token),
        ..
    }) = router_data.response.to_owned()
    {
        payment_data.push_sessions_token(session_token);
    };

    let (connector_request, should_continue_further) = if should_continue_further {
        // Check if the actual flow specific request can be built with available data
        router_data
            .build_flow_specific_connector_request(state, &connector, call_connector_action.clone())
            .await?
    } else {
        (None, false)
    };

    if should_add_task_to_process_tracker(payment_data) {
        operation
            .to_domain()?
            .add_task_to_process_tracker(
                state,
                payment_data.get_payment_attempt(),
                validate_result.requeue,
                schedule_time,
            )
            .await
            .map_err(|error| logger::error!(process_tracker_error=?error))
            .ok();
    }

    let updated_customer = None;
    let frm_suggestion = None;

    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            customer.clone(),
            merchant_context.get_merchant_account().storage_scheme,
            updated_customer,
            merchant_context.get_merchant_key_store(),
            frm_suggestion,
            header_payload.clone(),
        )
        .await?;

    let router_data = if should_continue_further {
        // The status of payment_attempt and intent will be updated in the previous step
        // update this in router_data.
        // This is added because few connector integrations do not update the status,
        // and rely on previous status set in router_data
        router_data.status = payment_data.get_payment_attempt().status;
        router_data
            .decide_flows(
                state,
                &connector,
                call_connector_action,
                connector_request,
                business_profile,
                header_payload.clone(),
                return_raw_connector_response,
            )
            .await
    } else {
        Ok(router_data)
    }?;

    let etime_connector = Instant::now();
    let duration_connector = etime_connector.saturating_duration_since(stime_connector);
    tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));

    Ok((router_data, merchant_connector_account))
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn proxy_for_call_connector_service<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    business_profile: &domain::Profile,
    return_raw_connector_response: Option<bool>,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let stime_connector = Instant::now();

    let merchant_connector_account =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
            helpers::get_merchant_connector_account_v2(
                state,
                merchant_context.get_merchant_key_store(),
                connector.merchant_connector_id.as_ref(),
            )
            .await?,
        ));
    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let mut router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            &None,
            &merchant_connector_account,
            None,
            Some(header_payload.clone()),
        )
        .await?;

    let add_access_token_result = router_data
        .add_access_token(
            state,
            &connector,
            merchant_context,
            payment_data.get_creds_identifier(),
        )
        .await?;

    router_data = router_data.add_session_token(state, &connector).await?;

    let mut should_continue_further = access_token::update_router_data_with_access_token_result(
        &add_access_token_result,
        &mut router_data,
        &call_connector_action,
    );

    let (connector_request, should_continue_further) = if should_continue_further {
        router_data
            .build_flow_specific_connector_request(state, &connector, call_connector_action.clone())
            .await?
    } else {
        (None, false)
    };

    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            None,
            merchant_context.get_merchant_account().storage_scheme,
            None,
            merchant_context.get_merchant_key_store(),
            None,
            header_payload.clone(),
        )
        .await?;

    let router_data = if should_continue_further {
        router_data
            .decide_flows(
                state,
                &connector,
                call_connector_action,
                connector_request,
                business_profile,
                header_payload.clone(),
                return_raw_connector_response,
            )
            .await
    } else {
        Ok(router_data)
    }?;

    let etime_connector = Instant::now();
    let duration_connector = etime_connector.saturating_duration_since(stime_connector);
    tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));

    Ok(router_data)
}

#[cfg(feature = "v2")]
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all)]
pub async fn call_connector_service_for_external_vault_proxy<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    business_profile: &domain::Profile,
    return_raw_connector_response: Option<bool>,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    let stime_connector = Instant::now();

    let merchant_connector_account =
        domain::MerchantConnectorAccountTypeDetails::MerchantConnectorAccount(Box::new(
            helpers::get_merchant_connector_account_v2(
                state,
                merchant_context.get_merchant_key_store(),
                connector.merchant_connector_id.as_ref(),
            )
            .await?,
        ));
    operation
        .to_domain()?
        .populate_payment_data(
            state,
            payment_data,
            merchant_context,
            business_profile,
            &connector,
        )
        .await?;

    let mut router_data = payment_data
        .construct_router_data(
            state,
            connector.connector.id(),
            merchant_context,
            &None,
            &merchant_connector_account,
            None,
            Some(header_payload.clone()),
        )
        .await?;

    // let add_access_token_result = router_data
    //     .add_access_token(
    //         state,
    //         &connector,
    //         merchant_context,
    //         payment_data.get_creds_identifier(),
    //     )
    //     .await?;

    // router_data = router_data.add_session_token(state, &connector).await?;

    // let mut should_continue_further = access_token::update_router_data_with_access_token_result(
    //     &add_access_token_result,
    //     &mut router_data,
    //     &call_connector_action,
    // );
    let should_continue_further = true;

    let (connector_request, should_continue_further) = if should_continue_further {
        router_data
            .build_flow_specific_connector_request(state, &connector, call_connector_action.clone())
            .await?
    } else {
        (None, false)
    };

    (_, *payment_data) = operation
        .to_update_tracker()?
        .update_trackers(
            state,
            req_state,
            payment_data.clone(),
            None,
            merchant_context.get_merchant_account().storage_scheme,
            None,
            merchant_context.get_merchant_key_store(),
            None,
            header_payload.clone(),
        )
        .await?;

    let router_data = if should_continue_further {
        router_data
            .decide_flows(
                state,
                &connector,
                call_connector_action,
                connector_request,
                business_profile,
                header_payload.clone(),
                return_raw_connector_response,
            )
            .await
    } else {
        Ok(router_data)
    }?;

    let etime_connector = Instant::now();
    let duration_connector = etime_connector.saturating_duration_since(stime_connector);
    tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));

    Ok(router_data)
}
struct ApplePayWallet;
struct PazeWallet;
struct GooglePayWallet;

#[async_trait::async_trait]
pub trait WalletFlow<F, D>: Send + Sync
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    /// Check if wallet data is already decrypted and return token if so
    fn check_predecrypted_token(
        &self,
        _payment_data: &D,
    ) -> CustomResult<Option<PaymentMethodToken>, errors::ApiErrorResponse> {
        // Default implementation returns None (no pre-decrypted data)
        Ok(None)
    }

    fn decide_wallet_flow(
        &self,
        state: &SessionState,
        payment_data: &D,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<DecideWalletFlow>, errors::ApiErrorResponse>;

    async fn decrypt_wallet_token(
        &self,
        wallet_flow: &DecideWalletFlow,
        payment_data: &D,
    ) -> CustomResult<PaymentMethodToken, errors::ApiErrorResponse>;
}

#[async_trait::async_trait]
impl<F, D> WalletFlow<F, D> for PazeWallet
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    fn decide_wallet_flow(
        &self,
        state: &SessionState,
        _payment_data: &D,
        _merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<DecideWalletFlow>, errors::ApiErrorResponse> {
        let paze_keys = state
            .conf
            .paze_decrypt_keys
            .as_ref()
            .get_required_value("Paze decrypt keys")
            .attach_printable("Paze decrypt keys not found in the configuration")?;

        let wallet_flow = DecideWalletFlow::PazeDecrypt(PazePaymentProcessingDetails {
            paze_private_key: paze_keys.get_inner().paze_private_key.clone(),
            paze_private_key_passphrase: paze_keys.get_inner().paze_private_key_passphrase.clone(),
        });
        Ok(Some(wallet_flow))
    }

    async fn decrypt_wallet_token(
        &self,
        wallet_flow: &DecideWalletFlow,
        payment_data: &D,
    ) -> CustomResult<PaymentMethodToken, errors::ApiErrorResponse> {
        let paze_payment_processing_details = wallet_flow
            .get_paze_payment_processing_details()
            .get_required_value("Paze payment processing details")
            .attach_printable(
                "Paze payment processing details not found in Paze decryption flow",
            )?;

        let paze_wallet_data = payment_data
                .get_payment_method_data()
                .and_then(|payment_method_data| payment_method_data.get_wallet_data())
                .and_then(|wallet_data| wallet_data.get_paze_wallet_data())
                .get_required_value("Paze wallet token").attach_printable(
                    "Paze wallet data not found in the payment method data during the Paze decryption flow",
                )?;

        let paze_data = decrypt_paze_token(
            paze_wallet_data.clone(),
            paze_payment_processing_details.paze_private_key.clone(),
            paze_payment_processing_details
                .paze_private_key_passphrase
                .clone(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("failed to decrypt paze token")?;

        let paze_decrypted_data = paze_data
            .parse_value::<hyperswitch_domain_models::router_data::PazeDecryptedData>(
                "PazeDecryptedData",
            )
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("failed to parse PazeDecryptedData")?;
        Ok(PaymentMethodToken::PazeDecrypt(Box::new(
            paze_decrypted_data,
        )))
    }
}

#[async_trait::async_trait]
impl<F, D> WalletFlow<F, D> for ApplePayWallet
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    fn check_predecrypted_token(
        &self,
        payment_data: &D,
    ) -> CustomResult<Option<PaymentMethodToken>, errors::ApiErrorResponse> {
        let apple_pay_wallet_data = payment_data
            .get_payment_method_data()
            .and_then(|payment_method_data| payment_method_data.get_wallet_data())
            .and_then(|wallet_data| wallet_data.get_apple_pay_wallet_data());

        let result = if let Some(data) = apple_pay_wallet_data {
            match &data.payment_data {
                common_payments_types::ApplePayPaymentData::Encrypted(_) => None,
                common_payments_types::ApplePayPaymentData::Decrypted(
                    apple_pay_predecrypt_data,
                ) => {
                    helpers::validate_card_expiry(
                        &apple_pay_predecrypt_data.application_expiration_month,
                        &apple_pay_predecrypt_data.application_expiration_year,
                    )?;
                    Some(PaymentMethodToken::ApplePayDecrypt(Box::new(
                        apple_pay_predecrypt_data.clone(),
                    )))
                }
            }
        } else {
            None
        };
        Ok(result)
    }

    fn decide_wallet_flow(
        &self,
        state: &SessionState,
        payment_data: &D,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<DecideWalletFlow>, errors::ApiErrorResponse> {
        let apple_pay_metadata = check_apple_pay_metadata(state, Some(merchant_connector_account));

        add_apple_pay_flow_metrics(
            &apple_pay_metadata,
            payment_data.get_payment_attempt().connector.clone(),
            payment_data.get_payment_attempt().merchant_id.clone(),
        );

        let wallet_flow = match apple_pay_metadata {
            Some(domain::ApplePayFlow::Simplified(payment_processing_details)) => Some(
                DecideWalletFlow::ApplePayDecrypt(payment_processing_details),
            ),
            Some(domain::ApplePayFlow::Manual) | None => None,
        };
        Ok(wallet_flow)
    }

    async fn decrypt_wallet_token(
        &self,
        wallet_flow: &DecideWalletFlow,
        payment_data: &D,
    ) -> CustomResult<PaymentMethodToken, errors::ApiErrorResponse> {
        let apple_pay_payment_processing_details = wallet_flow
            .get_apple_pay_payment_processing_details()
            .get_required_value("Apple Pay payment processing details")
            .attach_printable(


// Implementation: impl GetPaymentMethodDetails for for DummyConnectorWallet
// File: crates/router/src/routes/dummy_connector/types.rs
// Module: router
// Methods: 2 total (0 public)

impl GetPaymentMethodDetails for for DummyConnectorWallet


// Struct: UpdateSuccessRateWindowEventResponse
// File: crates/router/src/core/payments/routing/utils.rs
// Module: router
// Implementations: 0

pub struct UpdateSuccessRateWindowEventResponse


// Struct: DecryptDataResponse
// File: crates/common_utils/src/types/keymanager.rs
// Module: common_utils
// Implementations: 0

pub struct DecryptDataResponse


// Struct: MetricsBucketResponse
// File: crates/api_models/src/analytics/auth_events.rs
// Module: api_models
// Implementations: 0

pub struct MetricsBucketResponse


// Implementation: impl api::RefundSync for for Worldpayvantiv
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Worldpayvantiv


// Implementation: impl PaymentIntentMetricsBucketIdentifier
// File: crates/api_models/src/analytics/payment_intents.rs
// Module: api_models
// Methods: 1 total (1 public)

impl PaymentIntentMetricsBucketIdentifier


// Function: get_single_merchant_id_and_profile_id
// File: crates/router/src/utils/user_role.rs
// Module: router

pub fn get_single_merchant_id_and_profile_id(
    state: &SessionState,
    user_role: &UserRole,
) -> UserResult<(id_type::MerchantId, id_type::ProfileId)>


// Implementation: impl api::PaymentAuthorize for for Hipay
// File: crates/hyperswitch_connectors/src/connectors/hipay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Hipay


// File: crates/router/src/services/authorization/info.rs
// Module: router
// Public functions: 2

use std::ops::Not;

use api_models::user_role::GroupInfo;
use common_enums::{ParentGroup, PermissionGroup};
use strum::IntoEnumIterator;

// TODO: To be deprecated
pub fn get_group_authorization_info() -> Option<Vec<GroupInfo>> {
    let groups = PermissionGroup::iter()
        .filter_map(get_group_info_from_permission_group)
        .collect::<Vec<_>>();

    groups.is_empty().not().then_some(groups)
}

// TODO: To be deprecated
fn get_group_info_from_permission_group(group: PermissionGroup) -> Option<GroupInfo> {
    let description = get_group_description(group)?;
    Some(GroupInfo { group, description })
}

// TODO: To be deprecated
fn get_group_description(group: PermissionGroup) -> Option<&'static str> {
    match group {
        PermissionGroup::OperationsView => {
            Some("View Payments, Refunds, Payouts, Mandates, Disputes and Customers")
        }
        PermissionGroup::OperationsManage => {
            Some("Create, modify and delete Payments, Refunds, Payouts, Mandates, Disputes and Customers")
        }
        PermissionGroup::ConnectorsView => {
            Some("View connected Payment Processors, Payout Processors and Fraud & Risk Manager details")
        }
        PermissionGroup::ConnectorsManage => Some("Create, modify and delete connectors like Payment Processors, Payout Processors and Fraud & Risk Manager"),
        PermissionGroup::WorkflowsView => {
            Some("View Routing, 3DS Decision Manager, Surcharge Decision Manager")
        }
        PermissionGroup::WorkflowsManage => {
            Some("Create, modify and delete Routing, 3DS Decision Manager, Surcharge Decision Manager")
        }
        PermissionGroup::AnalyticsView => Some("View Analytics"),
        PermissionGroup::UsersView => Some("View Users"),
        PermissionGroup::UsersManage => Some("Manage and invite Users to the Team"),
        PermissionGroup::MerchantDetailsView | PermissionGroup::AccountView => Some("View Merchant Details"),
        PermissionGroup::MerchantDetailsManage | PermissionGroup::AccountManage => Some("Create, modify and delete Merchant Details like api keys, webhooks, etc"),
        PermissionGroup::OrganizationManage => Some("Manage organization level tasks like create new Merchant accounts, Organization level roles, etc"),
        PermissionGroup::ReconReportsView => Some("View reconciliation reports and analytics"),
        PermissionGroup::ReconReportsManage => Some("Manage reconciliation reports"),
        PermissionGroup::ReconOpsView => Some("View and access all reconciliation operations including reports and analytics"),
        PermissionGroup::ReconOpsManage => Some("Manage all reconciliation operations including reports and analytics"),
        PermissionGroup::ThemeView => Some("View Themes"),
        PermissionGroup::ThemeManage => Some("Manage Themes"),
        PermissionGroup::InternalManage => None, // Internal group, no user-facing description
    }
}

pub fn get_parent_group_description(group: ParentGroup) -> Option<&'static str> {
    match group {
        ParentGroup::Operations => Some("Payments, Refunds, Payouts, Mandates, Disputes and Customers"),
        ParentGroup::Connectors => Some("Create, modify and delete connectors like Payment Processors, Payout Processors and Fraud & Risk Manager"),
        ParentGroup::Workflows => Some("Create, modify and delete Routing, 3DS Decision Manager, Surcharge Decision Manager"),
        ParentGroup::Analytics => Some("View Analytics"),
        ParentGroup::Users =>  Some("Manage and invite Users to the Team"),
        ParentGroup::Account => Some("Create, modify and delete Merchant Details like api keys, webhooks, etc"),
        ParentGroup::ReconOps => Some("View, manage reconciliation operations like upload and process files, run reconciliation etc"),
        ParentGroup::ReconReports => Some("View, manage reconciliation reports and analytics"),
        ParentGroup::Theme => Some("Manage and view themes for the organization"),
        ParentGroup::Internal => None, // Internal group, no user-facing description
    }
}


// Function: make_payout_data
// File: crates/router/src/core/payouts.rs
// Module: router

pub fn make_payout_data(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    auth_profile_id: Option<id_type::ProfileId>,
    req: &payouts::PayoutRequest,
    locale: &str,
) -> RouterResult<PayoutData>


// File: crates/router/src/routes/profile_acquirer.rs
// Module: router
// Public functions: 2

use actix_web::{web, HttpRequest, HttpResponse};
use api_models::profile_acquirer::{ProfileAcquirerCreate, ProfileAcquirerUpdate};
use router_env::{instrument, tracing, Flow};

use super::app::AppState;
use crate::{
    core::api_locking,
    services::{api, authentication as auth, authorization::permissions::Permission},
    types::domain,
};

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::ProfileAcquirerCreate))]
pub async fn create_profile_acquirer(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<ProfileAcquirerCreate>,
) -> HttpResponse {
    let flow = Flow::ProfileAcquirerCreate;
    let payload = json_payload.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state: super::SessionState, auth_data, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth_data.merchant_account, auth_data.key_store),
            ));
            crate::core::profile_acquirer::create_profile_acquirer(
                state,
                req,
                merchant_context.clone(),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: true,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileAccountWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(all(feature = "olap", feature = "v1"))]
#[instrument(skip_all, fields(flow = ?Flow::ProfileAcquirerUpdate))]
pub async fn profile_acquirer_update(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::ProfileId,
        common_utils::id_type::ProfileAcquirerId,
    )>,
    json_payload: web::Json<ProfileAcquirerUpdate>,
) -> HttpResponse {
    let flow = Flow::ProfileAcquirerUpdate;
    let (profile_id, profile_acquirer_id) = path.into_inner();
    let payload = json_payload.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state: super::SessionState, auth_data, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth_data.merchant_account, auth_data.key_store),
            ));
            crate::core::profile_acquirer::update_profile_acquirer_config(
                state,
                profile_id.clone(),
                profile_acquirer_id.clone(),
                req,
                merchant_context.clone(),
            )
        },
        auth::auth_type(
            &auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: true,
            }),
            &auth::JWTAuth {
                permission: Permission::ProfileAccountWrite,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


// Implementation: impl ApiEventMetric for for PaymentsRedirectResponseData
// File: crates/router/src/events/api_logs.rs
// Module: router
// Methods: 1 total (0 public)

impl ApiEventMetric for for PaymentsRedirectResponseData


// Function: payments_confirm
// File: crates/openapi/src/routes/payments.rs
// Module: openapi

pub fn payments_confirm()


// Module Structure
// File: crates/storage_impl/src/redis.rs
// Module: storage_impl

// Public submodules:
pub mod cache;
pub mod kv_store;
pub mod pub_sub;


// Implementation: impl ValidateStatusForOperation for for PaymentProxyIntent
// File: crates/router/src/core/payments/operations/proxy_payments_intent.rs
// Module: router
// Methods: 1 total (0 public)

impl ValidateStatusForOperation for for PaymentProxyIntent


// Struct: SerializeTupleVariant
// File: crates/masking/src/serde.rs
// Module: masking
// Implementations: 0

pub struct SerializeTupleVariant<T: Serializer>


// Function: unify_address
// File: crates/hyperswitch_domain_models/src/address.rs
// Module: hyperswitch_domain_models

// Documentation: Unify the address, giving priority to `self` when details are present in both

pub fn unify_address(&self, other: Option<&Self>) -> Self


// File: crates/payment_methods/src/core/migration.rs
// Module: payment_methods
// Public functions: 9
// Public structs: 3

use actix_multipart::form::{self, bytes, text};
use api_models::payment_methods as pm_api;
use csv::Reader;
use error_stack::ResultExt;
#[cfg(feature = "v1")]
use hyperswitch_domain_models::{api, merchant_context};
use masking::PeekInterface;
use rdkafka::message::ToBytes;
use router_env::{instrument, tracing};

use crate::core::errors;
#[cfg(feature = "v1")]
use crate::{controller as pm, state};
pub mod payment_methods;
pub use payment_methods::migrate_payment_method;

#[cfg(feature = "v1")]
type PmMigrationResult<T> =
    errors::CustomResult<api::ApplicationResponse<T>, errors::ApiErrorResponse>;

#[cfg(feature = "v1")]
pub async fn migrate_payment_methods(
    state: &state::PaymentMethodsState,
    payment_methods: Vec<pm_api::PaymentMethodRecord>,
    merchant_id: &common_utils::id_type::MerchantId,
    merchant_context: &merchant_context::MerchantContext,
    mca_ids: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
    controller: &dyn pm::PaymentMethodsController,
) -> PmMigrationResult<Vec<pm_api::PaymentMethodMigrationResponse>> {
    let mut result = Vec::with_capacity(payment_methods.len());

    for record in payment_methods {
        let req = pm_api::PaymentMethodMigrate::try_from((
            &record,
            merchant_id.clone(),
            mca_ids.as_ref(),
        ))
        .map_err(|err| errors::ApiErrorResponse::InvalidRequestData {
            message: format!("error: {err:?}"),
        })
        .attach_printable("record deserialization failed");

        let res = match req {
            Ok(migrate_request) => {
                let res = migrate_payment_method(
                    state,
                    migrate_request,
                    merchant_id,
                    merchant_context,
                    controller,
                )
                .await;
                match res {
                    Ok(api::ApplicationResponse::Json(response)) => Ok(response),
                    Err(e) => Err(e.to_string()),
                    _ => Err("Failed to migrate payment method".to_string()),
                }
            }
            Err(e) => Err(e.to_string()),
        };

        result.push(pm_api::PaymentMethodMigrationResponse::from((res, record)));
    }
    Ok(api::ApplicationResponse::Json(result))
}

#[derive(Debug, form::MultipartForm)]
pub struct PaymentMethodsMigrateForm {
    #[multipart(limit = "1MB")]
    pub file: bytes::Bytes,

    pub merchant_id: text::Text<common_utils::id_type::MerchantId>,

    pub merchant_connector_id:
        Option<text::Text<common_utils::id_type::MerchantConnectorAccountId>>,

    pub merchant_connector_ids: Option<text::Text<String>>,
}

struct MerchantConnectorValidator;

impl MerchantConnectorValidator {
    fn parse_comma_separated_ids(
        ids_string: &str,
    ) -> Result<Vec<common_utils::id_type::MerchantConnectorAccountId>, errors::ApiErrorResponse>
    {
        // Estimate capacity based on comma count
        let capacity = ids_string.matches(',').count() + 1;
        let mut result = Vec::with_capacity(capacity);

        for id in ids_string.split(',') {
            let trimmed_id = id.trim();
            if !trimmed_id.is_empty() {
                let mca_id =
                    common_utils::id_type::MerchantConnectorAccountId::wrap(trimmed_id.to_string())
                        .map_err(|_| errors::ApiErrorResponse::InvalidRequestData {
                            message: format!("Invalid merchant_connector_account_id: {trimmed_id}"),
                        })?;
                result.push(mca_id);
            }
        }

        Ok(result)
    }

    fn validate_form_csv_conflicts(
        records: &[pm_api::PaymentMethodRecord],
        form_has_single_id: bool,
        form_has_multiple_ids: bool,
    ) -> Result<(), errors::ApiErrorResponse> {
        if form_has_single_id {
            // If form has merchant_connector_id, CSV records should not have merchant_connector_ids
            for (index, record) in records.iter().enumerate() {
                if record.merchant_connector_ids.is_some() {
                    return Err(errors::ApiErrorResponse::InvalidRequestData {
                        message: format!(
                            "Record at line {} has merchant_connector_ids but form has merchant_connector_id. Only one should be provided",
                            index + 1
                        ),
                    });
                }
            }
        }

        if form_has_multiple_ids {
            // If form has merchant_connector_ids, CSV records should not have merchant_connector_id
            for (index, record) in records.iter().enumerate() {
                if record.merchant_connector_id.is_some() {
                    return Err(errors::ApiErrorResponse::InvalidRequestData {
                        message: format!(
                            "Record at line {} has merchant_connector_id but form has merchant_connector_ids. Only one should be provided",
                            index + 1
                        ),
                    });
                }
            }
        }

        Ok(())
    }
}

type MigrationValidationResult = Result<
    (
        common_utils::id_type::MerchantId,
        Vec<pm_api::PaymentMethodRecord>,
        Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
    ),
    errors::ApiErrorResponse,
>;

impl PaymentMethodsMigrateForm {
    pub fn validate_and_get_payment_method_records(self) -> MigrationValidationResult {
        // Step 1: Validate form-level conflicts
        let form_has_single_id = self.merchant_connector_id.is_some();
        let form_has_multiple_ids = self.merchant_connector_ids.is_some();

        if form_has_single_id && form_has_multiple_ids {
            return Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Both merchant_connector_id and merchant_connector_ids cannot be provided"
                    .to_string(),
            });
        }

        // Ensure at least one is provided
        if !form_has_single_id && !form_has_multiple_ids {
            return Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Either merchant_connector_id or merchant_connector_ids must be provided"
                    .to_string(),
            });
        }

        // Step 2: Parse CSV
        let records = parse_csv(self.file.data.to_bytes()).map_err(|e| {
            errors::ApiErrorResponse::PreconditionFailed {
                message: e.to_string(),
            }
        })?;

        // Step 3: Validate CSV vs Form conflicts
        MerchantConnectorValidator::validate_form_csv_conflicts(
            &records,
            form_has_single_id,
            form_has_multiple_ids,
        )?;

        // Step 4: Prepare the merchant connector account IDs for return
        let mca_ids = if let Some(ref single_id) = self.merchant_connector_id {
            Some(vec![(**single_id).clone()])
        } else if let Some(ref ids_string) = self.merchant_connector_ids {
            let parsed_ids = MerchantConnectorValidator::parse_comma_separated_ids(ids_string)?;
            if parsed_ids.is_empty() {
                None
            } else {
                Some(parsed_ids)
            }
        } else {
            None
        };

        // Step 5: Return the updated structure
        Ok((self.merchant_id.clone(), records, mca_ids))
    }
}

fn parse_csv(data: &[u8]) -> csv::Result<Vec<pm_api::PaymentMethodRecord>> {
    let mut csv_reader = Reader::from_reader(data);
    let mut records = Vec::new();
    let mut id_counter = 0;
    for result in csv_reader.deserialize() {
        let mut record: pm_api::PaymentMethodRecord = result?;
        id_counter += 1;
        record.line_number = Some(id_counter);
        records.push(record);
    }
    Ok(records)
}

#[instrument(skip_all)]
pub fn validate_card_expiry(
    card_exp_month: &masking::Secret<String>,
    card_exp_year: &masking::Secret<String>,
) -> errors::CustomResult<(), errors::ApiErrorResponse> {
    let exp_month = card_exp_month
        .peek()
        .to_string()
        .parse::<u8>()
        .change_context(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "card_exp_month",
        })?;
    ::cards::CardExpirationMonth::try_from(exp_month).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Month".to_string(),
        },
    )?;

    let year_str = card_exp_year.peek().to_string();

    validate_card_exp_year(year_str).change_context(
        errors::ApiErrorResponse::PreconditionFailed {
            message: "Invalid Expiry Year".to_string(),
        },
    )?;

    Ok(())
}

fn validate_card_exp_year(year: String) -> Result<(), errors::ValidationError> {
    let year_str = year.to_string();
    if year_str.len() == 2 || year_str.len() == 4 {
        year_str
            .parse::<u16>()
            .map_err(|_| errors::ValidationError::InvalidValue {
                message: "card_exp_year".to_string(),
            })?;
        Ok(())
    } else {
        Err(errors::ValidationError::InvalidValue {
            message: "invalid card expiration year".to_string(),
        })
    }
}

#[derive(Debug)]
pub struct RecordMigrationStatus {
    pub card_migrated: Option<bool>,
    pub network_token_migrated: Option<bool>,
    pub connector_mandate_details_migrated: Option<bool>,
    pub network_transaction_migrated: Option<bool>,
}

#[derive(Debug)]
pub struct RecordMigrationStatusBuilder {
    pub card_migrated: Option<bool>,
    pub network_token_migrated: Option<bool>,
    pub connector_mandate_details_migrated: Option<bool>,
    pub network_transaction_migrated: Option<bool>,
}

impl RecordMigrationStatusBuilder {
    pub fn new() -> Self {
        Self {
            card_migrated: None,
            network_token_migrated: None,
            connector_mandate_details_migrated: None,
            network_transaction_migrated: None,
        }
    }

    pub fn card_migrated(&mut self, card_migrated: bool) {
        self.card_migrated = Some(card_migrated);
    }

    pub fn network_token_migrated(&mut self, network_token_migrated: Option<bool>) {
        self.network_token_migrated = network_token_migrated;
    }

    pub fn connector_mandate_details_migrated(
        &mut self,
        connector_mandate_details_migrated: Option<bool>,
    ) {
        self.connector_mandate_details_migrated = connector_mandate_details_migrated;
    }

    pub fn network_transaction_id_migrated(&mut self, network_transaction_migrated: Option<bool>) {
        self.network_transaction_migrated = network_transaction_migrated;
    }

    pub fn build(self) -> RecordMigrationStatus {
        RecordMigrationStatus {
            card_migrated: self.card_migrated,
            network_token_migrated: self.network_token_migrated,
            connector_mandate_details_migrated: self.connector_mandate_details_migrated,
            network_transaction_migrated: self.network_transaction_migrated,
        }
    }
}

impl Default for RecordMigrationStatusBuilder {
    fn default() -> Self {
        Self::new()
    }
}


// Struct: NexixpayRedirectionRequest
// File: crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NexixpayRedirectionRequest


// Struct: ProgramThreeDsDecisionRule
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct ProgramThreeDsDecisionRule


// Implementation: impl api::PaymentSync for for Helcim
// File: crates/hyperswitch_connectors/src/connectors/helcim.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Helcim


// Implementation: impl api::PaymentVoid for for Volt
// File: crates/hyperswitch_connectors/src/connectors/volt.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Volt


// Struct: PayoutLinkDetails
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 0

pub struct PayoutLinkDetails


// Implementation: impl ConnectorSpecifications for for DummyConnector
// File: crates/hyperswitch_connectors/src/connectors/dummyconnector.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl ConnectorSpecifications for for DummyConnector


// Implementation: impl Forte
// File: crates/hyperswitch_connectors/src/connectors/forte.rs
// Module: hyperswitch_connectors
// Methods: 1 total (1 public)

impl Forte


// Struct: CurrencyAuthKey
// File: crates/test_utils/src/connector_auth.rs
// Module: test_utils
// Implementations: 0

pub struct CurrencyAuthKey


// Function: retry_delivery_attempt
// File: crates/router/src/core/webhooks/webhook_events.rs
// Module: router

pub fn retry_delivery_attempt(
    state: SessionState,
    merchant_id: common_utils::id_type::MerchantId,
    event_id: String,
) -> RouterResponse<api::webhook_events::EventRetrieveResponse>


// Function: clone_connector
// File: crates/router/src/core/user.rs
// Module: router

pub fn clone_connector(
    state: SessionState,
    request: user_api::CloneConnectorRequest,
) -> UserResponse<api_models::admin::MerchantConnectorResponse>


// Function: check_node
// File: crates/hyperswitch_constraint_graph/src/graph.rs
// Module: hyperswitch_constraint_graph

pub fn check_node<C>(
        &self,
        ctx: &C,
        node_id: NodeId,
        relation: Relation,
        strength: Strength,
        memo: &mut Memoization<V>,
        cycle_map: &mut CycleCheck,
        domains: Option<&[String]>,
    ) -> Result<(), GraphError<V>>
    where
        C: CheckingContext<Value = V>,


// Struct: CaptureUpdateInternal
// File: crates/diesel_models/src/capture.rs
// Module: diesel_models
// Implementations: 0

pub struct CaptureUpdateInternal


// Implementation: impl api::RefundSync for for Stax
// File: crates/hyperswitch_connectors/src/connectors/stax.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Stax


// Struct: FiuuRecurringResponse
// File: crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct FiuuRecurringResponse


// Struct: GetAuthEventMetricRequest
// File: crates/api_models/src/analytics.rs
// Module: api_models
// Implementations: 0

pub struct GetAuthEventMetricRequest


// Function: get_name
// File: crates/router/src/types/domain/user.rs
// Module: router

pub fn get_name(&self) -> Secret<String>


// Struct: RecurringInformation
// File: crates/hyperswitch_connectors/src/connectors/nordea/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RecurringInformation


// Struct: SofortInfo
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SofortInfo


// Implementation: impl super::connector_events::events::ConnectorEventLogAnalytics for for ClickhouseClient
// File: crates/analytics/src/clickhouse.rs
// Module: analytics
// Methods: 0 total (0 public)

impl super::connector_events::events::ConnectorEventLogAnalytics for for ClickhouseClient


// Implementation: impl api::PaymentSession for for Phonepe
// File: crates/hyperswitch_connectors/src/connectors/phonepe.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Phonepe


// Implementation: impl api::ConnectorAccessToken for for Mpgs
// File: crates/hyperswitch_connectors/src/connectors/mpgs.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Mpgs


// Function: list_all_merchant_accounts
// File: crates/diesel_models/src/query/merchant_account.rs
// Module: diesel_models

pub fn list_all_merchant_accounts(
        conn: &PgPooledConn,
        limit: u32,
        offset: Option<u32>,
    ) -> StorageResult<Vec<Self>>


// Function: extended_card_info_toggle
// File: crates/router/src/core/admin.rs
// Module: router

pub fn extended_card_info_toggle(
    state: SessionState,
    merchant_id: &id_type::MerchantId,
    profile_id: &id_type::ProfileId,
    ext_card_info_choice: admin_types::ExtendedCardInfoChoice,
) -> RouterResponse<admin_types::ExtendedCardInfoChoice>


// Struct: ErrorResponse
// File: crates/pm_auth/src/types.rs
// Module: pm_auth
// Implementations: 1

pub struct ErrorResponse


// Struct: ChargebeeInvoiceBody
// File: crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 1

pub struct ChargebeeInvoiceBody