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

use std::str::FromStr;

use api_models::{
    admin::{self as admin_types},
    enums as api_enums, routing as routing_types,
};
use common_enums::{MerchantAccountRequestType, MerchantAccountType, OrganizationType};
use common_utils::{
    date_time,
    ext_traits::{AsyncExt, Encode, OptionExt, ValueExt},
    fp_utils, id_type, pii, type_name,
    types::keymanager::{self as km_types, KeyManagerState, ToEncryptable},
};
#[cfg(all(any(feature = "v1", feature = "v2"), feature = "olap"))]
use diesel_models::{business_profile::CardTestingGuardConfig, organization::OrganizationBridge};
use diesel_models::{configs, payment_method};
use error_stack::{report, FutureExt, ResultExt};
use external_services::http_client::client;
use hyperswitch_domain_models::merchant_connector_account::{
    FromRequestEncryptableMerchantConnectorAccount, UpdateEncryptableMerchantConnectorAccount,
};
use masking::{ExposeInterface, PeekInterface, Secret};
use pm_auth::types as pm_auth_types;
use uuid::Uuid;

#[cfg(any(feature = "v1", feature = "v2"))]
use crate::types::transformers::ForeignFrom;
use crate::{
    consts,
    core::{
        connector_validation::ConnectorAuthTypeAndMetadataValidation,
        disputes,
        encryption::transfer_encryption_key,
        errors::{self, RouterResponse, RouterResult, StorageErrorExt},
        payment_methods::{cards, transformers},
        payments::helpers,
        pm_auth::helpers::PaymentAuthConnectorDataExt,
        routing, utils as core_utils,
    },
    db::{AccountsStorageInterface, StorageInterface},
    logger,
    routes::{metrics, SessionState},
    services::{
        self,
        api::{self as service_api},
        authentication, pm_auth as payment_initiation_service,
    },
    types::{
        self,
        api::{self, admin},
        domain::{
            self,
            types::{self as domain_types, AsyncLift},
        },
        storage::{self, enums::MerchantStorageScheme},
        transformers::{ForeignInto, ForeignTryFrom, ForeignTryInto},
    },
    utils,
};

#[inline]
pub fn create_merchant_publishable_key() -> String {
    format!(
        "pk_{}_{}",
        router_env::env::prefix_for_env(),
        Uuid::new_v4().simple()
    )
}

pub async fn insert_merchant_configs(
    db: &dyn StorageInterface,
    merchant_id: &id_type::MerchantId,
) -> RouterResult<()> {
    db.insert_config(configs::ConfigNew {
        key: merchant_id.get_requires_cvv_key(),
        config: "true".to_string(),
    })
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error while setting requires_cvv config")?;

    db.insert_config(configs::ConfigNew {
        key: merchant_id.get_merchant_fingerprint_secret_key(),
        config: utils::generate_id(consts::FINGERPRINT_SECRET_LENGTH, "fs"),
    })
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Error while inserting merchant fingerprint secret")?;

    Ok(())
}

#[cfg(feature = "olap")]
fn add_publishable_key_to_decision_service(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
) {
    let state = state.clone();
    let publishable_key = merchant_context
        .get_merchant_account()
        .publishable_key
        .clone();
    let merchant_id = merchant_context.get_merchant_account().get_id().clone();

    authentication::decision::spawn_tracked_job(
        async move {
            authentication::decision::add_publishable_key(
                &state,
                publishable_key.into(),
                merchant_id,
                None,
            )
            .await
        },
        authentication::decision::ADD,
    );
}

#[cfg(feature = "olap")]
pub async fn create_organization(
    state: SessionState,
    req: api::OrganizationCreateRequest,
) -> RouterResponse<api::OrganizationResponse> {
    let db_organization = ForeignFrom::foreign_from(req);
    state
        .accounts_store
        .insert_organization(db_organization)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError {
            message: "Organization with the given organization_name already exists".to_string(),
        })
        .attach_printable("Error when creating organization")
        .map(ForeignFrom::foreign_from)
        .map(service_api::ApplicationResponse::Json)
}

#[cfg(feature = "olap")]
pub async fn update_organization(
    state: SessionState,
    org_id: api::OrganizationId,
    req: api::OrganizationUpdateRequest,
) -> RouterResponse<api::OrganizationResponse> {
    let organization_update = diesel_models::organization::OrganizationUpdate::Update {
        organization_name: req.organization_name,
        organization_details: req.organization_details,
        metadata: req.metadata,
        platform_merchant_id: req.platform_merchant_id,
    };
    state
        .accounts_store
        .update_organization_by_org_id(&org_id.organization_id, organization_update)
        .await
        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
            message: "organization with the given id does not exist".to_string(),
        })
        .attach_printable(format!(
            "Failed to update organization with organization_id: {:?}",
            org_id.organization_id
        ))
        .map(ForeignFrom::foreign_from)
        .map(service_api::ApplicationResponse::Json)
}

#[cfg(feature = "olap")]
pub async fn get_organization(
    state: SessionState,
    org_id: api::OrganizationId,
) -> RouterResponse<api::OrganizationResponse> {
    #[cfg(all(feature = "v1", feature = "olap"))]
    {
        CreateOrValidateOrganization::new(Some(org_id.organization_id))
            .create_or_validate(state.accounts_store.as_ref())
            .await
            .map(ForeignFrom::foreign_from)
            .map(service_api::ApplicationResponse::Json)
    }
    #[cfg(all(feature = "v2", feature = "olap"))]
    {
        CreateOrValidateOrganization::new(org_id.organization_id)
            .create_or_validate(state.accounts_store.as_ref())
            .await
            .map(ForeignFrom::foreign_from)
            .map(service_api::ApplicationResponse::Json)
    }
}

#[cfg(feature = "olap")]
pub async fn create_merchant_account(
    state: SessionState,
    req: api::MerchantAccountCreate,
    org_data_from_auth: Option<authentication::AuthenticationDataWithOrg>,
) -> RouterResponse<api::MerchantAccountResponse> {
    #[cfg(feature = "keymanager_create")]
    use common_utils::{keymanager, types::keymanager::EncryptionTransferRequest};

    let db = state.store.as_ref();

    let key = services::generate_aes256_key()
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to generate aes 256 key")?;

    let master_key = db.get_master_key();

    let key_manager_state: &KeyManagerState = &(&state).into();
    let merchant_id = req.get_merchant_reference_id();
    let identifier = km_types::Identifier::Merchant(merchant_id.clone());
    #[cfg(feature = "keymanager_create")]
    {
        use base64::Engine;

        use crate::consts::BASE64_ENGINE;

        if key_manager_state.enabled {
            keymanager::transfer_key_to_key_manager(
                key_manager_state,
                EncryptionTransferRequest {
                    identifier: identifier.clone(),
                    key: BASE64_ENGINE.encode(key),
                },
            )
            .await
            .change_context(errors::ApiErrorResponse::DuplicateMerchantAccount)
            .attach_printable("Failed to insert key to KeyManager")?;
        }
    }

    let key_store = domain::MerchantKeyStore {
        merchant_id: merchant_id.clone(),
        key: domain_types::crypto_operation(
            key_manager_state,
            type_name!(domain::MerchantKeyStore),
            domain_types::CryptoOperation::Encrypt(key.to_vec().into()),
            identifier.clone(),
            master_key,
        )
        .await
        .and_then(|val| val.try_into_operation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to decrypt data from key store")?,
        created_at: date_time::now(),
    };

    let domain_merchant_account = req
        .create_domain_model_from_request(
            &state,
            key_store.clone(),
            &merchant_id,
            org_data_from_auth,
        )
        .await?;
    let key_manager_state = &(&state).into();
    db.insert_merchant_key_store(
        key_manager_state,
        key_store.clone(),
        &master_key.to_vec().into(),
    )
    .await
    .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?;

    let merchant_account = db
        .insert_merchant(key_manager_state, domain_merchant_account, &key_store)
        .await
        .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?;

    let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
        merchant_account.clone(),
        key_store.clone(),
    )));
    add_publishable_key_to_decision_service(&state, &merchant_context);

    insert_merchant_configs(db, &merchant_id).await?;

    Ok(service_api::ApplicationResponse::Json(
        api::MerchantAccountResponse::foreign_try_from(merchant_account)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed while generating response")?,
    ))
}

#[cfg(feature = "olap")]
#[async_trait::async_trait]
trait MerchantAccountCreateBridge {
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        key: domain::MerchantKeyStore,
        identifier: &id_type::MerchantId,
        org_data_from_auth: Option<authentication::AuthenticationDataWithOrg>,
    ) -> RouterResult<domain::MerchantAccount>;
}

#[cfg(all(feature = "v1", feature = "olap"))]
#[async_trait::async_trait]
impl MerchantAccountCreateBridge for api::MerchantAccountCreate {
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        key_store: domain::MerchantKeyStore,
        identifier: &id_type::MerchantId,
        org_data_from_auth: Option<authentication::AuthenticationDataWithOrg>,
    ) -> RouterResult<domain::MerchantAccount> {
        let db = &*state.accounts_store;
        let publishable_key = create_merchant_publishable_key();

        let primary_business_details = self.get_primary_details_as_value().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "primary_business_details",
            },
        )?;

        let webhook_details = self.webhook_details.clone().map(ForeignInto::foreign_into);

        let pm_collect_link_config = self.get_pm_link_config_as_value().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "pm_collect_link_config",
            },
        )?;

        let merchant_details = self.get_merchant_details_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "merchant_details",
            },
        )?;

        self.parse_routing_algorithm()
            .change_context(errors::ApiErrorResponse::InvalidDataValue {
                field_name: "routing_algorithm",
            })
            .attach_printable("Invalid routing algorithm given")?;

        let metadata = self.get_metadata_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "metadata",
            },
        )?;

        // Get the enable payment response hash as a boolean, where the default value is true
        let enable_payment_response_hash = self.get_enable_payment_response_hash();

        let payment_response_hash_key = self.get_payment_response_hash_key();

        let parent_merchant_id = get_parent_merchant(
            state,
            self.sub_merchants_enabled,
            self.parent_merchant_id.as_ref(),
            &key_store,
        )
        .await?;

        let org_id = match (&self.organization_id, &org_data_from_auth) {
            (Some(req_org_id), Some(auth)) => {
                if req_org_id != &auth.organization_id {
                    return Err(errors::ApiErrorResponse::InvalidRequestData {
                        message: "Mismatched organization_id in request and authenticated context"
                            .to_string(),
                    }
                    .into());
                }
                Some(req_org_id.clone())
            }
            (None, Some(auth)) => Some(auth.organization_id.clone()),
            (req_org_id, _) => req_org_id.clone(),
        };

        let organization = CreateOrValidateOrganization::new(org_id)
            .create_or_validate(db)
            .await?;

        let merchant_account_type = match organization.get_organization_type() {
            OrganizationType::Standard => {
                match self.merchant_account_type.unwrap_or_default() {
                    // Allow only if explicitly Standard or not provided
                    MerchantAccountRequestType::Standard => MerchantAccountType::Standard,
                    MerchantAccountRequestType::Connected => {
                        return Err(errors::ApiErrorResponse::InvalidRequestData {
                            message:
                                "Merchant account type must be Standard for a Standard Organization"
                                    .to_string(),
                        }
                        .into());
                    }
                }
            }

            OrganizationType::Platform => {
                let accounts = state
                    .store
                    .list_merchant_accounts_by_organization_id(
                        &state.into(),
                        &organization.get_organization_id(),
                    )
                    .await
                    .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

                let platform_account_exists = accounts
                    .iter()
                    .any(|account| account.merchant_account_type == MerchantAccountType::Platform);

                if accounts.is_empty() || !platform_account_exists {
                    // First merchant in a Platform org must be Platform
                    MerchantAccountType::Platform
                } else {
                    match self.merchant_account_type.unwrap_or_default() {
                        MerchantAccountRequestType::Standard => MerchantAccountType::Standard,
                        MerchantAccountRequestType::Connected => {
                            if state.conf.platform.allow_connected_merchants {
                                MerchantAccountType::Connected
                            } else {
                                return Err(errors::ApiErrorResponse::InvalidRequestData {
                                    message: "Connected merchant accounts are not allowed"
                                        .to_string(),
                                }
                                .into());
                            }
                        }
                    }
                }
            }
        };

        let key = key_store.key.clone().into_inner();
        let key_manager_state = state.into();

        let merchant_account = async {
            Ok::<_, error_stack::Report<common_utils::errors::CryptoError>>(
                domain::MerchantAccountSetter {
                    merchant_id: identifier.clone(),
                    merchant_name: self
                        .merchant_name
                        .async_lift(|inner| async {
                            domain_types::crypto_operation(
                                &key_manager_state,
                                type_name!(domain::MerchantAccount),
                                domain_types::CryptoOperation::EncryptOptional(inner),
                                km_types::Identifier::Merchant(key_store.merchant_id.clone()),
                                key.peek(),
                            )
                            .await
                            .and_then(|val| val.try_into_optionaloperation())
                        })
                        .await?,
                    merchant_details: merchant_details
                        .async_lift(|inner| async {
                            domain_types::crypto_operation(
                                &key_manager_state,
                                type_name!(domain::MerchantAccount),
                                domain_types::CryptoOperation::EncryptOptional(inner),
                                km_types::Identifier::Merchant(key_store.merchant_id.clone()),
                                key.peek(),
                            )
                            .await
                            .and_then(|val| val.try_into_optionaloperation())
                        })
                        .await?,
                    return_url: self.return_url.map(|a| a.to_string()),
                    webhook_details,
                    routing_algorithm: Some(serde_json::json!({
                        "algorithm_id": null,
                        "timestamp": 0
                    })),
                    sub_merchants_enabled: self.sub_merchants_enabled,
                    parent_merchant_id,
                    enable_payment_response_hash,
                    payment_response_hash_key,
                    redirect_to_merchant_with_http_post: self
                        .redirect_to_merchant_with_http_post
                        .unwrap_or_default(),
                    publishable_key,
                    locker_id: self.locker_id,
                    metadata,
                    storage_scheme: MerchantStorageScheme::PostgresOnly,
                    primary_business_details,
                    created_at: date_time::now(),
                    modified_at: date_time::now(),
                    intent_fulfillment_time: None,
                    frm_routing_algorithm: self.frm_routing_algorithm,
                    #[cfg(feature = "payouts")]
                    payout_routing_algorithm: self.payout_routing_algorithm,
                    #[cfg(not(feature = "payouts"))]
                    payout_routing_algorithm: None,
                    organization_id: organization.get_organization_id(),
                    is_recon_enabled: false,
                    default_profile: None,
                    recon_status: diesel_models::enums::ReconStatus::NotRequested,
                    payment_link_config: None,
                    pm_collect_link_config,
                    version: common_types::consts::API_VERSION,
                    is_platform_account: false,
                    product_type: self.product_type,
                    merchant_account_type,
                },
            )
        }
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)?;

        let mut domain_merchant_account = domain::MerchantAccount::from(merchant_account);

        CreateProfile::new(self.primary_business_details.clone())
            .create_profiles(state, &mut domain_merchant_account, &key_store)
            .await?;

        Ok(domain_merchant_account)
    }
}

#[cfg(feature = "olap")]
enum CreateOrValidateOrganization {
    /// Creates a new organization
    #[cfg(feature = "v1")]
    Create,
    /// Validates if this organization exists in the records
    Validate {
        organization_id: id_type::OrganizationId,
    },
}

#[cfg(feature = "olap")]
impl CreateOrValidateOrganization {
    #[cfg(all(feature = "v1", feature = "olap"))]
    /// Create an action to either create or validate the given organization_id
    /// If organization_id is passed, then validate if this organization exists
    /// If not passed, create a new organization
    fn new(organization_id: Option<id_type::OrganizationId>) -> Self {
        if let Some(organization_id) = organization_id {
            Self::Validate { organization_id }
        } else {
            Self::Create
        }
    }

    #[cfg(all(feature = "v2", feature = "olap"))]
    /// Create an action to validate the provided organization_id
    fn new(organization_id: id_type::OrganizationId) -> Self {
        Self::Validate { organization_id }
    }

    #[cfg(feature = "olap")]
    /// Apply the action, whether to create the organization or validate the given organization_id
    async fn create_or_validate(
        &self,
        db: &dyn AccountsStorageInterface,
    ) -> RouterResult<diesel_models::organization::Organization> {
        match self {
            #[cfg(feature = "v1")]
            Self::Create => {
                let new_organization = api_models::organization::OrganizationNew::new(
                    OrganizationType::Standard,
                    None,
                );
                let db_organization = ForeignFrom::foreign_from(new_organization);
                db.insert_organization(db_organization)
                    .await
                    .to_duplicate_response(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Error when creating organization")
            }
            Self::Validate { organization_id } => db
                .find_organization_by_org_id(organization_id)
                .await
                .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
                    message: "organization with the given id does not exist".to_string(),
                }),
        }
    }
}

#[cfg(all(feature = "v1", feature = "olap"))]
enum CreateProfile {
    /// Create profiles from primary business details
    /// If there is only one profile created, then set this profile as default
    CreateFromPrimaryBusinessDetails {
        primary_business_details: Vec<admin_types::PrimaryBusinessDetails>,
    },
    /// Create a default profile, set this as default profile
    CreateDefaultProfile,
}

#[cfg(all(feature = "v1", feature = "olap"))]
impl CreateProfile {
    /// Create a new profile action from the given information
    /// If primary business details exist, then create profiles from them
    /// If primary business details are empty, then create default profile
    fn new(primary_business_details: Option<Vec<admin_types::PrimaryBusinessDetails>>) -> Self {
        match primary_business_details {
            Some(primary_business_details) if !primary_business_details.is_empty() => {
                Self::CreateFromPrimaryBusinessDetails {
                    primary_business_details,
                }
            }
            _ => Self::CreateDefaultProfile,
        }
    }

    async fn create_profiles(
        &self,
        state: &SessionState,
        merchant_account: &mut domain::MerchantAccount,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<()> {
        match self {
            Self::CreateFromPrimaryBusinessDetails {
                primary_business_details,
            } => {
                let business_profiles = Self::create_profiles_for_each_business_details(
                    state,
                    merchant_account.clone(),
                    primary_business_details,
                    key_store,
                )
                .await?;

                // Update the default business profile in merchant account
                if business_profiles.len() == 1 {
                    merchant_account.default_profile = business_profiles
                        .first()
                        .map(|business_profile| business_profile.get_id().to_owned())
                }
            }
            Self::CreateDefaultProfile => {
                let business_profile = self
                    .create_default_business_profile(state, merchant_account.clone(), key_store)
                    .await?;

                merchant_account.default_profile = Some(business_profile.get_id().to_owned());
            }
        }

        Ok(())
    }

    /// Create default profile
    async fn create_default_business_profile(
        &self,
        state: &SessionState,
        merchant_account: domain::MerchantAccount,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<domain::Profile> {
        let business_profile = create_and_insert_business_profile(
            state,
            api_models::admin::ProfileCreate::default(),
            merchant_account.clone(),
            key_store,
        )
        .await?;

        Ok(business_profile)
    }

    /// Create profile for each primary_business_details,
    /// If there is no default profile in merchant account and only one primary_business_detail
    /// is available, then create a default profile.
    async fn create_profiles_for_each_business_details(
        state: &SessionState,
        merchant_account: domain::MerchantAccount,
        primary_business_details: &Vec<admin_types::PrimaryBusinessDetails>,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<Vec<domain::Profile>> {
        let mut business_profiles_vector = Vec::with_capacity(primary_business_details.len());

        // This must ideally be run in a transaction,
        // if there is an error in inserting some profile, because of unique constraints
        // the whole query must be rolled back
        for business_profile in primary_business_details {
            let profile_name =
                format!("{}_{}", business_profile.country, business_profile.business);

            let profile_create_request = api_models::admin::ProfileCreate {
                profile_name: Some(profile_name),
                ..Default::default()
            };

            create_and_insert_business_profile(
                state,
                profile_create_request,
                merchant_account.clone(),
                key_store,
            )
            .await
            .map_err(|profile_insert_error| {
                logger::warn!("Profile already exists {profile_insert_error:?}");
            })
            .map(|business_profile| business_profiles_vector.push(business_profile))
            .ok();
        }

        Ok(business_profiles_vector)
    }
}

#[cfg(all(feature = "v2", feature = "olap"))]
#[async_trait::async_trait]
impl MerchantAccountCreateBridge for api::MerchantAccountCreate {
    async fn create_domain_model_from_request(
        self,
        state: &SessionState,
        key_store: domain::MerchantKeyStore,
        identifier: &id_type::MerchantId,
        _org_data: Option<authentication::AuthenticationDataWithOrg>,
    ) -> RouterResult<domain::MerchantAccount> {
        let publishable_key = create_merchant_publishable_key();
        let db = &*state.accounts_store;

        let metadata = self.get_metadata_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "metadata",
            },
        )?;

        let merchant_details = self.get_merchant_details_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "merchant_details",
            },
        )?;

        let organization = CreateOrValidateOrganization::new(self.organization_id.clone())
            .create_or_validate(db)
            .await?;

        // V2 currently supports creation of Standard merchant accounts only, irrespective of organization type
        let merchant_account_type = MerchantAccountType::Standard;

        let key = key_store.key.into_inner();
        let id = identifier.to_owned();
        let key_manager_state = state.into();
        let identifier = km_types::Identifier::Merchant(id.clone());

        async {
            Ok::<_, error_stack::Report<common_utils::errors::CryptoError>>(
                domain::MerchantAccount::from(domain::MerchantAccountSetter {
                    id,
                    merchant_name: Some(
                        domain_types::crypto_operation(
                            &key_manager_state,
                            type_name!(domain::MerchantAccount),
                            domain_types::CryptoOperation::Encrypt(
                                self.merchant_name
                                    .map(|merchant_name| merchant_name.into_inner()),
                            ),
                            identifier.clone(),
                            key.peek(),
                        )
                        .await
                        .and_then(|val| val.try_into_operation())?,
                    ),
                    merchant_details: merchant_details
                        .async_lift(|inner| async {
                            domain_types::crypto_operation(
                                &key_manager_state,
                                type_name!(domain::MerchantAccount),
                                domain_types::CryptoOperation::EncryptOptional(inner),
                                identifier.clone(),
                                key.peek(),
                            )
                            .await
                            .and_then(|val| val.try_into_optionaloperation())
                        })
                        .await?,
                    publishable_key,
                    metadata,
                    storage_scheme: MerchantStorageScheme::PostgresOnly,
                    created_at: date_time::now(),
                    modified_at: date_time::now(),
                    organization_id: organization.get_organization_id(),
                    recon_status: diesel_models::enums::ReconStatus::NotRequested,
                    is_platform_account: false,
                    version: common_types::consts::API_VERSION,
                    product_type: self.product_type,
                    merchant_account_type,
                }),
            )
        }
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("failed to encrypt merchant details")
    }
}

#[cfg(all(feature = "olap", feature = "v2"))]
pub async fn list_merchant_account(
    state: SessionState,
    organization_id: api_models::organization::OrganizationId,
) -> RouterResponse<Vec<api::MerchantAccountResponse>> {
    let merchant_accounts = state
        .store
        .list_merchant_accounts_by_organization_id(
            &(&state).into(),
            &organization_id.organization_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_accounts = merchant_accounts
        .into_iter()
        .map(|merchant_account| {
            api::MerchantAccountResponse::foreign_try_from(merchant_account).change_context(
                errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "merchant_account",
                },
            )
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(services::ApplicationResponse::Json(merchant_accounts))
}

#[cfg(all(feature = "olap", feature = "v1"))]
pub async fn list_merchant_account(
    state: SessionState,
    req: api_models::admin::MerchantAccountListRequest,
    org_data_from_auth: Option<authentication::AuthenticationDataWithOrg>,
) -> RouterResponse<Vec<api::MerchantAccountResponse>> {
    if let Some(auth) = org_data_from_auth {
        if auth.organization_id != req.organization_id {
            return Err(errors::ApiErrorResponse::InvalidRequestData {
                message: "Organization ID in request and authentication do not match".to_string(),
            }
            .into());
        }
    }
    let merchant_accounts = state
        .store
        .list_merchant_accounts_by_organization_id(&(&state).into(), &req.organization_id)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_accounts = merchant_accounts
        .into_iter()
        .map(|merchant_account| {
            api::MerchantAccountResponse::foreign_try_from(merchant_account).change_context(
                errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "merchant_account",
                },
            )
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(services::ApplicationResponse::Json(merchant_accounts))
}

pub async fn get_merchant_account(
    state: SessionState,
    req: api::MerchantId,
    _profile_id: Option<id_type::ProfileId>,
) -> RouterResponse<api::MerchantAccountResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let key_store = db
        .get_merchant_key_store_by_merchant_id(
            key_manager_state,
            &req.merchant_id,
            &db.get_master_key().to_vec().into(),
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let merchant_account = db
        .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    Ok(service_api::ApplicationResponse::Json(
        api::MerchantAccountResponse::foreign_try_from(merchant_account)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to construct response")?,
    ))
}

#[cfg(feature = "v1")]
/// For backwards compatibility, whenever new business labels are passed in
/// primary_business_details, create a profile
pub async fn create_profile_from_business_labels(
    state: &SessionState,
    db: &dyn StorageInterface,
    key_store: &domain::MerchantKeyStore,
    merchant_id: &id_type::MerchantId,
    new_business_details: Vec<admin_types::PrimaryBusinessDetails>,
) -> RouterResult<()> {
    let key_manager_state = &state.into();
    let merchant_account = db
        .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, key_store)
        .await
        .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;

    let old_business_details = merchant_account
        .primary_business_details
        .clone()
        .parse_value::<Vec<admin_types::PrimaryBusinessDetails>>("PrimaryBusinessDetails")
        .change_context(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "routing_algorithm",
        })
        .attach_printable("Invalid routing algorithm given")?;

    // find the diff between two vectors
    let business_profiles_to_create = new_business_details
        .into_iter()
        .filter(|business_details| !old_business_details.contains(business_details))
        .collect::<Vec<_>>();

    for business_profile in business_profiles_to_create {
        let profile_name = format!("{}_{}", business_profile.country, business_profile.business);

        let profile_create_request = admin_types::ProfileCreate {
            profile_name: Some(profile_name),
            ..Default::default()
        };

        let profile_create_result = create_and_insert_business_profile(
            state,
            profile_create_request,
            merchant_account.clone(),
            key_store,
        )
        .await
        .map_err(|profile_insert_error| {
            // If there is any duplicate error, we need not take any action
            logger::warn!("Profile already exists {profile_insert_error:?}");
        });

        // If a profile is created, then unset the default profile
        if profile_create_result.is_ok() && merchant_account.default_profile.is_some() {
            let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile;
            db.update_merchant(
                key_manager_state,
                merchant_account.clone(),
                unset_default_profile,
                key_store,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
        }
    }

    Ok(())
}

#[cfg(any(feature = "v1", feature = "v2", feature = "olap"))]
#[async_trait::async_trait]
trait MerchantAccountUpdateBridge {
    async fn get_update_merchant_object(
        self,
        state: &SessionState,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<storage::MerchantAccountUpdate>;
}

#[cfg(feature = "v1")]
#[async_trait::async_trait]
impl MerchantAccountUpdateBridge for api::MerchantAccountUpdate {
    async fn get_update_merchant_object(
        self,
        state: &SessionState,
        merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<storage::MerchantAccountUpdate> {
        let key_manager_state = &state.into();
        let key = key_store.key.get_inner().peek();

        let db = state.store.as_ref();

        let primary_business_details = self.get_primary_details_as_value().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "primary_business_details",
            },
        )?;

        let pm_collect_link_config = self.get_pm_link_config_as_value().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "pm_collect_link_config",
            },
        )?;

        let merchant_details = self.get_merchant_details_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "merchant_details",
            },
        )?;

        self.parse_routing_algorithm().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "routing_algorithm",
            },
        )?;

        let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);

        let parent_merchant_id = get_parent_merchant(
            state,
            self.sub_merchants_enabled,
            self.parent_merchant_id.as_ref(),
            key_store,
        )
        .await?;

        // This supports changing the business profile by passing in the profile_id
        let business_profile_id_update = if let Some(ref profile_id) = self.default_profile {
            // Validate whether profile_id passed in request is valid and is linked to the merchant
            core_utils::validate_and_get_business_profile(
                state.store.as_ref(),
                key_manager_state,
                key_store,
                Some(profile_id),
                merchant_id,
            )
            .await?
            .map(|business_profile| Some(business_profile.get_id().to_owned()))
        } else {
            None
        };

        #[cfg(any(feature = "v1", feature = "v2"))]
        // In order to support backwards compatibility, if a business_labels are passed in the update
        // call, then create new profiles with the profile_name as business_label
        self.primary_business_details
            .clone()
            .async_map(|primary_business_details| async {
                let _ = create_profile_from_business_labels(
                    state,
                    db,
                    key_store,
                    merchant_id,
                    primary_business_details,
                )
                .await;
            })
            .await;

        let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone());
        Ok(storage::MerchantAccountUpdate::Update {
            merchant_name: self
                .merchant_name
                .map(Secret::new)
                .async_lift(|inner| async {
                    domain_types::crypto_operation(
                        key_manager_state,
                        type_name!(storage::MerchantAccount),
                        domain_types::CryptoOperation::EncryptOptional(inner),
                        identifier.clone(),
                        key,
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to encrypt merchant name")?,
            merchant_details: merchant_details
                .async_lift(|inner| async {
                    domain_types::crypto_operation(
                        key_manager_state,
                        type_name!(storage::MerchantAccount),
                        domain_types::CryptoOperation::EncryptOptional(inner),
                        identifier.clone(),
                        key,
                    )
                    .await
                    .and_then(|val| val.try_into_optionaloperation())
                })
                .await
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .attach_printable("Unable to encrypt merchant details")?,
            return_url: self.return_url.map(|a| a.to_string()),
            webhook_details,
            sub_merchants_enabled: self.sub_merchants_enabled,
            parent_merchant_id,
            enable_payment_response_hash: self.enable_payment_response_hash,
            payment_response_hash_key: self.payment_response_hash_key,
            redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
            locker_id: self.locker_id,
            metadata: self.metadata,
            publishable_key: None,
            primary_business_details,
            frm_routing_algorithm: self.frm_routing_algorithm,
            intent_fulfillment_time: None,
            #[cfg(feature = "payouts")]
            payout_routing_algorithm: self.payout_routing_algorithm,
            #[cfg(not(feature = "payouts"))]
            payout_routing_algorithm: None,
            default_profile: business_profile_id_update,
            payment_link_config: None,
            pm_collect_link_config,
            routing_algorithm: self.routing_algorithm,
        })
    }
}

#[cfg(feature = "v2")]
#[async_trait::async_trait]
impl MerchantAccountUpdateBridge for api::MerchantAccountUpdate {
    async fn get_update_merchant_object(
        self,
        state: &SessionState,
        _merchant_id: &id_type::MerchantId,
        key_store: &domain::MerchantKeyStore,
    ) -> RouterResult<storage::MerchantAccountUpdate> {
        let key_manager_state = &state.into();
        let key = key_store.key.get_inner().peek();

        let merchant_details = self.get_merchant_details_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "merchant_details",
            },
        )?;

        let metadata = self.get_metadata_as_secret().change_context(
            errors::ApiErrorResponse::InvalidDataValue {
                field_name: "metadata",
            },
        )?;


// Implementation: impl Hypersense
// File: crates/router/src/routes/app.rs
// Module: router
// Methods: 1 total (1 public)

impl Hypersense


// Implementation: impl EmailData for for ReconActivation
// File: crates/router/src/services/email/types.rs
// Module: router
// Methods: 1 total (0 public)

impl EmailData for for ReconActivation


// Implementation: impl PartialEq for for ApiEventMetricsBucketIdentifier
// File: crates/api_models/src/analytics/api_event.rs
// Module: api_models
// Methods: 1 total (0 public)

impl PartialEq for for ApiEventMetricsBucketIdentifier


// Struct: GetPaymentMethodResponse
// File: crates/router/src/core/payment_methods/transformers.rs
// Module: router
// Implementations: 0

pub struct GetPaymentMethodResponse


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

pub struct AirwallexRedirectNextAction


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

impl api::PaymentSync for for Paypal


// Implementation: impl PayoutAttempt
// File: crates/diesel_models/src/query/payout_attempt.rs
// Module: diesel_models
// Methods: 8 total (0 public)

impl PayoutAttempt


// Function: generate_authorization_signature
// File: crates/hyperswitch_connectors/src/connectors/authipay.rs
// Module: hyperswitch_connectors

pub fn generate_authorization_signature(
        &self,
        auth: authipay::AuthipayAuthType,
        request_id: &str,
        payload: &str,
        timestamp: i128,
    ) -> CustomResult<String, errors::ConnectorError>


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

pub struct NuveiPaymentsRequest


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

pub mod accumulator;
mod core;

pub mod filters;
pub mod metrics;
pub mod types;
pub use accumulator::{FrmMetricAccumulator, FrmMetricsAccumulator};

pub use self::core::{get_filters, get_metrics};


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

pub struct PaymentFilters


// Trait: KvStorePartition
// File: crates/storage_impl/src/redis/kv_store.rs
// Module: storage_impl

pub trait KvStorePartition


// Function: generate
// File: crates/common_utils/src/id_type/global_id/customer.rs
// Module: common_utils

// Documentation: Generate a new GlobalCustomerId from a cell id

pub fn generate(cell_id: &crate::id_type::CellId) -> Self


// Struct: HyperwalletPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/hyperwallet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct HyperwalletPaymentsResponse


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

impl ThemeInterface for for KafkaStore


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

pub struct XenditMetadata


// Function: signout_hypersense_token
// File: crates/router/src/routes/hypersense.rs
// Module: router

pub fn signout_hypersense_token(
    state: web::Data<AppState>,
    http_req: HttpRequest,
    json_payload: web::Json<external_service_auth_api::ExternalSignoutTokenRequest>,
) -> HttpResponse


// Struct: KafkaRefundEvent
// File: crates/router/src/services/kafka/refund_event.rs
// Module: router
// Implementations: 4
// Traits: super::KafkaMessage, super::KafkaMessage

pub struct KafkaRefundEvent<'a>


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

pub struct TokenizedWalletValue1


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

impl api::PaymentToken for for Xendit


// Function: get_payments_aggregates_profile
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn get_payments_aggregates_profile(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<common_utils::types::TimeRange>,
) -> impl Responder


// Function: profile_payments_list_by_filter
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn profile_payments_list_by_filter(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<payment_types::PaymentListFilterConstraints>,
) -> impl Responder


    ) -> errors::CustomResult<String, errors::ValidationError> {
        match self {
            Self::ConnectorTransactionId(txn_id) => Ok(txn_id.to_string()),
            _ => Err(errors::ValidationError::IncorrectValueProvided {
                field_name: "connector_transaction_id",
            })
            .attach_printable("Expected connector transaction ID not found"),
        }
    }
}

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct SurchargeDetails {
    /// original_amount
    pub original_amount: MinorUnit,
    /// surcharge value
    pub surcharge: common_utils::types::Surcharge,
    /// tax on surcharge value
    pub tax_on_surcharge:
        Option<common_utils::types::Percentage<{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH }>>,
    /// surcharge amount for this payment
    pub surcharge_amount: MinorUnit,
    /// tax on surcharge amount for this payment
    pub tax_on_surcharge_amount: MinorUnit,
}

impl SurchargeDetails {
    pub fn get_total_surcharge_amount(&self) -> MinorUnit {
        self.surcharge_amount + self.tax_on_surcharge_amount
    }
}

#[cfg(feature = "v1")]
impl
    From<(
        &RequestSurchargeDetails,
        &payments::payment_attempt::PaymentAttempt,
    )> for SurchargeDetails
{
    fn from(
        (request_surcharge_details, payment_attempt): (
            &RequestSurchargeDetails,
            &payments::payment_attempt::PaymentAttempt,
        ),
    ) -> Self {
        let surcharge_amount = request_surcharge_details.surcharge_amount;
        let tax_on_surcharge_amount = request_surcharge_details.tax_amount.unwrap_or_default();
        Self {
            original_amount: payment_attempt.net_amount.get_order_amount(),
            surcharge: common_utils::types::Surcharge::Fixed(
                request_surcharge_details.surcharge_amount,
            ),
            tax_on_surcharge: None,
            surcharge_amount,
            tax_on_surcharge_amount,
        }
    }
}

#[cfg(feature = "v2")]
impl
    From<(
        &RequestSurchargeDetails,
        &payments::payment_attempt::PaymentAttempt,
    )> for SurchargeDetails
{
    fn from(
        (_request_surcharge_details, _payment_attempt): (
            &RequestSurchargeDetails,
            &payments::payment_attempt::PaymentAttempt,
        ),
    ) -> Self {
        todo!()
    }
}

#[derive(Debug, Clone)]
pub struct AuthenticationData {
    pub eci: Option<String>,
    pub cavv: Secret<String>,
    pub threeds_server_transaction_id: Option<String>,
    pub message_version: Option<common_utils::types::SemanticVersion>,
    pub ds_trans_id: Option<String>,
    pub created_at: time::PrimitiveDateTime,
    pub challenge_code: Option<String>,
    pub challenge_cancel: Option<String>,
    pub challenge_code_reason: Option<String>,
    pub message_extension: Option<pii::SecretSerdeValue>,
    pub acs_trans_id: Option<String>,
    pub authentication_type: Option<common_enums::DecoupledAuthenticationType>,
}

#[derive(Debug, Clone)]
pub struct RefundsData {
    pub refund_id: String,
    pub connector_transaction_id: String,

    pub connector_refund_id: Option<String>,
    pub currency: storage_enums::Currency,
    /// Amount for the payment against which this refund is issued
    pub payment_amount: i64,

    pub reason: Option<String>,
    pub webhook_url: Option<String>,
    /// Amount to be refunded
    pub refund_amount: i64,
    /// Arbitrary metadata required for refund
    pub connector_metadata: Option<serde_json::Value>,
    /// refund method
    pub refund_connector_metadata: Option<pii::SecretSerdeValue>,
    pub browser_info: Option<BrowserInformation>,
    /// Charges associated with the payment
    pub split_refunds: Option<SplitRefundsRequest>,

    // New amount for amount frame work
    pub minor_payment_amount: MinorUnit,
    pub minor_refund_amount: MinorUnit,
    pub integrity_object: Option<RefundIntegrityObject>,
    pub refund_status: storage_enums::RefundStatus,
    pub merchant_account_id: Option<Secret<String>>,
    pub merchant_config_currency: Option<storage_enums::Currency>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub additional_payment_method_data: Option<AdditionalPaymentData>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct RefundIntegrityObject {
    /// refund currency
    pub currency: storage_enums::Currency,
    /// refund amount
    pub refund_amount: MinorUnit,
}

#[derive(Debug, serde::Deserialize, Clone)]
pub enum SplitRefundsRequest {
    StripeSplitRefund(StripeSplitRefund),
    AdyenSplitRefund(common_types::domain::AdyenSplitData),
    XenditSplitRefund(common_types::domain::XenditSplitSubMerchantData),
}

#[derive(Debug, serde::Deserialize, Clone)]
pub struct StripeSplitRefund {
    pub charge_id: String,
    pub transfer_account_id: String,
    pub charge_type: api_models::enums::PaymentChargeType,
    pub options: ChargeRefundsOptions,
}

#[derive(Debug, serde::Deserialize, Clone)]
pub struct ChargeRefunds {
    pub charge_id: String,
    pub transfer_account_id: String,
    pub charge_type: api_models::enums::PaymentChargeType,
    pub options: ChargeRefundsOptions,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum ChargeRefundsOptions {
    Destination(DestinationChargeRefund),
    Direct(DirectChargeRefund),
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DirectChargeRefund {
    pub revert_platform_fee: bool,
}

#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DestinationChargeRefund {
    pub revert_platform_fee: bool,
    pub revert_transfer: bool,
}

#[derive(Debug, Clone)]
pub struct AccessTokenAuthenticationRequestData {
    pub auth_creds: router_data::ConnectorAuthType,
}

impl TryFrom<router_data::ConnectorAuthType> for AccessTokenAuthenticationRequestData {
    type Error = ApiErrorResponse;
    fn try_from(connector_auth: router_data::ConnectorAuthType) -> Result<Self, Self::Error> {
        Ok(Self {
            auth_creds: connector_auth,
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccessTokenRequestData {
    pub app_id: Secret<String>,
    pub id: Option<Secret<String>>,
    pub authentication_token: Option<AccessTokenAuthenticationResponse>,
    // Add more keys if required
}

// This is for backward compatibility
impl TryFrom<router_data::ConnectorAuthType> for AccessTokenRequestData {
    type Error = ApiErrorResponse;
    fn try_from(connector_auth: router_data::ConnectorAuthType) -> Result<Self, Self::Error> {
        match connector_auth {
            router_data::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                app_id: api_key,
                id: None,
                authentication_token: None,
            }),
            router_data::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                app_id: api_key,
                id: Some(key1),
                authentication_token: None,
            }),
            router_data::ConnectorAuthType::SignatureKey { api_key, key1, .. } => Ok(Self {
                app_id: api_key,
                id: Some(key1),
                authentication_token: None,
            }),
            router_data::ConnectorAuthType::MultiAuthKey { api_key, key1, .. } => Ok(Self {
                app_id: api_key,
                id: Some(key1),
                authentication_token: None,
            }),

            _ => Err(ApiErrorResponse::InvalidDataValue {
                field_name: "connector_account_details",
            }),
        }
    }
}

impl
    TryFrom<(
        router_data::ConnectorAuthType,
        Option<AccessTokenAuthenticationResponse>,
    )> for AccessTokenRequestData
{
    type Error = ApiErrorResponse;
    fn try_from(
        (connector_auth, authentication_token): (
            router_data::ConnectorAuthType,
            Option<AccessTokenAuthenticationResponse>,
        ),
    ) -> Result<Self, Self::Error> {
        let mut access_token_request_data = Self::try_from(connector_auth)?;
        access_token_request_data.authentication_token = authentication_token;
        Ok(access_token_request_data)
    }
}

#[derive(Default, Debug, Clone)]
pub struct AcceptDisputeRequestData {
    pub dispute_id: String,
    pub connector_dispute_id: String,
    pub dispute_status: storage_enums::DisputeStatus,
}

#[derive(Default, Debug, Clone)]
pub struct DefendDisputeRequestData {
    pub dispute_id: String,
    pub connector_dispute_id: String,
}

#[derive(Default, Debug, Clone)]
pub struct SubmitEvidenceRequestData {
    pub dispute_id: String,
    pub dispute_status: storage_enums::DisputeStatus,
    pub connector_dispute_id: String,
    pub access_activity_log: Option<String>,
    pub billing_address: Option<String>,
    //cancellation policy
    pub cancellation_policy: Option<Vec<u8>>,
    pub cancellation_policy_file_type: Option<String>,
    pub cancellation_policy_provider_file_id: Option<String>,
    pub cancellation_policy_disclosure: Option<String>,
    pub cancellation_rebuttal: Option<String>,
    //customer communication
    pub customer_communication: Option<Vec<u8>>,
    pub customer_communication_file_type: Option<String>,
    pub customer_communication_provider_file_id: Option<String>,
    pub customer_email_address: Option<String>,
    pub customer_name: Option<String>,
    pub customer_purchase_ip: Option<String>,
    //customer signature
    pub customer_signature: Option<Vec<u8>>,
    pub customer_signature_file_type: Option<String>,
    pub customer_signature_provider_file_id: Option<String>,
    //product description
    pub product_description: Option<String>,
    //receipts
    pub receipt: Option<Vec<u8>>,
    pub receipt_file_type: Option<String>,
    pub receipt_provider_file_id: Option<String>,
    //refund policy
    pub refund_policy: Option<Vec<u8>>,
    pub refund_policy_file_type: Option<String>,
    pub refund_policy_provider_file_id: Option<String>,
    pub refund_policy_disclosure: Option<String>,
    pub refund_refusal_explanation: Option<String>,
    //service docs
    pub service_date: Option<String>,
    pub service_documentation: Option<Vec<u8>>,
    pub service_documentation_file_type: Option<String>,
    pub service_documentation_provider_file_id: Option<String>,
    //shipping details docs
    pub shipping_address: Option<String>,
    pub shipping_carrier: Option<String>,
    pub shipping_date: Option<String>,
    pub shipping_documentation: Option<Vec<u8>>,
    pub shipping_documentation_file_type: Option<String>,
    pub shipping_documentation_provider_file_id: Option<String>,
    pub shipping_tracking_number: Option<String>,
    //invoice details
    pub invoice_showing_distinct_transactions: Option<Vec<u8>>,
    pub invoice_showing_distinct_transactions_file_type: Option<String>,
    pub invoice_showing_distinct_transactions_provider_file_id: Option<String>,
    //subscription details
    pub recurring_transaction_agreement: Option<Vec<u8>>,
    pub recurring_transaction_agreement_file_type: Option<String>,
    pub recurring_transaction_agreement_provider_file_id: Option<String>,
    //uncategorized details
    pub uncategorized_file: Option<Vec<u8>>,
    pub uncategorized_file_type: Option<String>,
    pub uncategorized_file_provider_file_id: Option<String>,
    pub uncategorized_text: Option<String>,
}

#[derive(Debug, Serialize, Clone)]
pub struct FetchDisputesRequestData {
    pub created_from: time::PrimitiveDateTime,
    pub created_till: time::PrimitiveDateTime,
}

#[derive(Clone, Debug)]
pub struct RetrieveFileRequestData {
    pub provider_file_id: String,
    pub connector_dispute_id: Option<String>,
}

#[serde_as]
#[derive(Clone, Debug, serde::Serialize)]
pub struct UploadFileRequestData {
    pub file_key: String,
    #[serde(skip)]
    pub file: Vec<u8>,
    #[serde_as(as = "serde_with::DisplayFromStr")]
    pub file_type: mime::Mime,
    pub file_size: i32,
    pub dispute_id: String,
    pub connector_dispute_id: String,
}

#[cfg(feature = "payouts")]
#[derive(Debug, Clone)]
pub struct PayoutsData {
    pub payout_id: id_type::PayoutId,
    pub amount: i64,
    pub connector_payout_id: Option<String>,
    pub destination_currency: storage_enums::Currency,
    pub source_currency: storage_enums::Currency,
    pub payout_type: Option<storage_enums::PayoutType>,
    pub entity_type: storage_enums::PayoutEntityType,
    pub customer_details: Option<CustomerDetails>,
    pub vendor_details: Option<api_models::payouts::PayoutVendorAccountDetails>,

    // New minor amount for amount framework
    pub minor_amount: MinorUnit,
    pub priority: Option<storage_enums::PayoutSendPriority>,
    pub connector_transfer_method_id: Option<String>,
    pub webhook_url: Option<String>,
}

#[derive(Debug, Default, Clone)]
pub struct CustomerDetails {
    pub customer_id: Option<id_type::CustomerId>,
    pub name: Option<Secret<String, masking::WithType>>,
    pub email: Option<pii::Email>,
    pub phone: Option<Secret<String, masking::WithType>>,
    pub phone_country_code: Option<String>,
    pub tax_registration_id: Option<Secret<String, masking::WithType>>,
}

#[derive(Debug, Clone)]
pub struct VerifyWebhookSourceRequestData {
    pub webhook_headers: actix_web::http::header::HeaderMap,
    pub webhook_body: Vec<u8>,
    pub merchant_secret: api_models::webhooks::ConnectorWebhookSecrets,
}

#[derive(Debug, Clone)]
pub struct MandateRevokeRequestData {
    pub mandate_id: String,
    pub connector_mandate_id: Option<String>,
}

#[derive(Debug, Clone)]
pub struct PaymentsSessionData {
    pub amount: i64,
    pub currency: common_enums::Currency,
    pub country: Option<common_enums::CountryAlpha2>,
    pub surcharge_details: Option<SurchargeDetails>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub email: Option<pii::Email>,
    // Minor Unit amount for amount frame work
    pub minor_amount: MinorUnit,
    pub apple_pay_recurring_details: Option<api_models::payments::ApplePayRecurringPaymentRequest>,
    pub customer_name: Option<Secret<String>>,
    pub order_tax_amount: Option<MinorUnit>,
    pub shipping_cost: Option<MinorUnit>,
    pub metadata: Option<Secret<serde_json::Value>>,
}

#[derive(Debug, Clone, Default)]
pub struct PaymentsTaxCalculationData {
    pub amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub shipping_cost: Option<MinorUnit>,
    pub order_details: Option<Vec<OrderDetailsWithAmount>>,
    pub shipping_address: address::Address,
}

#[derive(Debug, Clone, Default)]
pub struct SdkPaymentsSessionUpdateData {
    pub order_tax_amount: MinorUnit,
    // amount here would include amount, surcharge_amount, order_tax_amount and shipping_cost
    pub amount: MinorUnit,
    /// original amount sent by the merchant
    pub order_amount: MinorUnit,
    pub currency: storage_enums::Currency,
    pub session_id: Option<String>,
    pub shipping_cost: Option<MinorUnit>,
}

#[derive(Debug, Clone)]
pub struct SetupMandateRequestData {
    pub currency: storage_enums::Currency,
    pub payment_method_data: PaymentMethodData,
    pub amount: Option<i64>,
    pub confirm: bool,
    pub statement_descriptor_suffix: Option<String>,
    pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
    pub mandate_id: Option<api_models::payments::MandateIds>,
    pub setup_future_usage: Option<storage_enums::FutureUsage>,
    pub off_session: Option<bool>,
    pub setup_mandate_details: Option<mandates::MandateData>,
    pub router_return_url: Option<String>,
    pub webhook_url: Option<String>,
    pub browser_info: Option<BrowserInformation>,
    pub email: Option<pii::Email>,
    pub customer_name: Option<Secret<String>>,
    pub return_url: Option<String>,
    pub payment_method_type: Option<storage_enums::PaymentMethodType>,
    pub request_incremental_authorization: bool,
    pub metadata: Option<pii::SecretSerdeValue>,
    pub complete_authorize_url: Option<String>,
    pub capture_method: Option<storage_enums::CaptureMethod>,
    pub enrolled_for_3ds: bool,
    pub related_transaction_id: Option<String>,

    // MinorUnit for amount framework
    pub minor_amount: Option<MinorUnit>,
    pub shipping_cost: Option<MinorUnit>,
    pub connector_testing_data: Option<pii::SecretSerdeValue>,
    pub customer_id: Option<id_type::CustomerId>,
    pub enable_partial_authorization:
        Option<common_types::primitive_wrappers::EnablePartialAuthorizationBool>,
    pub payment_channel: Option<storage_enums::PaymentChannel>,
    pub is_stored_credential: Option<bool>,
}

#[derive(Debug, Clone)]
pub struct VaultRequestData {
    pub payment_method_vaulting_data: Option<PaymentMethodVaultingData>,
    pub connector_vault_id: Option<String>,
    pub connector_customer_id: Option<String>,
}

#[derive(Debug, Serialize, Clone)]
pub struct DisputeSyncData {
    pub dispute_id: String,
    pub connector_dispute_id: String,
}


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

use actix_web::{web, HttpRequest, Responder};
use router_env::{instrument, tracing, Flow};

use super::app::AppState;
use crate::{
    core::{
        api_locking,
        webhooks::{self, types},
    },
    services::{api, authentication as auth},
    types::domain,
};

#[instrument(skip_all, fields(flow = ?Flow::IncomingWebhookReceive))]
#[cfg(feature = "v1")]
pub async fn receive_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(common_utils::id_type::MerchantId, String)>,
) -> impl Responder {
    let flow = Flow::IncomingWebhookReceive;
    let (merchant_id, connector_id_or_name) = path.into_inner();

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, auth, _, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            webhooks::incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                req_state,
                &req,
                merchant_context,
                &connector_id_or_name,
                body.clone(),
                false,
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::IncomingRelayWebhookReceive))]
pub async fn receive_incoming_relay_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> impl Responder {
    let flow = Flow::IncomingWebhookReceive;
    let (merchant_id, connector_id) = path.into_inner();
    let is_relay_webhook = true;

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, auth, _, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            webhooks::incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                req_state,
                &req,
                merchant_context,
                connector_id.get_string_repr(),
                body.clone(),
                is_relay_webhook,
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::IncomingRelayWebhookReceive))]
pub async fn receive_incoming_relay_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::ProfileId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> impl Responder {
    let flow = Flow::IncomingWebhookReceive;
    let (merchant_id, profile_id, connector_id) = path.into_inner();
    let is_relay_webhook = true;

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, auth, _, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            webhooks::incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                req_state,
                &req,
                merchant_context,
                auth.profile,
                &connector_id,
                body.clone(),
                is_relay_webhook,
            )
        },
        &auth::MerchantIdAndProfileIdAuth {
            merchant_id,
            profile_id,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[instrument(skip_all, fields(flow = ?Flow::IncomingWebhookReceive))]
#[cfg(feature = "v2")]
pub async fn receive_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    path: web::Path<(
        common_utils::id_type::MerchantId,
        common_utils::id_type::ProfileId,
        common_utils::id_type::MerchantConnectorAccountId,
    )>,
) -> impl Responder {
    let flow = Flow::IncomingWebhookReceive;
    let (merchant_id, profile_id, connector_id) = path.into_inner();

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, auth, _, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            webhooks::incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                req_state,
                &req,
                merchant_context,
                auth.profile,
                &connector_id,
                body.clone(),
                false,
            )
        },
        &auth::MerchantIdAndProfileIdAuth {
            merchant_id,
            profile_id,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::IncomingNetworkTokenWebhookReceive))]
pub async fn receive_network_token_requestor_incoming_webhook<W: types::OutgoingWebhookType>(
    state: web::Data<AppState>,
    req: HttpRequest,
    body: web::Bytes,
    _path: web::Path<String>,
) -> impl Responder {
    let flow = Flow::IncomingNetworkTokenWebhookReceive;

    Box::pin(api::server_wrap(
        flow.clone(),
        state,
        &req,
        (),
        |state, _: (), _, _| {
            webhooks::network_token_incoming_webhooks_wrapper::<W>(
                &flow,
                state.to_owned(),
                &req,
                body.clone(),
            )
        },
        &auth::NoAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


// Struct: AcsProtocolVersion
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AcsProtocolVersion


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

pub struct ChargesPaymentRequest


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

pub struct Card


// Struct: ItaubankPaymentsSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/itaubank/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ItaubankPaymentsSyncResponse


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

pub struct BreadpayPaymentsResponse


// File: crates/hyperswitch_connectors/src/connectors/katapult/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 8

use common_enums::enums;
use common_utils::types::StringMinorUnit;
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};

use crate::types::{RefundsResponseRouterData, ResponseRouterData};

//TODO: Fill the struct with respective fields
pub struct KatapultRouterData<T> {
    pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
    pub router_data: T,
}

impl<T> From<(StringMinorUnit, T)> for KatapultRouterData<T> {
    fn from((amount, item): (StringMinorUnit, T)) -> Self {
        //Todo :  use utils to convert the amount to the type of amount that a connector accepts
        Self {
            amount,
            router_data: item,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, PartialEq)]
pub struct KatapultPaymentsRequest {
    amount: StringMinorUnit,
    card: KatapultCard,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct KatapultCard {
    number: cards::CardNumber,
    expiry_month: Secret<String>,
    expiry_year: Secret<String>,
    cvc: Secret<String>,
    complete: bool,
}

impl TryFrom<&KatapultRouterData<&PaymentsAuthorizeRouterData>> for KatapultPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &KatapultRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "Card payment method not implemented".to_string(),
            )
            .into()),
            _ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
        }
    }
}

//TODO: Fill the struct with respective fields
// Auth Struct
pub struct KatapultAuthType {
    pub(super) api_key: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for KatapultAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
                api_key: api_key.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum KatapultPaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<KatapultPaymentStatus> for common_enums::AttemptStatus {
    fn from(item: KatapultPaymentStatus) -> Self {
        match item {
            KatapultPaymentStatus::Succeeded => Self::Charged,
            KatapultPaymentStatus::Failed => Self::Failure,
            KatapultPaymentStatus::Processing => Self::Authorizing,
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct KatapultPaymentsResponse {
    status: KatapultPaymentStatus,
    id: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, KatapultPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, KatapultPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            status: common_enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: None,
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
// REFUND :
// Type definition for RefundRequest
#[derive(Default, Debug, Serialize)]
pub struct KatapultRefundRequest {
    pub amount: StringMinorUnit,
}

impl<F> TryFrom<&KatapultRouterData<&RefundsRouterData<F>>> for KatapultRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &KatapultRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.amount.to_owned(),
        })
    }
}

// Type definition for Refund Response

#[allow(dead_code)]
#[derive(Debug, Copy, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed => Self::Failure,
            RefundStatus::Processing => Self::Pending,
            //TODO: Review mapping
        }
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct RefundResponse {
    id: String,
    status: RefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>> for 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.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct KatapultErrorResponse {
    pub status_code: u16,
    pub code: String,
    pub message: String,
    pub reason: Option<String>,
    pub network_advice_code: Option<String>,
    pub network_decline_code: Option<String>,
    pub network_error_message: Option<String>,
}


// Module Structure
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types

// Public exports:
pub use bool_wrappers::*;
pub use u32_wrappers::*;


// Implementation: impl super::KafkaMessage for for KafkaPaymentIntent
// File: crates/router/src/services/kafka/payment_intent.rs
// Module: router
// Methods: 2 total (0 public)

impl super::KafkaMessage for for KafkaPaymentIntent


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

impl Helcim


// Function: find_by_merchant_id_attempt_id
// File: crates/diesel_models/src/query/payment_attempt.rs
// Module: diesel_models

pub fn find_by_merchant_id_attempt_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        attempt_id: &str,
    ) -> StorageResult<Self>


// File: crates/hyperswitch_domain_models/src/merchant_key_store.rs
// Module: hyperswitch_domain_models
// Public structs: 1

use common_utils::{
    crypto::Encryptable,
    custom_serde, date_time,
    errors::{CustomResult, ValidationError},
    type_name,
    types::keymanager::{self, KeyManagerState},
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use time::PrimitiveDateTime;

use crate::type_encryption::{crypto_operation, CryptoOperation};

#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantKeyStore {
    pub merchant_id: common_utils::id_type::MerchantId,
    pub key: Encryptable<Secret<Vec<u8>>>,
    #[serde(with = "custom_serde::iso8601")]
    pub created_at: PrimitiveDateTime,
}

#[async_trait::async_trait]
impl super::behaviour::Conversion for MerchantKeyStore {
    type DstType = diesel_models::merchant_key_store::MerchantKeyStore;
    type NewDstType = diesel_models::merchant_key_store::MerchantKeyStoreNew;
    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStore {
            key: self.key.into(),
            merchant_id: self.merchant_id,
            created_at: self.created_at,
        })
    }

    async fn convert_back(
        state: &KeyManagerState,
        item: Self::DstType,
        key: &Secret<Vec<u8>>,
        _key_manager_identifier: keymanager::Identifier,
    ) -> CustomResult<Self, ValidationError>
    where
        Self: Sized,
    {
        let identifier = keymanager::Identifier::Merchant(item.merchant_id.clone());

        Ok(Self {
            key: crypto_operation(
                state,
                type_name!(Self::DstType),
                CryptoOperation::Decrypt(item.key),
                identifier,
                key.peek(),
            )
            .await
            .and_then(|val| val.try_into_operation())
            .change_context(ValidationError::InvalidValue {
                message: "Failed while decrypting customer data".to_string(),
            })?,
            merchant_id: item.merchant_id,
            created_at: item.created_at,
        })
    }

    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
        Ok(diesel_models::merchant_key_store::MerchantKeyStoreNew {
            merchant_id: self.merchant_id,
            key: self.key.into(),
            created_at: date_time::now(),
        })
    }
}


// Struct: GlobepayRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/globepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct GlobepayRefundRequest


// Trait: RouterDataAuthorize
// File: crates/router/src/core/payments/flows/authorize_flow.rs
// Module: router

pub trait RouterDataAuthorize


// Function: generate_id
// File: crates/router/src/utils.rs
// Module: router

pub fn generate_id(length: usize, prefix: &str) -> String


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

pub struct VariableDeletePaymentMethodFromVaultInput


// Function: handle_json_response_deserialization_failure
// File: crates/router/src/utils.rs
// Module: router

pub fn handle_json_response_deserialization_failure(
    res: types::Response,
    connector: &'static str,
) -> CustomResult<types::ErrorResponse, errors::ConnectorError>


// Struct: FrmFulfillmentSignifydApiResponse
// File: crates/router/src/core/fraud_check/types.rs
// Module: router
// Implementations: 0

pub struct FrmFulfillmentSignifydApiResponse


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

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

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


// File: crates/router_env/src/logger/defaults.rs
// Module: router_env

impl Default for super::config::LogFile {
    fn default() -> Self {
        Self {
            enabled: true,
            path: "logs".into(),
            file_name: "debug.log".into(),
            level: super::config::Level(tracing::Level::DEBUG),
            filtering_directive: None,
        }
    }
}

impl Default for super::config::LogConsole {
    fn default() -> Self {
        Self {
            enabled: false,
            level: super::config::Level(tracing::Level::INFO),
            log_format: super::config::LogFormat::Json,
            filtering_directive: None,
        }
    }
}


// Struct: PaymentCancelData
// File: crates/hyperswitch_domain_models/src/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PaymentCancelData<F>


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

pub struct BarclaycardConsumerAuthInformationEnrollmentResponse


// Struct: TsysSearchTransactionRequest
// File: crates/hyperswitch_connectors/src/connectors/tsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TsysSearchTransactionRequest


// Struct: PaypalPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaypalPaymentsRequest


// Struct: TsysResponse
// File: crates/hyperswitch_connectors/src/connectors/tsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TsysResponse


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

pub fn is_operation_allowed<Op: Debug>(operation: &Op) -> bool


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

pub struct OpensearchResultsTotal


// Implementation: impl InterpreterBackend
// File: crates/euclid/src/backend/interpreter.rs
// Module: euclid
// Methods: 7 total (0 public)

impl InterpreterBackend


// Struct: Jpmorgan
// File: crates/hyperswitch_connectors/src/connectors/jpmorgan.rs
// Module: hyperswitch_connectors
// Implementations: 17
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct Jpmorgan

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken ConnectorCommon ConnectorValidation webhooks::IncomingWebhook ConnectorSpecifications


// Function: get_bank_account_creds
// File: crates/router/src/core/pm_auth.rs
// Module: router

pub fn get_bank_account_creds(
    connector: PaymentAuthConnectorData,
    merchant_context: &domain::MerchantContext,
    connector_name: &str,
    access_token: &Secret<String>,
    auth_type: pm_auth_types::ConnectorAuthType,
    state: &SessionState,
    bank_account_id: Option<Secret<String>>,
) -> RouterResult<pm_auth_types::BankAccountCredentialsResponse>


            created_at: storage_model.created_at,
            last_modified_at: storage_model.last_modified_at,
            profile_id: storage_model.profile_id,
            status: storage_model.status,
            attempt_count: storage_model.attempt_count,
            confirm: storage_model.confirm,
            payout_link_id: storage_model.payout_link_id,
            client_secret: storage_model.client_secret,
            priority: storage_model.priority,
        }
    }
}
impl DataModelExt for PayoutsUpdate {
    type StorageModel = DieselPayoutsUpdate;
    fn to_storage_model(self) -> Self::StorageModel {
        match self {
            Self::Update {
                amount,
                destination_currency,
                source_currency,
                description,
                recurring,
                auto_fulfill,
                return_url,
                entity_type,
                metadata,
                profile_id,
                status,
                confirm,
                payout_type,
                address_id,
                customer_id,
            } => DieselPayoutsUpdate::Update {
                amount,
                destination_currency,
                source_currency,
                description,
                recurring,
                auto_fulfill,
                return_url,
                entity_type,
                metadata,
                profile_id,
                status,
                confirm,
                payout_type,
                address_id,
                customer_id,
            },
            Self::PayoutMethodIdUpdate { payout_method_id } => {
                DieselPayoutsUpdate::PayoutMethodIdUpdate { payout_method_id }
            }
            Self::RecurringUpdate { recurring } => {
                DieselPayoutsUpdate::RecurringUpdate { recurring }
            }
            Self::AttemptCountUpdate { attempt_count } => {
                DieselPayoutsUpdate::AttemptCountUpdate { attempt_count }
            }
            Self::StatusUpdate { status } => DieselPayoutsUpdate::StatusUpdate { status },
        }
    }

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


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

pub struct GetInfoResponse


// Struct: RetrieveFileResponse
// File: crates/hyperswitch_domain_models/src/router_response_types.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct RetrieveFileResponse


// Struct: WellsfargoRefundRequest
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WellsfargoRefundRequest


// Function: add_entry_to_blocklist
// File: crates/openapi/src/routes/blocklist.rs
// Module: openapi

pub fn add_entry_to_blocklist()


// File: crates/router/src/services/pm_auth.rs
// Module: router
// Public functions: 1

use pm_auth::{
    consts,
    core::errors::ConnectorError,
    types::{self as pm_auth_types, api::BoxedConnectorIntegration, PaymentAuthRouterData},
};

use crate::{
    core::errors::{self},
    logger,
    routes::SessionState,
    services::{self},
};

pub async fn execute_connector_processing_step<'b, T, Req, Resp>(
    state: &'b SessionState,
    connector_integration: BoxedConnectorIntegration<'_, T, Req, Resp>,
    req: &'b PaymentAuthRouterData<T, Req, Resp>,
    connector: &pm_auth_types::PaymentMethodAuthConnectors,
) -> errors::CustomResult<PaymentAuthRouterData<T, Req, Resp>, ConnectorError>
where
    T: Clone + 'static,
    Req: Clone + 'static,
    Resp: Clone + 'static,
{
    let mut router_data = req.clone();

    let connector_request = connector_integration.build_request(req, connector)?;

    match connector_request {
        Some(request) => {
            logger::debug!(connector_request=?request);
            let response = services::api::call_connector_api(
                state,
                request,
                "execute_connector_processing_step",
            )
            .await;
            logger::debug!(connector_response=?response);
            match response {
                Ok(body) => {
                    let response = match body {
                        Ok(body) => {
                            let body = pm_auth_types::Response {
                                headers: body.headers,
                                response: body.response,
                                status_code: body.status_code,
                            };
                            let connector_http_status_code = Some(body.status_code);
                            let mut data =
                                connector_integration.handle_response(&router_data, body)?;
                            data.connector_http_status_code = connector_http_status_code;

                            data
                        }
                        Err(body) => {
                            let body = pm_auth_types::Response {
                                headers: body.headers,
                                response: body.response,
                                status_code: body.status_code,
                            };
                            router_data.connector_http_status_code = Some(body.status_code);

                            let error = match body.status_code {
                                500..=511 => connector_integration.get_5xx_error_response(body)?,
                                _ => connector_integration.get_error_response(body)?,
                            };

                            router_data.response = Err(error);

                            router_data
                        }
                    };
                    Ok(response)
                }
                Err(error) => {
                    if error.current_context().is_upstream_timeout() {
                        let error_response = pm_auth_types::ErrorResponse {
                            code: consts::REQUEST_TIMEOUT_ERROR_CODE.to_string(),
                            message: consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string(),
                            reason: Some(consts::REQUEST_TIMEOUT_ERROR_MESSAGE.to_string()),
                            status_code: 504,
                        };
                        router_data.response = Err(error_response);
                        router_data.connector_http_status_code = Some(504);
                        Ok(router_data)
                    } else {
                        Err(error.change_context(ConnectorError::ProcessingStepFailed(None)))
                    }
                }
            }
        }
        None => Ok(router_data),
    }
}


// Function: get_gift_card_connector_key
// File: crates/common_utils/src/id_type/global_id/payment.rs
// Module: common_utils

// Documentation: Generate a key for gift card connector

pub fn get_gift_card_connector_key(&self) -> String


// Struct: ThreedsInfo
// File: crates/hyperswitch_connectors/src/connectors/datatrans/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ThreedsInfo


// Function: profiles_list
// File: crates/router/src/routes/profiles.rs
// Module: router

pub fn profiles_list(
    state: web::Data<AppState>,
    req: HttpRequest,
    path: web::Path<common_utils::id_type::MerchantId>,
) -> HttpResponse


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

// Documentation: Panics if `json_payload` array does not contain one `GetRefundMetricRequest` element.

pub fn get_merchant_refund_metrics(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<[GetRefundMetricRequest; 1]>,
    ) -> impl Responder


// Function: create_access_token_key
// File: crates/common_utils/src/access_token.rs
// Module: common_utils

// Documentation: Create a key for fetching the access token from redis

pub fn create_access_token_key(
    merchant_id: &id_type::MerchantId,
    merchant_connector_id_or_connector_name: impl Display,
) -> String


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

pub struct PaymentIntentFilterValue


// Function: get_payment_method_billing
// File: crates/hyperswitch_domain_models/src/payment_address.rs
// Module: hyperswitch_domain_models

pub fn get_payment_method_billing(&self) -> Option<&Address>


// Struct: AuthenticationConnectorDetails
// File: crates/diesel_models/src/business_profile.rs
// Module: diesel_models
// Implementations: 0

pub struct AuthenticationConnectorDetails


// Struct: KlarnaErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/klarna/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct KlarnaErrorResponse


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

impl api::PaymentSession for for Tokenio


// Function: metadata_is_advanced_rule_for_payments
// File: crates/diesel_models/src/routing_algorithm.rs
// Module: diesel_models

pub fn metadata_is_advanced_rule_for_payments(&self) -> bool


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

pub struct AirwallexWebhookDataResource


// File: crates/router/src/types/api/connector_onboarding.rs
// Module: router

pub mod paypal;


// Struct: BamboraErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BamboraErrorResponse


// Struct: RefundResponseData
// File: crates/hyperswitch_connectors/src/connectors/boku/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RefundResponseData


// Struct: PaysafePaymentHandlesSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaysafePaymentHandlesSyncResponse


// Implementation: impl CommonStorageInterface for for Store
// File: crates/router/src/db.rs
// Module: router
// Methods: 3 total (0 public)

impl CommonStorageInterface for for Store


// Function: get_retry_feature_data
// File: crates/common_types/src/domain.rs
// Module: common_types

// Documentation: Retrieves the retry feature data if it exists.

pub fn get_retry_feature_data(&self) -> Option<RetryFeatureData>


// Struct: MerchantSurchargeConfigs
// File: crates/api_models/src/surcharge_decision_configs.rs
// Module: api_models
// Implementations: 0

pub struct MerchantSurchargeConfigs


// Function: get_payment_processor_token_id_from_payment_attempt
// File: crates/router/src/core/revenue_recovery/types.rs
// Module: router

pub fn get_payment_processor_token_id_from_payment_attempt(
    payment_attempt: &PaymentAttempt,
) -> Option<String>


// Struct: OperationProperties
// File: crates/router_derive/src/macros/operation.rs
// Module: router_derive
// Implementations: 0

pub struct OperationProperties