// File: crates/storage_impl/src/payouts/payout_attempt.rs
// Module: storage_impl

use std::str::FromStr;

use api_models::enums::PayoutConnectors;
use common_utils::{errors::CustomResult, ext_traits::Encode, fallback_reverse_lookup_not_found};
use diesel_models::{
    enums::MerchantStorageScheme,
    kv,
    payout_attempt::{
        PayoutAttempt as DieselPayoutAttempt, PayoutAttemptNew as DieselPayoutAttemptNew,
        PayoutAttemptUpdate as DieselPayoutAttemptUpdate,
    },
    reverse_lookup::ReverseLookup,
    ReverseLookupNew,
};
use error_stack::ResultExt;
use hyperswitch_domain_models::payouts::{
    payout_attempt::{
        PayoutAttempt, PayoutAttemptInterface, PayoutAttemptNew, PayoutAttemptUpdate,
        PayoutListFilters,
    },
    payouts::Payouts,
};
use redis_interface::HsetnxReply;
use router_env::{instrument, logger, tracing};

use crate::{
    diesel_error_to_data_error, errors,
    errors::RedisErrorExt,
    kv_router_store::KVRouterStore,
    lookup::ReverseLookupInterface,
    redis::kv_store::{decide_storage_scheme, kv_wrapper, KvOperation, Op, PartitionKey},
    utils::{self, pg_connection_read, pg_connection_write},
    DataModelExt, DatabaseStore,
};

#[async_trait::async_trait]
impl<T: DatabaseStore> PayoutAttemptInterface for KVRouterStore<T> {
    type Error = errors::StorageError;
    #[instrument(skip_all)]
    async fn insert_payout_attempt(
        &self,
        new_payout_attempt: PayoutAttemptNew,
        payouts: &Payouts,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPayoutAttempt>(
            self,
            storage_scheme,
            Op::Insert,
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .insert_payout_attempt(new_payout_attempt, payouts, storage_scheme)
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let merchant_id = new_payout_attempt.merchant_id.clone();
                let payout_attempt_id = new_payout_attempt.payout_id.clone();
                let key = PartitionKey::MerchantIdPayoutAttemptId {
                    merchant_id: &merchant_id,
                    payout_attempt_id: payout_attempt_id.get_string_repr(),
                };
                let key_str = key.to_string();
                let created_attempt = PayoutAttempt {
                    payout_attempt_id: new_payout_attempt.payout_attempt_id.clone(),
                    payout_id: new_payout_attempt.payout_id.clone(),
                    additional_payout_method_data: new_payout_attempt
                        .additional_payout_method_data
                        .clone(),
                    customer_id: new_payout_attempt.customer_id.clone(),
                    merchant_id: new_payout_attempt.merchant_id.clone(),
                    address_id: new_payout_attempt.address_id.clone(),
                    connector: new_payout_attempt.connector.clone(),
                    connector_payout_id: new_payout_attempt.connector_payout_id.clone(),
                    payout_token: new_payout_attempt.payout_token.clone(),
                    status: new_payout_attempt.status,
                    is_eligible: new_payout_attempt.is_eligible,
                    error_message: new_payout_attempt.error_message.clone(),
                    error_code: new_payout_attempt.error_code.clone(),
                    business_country: new_payout_attempt.business_country,
                    business_label: new_payout_attempt.business_label.clone(),
                    created_at: new_payout_attempt.created_at,
                    last_modified_at: new_payout_attempt.last_modified_at,
                    profile_id: new_payout_attempt.profile_id.clone(),
                    merchant_connector_id: new_payout_attempt.merchant_connector_id.clone(),
                    routing_info: new_payout_attempt.routing_info.clone(),
                    unified_code: new_payout_attempt.unified_code.clone(),
                    unified_message: new_payout_attempt.unified_message.clone(),
                    merchant_order_reference_id: new_payout_attempt
                        .merchant_order_reference_id
                        .clone(),
                };

                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Insert {
                        insertable: Box::new(kv::Insertable::PayoutAttempt(
                            new_payout_attempt.to_storage_model(),
                        )),
                    },
                };

                // Reverse lookup for payout_attempt_id
                let field = format!("poa_{}", created_attempt.payout_attempt_id);
                let reverse_lookup = ReverseLookupNew {
                    lookup_id: format!(
                        "poa_{}_{}",
                        &created_attempt.merchant_id.get_string_repr(),
                        &created_attempt.payout_attempt_id,
                    ),
                    pk_id: key_str.clone(),
                    sk_id: field.clone(),
                    source: "payout_attempt".to_string(),
                    updated_by: storage_scheme.to_string(),
                };
                self.insert_reverse_lookup(reverse_lookup, storage_scheme)
                    .await?;

                match Box::pin(kv_wrapper::<DieselPayoutAttempt, _, _>(
                    self,
                    KvOperation::<DieselPayoutAttempt>::HSetNx(
                        &field,
                        &created_attempt.clone().to_storage_model(),
                        redis_entry,
                    ),
                    key,
                ))
                .await
                .map_err(|err| err.to_redis_failed_response(&key_str))?
                .try_into_hsetnx()
                {
                    Ok(HsetnxReply::KeyNotSet) => Err(errors::StorageError::DuplicateValue {
                        entity: "payout attempt",
                        key: Some(key_str),
                    }
                    .into()),
                    Ok(HsetnxReply::KeySet) => Ok(created_attempt),
                    Err(error) => Err(error.change_context(errors::StorageError::KVError)),
                }
            }
        }
    }

    #[instrument(skip_all)]
    async fn update_payout_attempt(
        &self,
        this: &PayoutAttempt,
        payout_update: PayoutAttemptUpdate,
        payouts: &Payouts,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let key = PartitionKey::MerchantIdPayoutAttemptId {
            merchant_id: &this.merchant_id,
            payout_attempt_id: this.payout_id.get_string_repr(),
        };
        let field = format!("poa_{}", this.payout_attempt_id);
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPayoutAttempt>(
            self,
            storage_scheme,
            Op::Update(key.clone(), &field, None),
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .update_payout_attempt(this, payout_update, payouts, storage_scheme)
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let key_str = key.to_string();

                let diesel_payout_update = payout_update.clone().to_storage_model();
                let origin_diesel_payout = this.clone().to_storage_model();

                let diesel_payout = diesel_payout_update
                    .clone()
                    .apply_changeset(origin_diesel_payout.clone());
                // Check for database presence as well Maybe use a read replica here ?

                let redis_value = diesel_payout
                    .encode_to_string_of_json()
                    .change_context(errors::StorageError::SerializationFailed)?;

                let redis_entry = kv::TypedSql {
                    op: kv::DBOperation::Update {
                        updatable: Box::new(kv::Updateable::PayoutAttemptUpdate(
                            kv::PayoutAttemptUpdateMems {
                                orig: origin_diesel_payout,
                                update_data: diesel_payout_update,
                            },
                        )),
                    },
                };

                let updated_attempt = PayoutAttempt::from_storage_model(
                    payout_update
                        .to_storage_model()
                        .apply_changeset(this.clone().to_storage_model()),
                );
                let old_connector_payout_id = this.connector_payout_id.clone();

                match (
                    old_connector_payout_id,
                    updated_attempt.connector_payout_id.clone(),
                ) {
                    (Some(old), Some(new)) if old != new => {
                        add_connector_payout_id_to_reverse_lookup(
                            self,
                            key_str.as_str(),
                            &this.merchant_id,
                            updated_attempt.payout_attempt_id.as_str(),
                            new.as_str(),
                            storage_scheme,
                        )
                        .await?;
                    }
                    (None, Some(new)) => {
                        add_connector_payout_id_to_reverse_lookup(
                            self,
                            key_str.as_str(),
                            &this.merchant_id,
                            updated_attempt.payout_attempt_id.as_str(),
                            new.as_str(),
                            storage_scheme,
                        )
                        .await?;
                    }
                    _ => {}
                }

                Box::pin(kv_wrapper::<(), _, _>(
                    self,
                    KvOperation::<DieselPayoutAttempt>::Hset((&field, redis_value), redis_entry),
                    key,
                ))
                .await
                .map_err(|err| err.to_redis_failed_response(&key_str))?
                .try_into_hset()
                .change_context(errors::StorageError::KVError)?;

                Ok(PayoutAttempt::from_storage_model(diesel_payout))
            }
        }
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let storage_scheme = Box::pin(decide_storage_scheme::<_, DieselPayoutAttempt>(
            self,
            storage_scheme,
            Op::Find,
        ))
        .await;
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .find_payout_attempt_by_merchant_id_payout_attempt_id(
                        merchant_id,
                        payout_attempt_id,
                        storage_scheme,
                    )
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let lookup_id =
                    format!("poa_{}_{payout_attempt_id}", merchant_id.get_string_repr());
                let lookup = fallback_reverse_lookup_not_found!(
                    self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                        .await,
                    self.router_store
                        .find_payout_attempt_by_merchant_id_payout_attempt_id(
                            merchant_id,
                            payout_attempt_id,
                            storage_scheme
                        )
                        .await
                );
                let key = PartitionKey::CombinationKey {
                    combination: &lookup.pk_id,
                };
                Box::pin(utils::try_redis_get_else_try_database_get(
                    async {
                        Box::pin(kv_wrapper(
                            self,
                            KvOperation::<DieselPayoutAttempt>::HGet(&lookup.sk_id),
                            key,
                        ))
                        .await?
                        .try_into_hget()
                    },
                    || async {
                        self.router_store
                            .find_payout_attempt_by_merchant_id_payout_attempt_id(
                                merchant_id,
                                payout_attempt_id,
                                storage_scheme,
                            )
                            .await
                    },
                ))
                .await
            }
        }
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_connector_payout_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_payout_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        match storage_scheme {
            MerchantStorageScheme::PostgresOnly => {
                self.router_store
                    .find_payout_attempt_by_merchant_id_connector_payout_id(
                        merchant_id,
                        connector_payout_id,
                        storage_scheme,
                    )
                    .await
            }
            MerchantStorageScheme::RedisKv => {
                let lookup_id = format!(
                    "po_conn_payout_{}_{connector_payout_id}",
                    merchant_id.get_string_repr()
                );
                let lookup = fallback_reverse_lookup_not_found!(
                    self.get_lookup_by_lookup_id(&lookup_id, storage_scheme)
                        .await,
                    self.router_store
                        .find_payout_attempt_by_merchant_id_connector_payout_id(
                            merchant_id,
                            connector_payout_id,
                            storage_scheme,
                        )
                        .await
                );
                let key = PartitionKey::CombinationKey {
                    combination: &lookup.pk_id,
                };
                Box::pin(utils::try_redis_get_else_try_database_get(
                    async {
                        Box::pin(kv_wrapper(
                            self,
                            KvOperation::<DieselPayoutAttempt>::HGet(&lookup.sk_id),
                            key,
                        ))
                        .await?
                        .try_into_hget()
                    },
                    || async {
                        self.router_store
                            .find_payout_attempt_by_merchant_id_connector_payout_id(
                                merchant_id,
                                connector_payout_id,
                                storage_scheme,
                            )
                            .await
                    },
                ))
                .await
            }
        }
    }

    #[instrument(skip_all)]
    async fn get_filters_for_payouts(
        &self,
        payouts: &[Payouts],
        merchant_id: &common_utils::id_type::MerchantId,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutListFilters, errors::StorageError> {
        self.router_store
            .get_filters_for_payouts(payouts, merchant_id, storage_scheme)
            .await
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_order_reference_id: &str,
        storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        self.router_store
            .find_payout_attempt_by_merchant_id_merchant_order_reference_id(
                merchant_id,
                merchant_order_reference_id,
                storage_scheme,
            )
            .await
    }
}

#[async_trait::async_trait]
impl<T: DatabaseStore> PayoutAttemptInterface for crate::RouterStore<T> {
    type Error = errors::StorageError;
    #[instrument(skip_all)]
    async fn insert_payout_attempt(
        &self,
        new: PayoutAttemptNew,
        _payouts: &Payouts,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;
        new.to_storage_model()
            .insert(&conn)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(PayoutAttempt::from_storage_model)
    }

    #[instrument(skip_all)]
    async fn update_payout_attempt(
        &self,
        this: &PayoutAttempt,
        payout: PayoutAttemptUpdate,
        _payouts: &Payouts,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let conn = pg_connection_write(self).await?;
        this.clone()
            .to_storage_model()
            .update_with_attempt_id(&conn, payout.to_storage_model())
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(PayoutAttempt::from_storage_model)
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_payout_attempt_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        payout_attempt_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPayoutAttempt::find_by_merchant_id_payout_attempt_id(
            &conn,
            merchant_id,
            payout_attempt_id,
        )
        .await
        .map(PayoutAttempt::from_storage_model)
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_connector_payout_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_payout_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPayoutAttempt::find_by_merchant_id_connector_payout_id(
            &conn,
            merchant_id,
            connector_payout_id,
        )
        .await
        .map(PayoutAttempt::from_storage_model)
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }

    #[instrument(skip_all)]
    async fn get_filters_for_payouts(
        &self,
        payouts: &[Payouts],
        merchant_id: &common_utils::id_type::MerchantId,
        _storage_scheme: MerchantStorageScheme,
    ) -> CustomResult<PayoutListFilters, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        let payouts = payouts
            .iter()
            .cloned()
            .map(|payouts| payouts.to_storage_model())
            .collect::<Vec<diesel_models::Payouts>>();
        DieselPayoutAttempt::get_filters_for_payouts(&conn, payouts.as_slice(), merchant_id)
            .await
            .map_err(|er| {
                let new_err = diesel_error_to_data_error(*er.current_context());
                er.change_context(new_err)
            })
            .map(
                |(connector, currency, status, payout_method)| PayoutListFilters {
                    connector: connector
                        .iter()
                        .filter_map(|c| {
                            PayoutConnectors::from_str(c)
                                .map_err(|e| {
                                    logger::error!(
                                        "Failed to parse payout connector '{}' - {}",
                                        c,
                                        e
                                    );
                                })
                                .ok()
                        })
                        .collect(),
                    currency,
                    status,
                    payout_method,
                },
            )
    }

    #[instrument(skip_all)]
    async fn find_payout_attempt_by_merchant_id_merchant_order_reference_id(
        &self,
        merchant_id: &common_utils::id_type::MerchantId,
        merchant_order_reference_id: &str,
        _storage_scheme: MerchantStorageScheme,
    ) -> error_stack::Result<PayoutAttempt, errors::StorageError> {
        let conn = pg_connection_read(self).await?;
        DieselPayoutAttempt::find_by_merchant_id_merchant_order_reference_id(
            &conn,
            merchant_id,
            merchant_order_reference_id,
        )
        .await
        .map(PayoutAttempt::from_storage_model)
        .map_err(|er| {
            let new_err = diesel_error_to_data_error(*er.current_context());
            er.change_context(new_err)
        })
    }
}

impl DataModelExt for PayoutAttempt {
    type StorageModel = DieselPayoutAttempt;

    fn to_storage_model(self) -> Self::StorageModel {
        DieselPayoutAttempt {
            payout_attempt_id: self.payout_attempt_id,
            payout_id: self.payout_id,
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            address_id: self.address_id,
            connector: self.connector,
            connector_payout_id: self.connector_payout_id,
            payout_token: self.payout_token,
            status: self.status,
            is_eligible: self.is_eligible,
            error_message: self.error_message,
            error_code: self.error_code,
            business_country: self.business_country,
            business_label: self.business_label,
            created_at: self.created_at,
            last_modified_at: self.last_modified_at,
            profile_id: self.profile_id,
            merchant_connector_id: self.merchant_connector_id,
            routing_info: self.routing_info,
            unified_code: self.unified_code,
            unified_message: self.unified_message,
            additional_payout_method_data: self.additional_payout_method_data,
            merchant_order_reference_id: self.merchant_order_reference_id,
        }
    }

    fn from_storage_model(storage_model: Self::StorageModel) -> Self {
        Self {
            payout_attempt_id: storage_model.payout_attempt_id,
            payout_id: storage_model.payout_id,
            customer_id: storage_model.customer_id,
            merchant_id: storage_model.merchant_id,
            address_id: storage_model.address_id,
            connector: storage_model.connector,
            connector_payout_id: storage_model.connector_payout_id,
            payout_token: storage_model.payout_token,
            status: storage_model.status,
            is_eligible: storage_model.is_eligible,
            error_message: storage_model.error_message,
            error_code: storage_model.error_code,
            business_country: storage_model.business_country,
            business_label: storage_model.business_label,
            created_at: storage_model.created_at,
            last_modified_at: storage_model.last_modified_at,
            profile_id: storage_model.profile_id,
            merchant_connector_id: storage_model.merchant_connector_id,
            routing_info: storage_model.routing_info,
            unified_code: storage_model.unified_code,
            unified_message: storage_model.unified_message,
            additional_payout_method_data: storage_model.additional_payout_method_data,
            merchant_order_reference_id: storage_model.merchant_order_reference_id,
        }
    }
}
impl DataModelExt for PayoutAttemptNew {
    type StorageModel = DieselPayoutAttemptNew;

    fn to_storage_model(self) -> Self::StorageModel {
        DieselPayoutAttemptNew {
            payout_attempt_id: self.payout_attempt_id,
            payout_id: self.payout_id,
            customer_id: self.customer_id,
            merchant_id: self.merchant_id,
            address_id: self.address_id,
            connector: self.connector,
            connector_payout_id: self.connector_payout_id,
            payout_token: self.payout_token,
            status: self.status,
            is_eligible: self.is_eligible,
            error_message: self.error_message,
            error_code: self.error_code,
            business_country: self.business_country,
            business_label: self.business_label,
            created_at: self.created_at,
            last_modified_at: self.last_modified_at,
            profile_id: self.profile_id,
            merchant_connector_id: self.merchant_connector_id,
            routing_info: self.routing_info,
            unified_code: self.unified_code,
            unified_message: self.unified_message,
            additional_payout_method_data: self.additional_payout_method_data,
            merchant_order_reference_id: self.merchant_order_reference_id,
        }
    }

    fn from_storage_model(storage_model: Self::StorageModel) -> Self {
        Self {
            payout_attempt_id: storage_model.payout_attempt_id,
            payout_id: storage_model.payout_id,
            customer_id: storage_model.customer_id,
            merchant_id: storage_model.merchant_id,
            address_id: storage_model.address_id,
            connector: storage_model.connector,
            connector_payout_id: storage_model.connector_payout_id,
            payout_token: storage_model.payout_token,
            status: storage_model.status,
            is_eligible: storage_model.is_eligible,
            error_message: storage_model.error_message,
            error_code: storage_model.error_code,
            business_country: storage_model.business_country,
            business_label: storage_model.business_label,
            created_at: storage_model.created_at,
            last_modified_at: storage_model.last_modified_at,
            profile_id: storage_model.profile_id,
            merchant_connector_id: storage_model.merchant_connector_id,
            routing_info: storage_model.routing_info,
            unified_code: storage_model.unified_code,
            unified_message: storage_model.unified_message,
            additional_payout_method_data: storage_model.additional_payout_method_data,
            merchant_order_reference_id: storage_model.merchant_order_reference_id,
        }
    }
}
impl DataModelExt for PayoutAttemptUpdate {
    type StorageModel = DieselPayoutAttemptUpdate;
    fn to_storage_model(self) -> Self::StorageModel {
        match self {
            Self::StatusUpdate {
                connector_payout_id,
                status,
                error_message,
                error_code,
                is_eligible,
                unified_code,
                unified_message,
            } => DieselPayoutAttemptUpdate::StatusUpdate {
                connector_payout_id,
                status,
                error_message,
                error_code,
                is_eligible,
                unified_code,
                unified_message,
            },
            Self::PayoutTokenUpdate { payout_token } => {
                DieselPayoutAttemptUpdate::PayoutTokenUpdate { payout_token }
            }
            Self::BusinessUpdate {
                business_country,
                business_label,
                address_id,
                customer_id,
            } => DieselPayoutAttemptUpdate::BusinessUpdate {
                business_country,
                business_label,
                address_id,
                customer_id,
            },
            Self::UpdateRouting {
                connector,
                routing_info,
                merchant_connector_id,
            } => DieselPayoutAttemptUpdate::UpdateRouting {
                connector,
                routing_info,
                merchant_connector_id,
            },
            Self::AdditionalPayoutMethodDataUpdate {
                additional_payout_method_data,
            } => DieselPayoutAttemptUpdate::AdditionalPayoutMethodDataUpdate {
                additional_payout_method_data,
            },
        }
    }

    #[allow(clippy::todo)]
    fn from_storage_model(_storage_model: Self::StorageModel) -> Self {
        todo!("Reverse map should no longer be needed")
    }
}

#[inline]
#[instrument(skip_all)]
async fn add_connector_payout_id_to_reverse_lookup<T: DatabaseStore>(
    store: &KVRouterStore<T>,
    key: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    updated_attempt_attempt_id: &str,
    connector_payout_id: &str,
    storage_scheme: MerchantStorageScheme,
) -> CustomResult<ReverseLookup, errors::StorageError> {
    let field = format!("poa_{updated_attempt_attempt_id}");
    let reverse_lookup_new = ReverseLookupNew {
        lookup_id: format!(
            "po_conn_payout_{}_{}",
            merchant_id.get_string_repr(),
            connector_payout_id
        ),
        pk_id: key.to_owned(),
        sk_id: field.clone(),
        source: "payout_attempt".to_string(),
        updated_by: storage_scheme.to_string(),
    };
    store
        .insert_reverse_lookup(reverse_lookup_new, storage_scheme)
        .await
}


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

impl PaymentSession for for DummyConnector


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/klarna.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: AverageAccumulator
// File: crates/analytics/src/sdk_events/accumulator.rs
// Module: analytics
// Implementations: 1
// Traits: SdkEventMetricAccumulator

pub struct AverageAccumulator


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

impl Refund for for Plaid


// Struct: MerchantDetails
// File: crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MerchantDetails


// Function: find_most_specific_theme_in_lineage
// File: crates/diesel_models/src/query/user/theme.rs
// Module: diesel_models

pub fn find_most_specific_theme_in_lineage(
        conn: &PgPooledConn,
        lineage: ThemeLineage,
    ) -> StorageResult<Self>


// Implementation: impl common_utils::events::ApiEventMetric for for CardNetworkTokenizeRequest
// File: crates/api_models/src/payment_methods.rs
// Module: api_models
// Methods: 0 total (0 public)

impl common_utils::events::ApiEventMetric for for CardNetworkTokenizeRequest


// File: crates/router/src/types/api/files.rs
// Module: router
// Public structs: 3

use api_models::enums::FileUploadProvider;
pub use hyperswitch_domain_models::router_flow_types::files::{Retrieve, Upload};
pub use hyperswitch_interfaces::api::files::{FilePurpose, FileUpload, RetrieveFile, UploadFile};
use masking::{Deserialize, Serialize};
use serde_with::serde_as;

pub use super::files_v2::{FileUploadV2, RetrieveFileV2, UploadFileV2};
use crate::{
    core::errors,
    types::{self, transformers::ForeignTryFrom},
};

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct FileId {
    pub file_id: String,
}

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct FileRetrieveRequest {
    pub file_id: String,
    pub dispute_id: Option<String>,
}

#[derive(Debug)]
pub enum FileDataRequired {
    Required,
    NotRequired,
}

impl ForeignTryFrom<FileUploadProvider> for types::Connector {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(item: FileUploadProvider) -> Result<Self, Self::Error> {
        match item {
            FileUploadProvider::Stripe => Ok(Self::Stripe),
            FileUploadProvider::Checkout => Ok(Self::Checkout),
            FileUploadProvider::Worldpayvantiv => Ok(Self::Worldpayvantiv),
            FileUploadProvider::Router => Err(errors::ApiErrorResponse::NotSupported {
                message: "File upload provider is not a connector".to_owned(),
            }
            .into()),
        }
    }
}

impl ForeignTryFrom<&types::Connector> for FileUploadProvider {
    type Error = error_stack::Report<errors::ApiErrorResponse>;
    fn foreign_try_from(item: &types::Connector) -> Result<Self, Self::Error> {
        match *item {
            types::Connector::Stripe => Ok(Self::Stripe),
            types::Connector::Checkout => Ok(Self::Checkout),
            types::Connector::Worldpayvantiv => Ok(Self::Worldpayvantiv),
            _ => Err(errors::ApiErrorResponse::NotSupported {
                message: "Connector not supported as file provider".to_owned(),
            }
            .into()),
        }
    }
}

#[serde_as]
#[derive(Debug, Clone, serde::Serialize)]
pub struct CreateFileRequest {
    pub file: Vec<u8>,
    pub file_name: Option<String>,
    pub file_size: i32,
    #[serde_as(as = "serde_with::DisplayFromStr")]
    pub file_type: mime::Mime,
    pub purpose: FilePurpose,
    pub dispute_id: Option<String>,
}


// Struct: MerchantAccountCreateWithoutOrgId
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct MerchantAccountCreateWithoutOrgId


// Struct: PaymentAttemptBatchNew
// File: crates/diesel_models/src/user/sample_data.rs
// Module: diesel_models
// Implementations: 2

pub struct PaymentAttemptBatchNew


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

impl api::PaymentSession for for Silverflow


// Function: update
// File: crates/diesel_models/src/query/connector_response.rs
// Module: diesel_models

pub fn update(
        self,
        conn: &PgPooledConn,
        connector_response: ConnectorResponseUpdate,
    ) -> StorageResult<Self>


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

pub mod transformers;

use std::{fmt::Debug, sync::LazyLock};

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::ByteSliceExt,
    request::{Method, Request, RequestBuilder, RequestContent},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{
            Authorize, Capture, CreateConnectorCustomer, PSync, PaymentMethodToken, Session,
            SetupMandate, Void,
        },
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, ConnectorCustomerData, PaymentMethodTokenizationData,
        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData,
        PaymentsSyncData, RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        ConnectorCustomerRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
        PaymentsCaptureRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
        TokenizationRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts, errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{Mask, PeekInterface, Secret};
use transformers as stax;

use self::stax::StaxWebhookEventType;
use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{self, RefundsRequestData},
};
#[derive(Debug, Clone)]
pub struct Stax;

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Stax
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(),
            types::PaymentsAuthorizeType::get_content_type(self)
                .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 Stax {
    fn id(&self) -> &'static str {
        "stax"
    }

    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.stax.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = stax::StaxAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;

        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            format!("Bearer {}", auth.api_key.peek()).into_masked(),
        )])
    }

    fn build_error_response(
        &self,
        res: Response,
        _event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        Ok(ErrorResponse {
            status_code: res.status_code,
            code: consts::NO_ERROR_CODE.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: Some(
                std::str::from_utf8(&res.response)
                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)?
                    .to_owned(),
            ),
            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 Stax {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<enums::CaptureMethod>,
        _payment_method: enums::PaymentMethod,
        _pmt: Option<enums::PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        match capture_method {
            enums::CaptureMethod::Automatic
            | enums::CaptureMethod::Manual
            | enums::CaptureMethod::SequentialAutomatic => Ok(()),
            enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err(
                utils::construct_not_supported_error_report(capture_method, self.id()),
            ),
        }
    }
}

impl api::ConnectorCustomer for Stax {}

impl ConnectorIntegration<CreateConnectorCustomer, ConnectorCustomerData, PaymentsResponseData>
    for Stax
{
    fn get_headers(
        &self,
        req: &ConnectorCustomerRouterData,
        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: &ConnectorCustomerRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}customer", self.base_url(connectors),))
    }

    fn get_request_body(
        &self,
        req: &ConnectorCustomerRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = stax::StaxCustomerRequest::try_from(req)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &ConnectorCustomerRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<ConnectorCustomerRouterData, errors::ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let response: stax::StaxCustomerResponse = res
            .response
            .parse_struct("StaxCustomerResponse")
            .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<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Stax
{
    fn get_headers(
        &self,
        req: &TokenizationRouterData,
        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: &TokenizationRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}payment-method/", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &TokenizationRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = stax::StaxTokenRequest::try_from(req)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &TokenizationRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<TokenizationRouterData, errors::ConnectorError>
    where
        PaymentsResponseData: Clone,
    {
        let response: stax::StaxTokenResponse = res
            .response
            .parse_struct("StaxTokenResponse")
            .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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }
    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<Session, PaymentsSessionData, PaymentsResponseData> for Stax {
    //TODO: implement sessions flow
}

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

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Stax {
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Stax".to_string())
                .into(),
        )
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Stax {
    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!("{}charge", self.base_url(connectors),))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = stax::StaxRouterData::try_from((
            &self.get_currency_unit(),
            req.request.currency,
            req.request.amount,
            req,
        ))?;
        let connector_req = stax::StaxPaymentsRequest::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: stax::StaxPaymentsResponse = res
            .response
            .parse_struct("StaxPaymentsAuthorizeResponse")
            .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 Stax {
    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 connector_payment_id = req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;

        Ok(format!(
            "{}/transaction/{connector_payment_id}",
            self.base_url(connectors),
        ))
    }

    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: stax::StaxPaymentsResponse = res
            .response
            .parse_struct("StaxPaymentsSyncResponse")
            .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 Stax {
    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> {
        Ok(format!(
            "{}/transaction/{}/capture",
            self.base_url(connectors),
            req.request.connector_transaction_id,
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = stax::StaxRouterData::try_from((
            &self.get_currency_unit(),
            req.request.currency,
            req.request.amount_to_capture,
            req,
        ))?;
        let connector_req = stax::StaxCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    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: stax::StaxPaymentsResponse = res
            .response
            .parse_struct("StaxPaymentsCaptureResponse")
            .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 Stax {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        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: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!(
            "{}/transaction/{}/void-or-refund",
            self.base_url(connectors),
            req.request.connector_transaction_id,
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
        let response: stax::StaxPaymentsResponse = res
            .response
            .parse_struct("StaxPaymentsVoidResponse")
            .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<Execute, RefundsData, RefundsResponseData> for Stax {
    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> {
        let connector_transaction_id = if req.request.connector_metadata.is_some() {
            let stax_capture: stax::StaxMetaData =
                utils::to_connector_meta(req.request.connector_metadata.clone())?;
            stax_capture.capture_id
        } else {
            req.request.connector_transaction_id.clone()
        };

        Ok(format!(
            "{}/transaction/{}/refund",
            self.base_url(connectors),
            connector_transaction_id,
        ))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_router_data = stax::StaxRouterData::try_from((
            &self.get_currency_unit(),
            req.request.currency,
            req.request.refund_amount,
            req,
        ))?;
        let connector_req = stax::StaxRefundRequest::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: stax::RefundResponse = res
            .response
            .parse_struct("StaxRefundResponse")
            .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 Stax {
    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> {
        Ok(format!(
            "{}/transaction/{}",
            self.base_url(connectors),
            req.request.get_connector_refund_id()?,
        ))
    }

    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)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: stax::RefundResponse = res
            .response
            .parse_struct("StaxRefundSyncResponse")
            .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 Stax {
    async fn verify_webhook_source(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_details: Option<common_utils::pii::SecretSerdeValue>,
        _connector_account_details: common_utils::crypto::Encryptable<Secret<serde_json::Value>>,
        _connector_label: &str,
    ) -> CustomResult<bool, errors::ConnectorError> {
        Ok(false)
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let webhook_body: stax::StaxWebhookBody = request
            .body
            .parse_struct("StaxWebhookBody")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;

        match webhook_body.transaction_type {
            StaxWebhookEventType::Refund => Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::ConnectorRefundId(webhook_body.id),
            )),
            StaxWebhookEventType::Unknown => {
                Err(errors::ConnectorError::WebhookEventTypeNotFound.into())
            }
            StaxWebhookEventType::PreAuth
            | StaxWebhookEventType::Capture
            | StaxWebhookEventType::Charge
            | StaxWebhookEventType::Void => Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    match webhook_body.transaction_type {
                        StaxWebhookEventType::Capture => webhook_body
                            .auth_id
                            .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                        _ => webhook_body.id,
                    },
                ),
            )),
        }
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        let details: stax::StaxWebhookBody = request
            .body
            .parse_struct("StaxWebhookEventType")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;

        Ok(match &details.transaction_type {
            StaxWebhookEventType::Refund => match &details.success {
                true => IncomingWebhookEvent::RefundSuccess,
                false => IncomingWebhookEvent::RefundFailure,
            },
            StaxWebhookEventType::Capture | StaxWebhookEventType::Charge => {
                match &details.success {
                    true => IncomingWebhookEvent::PaymentIntentSuccess,
                    false => IncomingWebhookEvent::PaymentIntentFailure,
                }
            }
            StaxWebhookEventType::PreAuth
            | StaxWebhookEventType::Void
            | StaxWebhookEventType::Unknown => IncomingWebhookEvent::EventNotSupported,
        })
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let reference_object: serde_json::Value = serde_json::from_slice(request.body)
            .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?;
        Ok(Box::new(reference_object))
    }
}

static STAX_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        enums::CaptureMethod::Automatic,
        enums::CaptureMethod::Manual,
        enums::CaptureMethod::SequentialAutomatic,
    ];

    let supported_card_network = vec![
        common_enums::CardNetwork::Visa,
        common_enums::CardNetwork::Mastercard,
        common_enums::CardNetwork::AmericanExpress,
        common_enums::CardNetwork::Discover,
    ];

    let mut stax_supported_payment_methods = SupportedPaymentMethods::new();

    stax_supported_payment_methods.add(
        enums::PaymentMethod::BankDebit,
        enums::PaymentMethodType::Ach,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: None,
        },
    );

    stax_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Credit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::NotSupported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    stax_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Debit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::NotSupported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::NotSupported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    stax_supported_payment_methods
});

static STAX_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Stax",
    description:
        "Stax is a payment processing platform that helps businesses accept payments and manage their payment ecosystem ",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static STAX_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 2] =
    [enums::EventClass::Payments, enums::EventClass::Refunds];

impl ConnectorSpecifications for Stax {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&STAX_CONNECTOR_INFO)
    }

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

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

    fn should_call_connector_customer(
        &self,
        _payment_attempt: &hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt,
    ) -> bool {
        true
    }
}


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

pub struct ReferrerData


// Function: reset_tracking_id
// File: crates/router/src/routes/connector_onboarding.rs
// Module: router

pub fn reset_tracking_id(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<api_types::ResetTrackingIdRequest>,
) -> HttpResponse


// Struct: BillwerkPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/billwerk/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillwerkPaymentsRequest


// Struct: AdyenInvalidField
// File: crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AdyenInvalidField


// Function: new
// File: crates/diesel_models/src/organization.rs
// Module: diesel_models

pub fn new(org_new: OrganizationNew) -> Self


// File: crates/router_derive/src/macros/helpers.rs
// Module: router_derive
// Public functions: 2

use proc_macro2::Span;
use quote::ToTokens;
use syn::{parse::Parse, punctuated::Punctuated, spanned::Spanned, Attribute, Token};

pub fn non_enum_error() -> syn::Error {
    syn::Error::new(Span::call_site(), "This macro only supports enums.")
}

pub(super) fn occurrence_error<T: ToTokens>(
    first_keyword: T,
    second_keyword: T,
    attr: &str,
) -> syn::Error {
    let mut error = syn::Error::new_spanned(
        second_keyword,
        format!("Found multiple occurrences of error({attr})"),
    );
    error.combine(syn::Error::new_spanned(first_keyword, "first one here"));
    error
}

pub(super) fn syn_error(span: Span, message: &str) -> syn::Error {
    syn::Error::new(span, message)
}

/// Get all the variants of a enum in the form of a string
pub fn get_possible_values_for_enum<T>() -> String
where
    T: strum::IntoEnumIterator + ToString,
{
    T::iter()
        .map(|variants| variants.to_string())
        .collect::<Vec<_>>()
        .join(", ")
}

pub(super) fn get_metadata_inner<'a, T: Parse + Spanned>(
    ident: &str,
    attrs: impl IntoIterator<Item = &'a Attribute>,
) -> syn::Result<Vec<T>> {
    attrs
        .into_iter()
        .filter(|attr| attr.path().is_ident(ident))
        .try_fold(Vec::new(), |mut vec, attr| {
            vec.extend(attr.parse_args_with(Punctuated::<T, Token![,]>::parse_terminated)?);
            Ok(vec)
        })
}

pub(super) fn get_struct_fields(
    data: syn::Data,
) -> syn::Result<Punctuated<syn::Field, syn::token::Comma>> {
    if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = data
    {
        Ok(named.to_owned())
    } else {
        Err(syn::Error::new(
            Span::call_site(),
            "This macro cannot be used on structs with no fields",
        ))
    }
}


// File: crates/diesel_models/src/query/authentication.rs
// Module: diesel_models
// Public functions: 4

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use super::generics;
use crate::{
    authentication::{
        Authentication, AuthenticationNew, AuthenticationUpdate, AuthenticationUpdateInternal,
    },
    errors,
    schema::authentication::dsl,
    PgPooledConn, StorageResult,
};

impl AuthenticationNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Authentication> {
        generics::generic_insert(conn, self).await
    }
}

impl Authentication {
    pub async fn update_by_merchant_id_authentication_id(
        conn: &PgPooledConn,
        merchant_id: common_utils::id_type::MerchantId,
        authentication_id: common_utils::id_type::AuthenticationId,
        authorization_update: AuthenticationUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::authentication_id.eq(authentication_id.to_owned())),
            AuthenticationUpdateInternal::from(authorization_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NotFound => Err(error.attach_printable(
                    "Authentication with the given Authentication ID does not exist",
                )),
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
                        conn,
                        dsl::merchant_id
                            .eq(merchant_id.to_owned())
                            .and(dsl::authentication_id.eq(authentication_id.to_owned())),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_by_merchant_id_authentication_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::authentication_id.eq(authentication_id.to_owned())),
        )
        .await
    }

    pub async fn find_authentication_by_merchant_id_connector_authentication_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_authentication_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_authentication_id.eq(connector_authentication_id.to_owned())),
        )
        .await
    }
}


// Function: get_frm_filters
// File: crates/router/src/analytics.rs
// Module: router

pub fn get_frm_filters(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<GetFrmFilterRequest>,
    ) -> impl Responder


// Struct: UploadFileRequest
// File: crates/api_models/src/user/theme.rs
// Module: api_models
// Implementations: 0

pub struct UploadFileRequest


// Struct: PaymentDmnNotification
// File: crates/hyperswitch_connectors/src/connectors/nuvei/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentDmnNotification


// File: crates/analytics/src/sdk_events/metrics/payment_attempts.rs
// Module: analytics

use std::collections::HashSet;

use api_models::analytics::{
    sdk_events::{
        SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
    },
    Granularity, TimeRange,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use time::PrimitiveDateTime;

use super::SdkEventMetricRow;
use crate::{
    query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
    types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
};

#[derive(Default)]
pub(super) struct PaymentAttempts;

#[async_trait::async_trait]
impl<T> super::SdkEventMetric<T> for PaymentAttempts
where
    T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
    PrimitiveDateTime: ToSql<T>,
    AnalyticsCollection: ToSql<T>,
    Granularity: GroupByClause<T>,
    Aggregate<&'static str>: ToSql<T>,
    Window<&'static str>: ToSql<T>,
{
    async fn load_metrics(
        &self,
        dimensions: &[SdkEventDimensions],
        publishable_key: &str,
        filters: &SdkEventFilters,
        granularity: Option<Granularity>,
        time_range: &TimeRange,
        pool: &T,
    ) -> MetricsResult<HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
        let mut query_builder: QueryBuilder<T> =
            QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
        let dimensions = dimensions.to_vec();

        for dim in dimensions.iter() {
            query_builder.add_select_column(dim).switch()?;
        }

        query_builder
            .add_select_column(Aggregate::Count {
                field: None,
                alias: Some("count"),
            })
            .switch()?;

        if let Some(granularity) = granularity {
            query_builder
                .add_granularity_in_mins(granularity)
                .switch()?;
        }

        filters.set_filter_clause(&mut query_builder).switch()?;

        query_builder
            .add_filter_clause("merchant_id", publishable_key)
            .switch()?;

        query_builder
            .add_bool_filter_clause("first_event", 1)
            .switch()?;

        query_builder
            .add_filter_clause("event_name", SdkEventNames::PaymentAttempt)
            .switch()?;

        time_range
            .set_filter_clause(&mut query_builder)
            .attach_printable("Error filtering time range")
            .switch()?;

        for dim in dimensions.iter() {
            query_builder
                .add_group_by_clause(dim)
                .attach_printable("Error grouping by dimensions")
                .switch()?;
        }

        if let Some(_granularity) = granularity.as_ref() {
            query_builder
                .add_group_by_clause("time_bucket")
                .attach_printable("Error adding granularity")
                .switch()?;
        }

        query_builder
            .execute_query::<SdkEventMetricRow, _>(pool)
            .await
            .change_context(MetricsError::QueryBuildingError)?
            .change_context(MetricsError::QueryExecutionFailure)?
            .into_iter()
            .map(|i| {
                Ok((
                    SdkEventMetricsBucketIdentifier::new(
                        i.payment_method.clone(),
                        i.platform.clone(),
                        i.browser_name.clone(),
                        i.source.clone(),
                        i.component.clone(),
                        i.payment_experience.clone(),
                        i.time_bucket.clone(),
                    ),
                    i,
                ))
            })
            .collect::<error_stack::Result<
                HashSet<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
                crate::query::PostProcessingError,
            >>()
            .change_context(MetricsError::PostProcessingFailure)
    }
}


// Struct: BraintreeErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BraintreeErrorResponse


// Function: shutdown_listener
// File: crates/drainer/src/handler.rs
// Module: drainer

pub fn shutdown_listener(&self, mut rx: mpsc::Receiver<()


// Implementation: impl MultipleCaptureSyncResponse for for GlobalpayPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/globalpay/transformers.rs
// Module: hyperswitch_connectors
// Methods: 5 total (0 public)

impl MultipleCaptureSyncResponse for for GlobalpayPaymentsResponse


// Struct: SepaDetails
// File: crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SepaDetails


// Implementation: impl api::Payment for for Archipel
// File: crates/hyperswitch_connectors/src/connectors/archipel.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Archipel


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

impl ApiEventMetric for for PollId


// Struct: VantivDisputeErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VantivDisputeErrorResponse


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

impl api::PaymentSession for for Xendit


// Function: generate_merchant_dispute_report
// File: crates/router/src/analytics.rs
// Module: router

pub fn generate_merchant_dispute_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder


// Implementation: impl api::PayoutCancel for for Nomupay
// File: crates/hyperswitch_connectors/src/connectors/nomupay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PayoutCancel for for Nomupay


// Implementation: impl api::RefundExecute for for Gocardless
// File: crates/hyperswitch_connectors/src/connectors/gocardless.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Gocardless


// Implementation: impl api::RefundExecute for for Taxjar
// File: crates/hyperswitch_connectors/src/connectors/taxjar.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Taxjar


// Function: extract_connector_customer_id_from_payment_intent
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models

// Documentation: Extract customer_id from payment intent feature metadata

pub fn extract_connector_customer_id_from_payment_intent(
        &self,
    ) -> Result<String, common_utils::errors::ValidationError>


// File: crates/router/src/routes/authentication.rs
// Module: router
// Public functions: 5

use actix_web::{web, HttpRequest, Responder};
use api_models::authentication::{AuthenticationAuthenticateRequest, AuthenticationCreateRequest};
#[cfg(feature = "v1")]
use api_models::authentication::{
    AuthenticationEligibilityRequest, AuthenticationSyncPostUpdateRequest,
    AuthenticationSyncRequest,
};
use router_env::{instrument, tracing, Flow};

use crate::{
    core::{api_locking, unified_authentication_service},
    routes::app::{self},
    services::{api, authentication as auth},
    types::domain,
};

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::AuthenticationCreate))]
pub async fn authentication_create(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    json_payload: web::Json<AuthenticationCreateRequest>,
) -> impl Responder {
    let flow = Flow::AuthenticationCreate;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_create_core(state, merchant_context, req)
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::AuthenticationEligibility))]
pub async fn authentication_eligibility(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    json_payload: web::Json<AuthenticationEligibilityRequest>,
    path: web::Path<common_utils::id_type::AuthenticationId>,
) -> impl Responder {
    let flow = Flow::AuthenticationEligibility;

    let api_auth = auth::ApiKeyAuth::default();
    let payload = json_payload.into_inner();

    let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth)
    {
        Ok((auth, _auth_flow)) => (auth, _auth_flow),
        Err(e) => return api::log_and_return_error_response(e),
    };

    let authentication_id = path.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_eligibility_core(
                state,
                merchant_context,
                req,
                authentication_id.clone(),
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::AuthenticationAuthenticate))]
pub async fn authentication_authenticate(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    json_payload: web::Json<AuthenticationAuthenticateRequest>,
    path: web::Path<common_utils::id_type::AuthenticationId>,
) -> impl Responder {
    let flow = Flow::AuthenticationAuthenticate;
    let authentication_id = path.into_inner();
    let api_auth = auth::ApiKeyAuth::default();
    let payload = AuthenticationAuthenticateRequest {
        authentication_id,
        ..json_payload.into_inner()
    };

    let (auth, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok((auth, auth_flow)) => (auth, auth_flow),
            Err(e) => return api::log_and_return_error_response(e),
        };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_authenticate_core(
                state,
                merchant_context,
                req,
                auth_flow,
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::AuthenticationSync))]
pub async fn authentication_sync(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::AuthenticationId,
    )>,
    json_payload: web::Query<AuthenticationSyncRequest>,
) -> impl Responder {
    let flow = Flow::AuthenticationSync;
    let api_auth = auth::ApiKeyAuth::default();
    let (_merchant_id, authentication_id) = path.into_inner();
    let payload = AuthenticationSyncRequest {
        authentication_id,
        ..json_payload.into_inner()
    };
    let (auth, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok((auth, auth_flow)) => (auth, auth_flow),
            Err(e) => return api::log_and_return_error_response(e),
        };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_sync_core(
                state,
                merchant_context,
                auth_flow,
                req,
            )
        },
        &*auth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::AuthenticationSyncPostUpdate))]
pub async fn authentication_sync_post_update(
    state: web::Data<app::AppState>,
    req: HttpRequest,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::AuthenticationId,
    )>,
) -> impl Responder {
    let flow = Flow::AuthenticationSyncPostUpdate;
    let (merchant_id, authentication_id) = path.into_inner();
    let payload = AuthenticationSyncPostUpdateRequest { authentication_id };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            unified_authentication_service::authentication_post_sync_core(
                state,
                merchant_context,
                req,
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}