// File: crates/router/src/core/payouts.rs
// Module: router
// Public functions: 41
// Public structs: 1

pub mod access_token;
pub mod helpers;
#[cfg(feature = "payout_retry")]
pub mod retry;
pub mod transformers;
pub mod validator;
use std::{
    collections::{HashMap, HashSet},
    vec::IntoIter,
};

#[cfg(feature = "olap")]
use api_models::payments as payment_enums;
use api_models::{self, enums as api_enums, payouts::PayoutLinkResponse};
#[cfg(feature = "payout_retry")]
use common_enums::PayoutRetryType;
use common_utils::{
    consts,
    ext_traits::{AsyncExt, ValueExt},
    id_type::{self, GenerateId},
    link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus},
    types::{MinorUnit, UnifiedCode, UnifiedMessage},
};
use diesel_models::{
    enums as storage_enums,
    generic_link::{GenericLinkNew, PayoutLink},
    CommonMandateReference, PayoutsMandateReference, PayoutsMandateReferenceRecord,
};
use error_stack::{report, ResultExt};
#[cfg(feature = "olap")]
use futures::future::join_all;
use hyperswitch_domain_models::{self as domain_models, payment_methods::PaymentMethod};
use masking::{PeekInterface, Secret};
#[cfg(feature = "payout_retry")]
use retry::GsmValidation;
use router_env::{instrument, logger, tracing, Env};
use scheduler::utils as pt_utils;
use serde_json;
use time::Duration;

#[cfg(feature = "olap")]
use crate::types::domain::behaviour::Conversion;
#[cfg(feature = "olap")]
use crate::types::PayoutActionData;
use crate::{
    core::{
        errors::{
            self, ConnectorErrorExt, CustomResult, RouterResponse, RouterResult, StorageErrorExt,
        },
        payments::{self, customers, helpers as payment_helpers},
        utils as core_utils,
    },
    db::StorageInterface,
    routes::SessionState,
    services,
    types::{
        self,
        api::{self, payments as payment_api_types, payouts},
        domain,
        storage::{self, PaymentRoutingInfo},
        transformers::{ForeignFrom, ForeignTryFrom},
    },
    utils::{self, OptionExt},
};

// ********************************************** TYPES **********************************************
#[derive(Clone)]
pub struct PayoutData {
    pub billing_address: Option<domain_models::address::Address>,
    pub business_profile: domain::Profile,
    pub customer_details: Option<domain::Customer>,
    pub merchant_connector_account: Option<payment_helpers::MerchantConnectorAccountType>,
    pub payouts: storage::Payouts,
    pub payout_attempt: storage::PayoutAttempt,
    pub payout_method_data: Option<payouts::PayoutMethodData>,
    pub profile_id: id_type::ProfileId,
    pub should_terminate: bool,
    pub payout_link: Option<PayoutLink>,
    pub current_locale: String,
    pub payment_method: Option<PaymentMethod>,
    pub connector_transfer_method_id: Option<String>,
}

// ********************************************** CORE FLOWS **********************************************
pub fn get_next_connector(
    connectors: &mut IntoIter<api::ConnectorRoutingData>,
) -> RouterResult<api::ConnectorRoutingData> {
    connectors
        .next()
        .ok_or(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Connector not found in connectors iterator")
}

#[cfg(all(feature = "payouts", feature = "v1"))]
pub async fn get_connector_choice(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector: Option<String>,
    routing_algorithm: Option<serde_json::Value>,
    payout_data: &mut PayoutData,
    eligible_connectors: Option<Vec<api_enums::PayoutConnectors>>,
) -> RouterResult<api::ConnectorCallType> {
    let eligible_routable_connectors = eligible_connectors.map(|connectors| {
        connectors
            .into_iter()
            .map(api::enums::RoutableConnectors::from)
            .collect()
    });
    let connector_choice = helpers::get_default_payout_connector(state, routing_algorithm).await?;
    match connector_choice {
        api::ConnectorChoice::SessionMultiple(_) => {
            Err(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Invalid connector choice - SessionMultiple")?
        }

        api::ConnectorChoice::StraightThrough(straight_through) => {
            let request_straight_through: api::routing::StraightThroughAlgorithm = straight_through
                .clone()
                .parse_value("StraightThroughAlgorithm")
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Invalid straight through routing rules format")?;
            payout_data.payout_attempt.routing_info = Some(straight_through);
            let mut routing_data = storage::RoutingData {
                routed_through: connector,
                merchant_connector_id: None,
                algorithm: Some(request_straight_through.clone()),
                routing_info: PaymentRoutingInfo {
                    algorithm: None,
                    pre_routing_results: None,
                },
            };
            helpers::decide_payout_connector(
                state,
                merchant_context,
                Some(request_straight_through),
                &mut routing_data,
                payout_data,
                eligible_routable_connectors,
            )
            .await
        }

        api::ConnectorChoice::Decide => {
            let mut routing_data = storage::RoutingData {
                routed_through: connector,
                merchant_connector_id: None,
                algorithm: None,
                routing_info: PaymentRoutingInfo {
                    algorithm: None,
                    pre_routing_results: None,
                },
            };
            helpers::decide_payout_connector(
                state,
                merchant_context,
                None,
                &mut routing_data,
                payout_data,
                eligible_routable_connectors,
            )
            .await
        }
    }
}

#[instrument(skip_all)]
pub async fn make_connector_decision(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_call_type: api::ConnectorCallType,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    match connector_call_type {
        api::ConnectorCallType::PreDetermined(routing_data) => {
            Box::pin(call_connector_payout(
                state,
                merchant_context,
                &routing_data.connector_data,
                payout_data,
            ))
            .await?;

            #[cfg(feature = "payout_retry")]
            {
                let config_bool = retry::config_should_call_gsm_payout(
                    &*state.store,
                    merchant_context.get_merchant_account().get_id(),
                    PayoutRetryType::SingleConnector,
                )
                .await;

                if config_bool && payout_data.should_call_gsm() {
                    Box::pin(retry::do_gsm_single_connector_actions(
                        state,
                        routing_data.connector_data,
                        payout_data,
                        merchant_context,
                    ))
                    .await?;
                }
            }

            Ok(())
        }
        api::ConnectorCallType::Retryable(routing_data) => {
            let mut routing_data = routing_data.into_iter();

            let connector_data = get_next_connector(&mut routing_data)?.connector_data;

            Box::pin(call_connector_payout(
                state,
                merchant_context,
                &connector_data,
                payout_data,
            ))
            .await?;

            #[cfg(feature = "payout_retry")]
            {
                let config_multiple_connector_bool = retry::config_should_call_gsm_payout(
                    &*state.store,
                    merchant_context.get_merchant_account().get_id(),
                    PayoutRetryType::MultiConnector,
                )
                .await;

                if config_multiple_connector_bool && payout_data.should_call_gsm() {
                    Box::pin(retry::do_gsm_multiple_connector_actions(
                        state,
                        routing_data,
                        connector_data.clone(),
                        payout_data,
                        merchant_context,
                    ))
                    .await?;
                }

                let config_single_connector_bool = retry::config_should_call_gsm_payout(
                    &*state.store,
                    merchant_context.get_merchant_account().get_id(),
                    PayoutRetryType::SingleConnector,
                )
                .await;

                if config_single_connector_bool && payout_data.should_call_gsm() {
                    Box::pin(retry::do_gsm_single_connector_actions(
                        state,
                        connector_data,
                        payout_data,
                        merchant_context,
                    ))
                    .await?;
                }
            }

            Ok(())
        }
        _ => Err(errors::ApiErrorResponse::InternalServerError).attach_printable({
            "only PreDetermined and Retryable ConnectorCallTypes are supported".to_string()
        })?,
    }
}

#[cfg(feature = "v1")]
#[instrument(skip_all)]
pub async fn payouts_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_data: &mut PayoutData,
    routing_algorithm: Option<serde_json::Value>,
    eligible_connectors: Option<Vec<api_enums::PayoutConnectors>>,
) -> RouterResult<()> {
    let payout_attempt = &payout_data.payout_attempt;

    // Form connector data
    let connector_call_type = get_connector_choice(
        state,
        merchant_context,
        payout_attempt.connector.clone(),
        routing_algorithm,
        payout_data,
        eligible_connectors,
    )
    .await?;

    // Call connector steps
    Box::pin(make_connector_decision(
        state,
        merchant_context,
        connector_call_type,
        payout_data,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn payouts_core(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    payout_data: &mut PayoutData,
    routing_algorithm: Option<serde_json::Value>,
    eligible_connectors: Option<Vec<api_enums::PayoutConnectors>>,
) -> RouterResult<()> {
    todo!()
}

#[instrument(skip_all)]
pub async fn payouts_create_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payouts::PayoutCreateRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    // Validate create request
    let (payout_id, payout_method_data, profile_id, customer, payment_method) =
        validator::validate_create_request(&state, &merchant_context, &req).await?;

    // Create DB entries
    let mut payout_data = payout_create_db_entries(
        &state,
        &merchant_context,
        &req,
        &payout_id,
        &profile_id,
        payout_method_data.as_ref(),
        &state.locale,
        customer.as_ref(),
        payment_method.clone(),
    )
    .await?;

    let payout_attempt = payout_data.payout_attempt.to_owned();
    let payout_type = payout_data.payouts.payout_type.to_owned();

    // Persist payout method data in temp locker
    if req.payout_method_data.is_some() {
        let customer_id = payout_data
            .payouts
            .customer_id
            .clone()
            .get_required_value("customer_id when payout_method_data is provided")?;
        payout_data.payout_method_data = helpers::make_payout_method_data(
            &state,
            req.payout_method_data.as_ref(),
            payout_attempt.payout_token.as_deref(),
            &customer_id,
            &payout_attempt.merchant_id,
            payout_type,
            merchant_context.get_merchant_key_store(),
            Some(&mut payout_data),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;
    }

    if let Some(true) = payout_data.payouts.confirm {
        payouts_core(
            &state,
            &merchant_context,
            &mut payout_data,
            req.routing.clone(),
            req.connector.clone(),
        )
        .await?
    };

    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
}

#[instrument(skip_all)]
pub async fn payouts_confirm_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payouts::PayoutCreateRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    let mut payout_data = Box::pin(make_payout_data(
        &state,
        &merchant_context,
        None,
        &payouts::PayoutRequest::PayoutCreateRequest(Box::new(req.to_owned())),
        &state.locale,
    ))
    .await?;
    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;

    helpers::validate_payout_status_against_not_allowed_statuses(
        status,
        &[
            storage_enums::PayoutStatus::Cancelled,
            storage_enums::PayoutStatus::Success,
            storage_enums::PayoutStatus::Failed,
            storage_enums::PayoutStatus::Pending,
            storage_enums::PayoutStatus::Ineligible,
            storage_enums::PayoutStatus::RequiresFulfillment,
            storage_enums::PayoutStatus::RequiresVendorAccountCreation,
        ],
        "confirm",
    )?;

    helpers::update_payouts_and_payout_attempt(&mut payout_data, &merchant_context, &req, &state)
        .await?;

    let db = &*state.store;

    payout_data.payout_link = payout_data
        .payout_link
        .clone()
        .async_map(|pl| async move {
            let payout_link_update = storage::PayoutLinkUpdate::StatusUpdate {
                link_status: PayoutLinkStatus::Submitted,
            };
            db.update_payout_link(pl, payout_link_update)
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Error updating payout links in db")
        })
        .await
        .transpose()?;

    payouts_core(
        &state,
        &merchant_context,
        &mut payout_data,
        req.routing.clone(),
        req.connector.clone(),
    )
    .await?;

    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
}

pub async fn payouts_update_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payouts::PayoutCreateRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    let payout_id = req.payout_id.clone().get_required_value("payout_id")?;
    let mut payout_data = Box::pin(make_payout_data(
        &state,
        &merchant_context,
        None,
        &payouts::PayoutRequest::PayoutCreateRequest(Box::new(req.to_owned())),
        &state.locale,
    ))
    .await?;

    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;

    // Verify update feasibility
    if helpers::is_payout_terminal_state(status) || helpers::is_payout_initiated(status) {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "Payout {} cannot be updated for status {status}",
                payout_id.get_string_repr()
            ),
        }));
    }
    helpers::update_payouts_and_payout_attempt(&mut payout_data, &merchant_context, &req, &state)
        .await?;
    let payout_attempt = payout_data.payout_attempt.to_owned();

    if (req.connector.is_none(), payout_attempt.connector.is_some()) != (true, true) {
        // if the connector is not updated but was provided during payout create
        payout_data.payout_attempt.connector = None;
        payout_data.payout_attempt.routing_info = None;
    };

    // Update payout method data in temp locker
    if req.payout_method_data.is_some() {
        let customer_id = payout_data
            .payouts
            .customer_id
            .clone()
            .get_required_value("customer_id when payout_method_data is provided")?;
        payout_data.payout_method_data = helpers::make_payout_method_data(
            &state,
            req.payout_method_data.as_ref(),
            payout_attempt.payout_token.as_deref(),
            &customer_id,
            &payout_attempt.merchant_id,
            payout_data.payouts.payout_type,
            merchant_context.get_merchant_key_store(),
            Some(&mut payout_data),
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;
    }

    if let Some(true) = payout_data.payouts.confirm {
        payouts_core(
            &state,
            &merchant_context,
            &mut payout_data,
            req.routing.clone(),
            req.connector.clone(),
        )
        .await?;
    }

    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
}

#[cfg(all(feature = "payouts", feature = "v1"))]
#[instrument(skip_all)]
pub async fn payouts_retrieve_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id: Option<id_type::ProfileId>,
    req: payouts::PayoutRetrieveRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    let mut payout_data = Box::pin(make_payout_data(
        &state,
        &merchant_context,
        profile_id,
        &payouts::PayoutRequest::PayoutRetrieveRequest(req.to_owned()),
        &state.locale,
    ))
    .await?;
    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;

    if matches!(req.force_sync, Some(true)) && helpers::should_call_retrieve(status) {
        // Form connector data
        let connector_call_type = get_connector_choice(
            &state,
            &merchant_context,
            payout_attempt.connector.clone(),
            None,
            &mut payout_data,
            None,
        )
        .await?;

        complete_payout_retrieve(
            &state,
            &merchant_context,
            connector_call_type,
            &mut payout_data,
        )
        .await?;
    }

    Ok(services::ApplicationResponse::Json(
        response_handler(&state, &merchant_context, &payout_data).await?,
    ))
}

#[instrument(skip_all)]
pub async fn payouts_cancel_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payouts::PayoutActionRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    let mut payout_data = Box::pin(make_payout_data(
        &state,
        &merchant_context,
        None,
        &payouts::PayoutRequest::PayoutActionRequest(req.to_owned()),
        &state.locale,
    ))
    .await?;

    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;

    // Verify if cancellation can be triggered
    if helpers::is_payout_terminal_state(status) {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "Payout {:?} cannot be cancelled for status {}",
                payout_attempt.payout_id, status
            ),
        }));

    // Make local cancellation
    } else if helpers::is_eligible_for_local_payout_cancellation(status) {
        let status = storage_enums::PayoutStatus::Cancelled;
        let updated_payout_attempt = storage::PayoutAttemptUpdate::StatusUpdate {
            connector_payout_id: payout_attempt.connector_payout_id.to_owned(),
            status,
            error_message: Some("Cancelled by user".to_string()),
            error_code: None,
            is_eligible: None,
            unified_code: None,
            unified_message: None,
        };
        payout_data.payout_attempt = state
            .store
            .update_payout_attempt(
                &payout_attempt,
                updated_payout_attempt,
                &payout_data.payouts,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error updating payout_attempt in db")?;
        payout_data.payouts = state
            .store
            .update_payout(
                &payout_data.payouts,
                storage::PayoutsUpdate::StatusUpdate { status },
                &payout_data.payout_attempt,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error updating payouts in db")?;

    // Trigger connector's cancellation
    } else {
        // Form connector data
        let connector_data = match &payout_attempt.connector {
            Some(connector) => api::ConnectorData::get_payout_connector_by_name(
                &state.conf.connectors,
                connector,
                api::GetToken::Connector,
                payout_attempt.merchant_connector_id.clone(),
            )
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to get the connector data")?,
            _ => Err(errors::ApplicationError::InvalidConfigurationValueError(
                "Connector not found in payout_attempt - should not reach here".to_string(),
            ))
            .change_context(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "connector",
            })
            .attach_printable("Connector not found for payout cancellation")?,
        };

        Box::pin(cancel_payout(
            &state,
            &merchant_context,
            &connector_data,
            &mut payout_data,
        ))
        .await
        .attach_printable("Payout cancellation failed for given Payout request")?;
    }

    Ok(services::ApplicationResponse::Json(
        response_handler(&state, &merchant_context, &payout_data).await?,
    ))
}

#[instrument(skip_all)]
pub async fn payouts_fulfill_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payouts::PayoutActionRequest,
) -> RouterResponse<payouts::PayoutCreateResponse> {
    let mut payout_data = Box::pin(make_payout_data(
        &state,
        &merchant_context,
        None,
        &payouts::PayoutRequest::PayoutActionRequest(req.to_owned()),
        &state.locale,
    ))
    .await?;

    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;

    // Verify if fulfillment can be triggered
    if helpers::is_payout_terminal_state(status)
        || status != api_enums::PayoutStatus::RequiresFulfillment
    {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "Payout {:?} cannot be fulfilled for status {}",
                payout_attempt.payout_id, status
            ),
        }));
    }

    // Form connector data
    let connector_data = match &payout_attempt.connector {
        Some(connector) => api::ConnectorData::get_payout_connector_by_name(
            &state.conf.connectors,
            connector,
            api::GetToken::Connector,
            payout_attempt.merchant_connector_id.clone(),
        )
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get the connector data")?,
        _ => Err(errors::ApplicationError::InvalidConfigurationValueError(
            "Connector not found in payout_attempt - should not reach here.".to_string(),
        ))
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "connector",
        })
        .attach_printable("Connector not found for payout fulfillment")?,
    };

    helpers::fetch_payout_method_data(&state, &mut payout_data, &connector_data, &merchant_context)
        .await?;
    Box::pin(fulfill_payout(
        &state,
        &merchant_context,
        &connector_data,
        &mut payout_data,
    ))
    .await
    .attach_printable("Payout fulfillment failed for given Payout request")?;

    trigger_webhook_and_handle_response(&state, &merchant_context, &payout_data).await
}

#[cfg(all(feature = "olap", feature = "v2"))]
pub async fn payouts_list_core(
    _state: SessionState,
    _merchant_context: domain::MerchantContext,
    _profile_id_list: Option<Vec<id_type::ProfileId>>,
    _constraints: payouts::PayoutListConstraints,
) -> RouterResponse<payouts::PayoutListResponse> {
    todo!()
}

#[cfg(all(feature = "olap", feature = "v1"))]
pub async fn payouts_list_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
    constraints: payouts::PayoutListConstraints,
) -> RouterResponse<payouts::PayoutListResponse> {
    validator::validate_payout_list_request(&constraints)?;
    let merchant_id = merchant_context.get_merchant_account().get_id();
    let db = state.store.as_ref();
    let payouts = helpers::filter_by_constraints(
        db,
        &constraints,
        merchant_id,
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?;
    let payouts = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, payouts);

    let mut pi_pa_tuple_vec = PayoutActionData::new();

    for payout in payouts {
        match db
            .find_payout_attempt_by_merchant_id_payout_attempt_id(
                merchant_id,
                &utils::get_payout_attempt_id(
                    payout.payout_id.get_string_repr(),
                    payout.attempt_count,
                ),
                storage_enums::MerchantStorageScheme::PostgresOnly,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
        {
            Ok(payout_attempt) => {
                let domain_customer = match payout.customer_id.clone() {
                    #[cfg(feature = "v1")]
                    Some(customer_id) => db
                        .find_customer_by_customer_id_merchant_id(
                            &(&state).into(),
                            &customer_id,
                            merchant_id,
                            merchant_context.get_merchant_key_store(),
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .map_err(|err| {
                            let err_msg = format!(
                                "failed while fetching customer for customer_id - {customer_id:?}",
                            );
                            logger::warn!(?err, err_msg);
                        })
                        .ok(),
                    _ => None,
                };

                let payout_id_as_payment_id_type =
                    id_type::PaymentId::wrap(payout.payout_id.get_string_repr().to_string())
                        .change_context(errors::ApiErrorResponse::InvalidRequestData {
                            message: "payout_id contains invalid data".to_string(),
                        })
                        .attach_printable("Error converting payout_id to PaymentId type")?;

                let payment_addr = payment_helpers::create_or_find_address_for_payment_by_request(
                    &state,
                    None,
                    payout.address_id.as_deref(),
                    merchant_id,
                    payout.customer_id.as_ref(),
                    merchant_context.get_merchant_key_store(),
                    &payout_id_as_payment_id_type,
                    merchant_context.get_merchant_account().storage_scheme,
                )
                .await
                .transpose()
                .and_then(|addr| {
                    addr.map_err(|err| {
                        let err_msg = format!(
                            "billing_address missing for address_id : {:?}",
                            payout.address_id
                        );
                        logger::warn!(?err, err_msg);
                    })
                    .ok()
                    .as_ref()
                    .map(domain_models::address::Address::from)
                    .map(payment_enums::Address::from)
                });

                pi_pa_tuple_vec.push((
                    payout.to_owned(),
                    payout_attempt.to_owned(),
                    domain_customer,
                    payment_addr,
                ));
            }
            Err(err) => {
                let err_msg = format!(
                    "failed while fetching payout_attempt for payout_id - {:?}",
                    payout.payout_id
                );
                logger::warn!(?err, err_msg);
            }
        }
    }

    let data: Vec<api::PayoutCreateResponse> = pi_pa_tuple_vec
        .into_iter()
        .map(ForeignFrom::foreign_from)
        .collect();

    Ok(services::ApplicationResponse::Json(
        api::PayoutListResponse {
            size: data.len(),
            data,
            total_count: None,
        },
    ))
}

#[cfg(feature = "olap")]
pub async fn payouts_filtered_list_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
    filters: payouts::PayoutListFilterConstraints,
) -> RouterResponse<payouts::PayoutListResponse> {
    let limit = &filters.limit;
    validator::validate_payout_list_request_for_joins(*limit)?;
    let db = state.store.as_ref();
    let constraints = filters.clone().into();
    let list: Vec<(
        storage::Payouts,
        storage::PayoutAttempt,
        Option<diesel_models::Customer>,
        Option<diesel_models::Address>,
    )> = db
        .filter_payouts_and_attempts(
            merchant_context.get_merchant_account().get_id(),
            &constraints,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?;
    let list = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, list);
    let data: Vec<api::PayoutCreateResponse> =
        join_all(list.into_iter().map(|(p, pa, customer, address)| async {
            let customer: Option<domain::Customer> = customer
                .async_and_then(|cust| async {
                    domain::Customer::convert_back(
                        &(&state).into(),
                        cust,
                        &(merchant_context.get_merchant_key_store().clone()).key,
                        merchant_context
                            .get_merchant_key_store()
                            .merchant_id
                            .clone()
                            .into(),
                    )
                    .await
                    .map_err(|err| {
                        let msg = format!("failed to convert customer for id: {:?}", p.customer_id);
                        logger::warn!(?err, msg);
                    })
                    .ok()
                })
                .await;

            let payout_addr: Option<payment_enums::Address> = address
                .async_and_then(|addr| async {
                    domain::Address::convert_back(
                        &(&state).into(),
                        addr,
                        &(merchant_context.get_merchant_key_store().clone()).key,
                        merchant_context
                            .get_merchant_key_store()
                            .merchant_id
                            .clone()
                            .into(),
                    )
                    .await
                    .map(ForeignFrom::foreign_from)
                    .map_err(|err| {
                        let msg = format!("failed to convert address for id: {:?}", p.address_id);
                        logger::warn!(?err, msg);
                    })
                    .ok()
                })
                .await;

            Some((p, pa, customer, payout_addr))
        }))
        .await
        .into_iter()
        .flatten()
        .map(ForeignFrom::foreign_from)
        .collect();

    let active_payout_ids = db
        .filter_active_payout_ids_by_constraints(
            merchant_context.get_merchant_account().get_id(),
            &constraints,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to filter active payout ids based on the constraints")?;

    let total_count = db
        .get_total_count_of_filtered_payouts(
            merchant_context.get_merchant_account().get_id(),
            &active_payout_ids,
            filters.connector.clone(),
            filters.currency.clone(),
            filters.status.clone(),
            filters.payout_method.clone(),
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable_lazy(|| {
            format!(
                "Failed to fetch total count of filtered payouts for the given constraints - {filters:?}",

            )
        })?;

    Ok(services::ApplicationResponse::Json(
        api::PayoutListResponse {
            size: data.len(),
            data,
            total_count: Some(total_count),
        },
    ))
}

#[cfg(feature = "olap")]
pub async fn payouts_list_available_filters_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
    time_range: common_utils::types::TimeRange,
) -> RouterResponse<api::PayoutListFilters> {
    let db = state.store.as_ref();
    let payouts = db
        .filter_payouts_by_time_range_constraints(
            merchant_context.get_merchant_account().get_id(),
            &time_range,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

    let payouts = core_utils::filter_objects_based_on_profile_id_list(profile_id_list, payouts);

    let filters = db
        .get_filters_for_payouts(
            payouts.as_slice(),
            merchant_context.get_merchant_account().get_id(),
            storage_enums::MerchantStorageScheme::PostgresOnly,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

    Ok(services::ApplicationResponse::Json(
        api::PayoutListFilters {
            connector: filters.connector,
            currency: filters.currency,
            status: filters.status,
            payout_method: filters.payout_method,
        },
    ))
}

// ********************************************** HELPERS **********************************************
pub async fn call_connector_payout(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    let payout_attempt = &payout_data.payout_attempt.to_owned();
    let payouts = &payout_data.payouts.to_owned();

    // fetch merchant connector account if not present
    if payout_data.merchant_connector_account.is_none()
        || payout_data.payout_attempt.merchant_connector_id.is_none()
    {
        let merchant_connector_account = get_mca_from_profile_id(
            state,
            merchant_context,
            &payout_data.profile_id,
            &connector_data.connector_name.to_string(),
            payout_attempt
                .merchant_connector_id
                .clone()
                .or(connector_data.merchant_connector_id.clone())
                .as_ref(),
        )
        .await?;
        payout_data.payout_attempt.merchant_connector_id = merchant_connector_account.get_mca_id();
        payout_data.merchant_connector_account = Some(merchant_connector_account);
    }

    // update connector_name
    if payout_data.payout_attempt.connector.is_none()
        || payout_data.payout_attempt.connector != Some(connector_data.connector_name.to_string())
    {
        payout_data.payout_attempt.connector = Some(connector_data.connector_name.to_string());
        let updated_payout_attempt = storage::PayoutAttemptUpdate::UpdateRouting {
            connector: connector_data.connector_name.to_string(),
            routing_info: payout_data.payout_attempt.routing_info.clone(),
            merchant_connector_id: payout_data.payout_attempt.merchant_connector_id.clone(),
        };
        let db = &*state.store;
        payout_data.payout_attempt = db
            .update_payout_attempt(
                &payout_data.payout_attempt,
                updated_payout_attempt,
                payouts,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error updating routing info in payout_attempt")?;
    };

    // Fetch / store payout_method_data
    if payout_data.payout_method_data.is_none() || payout_attempt.payout_token.is_none() {
        helpers::fetch_payout_method_data(state, payout_data, connector_data, merchant_context)
            .await?;
    }
    // Eligibility flow
    Box::pin(complete_payout_eligibility(
        state,
        merchant_context,
        connector_data,
        payout_data,
    ))
    .await?;
    // Create customer flow
    Box::pin(complete_create_recipient(
        state,
        merchant_context,
        connector_data,
        payout_data,
    ))
    .await?;
    // Create customer's disbursement account flow
    Box::pin(complete_create_recipient_disburse_account(
        state,
        merchant_context,
        connector_data,
        payout_data,
    ))
    .await?;
    // Payout creation flow
    Box::pin(complete_create_payout(
        state,
        merchant_context,
        connector_data,
        payout_data,
    ))
    .await?;

    // Auto fulfillment flow
    let status = payout_data.payout_attempt.status;
    if payouts.auto_fulfill && status == storage_enums::PayoutStatus::RequiresFulfillment {
        Box::pin(fulfill_payout(
            state,
            merchant_context,
            connector_data,
            payout_data,
        ))
        .await
        .attach_printable("Payout fulfillment failed for given Payout request")?;
    }

    Ok(())
}

pub async fn complete_create_recipient(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    if !payout_data.should_terminate
        && matches!(
            payout_data.payout_attempt.status,
            common_enums::PayoutStatus::RequiresCreation
                | common_enums::PayoutStatus::RequiresConfirmation
                | common_enums::PayoutStatus::RequiresPayoutMethodData
        )
        && connector_data
            .connector_name
            .supports_create_recipient(payout_data.payouts.payout_type)
    {
        Box::pin(create_recipient(
            state,
            merchant_context,
            connector_data,
            payout_data,
        ))
        .await
        .attach_printable("Creation of customer failed")?;
    }

    Ok(())
}

#[cfg(feature = "v1")]
pub async fn create_recipient(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector_data: &api::ConnectorData,
    payout_data: &mut PayoutData,
) -> RouterResult<()> {
    let customer_details = payout_data.customer_details.to_owned();
    let connector_name = connector_data.connector_name.to_string();

    // Create the connector label using {profile_id}_{connector_name}
    let connector_label = format!(
        "{}_{}",
        payout_data.profile_id.get_string_repr(),
        connector_name
    );
    let (should_call_connector, _connector_customer_id) =
        helpers::should_call_payout_connector_create_customer(
            state,
            connector_data,
            &customer_details,
            &connector_label,
        );
    if should_call_connector {


// Struct: ToggleBlocklistResponse
// File: crates/api_models/src/blocklist.rs
// Module: api_models
// Implementations: 1
// Traits: ApiEventMetric

pub struct ToggleBlocklistResponse


// Implementation: impl PaymentAttemptUpdate
// File: crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
// Module: hyperswitch_domain_models
// Methods: 2 total (2 public)

impl PaymentAttemptUpdate


// Struct: Chargebee
// File: crates/hyperswitch_connectors/src/connectors/chargebee.rs
// Module: hyperswitch_connectors
// Implementations: 26
// Traits: ConnectorCustomer, api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, api::subscriptions::Subscriptions, api::revenue_recovery::RevenueRecoveryRecordBack, api::subscriptions::SubscriptionCreate, ConnectorCommon, ConnectorValidation, api::subscriptions::GetSubscriptionPlansFlow, GetSubscriptionPlansV2, api::subscriptions::GetSubscriptionPlanPricesFlow, GetSubscriptionPlanPricesV2, api::subscriptions::GetSubscriptionEstimateFlow, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Chargebee


// Struct: NovalnetRawCardDetails
// File: crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NovalnetRawCardDetails


// Struct: AirwallexCardDetails
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AirwallexCardDetails


// Struct: PaymentMethodFilters
// File: crates/kgraph_utils/src/types.rs
// Module: kgraph_utils
// Implementations: 0

pub struct PaymentMethodFilters


// Struct: SiftPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/sift/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SiftPaymentsResponse


// Struct: Action
// File: crates/hyperswitch_connectors/src/connectors/xendit/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Action


// Struct: ConstraintGraphBuilder
// File: crates/hyperswitch_constraint_graph/src/builder.rs
// Module: hyperswitch_constraint_graph
// Implementations: 1

pub struct ConstraintGraphBuilder<V: ValueNode>


// Struct: RequestBuilder
// File: crates/common_utils/src/request.rs
// Module: common_utils
// Implementations: 2
// Traits: Default

pub struct RequestBuilder


// Module Structure
// File: crates/api_models/src/routing.rs
// Module: api_models

// Public exports:
pub use euclid::{
    dssa::types::EuclidAnalysable,
    frontend::{
        ast,
        dir::{DirKeyKind, EuclidDirFilter},
    },
};


            .map(|s| s.to_string());

        Ok(Self {
            amount: Amount {
                total: item.amount,
                currency: item.router_data.request.currency.to_string(),
            },
            order: Some(FiservOrderRequest { order_id }),
            transaction_details: TransactionDetails {
                capture_flag: Some(true),
                reversal_reason_code: None,
                merchant_transaction_id: Some(
                    item.router_data.connector_request_reference_id.clone(),
                ),
                operation_type: Some(OperationType::Capture),
            },
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: Some(session.terminal_id),
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item
                    .router_data
                    .request
                    .connector_transaction_id
                    .to_string(),
            },
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservSyncRequest {
    merchant_details: MerchantDetails,
    reference_transaction_details: ReferenceTransactionDetails,
}

impl TryFrom<&types::PaymentsSyncRouterData> for FiservSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.connector_auth_type)?;
        Ok(Self {
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: None,
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item
                    .request
                    .get_connector_transaction_id()
                    .change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
            },
        })
    }
}

impl TryFrom<&types::RefundSyncRouterData> for FiservSyncRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefundSyncRouterData) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.connector_auth_type)?;
        Ok(Self {
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: None,
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item
                    .request
                    .connector_refund_id
                    .clone()
                    .ok_or(errors::ConnectorError::RequestEncodingFailed)?,
            },
        })
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FiservRefundRequest {
    amount: Amount,
    merchant_details: MerchantDetails,
    reference_transaction_details: ReferenceTransactionDetails,
}

impl<F> TryFrom<&FiservRouterData<&types::RefundsRouterData<F>>> for FiservRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &FiservRouterData<&types::RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        let auth: FiservAuthType = FiservAuthType::try_from(&item.router_data.connector_auth_type)?;
        let metadata = item
            .router_data
            .connector_meta_data
            .clone()
            .ok_or(errors::ConnectorError::RequestEncodingFailed)?;
        let session: FiservSessionObject = metadata
            .expose()
            .parse_value("FiservSessionObject")
            .change_context(errors::ConnectorError::InvalidConnectorConfig {
                config: "Merchant connector account metadata",
            })?;
        Ok(Self {
            amount: Amount {
                total: item.amount,
                currency: item.router_data.request.currency.to_string(),
            },
            merchant_details: MerchantDetails {
                merchant_id: auth.merchant_account,
                terminal_id: Some(session.terminal_id),
            },
            reference_transaction_details: ReferenceTransactionDetails {
                reference_transaction_id: item
                    .router_data
                    .request
                    .connector_transaction_id
                    .to_string(),
            },
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    pub gateway_response: GatewayResponse,
    pub payment_receipt: PaymentReceipt,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>>
    for types::RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item
                    .response
                    .gateway_response
                    .transaction_processing_details
                    .transaction_id,
                refund_status: enums::RefundStatus::from(
                    item.response.gateway_response.transaction_state,
                ),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, FiservSyncResponse>>
    for types::RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, FiservSyncResponse>,
    ) -> Result<Self, Self::Error> {
        let gateway_resp = item
            .response
            .sync_responses
            .first()
            .ok_or(errors::ConnectorError::ResponseHandlingFailed)?;
        let transaction_id = match gateway_resp {
            FiservPaymentsResponse::Charges(res) => {
                &res.gateway_response
                    .transaction_processing_details
                    .transaction_id
            }
            FiservPaymentsResponse::Checkout(res) => {
                &res.gateway_response
                    .transaction_processing_details
                    .transaction_id
            }
        };

        let transaction_state = match gateway_resp {
            FiservPaymentsResponse::Charges(res) => &res.gateway_response.transaction_state,
            FiservPaymentsResponse::Checkout(res) => &res.gateway_response.transaction_state,
        };
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: transaction_id.clone(),
                refund_status: enums::RefundStatus::from(transaction_state.clone()),
            }),
            ..item.data
        })
    }
}


// Struct: AccountIdConfigForCard
// File: crates/connector_configs/src/connector.rs
// Module: connector_configs
// Implementations: 0

pub struct AccountIdConfigForCard


// Implementation: impl api::PaymentsCompleteAuthorize for for Braintree
// File: crates/hyperswitch_connectors/src/connectors/braintree.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentsCompleteAuthorize for for Braintree


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

impl PaymentSession for for Threedsecureio


// Trait: GetTracker
// File: crates/router/src/core/payments/operations.rs
// Module: router

pub trait GetTracker<F: Clone, D, R>: Send


// Function: is_updatable
// File: crates/router/src/services/authorization/roles.rs
// Module: router

pub fn is_updatable(&self) -> bool


// File: crates/hyperswitch_connectors/src/connectors/loonio.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 1

pub mod transformers;

use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts as api_consts, errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use lazy_static::lazy_static;
use masking::{ExposeInterface, Mask};
use transformers as loonio;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Loonio {
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}

impl Loonio {
    pub fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
        }
    }
}

impl api::Payment for Loonio {}
impl api::PaymentSession for Loonio {}
impl api::ConnectorAccessToken for Loonio {}
impl api::MandateSetup for Loonio {}
impl api::PaymentAuthorize for Loonio {}
impl api::PaymentSync for Loonio {}
impl api::PaymentCapture for Loonio {}
impl api::PaymentVoid for Loonio {}
impl api::Refund for Loonio {}
impl api::RefundExecute for Loonio {}
impl api::RefundSync for Loonio {}
impl api::PaymentToken for Loonio {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Loonio
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Loonio
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

impl ConnectorCommon for Loonio {
    fn id(&self) -> &'static str {
        "loonio"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.loonio.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = loonio::LoonioAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![
            (
                headers::MERCHANTID.to_string(),
                auth.merchant_id.expose().into_masked(),
            ),
            (
                headers::MERCHANT_TOKEN.to_string(),
                auth.merchant_token.expose().into_masked(),
            ),
        ])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: loonio::LoonioErrorResponse = res
            .response
            .parse_struct("LoonioErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response
                .error_code
                .clone()
                .unwrap_or(api_consts::NO_ERROR_CODE.to_string()),
            message: response.message.clone(),
            reason: Some(response.message.clone()),
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Loonio {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Loonio {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Loonio {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Loonio {}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Loonio {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}api/v1/transactions/incoming/payment_form",
            self.base_url(connectors),
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = loonio::LoonioRouterData::from((amount, req));
        let connector_req = loonio::LoonioPaymentRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: loonio::LoonioPaymentsResponse = res
            .response
            .parse_struct("Loonio PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Loonio {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let base_url = self.base_url(connectors);
        let connector_payment_id = req.connector_request_reference_id.clone();
        Ok(format!(
            "{base_url}api/v1/transactions/{connector_payment_id}/details"
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: loonio::LoonioTransactionSyncResponse = res
            .response
            .parse_struct("loonio LoonioTransactionSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Loonio {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: loonio::LoonioPaymentsResponse = res
            .response
            .parse_struct("Loonio PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Loonio {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Loonio {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = loonio::LoonioRouterData::from((refund_amount, req));
        let connector_req = loonio::LoonioRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::RefundExecuteType::get_request_body(
                self, req, connectors,
            )?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: loonio::RefundResponse =
            res.response
                .parse_struct("loonio RefundResponse")
                .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Loonio {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(types::RefundSyncType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: loonio::RefundResponse = res
            .response
            .parse_struct("loonio RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Loonio {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

lazy_static! {
    static ref LOONIO_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {
        let supported_capture_methods = vec![enums::CaptureMethod::Automatic];

        let mut gigadat_supported_payment_methods = SupportedPaymentMethods::new();
        gigadat_supported_payment_methods.add(
            enums::PaymentMethod::BankRedirect,
            enums::PaymentMethodType::Interac,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::NotSupported,
                refunds: common_enums::FeatureStatus::Supported,
                supported_capture_methods,
                specific_features: None,
            },
        );

        gigadat_supported_payment_methods
    };
    static ref LOONIO_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Loonio",
        description: "Loonio is a payment processing platform that provides APIs for deposits and payouts via methods like Interac, PIX, EFT, and credit cards, with webhook support and transaction sync for real-time and manual status tracking.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };
    static ref LOONIO_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> = Vec::new();
}

impl ConnectorSpecifications for Loonio {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*LOONIO_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*LOONIO_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&*LOONIO_SUPPORTED_WEBHOOK_FLOWS)
    }
}


// Function: validate_maximum_refund_against_payment_attempt
// File: crates/router/src/core/utils/refunds_validator.rs
// Module: router

pub fn validate_maximum_refund_against_payment_attempt(
    all_refunds: &[diesel_refund::Refund],
    refund_max_attempts: usize,
) -> CustomResult<(), RefundValidationError>


// Implementation: impl api::Refund for for Opennode
// File: crates/hyperswitch_connectors/src/connectors/opennode.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Refund for for Opennode


// Struct: BluecodeMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/bluecode/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BluecodeMetadataObject


// Implementation: impl api::PaymentToken for for Wellsfargo
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Wellsfargo


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

pub fn signup_with_merchant_id(
    state: SessionState,
    request: user_api::SignUpWithMerchantIdRequest,
    auth_id: Option<String>,
    theme_id: Option<String>,
) -> UserResponse<user_api::SignUpWithMerchantIdResponse>


// Implementation: impl fmt::Display for for AnalysisError
// File: crates/euclid/src/dssa/types.rs
// Module: euclid
// Methods: 1 total (0 public)

impl fmt::Display for for AnalysisError


// Implementation: impl MerchantKeyStore
// File: crates/diesel_models/src/query/merchant_key_store.rs
// Module: diesel_models
// Methods: 4 total (0 public)

impl MerchantKeyStore


// File: crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs
// Module: hyperswitch_domain_models
// Public structs: 2

#[derive(Debug, Clone)]
pub struct Execute;
#[derive(Debug, Clone)]
pub struct RSync;


// Struct: WeChatPay
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct WeChatPay


// Function: from_storage
// File: crates/router/src/services/kafka/refund_event.rs
// Module: router

pub fn from_storage(refund: &'a Refund) -> Self


// Function: call_frm_service
// File: crates/router/src/core/fraud_check.rs
// Module: router

pub fn call_frm_service<D: Clone, F, Req, OperationData>(
    state: &SessionState,
    payment_data: &OperationData,
    frm_data: &mut FrmData,
    merchant_context: &domain::MerchantContext,
    customer: &Option<domain::Customer>,
) -> RouterResult<oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>>
where
    F: Send + Clone,

    OperationData: payments::OperationSessionGetters<D> + Send + Sync + Clone,

    // To create connector flow specific interface data
    FrmData: ConstructFlowSpecificData<F, Req, frm_types::FraudCheckResponseData>,
    oss_types::RouterData<F, Req, frm_types::FraudCheckResponseData>: FeatureFrm<F, Req> + Send,

    // To construct connector flow specific api
    dyn Connector: services::api::ConnectorIntegration<F, Req, frm_types::FraudCheckResponseData>,


// File: crates/analytics/src/disputes.rs
// Module: analytics

pub mod accumulators;
mod core;
pub mod filters;
pub mod metrics;
pub mod types;
pub use accumulators::{DisputeMetricAccumulator, DisputeMetricsAccumulator};

pub trait DisputeAnalytics: metrics::DisputeMetricAnalytics {}
pub use self::core::{get_filters, get_metrics};


// Struct: CustomerDefaultPaymentMethodResponse
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Implementations: 0

pub struct CustomerDefaultPaymentMethodResponse


// Function: validate_success_transaction
// File: crates/router/src/core/utils/refunds_validator.rs
// Module: router

pub fn validate_success_transaction(
    transaction: &storage::PaymentAttempt,
) -> CustomResult<(), RefundValidationError>


// Struct: ProcessingInformationResponse
// File: crates/hyperswitch_connectors/src/connectors/barclaycard/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ProcessingInformationResponse


// Struct: RedsysErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RedsysErrorResponse


// Function: get_payout_connector_by_name
// File: crates/router/src/types/api/connector_mapping.rs
// Module: router

pub fn get_payout_connector_by_name(
        _connectors: &Connectors,
        name: &str,
        connector_type: GetToken,
        connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
    ) -> CustomResult<Self, errors::ApiErrorResponse>


// Struct: NordeaConnectorMetadataObject
// File: crates/hyperswitch_connectors/src/connectors/nordea/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct NordeaConnectorMetadataObject


// Implementation: impl TempLockerCardSupport
// File: crates/router/src/core/payment_methods/cards.rs
// Module: router
// Methods: 1 total (0 public)

impl TempLockerCardSupport


// Function: customers_update
// File: crates/openapi/src/routes/customers.rs
// Module: openapi

pub fn customers_update()


// Struct: BreadpayErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/breadpay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BreadpayErrorResponse


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

impl api::RefundSync for for Tsys


// Struct: ErrorResponseStatus
// File: crates/hyperswitch_connectors/src/connectors/forte/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ErrorResponseStatus


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

impl api::PaymentAuthorize for for Bitpay


// File: crates/pm_auth/src/types/api.rs
// Module: pm_auth
// Public structs: 1

pub mod auth_service;

use std::fmt::Debug;

use common_utils::{
    errors::CustomResult,
    request::{Request, RequestContent},
};
use masking::Maskable;

use crate::{
    core::errors::ConnectorError,
    types::{
        self as auth_types,
        api::auth_service::{AuthService, PaymentInitiation},
    },
};

#[async_trait::async_trait]
pub trait ConnectorIntegration<T, Req, Resp>: ConnectorIntegrationAny<T, Req, Resp> + Sync {
    fn get_headers(
        &self,
        _req: &super::PaymentAuthRouterData<T, Req, Resp>,
        _connectors: &auth_types::PaymentMethodAuthConnectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(vec![])
    }

    fn get_content_type(&self) -> &'static str {
        mime::APPLICATION_JSON.essence_str()
    }

    fn get_url(
        &self,
        _req: &super::PaymentAuthRouterData<T, Req, Resp>,
        _connectors: &auth_types::PaymentMethodAuthConnectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(String::new())
    }

    fn get_request_body(
        &self,
        _req: &super::PaymentAuthRouterData<T, Req, Resp>,
    ) -> CustomResult<RequestContent, ConnectorError> {
        Ok(RequestContent::Json(Box::new(serde_json::json!(r#"{}"#))))
    }

    fn build_request(
        &self,
        _req: &super::PaymentAuthRouterData<T, Req, Resp>,
        _connectors: &auth_types::PaymentMethodAuthConnectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(None)
    }

    fn handle_response(
        &self,
        data: &super::PaymentAuthRouterData<T, Req, Resp>,
        _res: auth_types::Response,
    ) -> CustomResult<super::PaymentAuthRouterData<T, Req, Resp>, ConnectorError>
    where
        T: Clone,
        Req: Clone,
        Resp: Clone,
    {
        Ok(data.clone())
    }

    fn get_error_response(
        &self,
        _res: auth_types::Response,
    ) -> CustomResult<auth_types::ErrorResponse, ConnectorError> {
        Ok(auth_types::ErrorResponse::get_not_implemented())
    }

    fn get_5xx_error_response(
        &self,
        res: auth_types::Response,
    ) -> CustomResult<auth_types::ErrorResponse, ConnectorError> {
        let error_message = match res.status_code {
            500 => "internal_server_error",
            501 => "not_implemented",
            502 => "bad_gateway",
            503 => "service_unavailable",
            504 => "gateway_timeout",
            505 => "http_version_not_supported",
            506 => "variant_also_negotiates",
            507 => "insufficient_storage",
            508 => "loop_detected",
            510 => "not_extended",
            511 => "network_authentication_required",
            _ => "unknown_error",
        };
        Ok(auth_types::ErrorResponse {
            code: res.status_code.to_string(),
            message: error_message.to_string(),
            reason: String::from_utf8(res.response.to_vec()).ok(),
            status_code: res.status_code,
        })
    }
}

pub trait ConnectorCommonExt<Flow, Req, Resp>:
    ConnectorCommon + ConnectorIntegration<Flow, Req, Resp>
{
    fn build_headers(
        &self,
        _req: &auth_types::PaymentAuthRouterData<Flow, Req, Resp>,
        _connectors: &auth_types::PaymentMethodAuthConnectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(Vec::new())
    }
}

pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
    Box<&'a (dyn ConnectorIntegration<T, Req, Resp> + Send + Sync)>;

pub trait ConnectorIntegrationAny<T, Req, Resp>: Send + Sync + 'static {
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp>;
}

impl<S, T, Req, Resp> ConnectorIntegrationAny<T, Req, Resp> for S
where
    S: ConnectorIntegration<T, Req, Resp>,
{
    fn get_connector_integration(&self) -> BoxedConnectorIntegration<'_, T, Req, Resp> {
        Box::new(self)
    }
}

pub trait AuthServiceConnector: AuthService + Send + Debug + PaymentInitiation {}

impl<T: Send + Debug + AuthService + PaymentInitiation> AuthServiceConnector for T {}

pub type BoxedPaymentAuthConnector = Box<&'static (dyn AuthServiceConnector + Sync)>;

#[derive(Clone, Debug)]
pub struct PaymentAuthConnectorData {
    pub connector: BoxedPaymentAuthConnector,
    pub connector_name: super::PaymentMethodAuthConnectors,
}

pub trait ConnectorCommon {
    fn id(&self) -> &'static str;

    fn get_auth_header(
        &self,
        _auth_type: &auth_types::ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(Vec::new())
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a auth_types::PaymentMethodAuthConnectors) -> &'a str;

    fn build_error_response(
        &self,
        res: auth_types::Response,
    ) -> CustomResult<auth_types::ErrorResponse, ConnectorError> {
        Ok(auth_types::ErrorResponse {
            status_code: res.status_code,
            code: crate::consts::NO_ERROR_CODE.to_string(),
            message: crate::consts::NO_ERROR_MESSAGE.to_string(),
            reason: None,
        })
    }
}


            force_3ds_challenge: self.force_3ds_challenge,
            force_3ds_challenge_trigger: self.force_3ds_challenge_trigger,
            is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
            extended_return_url: self.return_url,
            is_payment_id_from_merchant: self.is_payment_id_from_merchant,
            payment_channel: self.payment_channel,
            tax_status: self.tax_status,
            discount_amount: self.discount_amount,
            order_date: self.order_date,
            shipping_amount_tax: self.shipping_amount_tax,
            duty_amount: self.duty_amount,
            enable_partial_authorization: self.enable_partial_authorization,
            enable_overcapture: self.enable_overcapture,
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        storage_model: Self::DstType,
        key: &Secret<Vec<u8>>,
        key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        async {
            let decrypted_data = crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::BatchDecrypt(super::EncryptedPaymentIntent::to_encryptable(
                    super::EncryptedPaymentIntent {
                        billing_details: storage_model.billing_details,
                        shipping_details: storage_model.shipping_details,
                        customer_details: storage_model.customer_details,
                    },
                )),
                key_manager_identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_batchoperation())?;

            let data = super::EncryptedPaymentIntent::from_encryptable(decrypted_data)
                .change_context(common_utils::errors::CryptoError::DecodingFailed)
                .attach_printable("Invalid batch operation data")?;

            Ok::<Self, error_stack::Report<common_utils::errors::CryptoError>>(Self {
                payment_id: storage_model.payment_id,
                merchant_id: storage_model.merchant_id.clone(),
                status: storage_model.status,
                amount: storage_model.amount,
                currency: storage_model.currency,
                amount_captured: storage_model.amount_captured,
                customer_id: storage_model.customer_id,
                description: storage_model.description,
                return_url: storage_model
                    .extended_return_url
                    .or(storage_model.return_url), // fallback to legacy
                metadata: storage_model.metadata,
                connector_id: storage_model.connector_id,
                shipping_address_id: storage_model.shipping_address_id,
                billing_address_id: storage_model.billing_address_id,
                statement_descriptor_name: storage_model.statement_descriptor_name,
                statement_descriptor_suffix: storage_model.statement_descriptor_suffix,
                created_at: storage_model.created_at,
                modified_at: storage_model.modified_at,
                last_synced: storage_model.last_synced,
                setup_future_usage: storage_model.setup_future_usage,
                off_session: storage_model.off_session,
                client_secret: storage_model.client_secret,
                active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id),
                business_country: storage_model.business_country,
                business_label: storage_model.business_label,
                order_details: storage_model.order_details,
                allowed_payment_method_types: storage_model.allowed_payment_method_types,
                connector_metadata: storage_model.connector_metadata,
                feature_metadata: storage_model.feature_metadata,
                attempt_count: storage_model.attempt_count,
                profile_id: storage_model.profile_id,
                merchant_decision: storage_model.merchant_decision,
                payment_link_id: storage_model.payment_link_id,
                payment_confirm_source: storage_model.payment_confirm_source,
                updated_by: storage_model.updated_by,
                surcharge_applicable: storage_model.surcharge_applicable,
                request_incremental_authorization: storage_model.request_incremental_authorization,
                incremental_authorization_allowed: storage_model.incremental_authorization_allowed,
                authorization_count: storage_model.authorization_count,
                fingerprint_id: storage_model.fingerprint_id,
                session_expiry: storage_model.session_expiry,
                request_external_three_ds_authentication: storage_model
                    .request_external_three_ds_authentication,
                split_payments: storage_model.split_payments,
                frm_metadata: storage_model.frm_metadata,
                shipping_cost: storage_model.shipping_cost,
                tax_details: storage_model.tax_details,
                customer_details: data.customer_details,
                billing_details: data.billing_details,
                merchant_order_reference_id: storage_model.merchant_order_reference_id,
                shipping_details: data.shipping_details,
                is_payment_processor_token_flow: storage_model.is_payment_processor_token_flow,
                organization_id: storage_model.organization_id,
                skip_external_tax_calculation: storage_model.skip_external_tax_calculation,
                request_extended_authorization: storage_model.request_extended_authorization,
                psd2_sca_exemption_type: storage_model.psd2_sca_exemption_type,
                processor_merchant_id: storage_model
                    .processor_merchant_id
                    .unwrap_or(storage_model.merchant_id),
                created_by: storage_model
                    .created_by
                    .and_then(|created_by| created_by.parse::<CreatedBy>().ok()),
                force_3ds_challenge: storage_model.force_3ds_challenge,
                force_3ds_challenge_trigger: storage_model.force_3ds_challenge_trigger,
                is_iframe_redirection_enabled: storage_model.is_iframe_redirection_enabled,
                is_payment_id_from_merchant: storage_model.is_payment_id_from_merchant,
                payment_channel: storage_model.payment_channel,
                tax_status: storage_model.tax_status,
                discount_amount: storage_model.discount_amount,
                shipping_amount_tax: storage_model.shipping_amount_tax,
                duty_amount: storage_model.duty_amount,
                order_date: storage_model.order_date,
                enable_partial_authorization: storage_model.enable_partial_authorization,
                enable_overcapture: storage_model.enable_overcapture,
            })
        }
        .await
        .change_context(ValidationError::InvalidValue {
            message: "Failed while decrypting payment intent".to_string(),
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(DieselPaymentIntentNew {
            payment_id: self.payment_id,
            merchant_id: self.merchant_id,
            status: self.status,
            amount: self.amount,
            currency: self.currency,
            amount_captured: self.amount_captured,
            customer_id: self.customer_id,
            description: self.description,
            return_url: None, // deprecated
            metadata: self.metadata,
            connector_id: self.connector_id,
            shipping_address_id: self.shipping_address_id,
            billing_address_id: self.billing_address_id,
            statement_descriptor_name: self.statement_descriptor_name,
            statement_descriptor_suffix: self.statement_descriptor_suffix,
            created_at: self.created_at,
            modified_at: self.modified_at,
            last_synced: self.last_synced,
            setup_future_usage: self.setup_future_usage,
            off_session: self.off_session,
            client_secret: self.client_secret,
            active_attempt_id: self.active_attempt.get_id(),
            business_country: self.business_country,
            business_label: self.business_label,
            order_details: self.order_details,
            allowed_payment_method_types: self.allowed_payment_method_types,
            connector_metadata: self.connector_metadata,
            feature_metadata: self.feature_metadata,
            attempt_count: self.attempt_count,
            profile_id: self.profile_id,
            merchant_decision: self.merchant_decision,
            payment_link_id: self.payment_link_id,
            payment_confirm_source: self.payment_confirm_source,
            updated_by: self.updated_by,
            surcharge_applicable: self.surcharge_applicable,
            request_incremental_authorization: self.request_incremental_authorization,
            incremental_authorization_allowed: self.incremental_authorization_allowed,
            authorization_count: self.authorization_count,
            fingerprint_id: self.fingerprint_id,
            session_expiry: self.session_expiry,
            request_external_three_ds_authentication: self.request_external_three_ds_authentication,
            charges: None,
            split_payments: self.split_payments,
            frm_metadata: self.frm_metadata,
            customer_details: self.customer_details.map(Encryption::from),
            billing_details: self.billing_details.map(Encryption::from),
            merchant_order_reference_id: self.merchant_order_reference_id,
            shipping_details: self.shipping_details.map(Encryption::from),
            is_payment_processor_token_flow: self.is_payment_processor_token_flow,
            organization_id: self.organization_id,
            shipping_cost: self.shipping_cost,
            tax_details: self.tax_details,
            skip_external_tax_calculation: self.skip_external_tax_calculation,
            request_extended_authorization: self.request_extended_authorization,
            psd2_sca_exemption_type: self.psd2_sca_exemption_type,
            platform_merchant_id: None,
            processor_merchant_id: Some(self.processor_merchant_id),
            created_by: self.created_by.map(|cb| cb.to_string()),
            force_3ds_challenge: self.force_3ds_challenge,
            force_3ds_challenge_trigger: self.force_3ds_challenge_trigger,
            is_iframe_redirection_enabled: self.is_iframe_redirection_enabled,
            extended_return_url: self.return_url,
            is_payment_id_from_merchant: self.is_payment_id_from_merchant,
            payment_channel: self.payment_channel,
            tax_status: self.tax_status,
            discount_amount: self.discount_amount,
            order_date: self.order_date,
            shipping_amount_tax: self.shipping_amount_tax,
            duty_amount: self.duty_amount,
            enable_partial_authorization: self.enable_partial_authorization,
            enable_overcapture: self.enable_overcapture,
        })
    }
}


// Implementation: impl FraudCheckInterface for for KafkaStore
// File: crates/router/src/db.rs
// Module: router
// Methods: 4 total (0 public)

impl FraudCheckInterface for for KafkaStore


// Struct: RedsysSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RedsysSyncResponse